Home | History | Annotate | Line # | Download | only in sdmmc
if_bwfm_sdio.c revision 1.3
      1 /* $NetBSD: if_bwfm_sdio.c,v 1.3 2018/05/11 07:41:11 maya 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 void		 bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t);
     77 uint8_t		 bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t);
     78 uint32_t	 bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t);
     79 void		 bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t,
     80 		     uint8_t);
     81 void		 bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t,
     82 		     uint32_t);
     83 
     84 uint32_t	 bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t);
     85 void		 bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t,
     86 		     uint32_t);
     87 int		 bwfm_sdio_buscore_prepare(struct bwfm_softc *);
     88 void		 bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t);
     89 
     90 int		 bwfm_sdio_txcheck(struct bwfm_softc *);
     91 int		 bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *);
     92 int		 bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t);
     93 int		 bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *);
     94 
     95 struct bwfm_bus_ops bwfm_sdio_bus_ops = {
     96 	.bs_init = NULL,
     97 	.bs_stop = NULL,
     98 	.bs_txcheck = bwfm_sdio_txcheck,
     99 	.bs_txdata = bwfm_sdio_txdata,
    100 	.bs_txctl = bwfm_sdio_txctl,
    101 	.bs_rxctl = bwfm_sdio_rxctl,
    102 };
    103 
    104 struct bwfm_buscore_ops bwfm_sdio_buscore_ops = {
    105 	.bc_read = bwfm_sdio_buscore_read,
    106 	.bc_write = bwfm_sdio_buscore_write,
    107 	.bc_prepare = bwfm_sdio_buscore_prepare,
    108 	.bc_reset = NULL,
    109 	.bc_setup = NULL,
    110 	.bc_activate = bwfm_sdio_buscore_activate,
    111 };
    112 
    113 CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc),
    114     bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL);
    115 
    116 int
    117 bwfm_sdio_match(device_t parent, cfdata_t match, void *aux)
    118 {
    119 	struct sdmmc_attach_args *saa = aux;
    120 	struct sdmmc_function *sf = saa->sf;
    121 	struct sdmmc_cis *cis;
    122 
    123 	/* Not SDIO. */
    124 	if (sf == NULL)
    125 		return 0;
    126 
    127 	/* Look for Broadcom 433[04]. */
    128 	cis = &sf->sc->sc_fn0->cis;
    129 	if (cis->manufacturer != 0x02d0 || (cis->product != 0x4330 &&
    130 	    cis->product != 0x4334))
    131 		return 0;
    132 
    133 	/* We need both functions, but ... */
    134 	if (sf->sc->sc_function_count <= 1)
    135 		return 0;
    136 
    137 	/* ... only attach for one. */
    138 	if (sf->number != 1)
    139 		return 0;
    140 
    141 	return 1;
    142 }
    143 
    144 void
    145 bwfm_sdio_attach(device_t parent, device_t self, void *aux)
    146 {
    147 	struct bwfm_sdio_softc *sc = device_private(self);
    148 	struct sdmmc_attach_args *saa = aux;
    149 	struct sdmmc_function *sf = saa->sf;
    150 	struct bwfm_core *core;
    151 
    152 	aprint_naive("\n");
    153 
    154 	sc->sc_sf = malloc((sf->sc->sc_function_count + 1) *
    155 	    sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK);
    156 
    157 	/* Copy all function pointers. */
    158 	SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) {
    159 		sc->sc_sf[sf->number] = sf;
    160 	}
    161 	sf = saa->sf;
    162 
    163 	/*
    164 	 * TODO: set block size to 64 for func 1, 512 for func 2.
    165 	 * We might need to work on the SDMMC stack to be able to set
    166 	 * a block size per function.  Currently the IO code uses the
    167 	 * SDHC controller's maximum block length.
    168 	 */
    169 
    170 	/* Enable Function 1. */
    171 	if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) {
    172 		aprint_error_dev(self, "cannot enable function 1\n");
    173 		goto err;
    174 	}
    175 
    176 	DPRINTFN(2, ("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc),
    177 	    bwfm_sdio_read_4(sc, 0x18000000)));
    178 
    179 	/* Force PLL off */
    180 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR,
    181 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
    182 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ);
    183 
    184 	sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops;
    185 	if (bwfm_chip_attach(&sc->sc_sc) != 0) {
    186 		aprint_error_dev(self, "cannot attach chip\n");
    187 		goto err;
    188 	}
    189 
    190 	/* TODO: drive strength */
    191 
    192 	bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL,
    193 	    bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) |
    194 	    BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET);
    195 
    196 	core = bwfm_chip_get_pmu(&sc->sc_sc);
    197 	bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL,
    198 	    bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL) |
    199 	    (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD <<
    200 	     BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT));
    201 
    202 	sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops;
    203 	sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops;
    204 	bwfm_attach(&sc->sc_sc);
    205 
    206 	return;
    207 
    208 err:
    209 	free(sc->sc_sf, M_DEVBUF);
    210 }
    211 
    212 int
    213 bwfm_sdio_detach(struct device *self, int flags)
    214 {
    215 	struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self;
    216 
    217 	bwfm_detach(&sc->sc_sc, flags);
    218 
    219 	free(sc->sc_sf, M_DEVBUF);
    220 
    221 	return 0;
    222 }
    223 
    224 void
    225 bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t bar0)
    226 {
    227 	if (sc->sc_bar0 == bar0)
    228 		return;
    229 
    230 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW,
    231 	    (bar0 >>  8) & 0x80);
    232 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID,
    233 	    (bar0 >> 16) & 0xff);
    234 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH,
    235 	    (bar0 >> 24) & 0xff);
    236 	sc->sc_bar0 = bar0;
    237 }
    238 
    239 uint8_t
    240 bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr)
    241 {
    242 	struct sdmmc_function *sf;
    243 	uint8_t rv;
    244 
    245 	/*
    246 	 * figure out how to read the register based on address range
    247 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    248 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    249 	 * The rest: function 1 silicon backplane core registers
    250 	 */
    251 	if ((addr & ~0x7ff) == 0)
    252 		sf = sc->sc_sf[0];
    253 	else
    254 		sf = sc->sc_sf[1];
    255 
    256 	rv = sdmmc_io_read_1(sf, addr);
    257 	return rv;
    258 }
    259 
    260 uint32_t
    261 bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr)
    262 {
    263 	struct sdmmc_function *sf;
    264 	uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
    265 	uint32_t rv;
    266 
    267 	bwfm_sdio_backplane(sc, bar0);
    268 
    269 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
    270 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
    271 
    272 	/*
    273 	 * figure out how to read the register based on address range
    274 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    275 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    276 	 * The rest: function 1 silicon backplane core registers
    277 	 */
    278 	if ((addr & ~0x7ff) == 0)
    279 		sf = sc->sc_sf[0];
    280 	else
    281 		sf = sc->sc_sf[1];
    282 
    283 	rv = sdmmc_io_read_4(sf, addr);
    284 	return rv;
    285 }
    286 
    287 void
    288 bwfm_sdio_write_1(struct bwfm_sdio_softc *sc, uint32_t addr, uint8_t data)
    289 {
    290 	struct sdmmc_function *sf;
    291 
    292 	/*
    293 	 * figure out how to read the register based on address range
    294 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    295 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    296 	 * The rest: function 1 silicon backplane core registers
    297 	 */
    298 	if ((addr & ~0x7ff) == 0)
    299 		sf = sc->sc_sf[0];
    300 	else
    301 		sf = sc->sc_sf[1];
    302 
    303 	sdmmc_io_write_1(sf, addr, data);
    304 }
    305 
    306 void
    307 bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data)
    308 {
    309 	struct sdmmc_function *sf;
    310 	uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
    311 
    312 	bwfm_sdio_backplane(sc, bar0);
    313 
    314 	addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
    315 	addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
    316 
    317 	/*
    318 	 * figure out how to read the register based on address range
    319 	 * 0x00 ~ 0x7FF: function 0 CCCR and FBR
    320 	 * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
    321 	 * The rest: function 1 silicon backplane core registers
    322 	 */
    323 	if ((addr & ~0x7ff) == 0)
    324 		sf = sc->sc_sf[0];
    325 	else
    326 		sf = sc->sc_sf[1];
    327 
    328 	sdmmc_io_write_4(sf, addr, data);
    329 }
    330 
    331 uint32_t
    332 bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg)
    333 {
    334 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    335 	uint32_t val;
    336 
    337 	val = bwfm_sdio_read_4(sc, reg);
    338 	/* TODO: Workaround for 4335/4339 */
    339 
    340 	return val;
    341 }
    342 
    343 void
    344 bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val)
    345 {
    346 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    347 	bwfm_sdio_write_4(sc, reg, val);
    348 }
    349 
    350 int
    351 bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm)
    352 {
    353 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    354 	uint8_t clkval, clkset, clkmask;
    355 	int i;
    356 
    357 	clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ |
    358 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF;
    359 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
    360 
    361 	clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL |
    362 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL;
    363 	clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
    364 
    365 	if ((clkval & ~clkmask) != clkset) {
    366 		printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc),
    367 		    clkset, clkval);
    368 		return 1;
    369 	}
    370 
    371 	for (i = 1000; i > 0; i--) {
    372 		clkval = bwfm_sdio_read_1(sc,
    373 		    BWFM_SDIO_FUNC1_CHIPCLKCSR);
    374 		if (clkval & clkmask)
    375 			break;
    376 	}
    377 	if (i == 0) {
    378 		printf("%s: timeout on ALPAV wait, clkval 0x%02x\n",
    379 		    DEVNAME(sc), clkval);
    380 		return 1;
    381 	}
    382 
    383 	clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
    384 	    BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP;
    385 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
    386 	delay(65);
    387 
    388 	bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0);
    389 
    390 	return 0;
    391 }
    392 
    393 void
    394 bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec)
    395 {
    396 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    397 	struct bwfm_core *core;
    398 
    399 	core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV);
    400 	bwfm_sdio_buscore_write(&sc->sc_sc,
    401 	    core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF);
    402 
    403 #if notyet
    404 	if (rstvec)
    405 		bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec));
    406 #endif
    407 }
    408 
    409 int
    410 bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m)
    411 {
    412 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    413 
    414 	return 0;
    415 }
    416 
    417 
    418 int
    419 bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
    420 {
    421 #ifdef BWFM_DEBUG
    422 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    423 #endif
    424 	int ret = 1;
    425 
    426 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    427 
    428 	return ret;
    429 }
    430 
    431 int
    432 bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len)
    433 {
    434 #ifdef BWFM_DEBUG
    435 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    436 #endif
    437 	int ret = 1;
    438 
    439 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    440 
    441 	return ret;
    442 }
    443 
    444 int
    445 bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len)
    446 {
    447 #ifdef BWFM_DEBUG
    448 	struct bwfm_sdio_softc *sc = (void *)bwfm;
    449 #endif
    450 	int ret = 1;
    451 
    452 	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
    453 
    454 	return ret;
    455 }
    456