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