| @@ -1,244 +1,583 @@ | | | @@ -1,244 +1,583 @@ |
1 | /* $NetBSD: if_bwfm_sdio.c,v 1.3 2018/05/11 07:41:11 maya Exp $ */ | | 1 | /* $NetBSD: if_bwfm_sdio.c,v 1.4 2019/09/01 05:51:45 mlelstv Exp $ */ |
2 | /* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */ | | 2 | /* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */ |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2010-2016 Broadcom Corporation | | 4 | * Copyright (c) 2010-2016 Broadcom Corporation |
5 | * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se> | | 5 | * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se> |
6 | * | | 6 | * |
7 | * Permission to use, copy, modify, and/or distribute this software for any | | 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 | | 8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. | | 9 | * copyright notice and this permission notice appear in all copies. |
10 | * | | 10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 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 | | 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ | | 18 | */ |
19 | | | 19 | |
20 | #include <sys/param.h> | | 20 | #include <sys/param.h> |
21 | #include <sys/systm.h> | | 21 | #include <sys/systm.h> |
22 | #include <sys/buf.h> | | 22 | #include <sys/buf.h> |
| | | 23 | #include <sys/endian.h> |
23 | #include <sys/kernel.h> | | 24 | #include <sys/kernel.h> |
24 | #include <sys/malloc.h> | | 25 | #include <sys/malloc.h> |
25 | #include <sys/device.h> | | 26 | #include <sys/device.h> |
26 | #include <sys/queue.h> | | 27 | #include <sys/queue.h> |
27 | #include <sys/socket.h> | | 28 | #include <sys/socket.h> |
28 | #include <sys/mutex.h> | | 29 | #include <sys/mutex.h> |
29 | #include <sys/workqueue.h> | | | |
30 | #include <sys/pcq.h> | | | |
31 | | | 30 | |
32 | #include <net/bpf.h> | | 31 | #include <net/bpf.h> |
33 | #include <net/if.h> | | 32 | #include <net/if.h> |
34 | #include <net/if_dl.h> | | 33 | #include <net/if_dl.h> |
35 | #include <net/if_media.h> | | 34 | #include <net/if_media.h> |
36 | #include <net/if_ether.h> | | 35 | #include <net/if_ether.h> |
37 | | | 36 | |
38 | #include <netinet/in.h> | | 37 | #include <netinet/in.h> |
39 | | | 38 | |
| | | 39 | #include <dev/firmload.h> |
| | | 40 | |
40 | #include <net80211/ieee80211_var.h> | | 41 | #include <net80211/ieee80211_var.h> |
41 | | | 42 | |
| | | 43 | #include <dev/sdmmc/sdmmcdevs.h> |
42 | #include <dev/sdmmc/sdmmcvar.h> | | 44 | #include <dev/sdmmc/sdmmcvar.h> |
43 | | | 45 | |
44 | #include <dev/ic/bwfmvar.h> | | 46 | #include <dev/ic/bwfmvar.h> |
45 | #include <dev/ic/bwfmreg.h> | | 47 | #include <dev/ic/bwfmreg.h> |
46 | | | 48 | #include <dev/sdmmc/if_bwfm_sdio.h> |
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 | | | 49 | |
55 | #ifdef BWFM_DEBUG | | 50 | #ifdef BWFM_DEBUG |
56 | #define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) | | 51 | #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) | | 52 | #define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0) |
58 | static int bwfm_debug = 2; | | 53 | static int bwfm_debug = 2; |
59 | #else | | 54 | #else |
60 | #define DPRINTF(x) do { ; } while (0) | | 55 | #define DPRINTF(x) do { ; } while (0) |
61 | #define DPRINTFN(n, x) do { ; } while (0) | | 56 | #define DPRINTFN(n, x) do { ; } while (0) |
62 | #endif | | 57 | #endif |
63 | | | 58 | |
64 | #define DEVNAME(sc) device_xname((sc)->sc_sc.sc_dev) | | 59 | #define DEVNAME(sc) device_xname((sc)->sc_sc.sc_dev) |
65 | | | 60 | |
| | | 61 | enum bwfm_sdio_clkstate { |
| | | 62 | CLK_NONE, |
| | | 63 | CLK_SDONLY, |
| | | 64 | CLK_PENDING, |
| | | 65 | CLK_AVAIL |
| | | 66 | }; |
| | | 67 | |
66 | struct bwfm_sdio_softc { | | 68 | struct bwfm_sdio_softc { |
67 | struct bwfm_softc sc_sc; | | 69 | struct bwfm_softc sc_sc; |
| | | 70 | kmutex_t sc_lock; |
| | | 71 | kmutex_t sc_intr_lock; |
| | | 72 | |
| | | 73 | bool sc_bwfm_attached; |
| | | 74 | |
68 | struct sdmmc_function **sc_sf; | | 75 | struct sdmmc_function **sc_sf; |
69 | uint32_t sc_bar0; | | 76 | size_t sc_sf_size; |
| | | 77 | |
| | | 78 | uint32_t sc_bar0; |
| | | 79 | enum bwfm_sdio_clkstate sc_clkstate; |
| | | 80 | bool sc_sr_enabled; |
| | | 81 | bool sc_alp_only; |
| | | 82 | bool sc_sleeping; |
| | | 83 | |
| | | 84 | struct sdmmc_task sc_task; |
| | | 85 | bool sc_task_queued; |
| | | 86 | |
| | | 87 | uint8_t sc_tx_seq; |
| | | 88 | uint8_t sc_tx_max_seq; |
| | | 89 | int sc_tx_count; |
| | | 90 | MBUFQ_HEAD() sc_tx_queue; |
| | | 91 | |
| | | 92 | struct mbuf *sc_rxctl_queue; |
| | | 93 | kcondvar_t sc_rxctl_cv; |
| | | 94 | |
| | | 95 | void *sc_ih; |
| | | 96 | struct bwfm_core *sc_cc; |
| | | 97 | |
| | | 98 | char *sc_bounce_buf; |
| | | 99 | size_t sc_bounce_size; |
| | | 100 | |
| | | 101 | uint32_t sc_console_addr; |
| | | 102 | char *sc_console_buf; |
| | | 103 | size_t sc_console_buf_size; |
| | | 104 | uint32_t sc_console_readidx; |
70 | }; | | 105 | }; |
71 | | | 106 | |
72 | int bwfm_sdio_match(device_t, cfdata_t, void *); | | 107 | int bwfm_sdio_match(device_t, cfdata_t, void *); |
73 | void bwfm_sdio_attach(device_t, struct device *, void *); | | 108 | void bwfm_sdio_attach(device_t, struct device *, void *); |
74 | int bwfm_sdio_detach(device_t, int); | | 109 | int bwfm_sdio_detach(device_t, int); |
75 | | | 110 | void bwfm_sdio_attachhook(device_t); |
76 | void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); | | 111 | |
77 | uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); | | 112 | void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); |
78 | uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); | | 113 | uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); |
79 | void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, | | 114 | uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); |
| | | 115 | void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, |
80 | uint8_t); | | 116 | uint8_t); |
81 | void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, | | 117 | void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, |
| | | 118 | uint32_t); |
| | | 119 | |
| | | 120 | uint32_t bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t); |
| | | 121 | void bwfm_sdio_dev_write(struct bwfm_sdio_softc *, uint32_t, |
82 | uint32_t); | | 122 | uint32_t); |
83 | | | 123 | |
84 | uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); | | 124 | uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); |
85 | void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t, | | 125 | void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t, |
86 | uint32_t); | | 126 | uint32_t); |
87 | int bwfm_sdio_buscore_prepare(struct bwfm_softc *); | | 127 | int bwfm_sdio_buscore_prepare(struct bwfm_softc *); |
88 | void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); | | 128 | void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); |
89 | | | 129 | |
90 | int bwfm_sdio_txcheck(struct bwfm_softc *); | | 130 | int bwfm_sdio_buf_read(struct bwfm_sdio_softc *, |
91 | int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *); | | 131 | struct sdmmc_function *, uint32_t, char *, size_t); |
92 | int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t); | | 132 | int bwfm_sdio_buf_write(struct bwfm_sdio_softc *, |
93 | int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); | | 133 | struct sdmmc_function *, uint32_t, char *, size_t); |
| | | 134 | |
| | | 135 | struct mbuf *bwfm_sdio_newbuf(void); |
| | | 136 | void bwfm_qput(struct mbuf **, struct mbuf *); |
| | | 137 | struct mbuf *bwfm_qget(struct mbuf **); |
| | | 138 | |
| | | 139 | uint32_t bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *, |
| | | 140 | uint32_t, char *, size_t, int); |
| | | 141 | uint32_t bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *, |
| | | 142 | char *, size_t, int); |
| | | 143 | |
| | | 144 | int bwfm_sdio_intr(void *); |
| | | 145 | void bwfm_sdio_task(void *); |
| | | 146 | void bwfm_sdio_task1(struct bwfm_sdio_softc *); |
| | | 147 | |
| | | 148 | int bwfm_nvram_convert(u_char *, size_t, size_t *); |
| | | 149 | int bwfm_sdio_load_microcode(struct bwfm_sdio_softc *, |
| | | 150 | u_char *, size_t, u_char *, size_t); |
| | | 151 | void bwfm_sdio_clkctl(struct bwfm_sdio_softc *, |
| | | 152 | enum bwfm_sdio_clkstate, bool); |
| | | 153 | void bwfm_sdio_htclk(struct bwfm_sdio_softc *, bool, bool); |
| | | 154 | |
| | | 155 | int bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *, bool, bool); |
| | | 156 | void bwfm_sdio_readshared(struct bwfm_sdio_softc *); |
| | | 157 | |
| | | 158 | int bwfm_sdio_txcheck(struct bwfm_softc *); |
| | | 159 | int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf **); |
| | | 160 | int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t); |
| | | 161 | int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); |
| | | 162 | |
| | | 163 | int bwfm_sdio_tx_ok(struct bwfm_sdio_softc *); |
| | | 164 | void bwfm_sdio_tx_frames(struct bwfm_sdio_softc *); |
| | | 165 | void bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *, |
| | | 166 | struct mbuf *); |
| | | 167 | void bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *, |
| | | 168 | struct mbuf *); |
| | | 169 | |
| | | 170 | void bwfm_sdio_rx_frames(struct bwfm_sdio_softc *); |
| | | 171 | void bwfm_sdio_rx_glom(struct bwfm_sdio_softc *, |
| | | 172 | uint16_t *, int, uint16_t *); |
| | | 173 | |
| | | 174 | #ifdef BWFM_DEBUG |
| | | 175 | void bwfm_sdio_debug_console(struct bwfm_sdio_softc *); |
| | | 176 | #endif |
94 | | | 177 | |
95 | struct bwfm_bus_ops bwfm_sdio_bus_ops = { | | 178 | struct bwfm_bus_ops bwfm_sdio_bus_ops = { |
96 | .bs_init = NULL, | | 179 | .bs_init = NULL, |
97 | .bs_stop = NULL, | | 180 | .bs_stop = NULL, |
98 | .bs_txcheck = bwfm_sdio_txcheck, | | 181 | .bs_txcheck = bwfm_sdio_txcheck, |
99 | .bs_txdata = bwfm_sdio_txdata, | | 182 | .bs_txdata = bwfm_sdio_txdata, |
100 | .bs_txctl = bwfm_sdio_txctl, | | 183 | .bs_txctl = bwfm_sdio_txctl, |
101 | .bs_rxctl = bwfm_sdio_rxctl, | | 184 | .bs_rxctl = bwfm_sdio_rxctl, |
102 | }; | | 185 | }; |
103 | | | 186 | |
104 | struct bwfm_buscore_ops bwfm_sdio_buscore_ops = { | | 187 | struct bwfm_buscore_ops bwfm_sdio_buscore_ops = { |
105 | .bc_read = bwfm_sdio_buscore_read, | | 188 | .bc_read = bwfm_sdio_buscore_read, |
106 | .bc_write = bwfm_sdio_buscore_write, | | 189 | .bc_write = bwfm_sdio_buscore_write, |
107 | .bc_prepare = bwfm_sdio_buscore_prepare, | | 190 | .bc_prepare = bwfm_sdio_buscore_prepare, |
108 | .bc_reset = NULL, | | 191 | .bc_reset = NULL, |
109 | .bc_setup = NULL, | | 192 | .bc_setup = NULL, |
110 | .bc_activate = bwfm_sdio_buscore_activate, | | 193 | .bc_activate = bwfm_sdio_buscore_activate, |
111 | }; | | 194 | }; |
112 | | | 195 | |
113 | CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc), | | 196 | CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc), |
114 | bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL); | | 197 | bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL); |
115 | | | 198 | |
| | | 199 | static const struct bwfm_sdio_product { |
| | | 200 | uint32_t manufacturer; |
| | | 201 | uint32_t product; |
| | | 202 | const char *cisinfo[4]; |
| | | 203 | } bwfm_sdio_products[] = { |
| | | 204 | { |
| | | 205 | SDMMC_VENDOR_BROADCOM, |
| | | 206 | SDMMC_PRODUCT_BROADCOM_BCM4330, |
| | | 207 | SDMMC_CIS_BROADCOM_BCM4330 |
| | | 208 | }, |
| | | 209 | { |
| | | 210 | SDMMC_VENDOR_BROADCOM, |
| | | 211 | SDMMC_PRODUCT_BROADCOM_BCM4334, |
| | | 212 | SDMMC_CIS_BROADCOM_BCM4334 |
| | | 213 | }, |
| | | 214 | { |
| | | 215 | SDMMC_VENDOR_BROADCOM, |
| | | 216 | SDMMC_PRODUCT_BROADCOM_BCM43143, |
| | | 217 | SDMMC_CIS_BROADCOM_BCM43143 |
| | | 218 | }, |
| | | 219 | { |
| | | 220 | SDMMC_VENDOR_BROADCOM, |
| | | 221 | SDMMC_PRODUCT_BROADCOM_BCM43430, |
| | | 222 | SDMMC_CIS_BROADCOM_BCM43430 |
| | | 223 | }, |
| | | 224 | }; |
| | | 225 | |
116 | int | | 226 | int |
117 | bwfm_sdio_match(device_t parent, cfdata_t match, void *aux) | | 227 | bwfm_sdio_match(device_t parent, cfdata_t match, void *aux) |
118 | { | | 228 | { |
119 | struct sdmmc_attach_args *saa = aux; | | 229 | struct sdmmc_attach_args *saa = aux; |
120 | struct sdmmc_function *sf = saa->sf; | | 230 | struct sdmmc_function *sf = saa->sf; |
121 | struct sdmmc_cis *cis; | | 231 | struct sdmmc_cis *cis; |
| | | 232 | const struct bwfm_sdio_product *bsp; |
| | | 233 | int i; |
122 | | | 234 | |
123 | /* Not SDIO. */ | | 235 | /* Not SDIO. */ |
124 | if (sf == NULL) | | 236 | if (sf == NULL) |
125 | return 0; | | 237 | return 0; |
126 | | | 238 | |
127 | /* Look for Broadcom 433[04]. */ | | | |
128 | cis = &sf->sc->sc_fn0->cis; | | 239 | cis = &sf->sc->sc_fn0->cis; |
129 | if (cis->manufacturer != 0x02d0 || (cis->product != 0x4330 && | | 240 | for (i = 0; i < __arraycount(bwfm_sdio_products); ++i) { |
130 | cis->product != 0x4334)) | | 241 | bsp = &bwfm_sdio_products[i]; |
| | | 242 | if (cis->manufacturer == bsp->manufacturer && |
| | | 243 | cis->product == bsp->product) |
| | | 244 | break; |
| | | 245 | } |
| | | 246 | if (i >= __arraycount(bwfm_sdio_products)) |
131 | return 0; | | 247 | return 0; |
132 | | | 248 | |
133 | /* We need both functions, but ... */ | | 249 | /* We need both functions, but ... */ |
134 | if (sf->sc->sc_function_count <= 1) | | 250 | if (sf->sc->sc_function_count <= 1) |
135 | return 0; | | 251 | return 0; |
136 | | | 252 | |
137 | /* ... only attach for one. */ | | 253 | /* ... only attach for one. */ |
138 | if (sf->number != 1) | | 254 | if (sf->number != 1) |
139 | return 0; | | 255 | return 0; |
140 | | | 256 | |
141 | return 1; | | 257 | return 1; |
142 | } | | 258 | } |
143 | | | 259 | |
144 | void | | 260 | void |
145 | bwfm_sdio_attach(device_t parent, device_t self, void *aux) | | 261 | bwfm_sdio_attach(device_t parent, device_t self, void *aux) |
146 | { | | 262 | { |
147 | struct bwfm_sdio_softc *sc = device_private(self); | | 263 | struct bwfm_sdio_softc *sc = device_private(self); |
148 | struct sdmmc_attach_args *saa = aux; | | 264 | struct sdmmc_attach_args *saa = aux; |
149 | struct sdmmc_function *sf = saa->sf; | | 265 | struct sdmmc_function *sf = saa->sf; |
150 | struct bwfm_core *core; | | 266 | struct bwfm_core *core; |
| | | 267 | uint32_t reg; |
| | | 268 | |
| | | 269 | sc->sc_sc.sc_dev = self; |
151 | | | 270 | |
152 | aprint_naive("\n"); | | 271 | aprint_naive("\n"); |
| | | 272 | aprint_normal("\n"); |
153 | | | 273 | |
154 | sc->sc_sf = malloc((sf->sc->sc_function_count + 1) * | | 274 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); |
155 | sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK); | | 275 | cv_init(&sc->sc_rxctl_cv, "bwfmctl"); |
| | | 276 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); |
| | | 277 | |
| | | 278 | sdmmc_init_task(&sc->sc_task, bwfm_sdio_task, sc); |
| | | 279 | sc->sc_task_queued = false; |
| | | 280 | |
| | | 281 | sc->sc_bounce_size = 64 * 1024; |
| | | 282 | sc->sc_bounce_buf = kmem_alloc(sc->sc_bounce_size, KM_SLEEP); |
| | | 283 | sc->sc_tx_seq = 0xff; |
| | | 284 | MBUFQ_INIT(&sc->sc_tx_queue); |
| | | 285 | sc->sc_rxctl_queue = NULL; |
| | | 286 | |
| | | 287 | sc->sc_sf_size = (sf->sc->sc_function_count + 1) |
| | | 288 | * sizeof(struct sdmmc_function *); |
| | | 289 | sc->sc_sf = kmem_zalloc(sc->sc_sf_size, KM_SLEEP); |
156 | | | 290 | |
157 | /* Copy all function pointers. */ | | 291 | /* Copy all function pointers. */ |
158 | SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) { | | 292 | SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) { |
159 | sc->sc_sf[sf->number] = sf; | | 293 | sc->sc_sf[sf->number] = sf; |
160 | } | | 294 | } |
161 | sf = saa->sf; | | | |
162 | | | 295 | |
163 | /* | | 296 | sdmmc_io_set_blocklen(sc->sc_sf[1]->sc, sc->sc_sf[1], 64); |
164 | * TODO: set block size to 64 for func 1, 512 for func 2. | | 297 | sdmmc_io_set_blocklen(sc->sc_sf[2]->sc, sc->sc_sf[2], 512); |
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 | | | 298 | |
170 | /* Enable Function 1. */ | | 299 | /* Enable Function 1. */ |
171 | if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { | | 300 | if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { |
172 | aprint_error_dev(self, "cannot enable function 1\n"); | | 301 | printf("%s: cannot enable function 1\n", DEVNAME(sc)); |
173 | goto err; | | 302 | return; |
174 | } | | 303 | } |
175 | | | 304 | |
176 | DPRINTFN(2, ("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), | | 305 | DPRINTF(("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), |
177 | bwfm_sdio_read_4(sc, 0x18000000))); | | 306 | bwfm_sdio_read_4(sc, 0x18000000))); |
178 | | | 307 | |
179 | /* Force PLL off */ | | 308 | /* Force PLL off */ |
180 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, | | 309 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, |
181 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | | | 310 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | |
182 | BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ); | | 311 | BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ); |
183 | | | 312 | |
184 | sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops; | | 313 | sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops; |
185 | if (bwfm_chip_attach(&sc->sc_sc) != 0) { | | 314 | if (bwfm_chip_attach(&sc->sc_sc) != 0) { |
186 | aprint_error_dev(self, "cannot attach chip\n"); | | 315 | aprint_error_dev(self, "cannot attach chip\n"); |
187 | goto err; | | 316 | return; |
| | | 317 | } |
| | | 318 | |
| | | 319 | sc->sc_cc = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_CHIPCOMMON); |
| | | 320 | if (sc->sc_cc == NULL) { |
| | | 321 | aprint_error_dev(self, "cannot find chipcommon core\n"); |
| | | 322 | return; |
| | | 323 | } |
| | | 324 | |
| | | 325 | core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); |
| | | 326 | if (core->co_rev >= 12) { |
| | | 327 | reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR); |
| | | 328 | if ((reg & BWFM_SDIO_FUNC1_SLEEPCSR_KSO) == 0) { |
| | | 329 | reg |= BWFM_SDIO_FUNC1_SLEEPCSR_KSO; |
| | | 330 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR, reg); |
| | | 331 | } |
188 | } | | 332 | } |
189 | | | 333 | |
190 | /* TODO: drive strength */ | | 334 | /* TODO: drive strength */ |
191 | | | 335 | |
192 | bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL, | | 336 | bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCTRL, |
193 | bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) | | | 337 | bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_CARDCTRL) | |
194 | BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET); | | 338 | BWFM_SDIO_CCCR_CARDCTRL_WLANRESET); |
195 | | | 339 | |
196 | core = bwfm_chip_get_pmu(&sc->sc_sc); | | 340 | core = bwfm_chip_get_pmu(&sc->sc_sc); |
197 | bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL, | | 341 | 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) | | | 342 | bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL) | |
199 | (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD << | | 343 | (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD << |
200 | BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT)); | | 344 | BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT)); |
201 | | | 345 | |
| | | 346 | sdmmc_io_function_disable(sc->sc_sf[2]); |
| | | 347 | |
| | | 348 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); |
| | | 349 | sc->sc_clkstate = CLK_SDONLY; |
| | | 350 | |
| | | 351 | config_mountroot(self, bwfm_sdio_attachhook); |
| | | 352 | } |
| | | 353 | |
| | | 354 | void |
| | | 355 | bwfm_sdio_attachhook(device_t self) |
| | | 356 | { |
| | | 357 | struct bwfm_sdio_softc *sc = device_private(self); |
| | | 358 | struct bwfm_softc *bwfm = &sc->sc_sc; |
| | | 359 | firmware_handle_t fwh; |
| | | 360 | const char *name, *nvname; |
| | | 361 | u_char *ucode, *nvram; |
| | | 362 | size_t size, nvlen, nvsize; |
| | | 363 | uint32_t reg, clk; |
| | | 364 | int error; |
| | | 365 | |
| | | 366 | DPRINTF(("%s: chip 0x%08x rev %u\n", DEVNAME(sc), |
| | | 367 | bwfm->sc_chip.ch_chip, bwfm->sc_chip.ch_chiprev)); |
| | | 368 | switch (bwfm->sc_chip.ch_chip) { |
| | | 369 | case BRCM_CC_4330_CHIP_ID: |
| | | 370 | name = "brcmfmac4330-sdio.bin"; |
| | | 371 | nvname = "brcmfmac4330-sdio.txt"; |
| | | 372 | break; |
| | | 373 | case BRCM_CC_4334_CHIP_ID: |
| | | 374 | name = "brcmfmac4334-sdio.bin"; |
| | | 375 | nvname = "brcmfmac4334-sdio.txt"; |
| | | 376 | break; |
| | | 377 | case BRCM_CC_4345_CHIP_ID: |
| | | 378 | name = "brcmfmac43455-sdio.bin"; |
| | | 379 | nvname = "brcmfmac43455-sdio.txt"; |
| | | 380 | break; |
| | | 381 | case BRCM_CC_43340_CHIP_ID: |
| | | 382 | name = "brcmfmac43340-sdio.bin"; |
| | | 383 | nvname = "brcmfmac43340-sdio.txt"; |
| | | 384 | break; |
| | | 385 | case BRCM_CC_4335_CHIP_ID: |
| | | 386 | if (bwfm->sc_chip.ch_chiprev < 2) { |
| | | 387 | name = "brcmfmac4335-sdio.bin"; |
| | | 388 | nvname = "brcmfmac4335-sdio.txt"; |
| | | 389 | } else { |
| | | 390 | name = "brcmfmac4339-sdio.bin"; |
| | | 391 | nvname = "brcmfmac4339-sdio.txt"; |
| | | 392 | } |
| | | 393 | break; |
| | | 394 | case BRCM_CC_4339_CHIP_ID: |
| | | 395 | name = "brcmfmac4339-sdio.bin"; |
| | | 396 | nvname = "brcmfmac4339-sdio.txt"; |
| | | 397 | break; |
| | | 398 | case BRCM_CC_43430_CHIP_ID: |
| | | 399 | if (bwfm->sc_chip.ch_chiprev == 0) { |
| | | 400 | name = "brcmfmac43430a0-sdio.bin"; |
| | | 401 | nvname = "brcmfmac43430a0-sdio.txt"; |
| | | 402 | } else { |
| | | 403 | name = "brcmfmac43430-sdio.bin"; |
| | | 404 | nvname = "brcmfmac43430-sdio.txt"; |
| | | 405 | } |
| | | 406 | break; |
| | | 407 | case BRCM_CC_4356_CHIP_ID: |
| | | 408 | name = "brcmfmac4356-sdio.bin"; |
| | | 409 | nvname = "brcmfmac4356-sdio.txt"; |
| | | 410 | break; |
| | | 411 | default: |
| | | 412 | printf("%s: unknown firmware for chip %s\n", |
| | | 413 | DEVNAME(sc), bwfm->sc_chip.ch_name); |
| | | 414 | goto err; |
| | | 415 | } |
| | | 416 | |
| | | 417 | if (firmware_open("if_bwfm", name, &fwh) != 0) { |
| | | 418 | printf("%s: failed firmware_open of file %s\n", |
| | | 419 | DEVNAME(sc), name); |
| | | 420 | goto err; |
| | | 421 | } |
| | | 422 | size = firmware_get_size(fwh); |
| | | 423 | ucode = firmware_malloc(size); |
| | | 424 | if (ucode == NULL) { |
| | | 425 | printf("%s: failed firmware_open of file %s\n", |
| | | 426 | DEVNAME(sc), name); |
| | | 427 | firmware_close(fwh); |
| | | 428 | goto err; |
| | | 429 | } |
| | | 430 | error = firmware_read(fwh, 0, ucode, size); |
| | | 431 | firmware_close(fwh); |
| | | 432 | if (error != 0) { |
| | | 433 | printf("%s: failed to read firmware (error %d)\n", |
| | | 434 | DEVNAME(sc), error); |
| | | 435 | goto err1; |
| | | 436 | } |
| | | 437 | |
| | | 438 | if (firmware_open("if_bwfm", nvname, &fwh) != 0) { |
| | | 439 | printf("%s: failed firmware_open of file %s\n", |
| | | 440 | DEVNAME(sc), nvname); |
| | | 441 | goto err1; |
| | | 442 | } |
| | | 443 | nvlen = firmware_get_size(fwh); |
| | | 444 | nvram = firmware_malloc(nvlen); |
| | | 445 | if (nvram == NULL) { |
| | | 446 | printf("%s: failed firmware_open of file %s\n", |
| | | 447 | DEVNAME(sc), name); |
| | | 448 | firmware_close(fwh); |
| | | 449 | goto err1; |
| | | 450 | } |
| | | 451 | error = firmware_read(fwh, 0, nvram, nvlen); |
| | | 452 | firmware_close(fwh); |
| | | 453 | if (error != 0) { |
| | | 454 | printf("%s: failed to read firmware (error %d)\n", |
| | | 455 | DEVNAME(sc), error); |
| | | 456 | goto err2; |
| | | 457 | } |
| | | 458 | |
| | | 459 | if (bwfm_nvram_convert(nvram, nvlen, &nvsize)) { |
| | | 460 | printf("%s: failed to convert nvram\n", DEVNAME(sc)); |
| | | 461 | goto err2; |
| | | 462 | } |
| | | 463 | |
| | | 464 | sc->sc_alp_only = true; |
| | | 465 | if (bwfm_sdio_load_microcode(sc, ucode, size, nvram, nvsize) != 0) { |
| | | 466 | printf("%s: could not load microcode\n", |
| | | 467 | DEVNAME(sc)); |
| | | 468 | goto err2; |
| | | 469 | } |
| | | 470 | sc->sc_alp_only = false; |
| | | 471 | |
| | | 472 | firmware_free(nvram, nvlen); |
| | | 473 | firmware_free(ucode, size); |
| | | 474 | |
| | | 475 | bwfm_sdio_clkctl(sc, CLK_AVAIL, false); |
| | | 476 | if (sc->sc_clkstate != CLK_AVAIL) { |
| | | 477 | printf("%s: could not access clock\n", |
| | | 478 | DEVNAME(sc)); |
| | | 479 | goto err; |
| | | 480 | } |
| | | 481 | |
| | | 482 | clk = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); |
| | | 483 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, |
| | | 484 | clk | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); |
| | | 485 | |
| | | 486 | bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOXDATA, |
| | | 487 | SDPCM_PROT_VERSION << SDPCM_PROT_VERSION_SHIFT); |
| | | 488 | if (sdmmc_io_function_enable(sc->sc_sf[2])) { |
| | | 489 | printf("%s: cannot enable function 2\n", DEVNAME(sc)); |
| | | 490 | goto err; |
| | | 491 | } |
| | | 492 | |
| | | 493 | bwfm_sdio_dev_write(sc, SDPCMD_HOSTINTMASK, |
| | | 494 | SDPCMD_INTSTATUS_HMB_SW_MASK | SDPCMD_INTSTATUS_CHIPACTIVE); |
| | | 495 | bwfm_sdio_write_1(sc, BWFM_SDIO_WATERMARK, 8); |
| | | 496 | |
| | | 497 | if (bwfm_chip_sr_capable(bwfm)) { |
| | | 498 | reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL); |
| | | 499 | reg |= BWFM_SDIO_FUNC1_WAKEUPCTRL_HTWAIT; |
| | | 500 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL, reg); |
| | | 501 | bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCAP, |
| | | 502 | BWFM_SDIO_CCCR_CARDCAP_CMD14_SUPPORT | |
| | | 503 | BWFM_SDIO_CCCR_CARDCAP_CMD14_EXT); |
| | | 504 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, |
| | | 505 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); |
| | | 506 | sc->sc_sr_enabled = 1; |
| | | 507 | } else { |
| | | 508 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clk); |
| | | 509 | } |
| | | 510 | |
| | | 511 | sc->sc_ih = sdmmc_intr_establish(sc->sc_sc.sc_dev->dv_parent, |
| | | 512 | bwfm_sdio_intr, sc, DEVNAME(sc)); |
| | | 513 | if (sc->sc_ih == NULL) { |
| | | 514 | aprint_error_dev(self, "could not establish interrupt\n"); |
| | | 515 | bwfm_sdio_clkctl(sc, CLK_NONE, false); |
| | | 516 | return; |
| | | 517 | } |
| | | 518 | sdmmc_intr_enable(sc->sc_sf[1]); |
| | | 519 | |
202 | sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops; | | 520 | sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops; |
203 | sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops; | | 521 | sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops; |
204 | bwfm_attach(&sc->sc_sc); | | 522 | bwfm_attach(&sc->sc_sc); |
| | | 523 | sc->sc_bwfm_attached = true; |
205 | | | 524 | |
206 | return; | | 525 | return; |
207 | | | 526 | |
| | | 527 | err2: |
| | | 528 | firmware_free(nvram, nvlen); |
| | | 529 | err1: |
| | | 530 | firmware_free(ucode, size); |
208 | err: | | 531 | err: |
209 | free(sc->sc_sf, M_DEVBUF); | | 532 | return; |
210 | } | | 533 | } |
211 | | | 534 | |
212 | int | | 535 | int |
213 | bwfm_sdio_detach(struct device *self, int flags) | | 536 | bwfm_sdio_detach(struct device *self, int flags) |
214 | { | | 537 | { |
215 | struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; | | 538 | struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; |
216 | | | 539 | |
217 | bwfm_detach(&sc->sc_sc, flags); | | 540 | #ifdef BWFM_DEBUG |
| | | 541 | bwfm_sdio_debug_console(sc); |
| | | 542 | #endif |
| | | 543 | |
| | | 544 | if (sc->sc_ih) { |
| | | 545 | sdmmc_intr_disable(sc->sc_sf[1]); |
| | | 546 | sdmmc_intr_disestablish(sc->sc_ih); |
| | | 547 | } |
| | | 548 | if (sc->sc_bwfm_attached) |
| | | 549 | bwfm_detach(&sc->sc_sc, flags); |
218 | | | 550 | |
219 | free(sc->sc_sf, M_DEVBUF); | | 551 | kmem_free(sc->sc_sf, sc->sc_sf_size); |
| | | 552 | kmem_free(sc->sc_bounce_buf, sc->sc_bounce_size); |
| | | 553 | |
| | | 554 | mutex_destroy(&sc->sc_intr_lock); |
| | | 555 | cv_destroy(&sc->sc_rxctl_cv); |
| | | 556 | mutex_destroy(&sc->sc_lock); |
220 | | | 557 | |
221 | return 0; | | 558 | return 0; |
222 | } | | 559 | } |
223 | | | 560 | |
224 | void | | 561 | void |
225 | bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t bar0) | | 562 | bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t addr) |
226 | { | | 563 | { |
| | | 564 | uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; |
| | | 565 | |
227 | if (sc->sc_bar0 == bar0) | | 566 | if (sc->sc_bar0 == bar0) |
228 | return; | | 567 | return; |
229 | | | 568 | |
230 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW, | | 569 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW, |
231 | (bar0 >> 8) & 0x80); | | 570 | (bar0 >> 8) & 0xff); |
232 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID, | | 571 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID, |
233 | (bar0 >> 16) & 0xff); | | 572 | (bar0 >> 16) & 0xff); |
234 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH, | | 573 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH, |
235 | (bar0 >> 24) & 0xff); | | 574 | (bar0 >> 24) & 0xff); |
236 | sc->sc_bar0 = bar0; | | 575 | sc->sc_bar0 = bar0; |
237 | } | | 576 | } |
238 | | | 577 | |
239 | uint8_t | | 578 | uint8_t |
240 | bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr) | | 579 | bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr) |
241 | { | | 580 | { |
242 | struct sdmmc_function *sf; | | 581 | struct sdmmc_function *sf; |
243 | uint8_t rv; | | 582 | uint8_t rv; |
244 | | | 583 | |
| @@ -251,30 +590,29 @@ bwfm_sdio_read_1(struct bwfm_sdio_softc | | | @@ -251,30 +590,29 @@ bwfm_sdio_read_1(struct bwfm_sdio_softc |
251 | if ((addr & ~0x7ff) == 0) | | 590 | if ((addr & ~0x7ff) == 0) |
252 | sf = sc->sc_sf[0]; | | 591 | sf = sc->sc_sf[0]; |
253 | else | | 592 | else |
254 | sf = sc->sc_sf[1]; | | 593 | sf = sc->sc_sf[1]; |
255 | | | 594 | |
256 | rv = sdmmc_io_read_1(sf, addr); | | 595 | rv = sdmmc_io_read_1(sf, addr); |
257 | return rv; | | 596 | return rv; |
258 | } | | 597 | } |
259 | | | 598 | |
260 | uint32_t | | 599 | uint32_t |
261 | bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr) | | 600 | bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr) |
262 | { | | 601 | { |
263 | struct sdmmc_function *sf; | | 602 | struct sdmmc_function *sf; |
264 | uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; | | | |
265 | uint32_t rv; | | 603 | uint32_t rv; |
266 | | | 604 | |
267 | bwfm_sdio_backplane(sc, bar0); | | 605 | bwfm_sdio_backplane(sc, addr); |
268 | | | 606 | |
269 | addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; | | 607 | addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; |
270 | addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; | | 608 | addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; |
271 | | | 609 | |
272 | /* | | 610 | /* |
273 | * figure out how to read the register based on address range | | 611 | * figure out how to read the register based on address range |
274 | * 0x00 ~ 0x7FF: function 0 CCCR and FBR | | 612 | * 0x00 ~ 0x7FF: function 0 CCCR and FBR |
275 | * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers | | 613 | * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers |
276 | * The rest: function 1 silicon backplane core registers | | 614 | * The rest: function 1 silicon backplane core registers |
277 | */ | | 615 | */ |
278 | if ((addr & ~0x7ff) == 0) | | 616 | if ((addr & ~0x7ff) == 0) |
279 | sf = sc->sc_sf[0]; | | 617 | sf = sc->sc_sf[0]; |
280 | else | | 618 | else |
| @@ -297,159 +635,1186 @@ bwfm_sdio_write_1(struct bwfm_sdio_softc | | | @@ -297,159 +635,1186 @@ bwfm_sdio_write_1(struct bwfm_sdio_softc |
297 | */ | | 635 | */ |
298 | if ((addr & ~0x7ff) == 0) | | 636 | if ((addr & ~0x7ff) == 0) |
299 | sf = sc->sc_sf[0]; | | 637 | sf = sc->sc_sf[0]; |
300 | else | | 638 | else |
301 | sf = sc->sc_sf[1]; | | 639 | sf = sc->sc_sf[1]; |
302 | | | 640 | |
303 | sdmmc_io_write_1(sf, addr, data); | | 641 | sdmmc_io_write_1(sf, addr, data); |
304 | } | | 642 | } |
305 | | | 643 | |
306 | void | | 644 | void |
307 | bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data) | | 645 | bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data) |
308 | { | | 646 | { |
309 | struct sdmmc_function *sf; | | 647 | struct sdmmc_function *sf; |
310 | uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; | | | |
311 | | | 648 | |
312 | bwfm_sdio_backplane(sc, bar0); | | 649 | bwfm_sdio_backplane(sc, addr); |
313 | | | 650 | |
314 | addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; | | 651 | addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; |
315 | addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; | | 652 | addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; |
316 | | | 653 | |
317 | /* | | 654 | /* |
318 | * figure out how to read the register based on address range | | 655 | * figure out how to read the register based on address range |
319 | * 0x00 ~ 0x7FF: function 0 CCCR and FBR | | 656 | * 0x00 ~ 0x7FF: function 0 CCCR and FBR |
320 | * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers | | 657 | * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers |
321 | * The rest: function 1 silicon backplane core registers | | 658 | * The rest: function 1 silicon backplane core registers |
322 | */ | | 659 | */ |
323 | if ((addr & ~0x7ff) == 0) | | 660 | if ((addr & ~0x7ff) == 0) |
324 | sf = sc->sc_sf[0]; | | 661 | sf = sc->sc_sf[0]; |
325 | else | | 662 | else |
326 | sf = sc->sc_sf[1]; | | 663 | sf = sc->sc_sf[1]; |
327 | | | 664 | |
328 | sdmmc_io_write_4(sf, addr, data); | | 665 | sdmmc_io_write_4(sf, addr, data); |
329 | } | | 666 | } |
330 | | | 667 | |
| | | 668 | int |
| | | 669 | bwfm_sdio_buf_read(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf, |
| | | 670 | uint32_t reg, char *data, size_t size) |
| | | 671 | { |
| | | 672 | int err; |
| | | 673 | |
| | | 674 | KASSERT(((vaddr_t)data & 0x3) == 0); |
| | | 675 | KASSERT((size & 0x3) == 0); |
| | | 676 | |
| | | 677 | if (sf == sc->sc_sf[1]) |
| | | 678 | err = sdmmc_io_read_region_1(sf, reg, data, size); |
| | | 679 | else |
| | | 680 | err = sdmmc_io_read_multi_1(sf, reg, data, size); |
| | | 681 | |
| | | 682 | if (err) |
| | | 683 | printf("%s: error %d\n", __func__, err); |
| | | 684 | |
| | | 685 | return err; |
| | | 686 | } |
| | | 687 | |
| | | 688 | int |
| | | 689 | bwfm_sdio_buf_write(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf, |
| | | 690 | uint32_t reg, char *data, size_t size) |
| | | 691 | { |
| | | 692 | int err; |
| | | 693 | |
| | | 694 | KASSERT(((vaddr_t)data & 0x3) == 0); |
| | | 695 | KASSERT((size & 0x3) == 0); |
| | | 696 | |
| | | 697 | err = sdmmc_io_write_region_1(sf, reg, data, size); |
| | | 698 | |
| | | 699 | if (err) |
| | | 700 | printf("%s: error %d\n", __func__, err); |
| | | 701 | |
| | | 702 | return err; |
| | | 703 | } |
| | | 704 | |
| | | 705 | uint32_t |
| | | 706 | bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *sc, uint32_t reg, |
| | | 707 | char *data, size_t left, int write) |
| | | 708 | { |
| | | 709 | uint32_t sbaddr, sdaddr, off; |
| | | 710 | size_t size; |
| | | 711 | int err; |
| | | 712 | |
| | | 713 | err = off = 0; |
| | | 714 | while (left > 0) { |
| | | 715 | sbaddr = reg + off; |
| | | 716 | bwfm_sdio_backplane(sc, sbaddr); |
| | | 717 | |
| | | 718 | sdaddr = sbaddr & BWFM_SDIO_SB_OFT_ADDR_MASK; |
| | | 719 | size = ulmin(left, (BWFM_SDIO_SB_OFT_ADDR_PAGE - sdaddr)); |
| | | 720 | sdaddr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; |
| | | 721 | |
| | | 722 | if (write) { |
| | | 723 | memcpy(sc->sc_bounce_buf, data + off, size); |
| | | 724 | if (roundup(size, 4) != size) |
| | | 725 | memset(sc->sc_bounce_buf + size, 0, |
| | | 726 | roundup(size, 4) - size); |
| | | 727 | err = bwfm_sdio_buf_write(sc, sc->sc_sf[1], sdaddr, |
| | | 728 | sc->sc_bounce_buf, roundup(size, 4)); |
| | | 729 | } else { |
| | | 730 | err = bwfm_sdio_buf_read(sc, sc->sc_sf[1], sdaddr, |
| | | 731 | sc->sc_bounce_buf, roundup(size, 4)); |
| | | 732 | memcpy(data + off, sc->sc_bounce_buf, size); |
| | | 733 | } |
| | | 734 | if (err) |
| | | 735 | break; |
| | | 736 | |
| | | 737 | off += size; |
| | | 738 | left -= size; |
| | | 739 | } |
| | | 740 | |
| | | 741 | return err; |
| | | 742 | } |
| | | 743 | |
| | | 744 | uint32_t |
| | | 745 | bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *sc, |
| | | 746 | char *data, size_t size, int write) |
| | | 747 | { |
| | | 748 | uint32_t addr; |
| | | 749 | int err; |
| | | 750 | |
| | | 751 | addr = sc->sc_cc->co_base; |
| | | 752 | bwfm_sdio_backplane(sc, addr); |
| | | 753 | |
| | | 754 | addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; |
| | | 755 | addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; |
| | | 756 | |
| | | 757 | if (write) |
| | | 758 | err = bwfm_sdio_buf_write(sc, sc->sc_sf[2], addr, data, size); |
| | | 759 | else |
| | | 760 | err = bwfm_sdio_buf_read(sc, sc->sc_sf[2], addr, data, size); |
| | | 761 | |
| | | 762 | return err; |
| | | 763 | } |
| | | 764 | |
| | | 765 | uint32_t |
| | | 766 | bwfm_sdio_dev_read(struct bwfm_sdio_softc *sc, uint32_t reg) |
| | | 767 | { |
| | | 768 | struct bwfm_core *core; |
| | | 769 | uint32_t val; |
| | | 770 | |
| | | 771 | core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); |
| | | 772 | val = bwfm_sdio_read_4(sc, core->co_base + reg); |
| | | 773 | /* TODO: Workaround for 4335/4339 */ |
| | | 774 | |
| | | 775 | return val; |
| | | 776 | } |
| | | 777 | |
| | | 778 | void |
| | | 779 | bwfm_sdio_dev_write(struct bwfm_sdio_softc *sc, uint32_t reg, uint32_t val) |
| | | 780 | { |
| | | 781 | struct bwfm_core *core; |
| | | 782 | |
| | | 783 | core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); |
| | | 784 | bwfm_sdio_write_4(sc, core->co_base + reg, val); |
| | | 785 | } |
| | | 786 | |
331 | uint32_t | | 787 | uint32_t |
332 | bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) | | 788 | bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) |
333 | { | | 789 | { |
334 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 790 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
335 | uint32_t val; | | 791 | uint32_t val; |
336 | | | 792 | |
| | | 793 | mutex_enter(&sc->sc_lock); |
337 | val = bwfm_sdio_read_4(sc, reg); | | 794 | val = bwfm_sdio_read_4(sc, reg); |
338 | /* TODO: Workaround for 4335/4339 */ | | 795 | /* TODO: Workaround for 4335/4339 */ |
| | | 796 | mutex_exit(&sc->sc_lock); |
339 | | | 797 | |
340 | return val; | | 798 | return val; |
341 | } | | 799 | } |
342 | | | 800 | |
343 | void | | 801 | void |
344 | bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) | | 802 | bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) |
345 | { | | 803 | { |
346 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 804 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
| | | 805 | |
| | | 806 | mutex_enter(&sc->sc_lock); |
347 | bwfm_sdio_write_4(sc, reg, val); | | 807 | bwfm_sdio_write_4(sc, reg, val); |
| | | 808 | mutex_exit(&sc->sc_lock); |
348 | } | | 809 | } |
349 | | | 810 | |
350 | int | | 811 | int |
351 | bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm) | | 812 | bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm) |
352 | { | | 813 | { |
353 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 814 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
354 | uint8_t clkval, clkset, clkmask; | | 815 | uint8_t clkval, clkset, clkmask; |
355 | int i; | | 816 | int i, error = 0; |
| | | 817 | |
| | | 818 | mutex_enter(&sc->sc_lock); |
356 | | | 819 | |
357 | clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ | | | 820 | clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ | |
358 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF; | | 821 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF; |
359 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset); | | 822 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset); |
360 | | | 823 | |
361 | clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL | | | 824 | clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL | |
362 | BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL; | | 825 | BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL; |
363 | clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); | | 826 | clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); |
364 | | | 827 | |
365 | if ((clkval & ~clkmask) != clkset) { | | 828 | if ((clkval & ~clkmask) != clkset) { |
366 | printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc), | | 829 | printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc), |
367 | clkset, clkval); | | 830 | clkset, clkval); |
368 | return 1; | | 831 | error = 1; |
| | | 832 | goto done; |
369 | } | | 833 | } |
370 | | | 834 | |
371 | for (i = 1000; i > 0; i--) { | | 835 | for (i = 1000; i > 0; i--) { |
372 | clkval = bwfm_sdio_read_1(sc, | | 836 | clkval = bwfm_sdio_read_1(sc, |
373 | BWFM_SDIO_FUNC1_CHIPCLKCSR); | | 837 | BWFM_SDIO_FUNC1_CHIPCLKCSR); |
374 | if (clkval & clkmask) | | 838 | if (clkval & clkmask) |
375 | break; | | 839 | break; |
376 | } | | 840 | } |
377 | if (i == 0) { | | 841 | if (i == 0) { |
378 | printf("%s: timeout on ALPAV wait, clkval 0x%02x\n", | | 842 | printf("%s: timeout on ALPAV wait, clkval 0x%02x\n", |
379 | DEVNAME(sc), clkval); | | 843 | DEVNAME(sc), clkval); |
380 | return 1; | | 844 | error = 1; |
| | | 845 | goto done; |
381 | } | | 846 | } |
382 | | | 847 | |
383 | clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | | | 848 | clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | |
384 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP; | | 849 | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP; |
385 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset); | | 850 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset); |
386 | delay(65); | | 851 | delay(65); |
387 | | | 852 | |
388 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0); | | 853 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0); |
389 | | | 854 | |
390 | return 0; | | 855 | done: |
| | | 856 | mutex_exit(&sc->sc_lock); |
| | | 857 | |
| | | 858 | return error; |
391 | } | | 859 | } |
392 | | | 860 | |
393 | void | | 861 | void |
394 | bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) | | 862 | bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) |
395 | { | | 863 | { |
396 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 864 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
397 | struct bwfm_core *core; | | 865 | struct bwfm_core *core; |
398 | | | 866 | |
399 | core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); | | 867 | core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); |
400 | bwfm_sdio_buscore_write(&sc->sc_sc, | | 868 | bwfm_sdio_buscore_write(&sc->sc_sc, |
401 | core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF); | | 869 | core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF); |
402 | | | 870 | |
403 | #if notyet | | 871 | mutex_enter(&sc->sc_lock); |
404 | if (rstvec) | | 872 | if (rstvec) |
405 | bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec)); | | 873 | bwfm_sdio_ram_read_write(sc, 0, (char *)&rstvec, |
406 | #endif | | 874 | sizeof(rstvec), 1); |
| | | 875 | mutex_exit(&sc->sc_lock); |
| | | 876 | } |
| | | 877 | |
| | | 878 | struct mbuf * |
| | | 879 | bwfm_sdio_newbuf(void) |
| | | 880 | { |
| | | 881 | struct mbuf *m; |
| | | 882 | |
| | | 883 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
| | | 884 | if (m == NULL) |
| | | 885 | return NULL; |
| | | 886 | |
| | | 887 | MCLGET(m, M_DONTWAIT); |
| | | 888 | if (!(m->m_flags & M_EXT)) { |
| | | 889 | m_freem(m); |
| | | 890 | return NULL; |
| | | 891 | } |
| | | 892 | |
| | | 893 | m->m_len = m->m_pkthdr.len = MCLBYTES; |
| | | 894 | return m; |
| | | 895 | } |
| | | 896 | |
| | | 897 | struct mbuf * |
| | | 898 | bwfm_qget(struct mbuf **q) |
| | | 899 | { |
| | | 900 | struct mbuf *m = NULL; |
| | | 901 | |
| | | 902 | if (*q != NULL) { |
| | | 903 | m = *q; |
| | | 904 | *q = m->m_next; |
| | | 905 | m->m_next = NULL; |
| | | 906 | } |
| | | 907 | |
| | | 908 | return m; |
| | | 909 | } |
| | | 910 | |
| | | 911 | void |
| | | 912 | bwfm_qput(struct mbuf **q, struct mbuf *m) |
| | | 913 | { |
| | | 914 | |
| | | 915 | if (*q == NULL) |
| | | 916 | *q = m; |
| | | 917 | else |
| | | 918 | m_cat(*q, m); |
407 | } | | 919 | } |
408 | | | 920 | |
409 | int | | 921 | int |
410 | bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m) | | 922 | bwfm_sdio_txcheck(struct bwfm_softc *bwfm) |
411 | { | | 923 | { |
412 | DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); | | 924 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
| | | 925 | int error = 0; |
413 | | | 926 | |
414 | return 0; | | 927 | mutex_enter(&sc->sc_lock); |
| | | 928 | if (sc->sc_tx_count >= 64) |
| | | 929 | error = ENOBUFS; |
| | | 930 | mutex_exit(&sc->sc_lock); |
| | | 931 | |
| | | 932 | return error; |
415 | } | | 933 | } |
416 | | | 934 | |
417 | | | 935 | |
418 | int | | 936 | int |
419 | bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m) | | 937 | bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf **mp) |
420 | { | | 938 | { |
421 | #ifdef BWFM_DEBUG | | | |
422 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 939 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
423 | #endif | | | |
424 | int ret = 1; | | | |
425 | | | 940 | |
426 | DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); | | 941 | if (sc->sc_tx_count >= 64) { |
| | | 942 | printf("%s: tx count limit reached\n",DEVNAME(sc)); |
| | | 943 | return ENOBUFS; |
| | | 944 | } |
| | | 945 | |
| | | 946 | mutex_enter(&sc->sc_lock); |
| | | 947 | sc->sc_tx_count++; |
| | | 948 | MBUFQ_ENQUEUE(&sc->sc_tx_queue, *mp); |
| | | 949 | mutex_exit(&sc->sc_lock); |
427 | | | 950 | |
428 | return ret; | | 951 | bwfm_sdio_intr(sc); |
| | | 952 | |
| | | 953 | return 0; |
429 | } | | 954 | } |
430 | | | 955 | |
431 | int | | 956 | int |
432 | bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len) | | 957 | bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len) |
433 | { | | 958 | { |
434 | #ifdef BWFM_DEBUG | | | |
435 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 959 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
| | | 960 | struct mbuf *m; |
| | | 961 | |
| | | 962 | KASSERT(len <= MCLBYTES); |
| | | 963 | |
| | | 964 | MGET(m, M_DONTWAIT, MT_CONTROL); |
| | | 965 | if (m == NULL) |
| | | 966 | goto fail; |
| | | 967 | if (len > MLEN) { |
| | | 968 | MCLGET(m, M_DONTWAIT); |
| | | 969 | if (!(m->m_flags & M_EXT)) { |
| | | 970 | m_freem(m); |
| | | 971 | goto fail; |
| | | 972 | } |
| | | 973 | } |
| | | 974 | memcpy(mtod(m, char *), buf, len); |
| | | 975 | m->m_len = len; |
| | | 976 | |
| | | 977 | mutex_enter(&sc->sc_lock); |
| | | 978 | MBUFQ_ENQUEUE(&sc->sc_tx_queue, m); |
| | | 979 | mutex_exit(&sc->sc_lock); |
| | | 980 | |
| | | 981 | bwfm_sdio_intr(sc); |
| | | 982 | |
| | | 983 | return 0; |
| | | 984 | |
| | | 985 | fail: |
| | | 986 | return ENOBUFS; |
| | | 987 | } |
| | | 988 | |
| | | 989 | int |
| | | 990 | bwfm_nvram_convert(u_char *buf, size_t len, size_t *newlenp) |
| | | 991 | { |
| | | 992 | u_char *src, *dst, *end = buf + len; |
| | | 993 | bool skip = false; |
| | | 994 | size_t count = 0, pad; |
| | | 995 | uint32_t token; |
| | | 996 | |
| | | 997 | for (src = buf, dst = buf; src != end; ++src) { |
| | | 998 | if (*src == '\n') { |
| | | 999 | if (count > 0) |
| | | 1000 | *dst++ = '\0'; |
| | | 1001 | count = 0; |
| | | 1002 | skip = false; |
| | | 1003 | continue; |
| | | 1004 | } |
| | | 1005 | if (skip) |
| | | 1006 | continue; |
| | | 1007 | if (*src == '#' && count == 0) { |
| | | 1008 | skip = true; |
| | | 1009 | continue; |
| | | 1010 | } |
| | | 1011 | if (*src == '\r') |
| | | 1012 | continue; |
| | | 1013 | *dst++ = *src; |
| | | 1014 | ++count; |
| | | 1015 | } |
| | | 1016 | |
| | | 1017 | count = dst - buf; |
| | | 1018 | pad = roundup(count + 1, 4) - count; |
| | | 1019 | |
| | | 1020 | if (count + pad + sizeof(token) > len) |
| | | 1021 | return 1; |
| | | 1022 | |
| | | 1023 | memset(dst, 0, pad); |
| | | 1024 | count += pad; |
| | | 1025 | dst += pad; |
| | | 1026 | |
| | | 1027 | token = (count / 4) & 0xffff; |
| | | 1028 | token |= ~token << 16; |
| | | 1029 | token = htole32(token); |
| | | 1030 | |
| | | 1031 | memcpy(dst, &token, sizeof(token)); |
| | | 1032 | count += sizeof(token); |
| | | 1033 | |
| | | 1034 | *newlenp = count ; |
| | | 1035 | |
| | | 1036 | return 0; |
| | | 1037 | } |
| | | 1038 | |
| | | 1039 | int |
| | | 1040 | bwfm_sdio_load_microcode(struct bwfm_sdio_softc *sc, u_char *ucode, size_t size, |
| | | 1041 | u_char *nvram, size_t nvlen) |
| | | 1042 | { |
| | | 1043 | struct bwfm_softc *bwfm = &sc->sc_sc; |
| | | 1044 | char *verify = NULL; |
| | | 1045 | int err = 0; |
| | | 1046 | |
| | | 1047 | bwfm_sdio_clkctl(sc, CLK_AVAIL, false); |
| | | 1048 | |
| | | 1049 | DPRINTF(("ucode %zu bytes to 0x%08lx\n", size, |
| | | 1050 | (u_long)bwfm->sc_chip.ch_rambase)); |
| | | 1051 | /* Upload firmware */ |
| | | 1052 | err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase, |
| | | 1053 | ucode, size, 1); |
| | | 1054 | if (err) |
| | | 1055 | goto out; |
| | | 1056 | |
| | | 1057 | /* Verify firmware */ |
| | | 1058 | verify = kmem_zalloc(size, KM_SLEEP); |
| | | 1059 | err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase, |
| | | 1060 | verify, size, 0); |
| | | 1061 | if (err || memcmp(verify, ucode, size)) { |
| | | 1062 | printf("%s: firmware verification failed\n", |
| | | 1063 | DEVNAME(sc)); |
| | | 1064 | kmem_free(verify, size); |
| | | 1065 | goto out; |
| | | 1066 | } |
| | | 1067 | kmem_free(verify, size); |
| | | 1068 | |
| | | 1069 | DPRINTF(("nvram %zu bytes to 0x%08lx\n", nvlen, |
| | | 1070 | (u_long)bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize |
| | | 1071 | - nvlen)); |
| | | 1072 | /* Upload nvram */ |
| | | 1073 | err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase + |
| | | 1074 | bwfm->sc_chip.ch_ramsize - nvlen, nvram, nvlen, 1); |
| | | 1075 | if (err) |
| | | 1076 | goto out; |
| | | 1077 | |
| | | 1078 | /* Verify nvram */ |
| | | 1079 | verify = kmem_zalloc(nvlen, KM_SLEEP); |
| | | 1080 | err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase + |
| | | 1081 | bwfm->sc_chip.ch_ramsize - nvlen, verify, nvlen, 0); |
| | | 1082 | if (err || memcmp(verify, nvram, nvlen)) { |
| | | 1083 | printf("%s: nvram verification failed\n", |
| | | 1084 | DEVNAME(sc)); |
| | | 1085 | kmem_free(verify, nvlen); |
| | | 1086 | goto out; |
| | | 1087 | } |
| | | 1088 | kmem_free(verify, nvlen); |
| | | 1089 | |
| | | 1090 | DPRINTF(("Reset core 0x%08x\n", *(uint32_t *)ucode)); |
| | | 1091 | /* Load reset vector from firmware and kickstart core. */ |
| | | 1092 | bwfm_chip_set_active(bwfm, *(uint32_t *)ucode); |
| | | 1093 | |
| | | 1094 | out: |
| | | 1095 | bwfm_sdio_clkctl(sc, CLK_SDONLY, false); |
| | | 1096 | return err; |
| | | 1097 | } |
| | | 1098 | |
| | | 1099 | void |
| | | 1100 | bwfm_sdio_clkctl(struct bwfm_sdio_softc *sc, enum bwfm_sdio_clkstate newstate, |
| | | 1101 | bool pendok) |
| | | 1102 | { |
| | | 1103 | enum bwfm_sdio_clkstate oldstate; |
| | | 1104 | |
| | | 1105 | oldstate = sc->sc_clkstate; |
| | | 1106 | if (oldstate == newstate) |
| | | 1107 | return; |
| | | 1108 | |
| | | 1109 | switch (newstate) { |
| | | 1110 | case CLK_AVAIL: |
| | | 1111 | if (oldstate == CLK_NONE) |
| | | 1112 | sc->sc_clkstate = CLK_SDONLY; /* XXX */ |
| | | 1113 | bwfm_sdio_htclk(sc, true, pendok); |
| | | 1114 | break; |
| | | 1115 | case CLK_SDONLY: |
| | | 1116 | if (oldstate == CLK_NONE) |
| | | 1117 | sc->sc_clkstate = newstate; |
| | | 1118 | else if (oldstate == CLK_AVAIL) |
| | | 1119 | bwfm_sdio_htclk(sc, false, false); |
| | | 1120 | else |
| | | 1121 | printf("%s: clkctl %d -> %d\n", DEVNAME(sc), |
| | | 1122 | sc->sc_clkstate, newstate); |
| | | 1123 | break; |
| | | 1124 | case CLK_NONE: |
| | | 1125 | if (oldstate == CLK_AVAIL) |
| | | 1126 | bwfm_sdio_htclk(sc, false, false); |
| | | 1127 | sc->sc_clkstate = newstate; |
| | | 1128 | break; |
| | | 1129 | default: |
| | | 1130 | break; |
| | | 1131 | } |
| | | 1132 | |
| | | 1133 | DPRINTF(("%s: %d -> %d = %d\n", DEVNAME(sc), oldstate, newstate, |
| | | 1134 | sc->sc_clkstate)); |
| | | 1135 | } |
| | | 1136 | |
| | | 1137 | void |
| | | 1138 | bwfm_sdio_htclk(struct bwfm_sdio_softc *sc, bool on, bool pendok) |
| | | 1139 | { |
| | | 1140 | uint32_t clkctl, devctl, req; |
| | | 1141 | int i; |
| | | 1142 | |
| | | 1143 | if (sc->sc_sr_enabled) { |
| | | 1144 | if (on) |
| | | 1145 | sc->sc_clkstate = CLK_AVAIL; |
| | | 1146 | else |
| | | 1147 | sc->sc_clkstate = CLK_SDONLY; |
| | | 1148 | return; |
| | | 1149 | } |
| | | 1150 | |
| | | 1151 | if (on) { |
| | | 1152 | if (sc->sc_alp_only) |
| | | 1153 | req = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ; |
| | | 1154 | else |
| | | 1155 | req = BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ; |
| | | 1156 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, req); |
| | | 1157 | |
| | | 1158 | clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); |
| | | 1159 | if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only) |
| | | 1160 | && pendok) { |
| | | 1161 | devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); |
| | | 1162 | devctl |= BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; |
| | | 1163 | bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); |
| | | 1164 | sc->sc_clkstate = CLK_PENDING; |
| | | 1165 | return; |
| | | 1166 | } else if (sc->sc_clkstate == CLK_PENDING) { |
| | | 1167 | devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); |
| | | 1168 | devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; |
| | | 1169 | bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); |
| | | 1170 | } |
| | | 1171 | |
| | | 1172 | for (i = 0; i < 5000; i++) { |
| | | 1173 | if (BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, |
| | | 1174 | sc->sc_alp_only)) |
| | | 1175 | break; |
| | | 1176 | clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR |
| | | 1177 | ); |
| | | 1178 | delay(1000); |
| | | 1179 | } |
| | | 1180 | if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only)) |
| | | 1181 | { |
| | | 1182 | printf("%s: HT avail timeout\n", DEVNAME(sc)); |
| | | 1183 | return; |
| | | 1184 | } |
| | | 1185 | |
| | | 1186 | sc->sc_clkstate = CLK_AVAIL; |
| | | 1187 | } else { |
| | | 1188 | if (sc->sc_clkstate == CLK_PENDING) { |
| | | 1189 | devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); |
| | | 1190 | devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; |
| | | 1191 | bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); |
| | | 1192 | } |
| | | 1193 | sc->sc_clkstate = CLK_SDONLY; |
| | | 1194 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); |
| | | 1195 | } |
| | | 1196 | } |
| | | 1197 | |
| | | 1198 | #if notyet |
| | | 1199 | int |
| | | 1200 | bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *sc, bool sleep, bool pendok) |
| | | 1201 | { |
| | | 1202 | uint32_t clkctl; |
| | | 1203 | |
| | | 1204 | if (sc->sleeping == sleep) |
| | | 1205 | return 0; |
| | | 1206 | |
| | | 1207 | if (sc->sc_sr_enabled) { |
| | | 1208 | if (sleep) { |
| | | 1209 | clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); |
| | | 1210 | if ((clkctl & BWFM_SDIO_FUNC1_CHIPCLKCSR_CSR_MASK) == 0) |
| | | 1211 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ); |
| | | 1212 | } |
| | | 1213 | /* kso_ctrl(sc, sleep) */ |
| | | 1214 | } |
| | | 1215 | |
| | | 1216 | if (sleep) { |
| | | 1217 | if (!sc->sc_sr_enabled) |
| | | 1218 | bwfm_sdio_clkctl(sc, CLK_NONE, pendok); |
| | | 1219 | } else { |
| | | 1220 | bwfm_sdio_clkctl(sc, CLK_AVAIL, pendok); |
| | | 1221 | } |
| | | 1222 | |
| | | 1223 | sc->sleeping = sleep; |
| | | 1224 | |
| | | 1225 | return 0; |
| | | 1226 | } |
436 | #endif | | 1227 | #endif |
437 | int ret = 1; | | | |
438 | | | 1228 | |
439 | DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); | | 1229 | void |
| | | 1230 | bwfm_sdio_readshared(struct bwfm_sdio_softc *sc) |
| | | 1231 | { |
| | | 1232 | struct bwfm_softc *bwfm = &sc->sc_sc; |
| | | 1233 | struct bwfm_sdio_sdpcm sdpcm; |
| | | 1234 | uint32_t addr, shaddr; |
| | | 1235 | int err; |
| | | 1236 | |
| | | 1237 | bwfm_sdio_clkctl(sc, CLK_AVAIL, false); |
| | | 1238 | if (sc->sc_clkstate != CLK_AVAIL) |
| | | 1239 | return; |
| | | 1240 | |
| | | 1241 | shaddr = bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4; |
| | | 1242 | if (!bwfm->sc_chip.ch_rambase && sc->sc_sr_enabled) |
| | | 1243 | shaddr -= bwfm->sc_chip.ch_srsize; |
| | | 1244 | |
| | | 1245 | err = bwfm_sdio_ram_read_write(sc, shaddr, (char *)&addr, |
| | | 1246 | sizeof(addr), 0); |
| | | 1247 | if (err) |
| | | 1248 | return; |
440 | | | 1249 | |
441 | return ret; | | 1250 | addr = le32toh(addr); |
| | | 1251 | if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) |
| | | 1252 | return; |
| | | 1253 | |
| | | 1254 | err = bwfm_sdio_ram_read_write(sc, addr, (char *)&sdpcm, |
| | | 1255 | sizeof(sdpcm), 0); |
| | | 1256 | if (err) |
| | | 1257 | return; |
| | | 1258 | |
| | | 1259 | sc->sc_console_addr = le32toh(sdpcm.console_addr); |
442 | } | | 1260 | } |
443 | | | 1261 | |
444 | int | | 1262 | int |
445 | bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len) | | 1263 | bwfm_sdio_intr(void *v) |
446 | { | | 1264 | { |
| | | 1265 | struct bwfm_sdio_softc *sc = (void *)v; |
| | | 1266 | |
| | | 1267 | DPRINTF(("%s: sdio_intr\n", DEVNAME(sc))); |
| | | 1268 | |
| | | 1269 | mutex_enter(&sc->sc_intr_lock); |
| | | 1270 | if (!sc->sc_task_queued) { |
| | | 1271 | sc->sc_task_queued = true; |
| | | 1272 | sdmmc_add_task(sc->sc_sf[1]->sc, &sc->sc_task); |
| | | 1273 | } |
| | | 1274 | mutex_exit(&sc->sc_intr_lock); |
| | | 1275 | return 1; |
| | | 1276 | } |
| | | 1277 | |
| | | 1278 | void |
| | | 1279 | bwfm_sdio_task(void *v) |
| | | 1280 | { |
| | | 1281 | struct bwfm_sdio_softc *sc = (void *)v; |
| | | 1282 | |
| | | 1283 | mutex_enter(&sc->sc_intr_lock); |
| | | 1284 | while (sc->sc_task_queued) { |
| | | 1285 | sc->sc_task_queued = false; |
| | | 1286 | mutex_exit(&sc->sc_intr_lock); |
| | | 1287 | |
| | | 1288 | mutex_enter(&sc->sc_lock); |
| | | 1289 | bwfm_sdio_task1(sc); |
447 | #ifdef BWFM_DEBUG | | 1290 | #ifdef BWFM_DEBUG |
448 | struct bwfm_sdio_softc *sc = (void *)bwfm; | | 1291 | bwfm_sdio_debug_console(sc); |
449 | #endif | | 1292 | #endif |
450 | int ret = 1; | | 1293 | mutex_exit(&sc->sc_lock); |
| | | 1294 | |
| | | 1295 | mutex_enter(&sc->sc_intr_lock); |
| | | 1296 | } |
| | | 1297 | mutex_exit(&sc->sc_intr_lock); |
| | | 1298 | } |
| | | 1299 | |
| | | 1300 | void |
| | | 1301 | bwfm_sdio_task1(struct bwfm_sdio_softc *sc) |
| | | 1302 | { |
| | | 1303 | uint32_t clkctl, devctl, intstat, hostint; |
| | | 1304 | |
| | | 1305 | if (!sc->sc_sr_enabled && sc->sc_clkstate == CLK_PENDING) { |
| | | 1306 | clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); |
| | | 1307 | if (BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(clkctl)) { |
| | | 1308 | devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); |
| | | 1309 | devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; |
| | | 1310 | bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); |
| | | 1311 | sc->sc_clkstate = CLK_AVAIL; |
| | | 1312 | } |
| | | 1313 | } |
| | | 1314 | |
| | | 1315 | intstat = bwfm_sdio_dev_read(sc, BWFM_SDPCMD_INTSTATUS); |
| | | 1316 | DPRINTF(("%s: intstat 0x%" PRIx32 "\n", DEVNAME(sc), intstat)); |
| | | 1317 | intstat &= (SDPCMD_INTSTATUS_HMB_SW_MASK|SDPCMD_INTSTATUS_CHIPACTIVE); |
| | | 1318 | /* XXX fc state */ |
| | | 1319 | if (intstat) |
| | | 1320 | bwfm_sdio_dev_write(sc, BWFM_SDPCMD_INTSTATUS, intstat); |
| | | 1321 | |
| | | 1322 | if (intstat & SDPCMD_INTSTATUS_HMB_HOST_INT) { |
| | | 1323 | hostint = bwfm_sdio_dev_read(sc, SDPCMD_TOHOSTMAILBOXDATA); |
| | | 1324 | DPRINTF(("%s: hostint 0x%" PRIx32 "\n", DEVNAME(sc), hostint)); |
| | | 1325 | bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOX, |
| | | 1326 | SDPCMD_TOSBMAILBOX_INT_ACK); |
| | | 1327 | if (hostint & SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED) |
| | | 1328 | intstat |= SDPCMD_INTSTATUS_HMB_FRAME_IND; |
| | | 1329 | if (hostint & SDPCMD_TOHOSTMAILBOXDATA_DEVREADY || |
| | | 1330 | hostint & SDPCMD_TOHOSTMAILBOXDATA_FWREADY) |
| | | 1331 | bwfm_sdio_readshared(sc); |
| | | 1332 | } |
| | | 1333 | |
| | | 1334 | /* FIXME: Might stall if we don't when not set. */ |
| | | 1335 | if (intstat & SDPCMD_INTSTATUS_HMB_FRAME_IND) |
| | | 1336 | bwfm_sdio_rx_frames(sc); |
| | | 1337 | |
| | | 1338 | if (MBUFQ_FIRST(&sc->sc_tx_queue)) |
| | | 1339 | bwfm_sdio_tx_frames(sc); |
| | | 1340 | } |
| | | 1341 | |
| | | 1342 | int |
| | | 1343 | bwfm_sdio_tx_ok(struct bwfm_sdio_softc *sc) |
| | | 1344 | { |
| | | 1345 | return (uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) != 0 && |
| | | 1346 | ((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) & 0x80) == 0; |
| | | 1347 | } |
| | | 1348 | |
| | | 1349 | void |
| | | 1350 | bwfm_sdio_tx_frames(struct bwfm_sdio_softc *sc) |
| | | 1351 | { |
| | | 1352 | struct mbuf *m; |
| | | 1353 | struct ifnet *ifp = sc->sc_sc.sc_ic.ic_ifp; |
| | | 1354 | bool ifstart = false; |
| | | 1355 | int i; |
| | | 1356 | |
| | | 1357 | if (!bwfm_sdio_tx_ok(sc)) |
| | | 1358 | return; |
| | | 1359 | |
| | | 1360 | i = uimin((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq), 32); |
| | | 1361 | while (i--) { |
| | | 1362 | MBUFQ_DEQUEUE(&sc->sc_tx_queue, m); |
| | | 1363 | if (m == NULL) |
| | | 1364 | break; |
| | | 1365 | |
| | | 1366 | if (m->m_type == MT_CONTROL) |
| | | 1367 | bwfm_sdio_tx_ctrlframe(sc, m); |
| | | 1368 | else { |
| | | 1369 | bwfm_sdio_tx_dataframe(sc, m); |
| | | 1370 | ifp->if_opackets++; |
| | | 1371 | ifstart = true; |
| | | 1372 | } |
| | | 1373 | |
| | | 1374 | m_freem(m); |
| | | 1375 | } |
| | | 1376 | |
| | | 1377 | if (ifstart) { |
| | | 1378 | ifp->if_flags &= ~IFF_OACTIVE; |
| | | 1379 | if_schedule_deferred_start(ifp); |
| | | 1380 | } |
| | | 1381 | } |
| | | 1382 | |
| | | 1383 | void |
| | | 1384 | bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *sc, struct mbuf *m) |
| | | 1385 | { |
| | | 1386 | struct bwfm_sdio_hwhdr *hwhdr; |
| | | 1387 | struct bwfm_sdio_swhdr *swhdr; |
| | | 1388 | size_t len, roundto; |
| | | 1389 | |
| | | 1390 | len = sizeof(*hwhdr) + sizeof(*swhdr) + m->m_len; |
| | | 1391 | |
| | | 1392 | /* Zero-pad to either block-size or 4-byte alignment. */ |
| | | 1393 | if (len > 512 && (len % 512) != 0) |
| | | 1394 | roundto = 512; |
| | | 1395 | else |
| | | 1396 | roundto = 4; |
| | | 1397 | |
| | | 1398 | KASSERT(roundup(len, roundto) <= sc->sc_bounce_size); |
| | | 1399 | |
| | | 1400 | hwhdr = (void *)sc->sc_bounce_buf; |
| | | 1401 | hwhdr->frmlen = htole16(len); |
| | | 1402 | hwhdr->cksum = htole16(~len); |
| | | 1403 | |
| | | 1404 | swhdr = (void *)&hwhdr[1]; |
| | | 1405 | swhdr->seqnr = sc->sc_tx_seq++; |
| | | 1406 | swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_CONTROL; |
| | | 1407 | swhdr->nextlen = 0; |
| | | 1408 | swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); |
| | | 1409 | swhdr->maxseqnr = 0; |
| | | 1410 | |
| | | 1411 | m_copydata(m, 0, m->m_len, &swhdr[1]); |
| | | 1412 | |
| | | 1413 | if (roundup(len, roundto) != len) |
| | | 1414 | memset(sc->sc_bounce_buf + len, 0, |
| | | 1415 | roundup(len, roundto) - len); |
| | | 1416 | |
| | | 1417 | bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, |
| | | 1418 | roundup(len, roundto), 1); |
| | | 1419 | } |
| | | 1420 | |
| | | 1421 | void |
| | | 1422 | bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *sc, struct mbuf *m) |
| | | 1423 | { |
| | | 1424 | struct bwfm_sdio_hwhdr *hwhdr; |
| | | 1425 | struct bwfm_sdio_swhdr *swhdr; |
| | | 1426 | struct bwfm_proto_bcdc_hdr *bcdc; |
| | | 1427 | size_t len, roundto; |
| | | 1428 | |
| | | 1429 | len = sizeof(*hwhdr) + sizeof(*swhdr) + sizeof(*bcdc) |
| | | 1430 | + m->m_pkthdr.len; |
| | | 1431 | |
| | | 1432 | /* Zero-pad to either block-size or 4-byte alignment. */ |
| | | 1433 | if (len > 512 && (len % 512) != 0) |
| | | 1434 | roundto = 512; |
| | | 1435 | else |
| | | 1436 | roundto = 4; |
| | | 1437 | |
| | | 1438 | KASSERT(roundup(len, roundto) <= sc->sc_bounce_size); |
| | | 1439 | |
| | | 1440 | hwhdr = (void *)sc->sc_bounce_buf; |
| | | 1441 | hwhdr->frmlen = htole16(len); |
| | | 1442 | hwhdr->cksum = htole16(~len); |
| | | 1443 | |
| | | 1444 | swhdr = (void *)&hwhdr[1]; |
| | | 1445 | swhdr->seqnr = sc->sc_tx_seq++; |
| | | 1446 | swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_DATA; |
| | | 1447 | swhdr->nextlen = 0; |
| | | 1448 | swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); |
| | | 1449 | swhdr->maxseqnr = 0; |
| | | 1450 | |
| | | 1451 | bcdc = (void *)&swhdr[1]; |
| | | 1452 | bcdc->data_offset = 0; |
| | | 1453 | bcdc->priority = WME_AC_BE; |
| | | 1454 | bcdc->flags = BWFM_BCDC_FLAG_VER(BWFM_BCDC_FLAG_PROTO_VER); |
| | | 1455 | bcdc->flags2 = 0; |
| | | 1456 | |
| | | 1457 | m_copydata(m, 0, m->m_pkthdr.len, &bcdc[1]); |
| | | 1458 | |
| | | 1459 | if (roundup(len, roundto) != len) |
| | | 1460 | memset(sc->sc_bounce_buf + len, 0, |
| | | 1461 | roundup(len, roundto) - len); |
| | | 1462 | |
| | | 1463 | bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, |
| | | 1464 | roundup(len, roundto), 1); |
| | | 1465 | |
| | | 1466 | sc->sc_tx_count--; |
| | | 1467 | } |
| | | 1468 | |
| | | 1469 | int |
| | | 1470 | bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *lenp) |
| | | 1471 | { |
| | | 1472 | struct bwfm_sdio_softc *sc = (void *)bwfm; |
| | | 1473 | struct mbuf *m; |
| | | 1474 | int err = 0; |
| | | 1475 | |
| | | 1476 | mutex_enter(&sc->sc_lock); |
| | | 1477 | while ((m = bwfm_qget(&sc->sc_rxctl_queue)) == NULL) { |
| | | 1478 | err = cv_timedwait(&sc->sc_rxctl_cv, &sc->sc_lock, |
| | | 1479 | mstohz(5000)); |
| | | 1480 | if (err == EWOULDBLOCK) |
| | | 1481 | break; |
| | | 1482 | } |
| | | 1483 | mutex_exit(&sc->sc_lock); |
| | | 1484 | |
| | | 1485 | if (err) |
| | | 1486 | return 1; |
| | | 1487 | |
| | | 1488 | if (m->m_len < *lenp) { |
| | | 1489 | m_freem(m); |
| | | 1490 | return 1; |
| | | 1491 | } |
| | | 1492 | |
| | | 1493 | *lenp = m->m_len; |
| | | 1494 | m_copydata(m, 0, m->m_len, buf); |
| | | 1495 | m_freem(m); |
| | | 1496 | return 0; |
| | | 1497 | } |
| | | 1498 | |
| | | 1499 | void |
| | | 1500 | bwfm_sdio_rx_frames(struct bwfm_sdio_softc *sc) |
| | | 1501 | { |
| | | 1502 | struct bwfm_sdio_hwhdr *hwhdr; |
| | | 1503 | struct bwfm_sdio_swhdr *swhdr; |
| | | 1504 | struct bwfm_proto_bcdc_hdr *bcdc; |
| | | 1505 | uint16_t *sublen, nextlen = 0; |
| | | 1506 | struct mbuf *m; |
| | | 1507 | size_t flen, off, hoff; |
| | | 1508 | char *data; |
| | | 1509 | int nsub; |
| | | 1510 | |
| | | 1511 | hwhdr = (struct bwfm_sdio_hwhdr *)sc->sc_bounce_buf; |
| | | 1512 | swhdr = (struct bwfm_sdio_swhdr *)&hwhdr[1]; |
| | | 1513 | data = (char *)&swhdr[1]; |
| | | 1514 | |
| | | 1515 | for (;;) { |
| | | 1516 | /* If we know the next size, just read ahead. */ |
| | | 1517 | if (nextlen) { |
| | | 1518 | if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, |
| | | 1519 | nextlen, 0)) |
| | | 1520 | break; |
| | | 1521 | } else { |
| | | 1522 | if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, |
| | | 1523 | sizeof(*hwhdr) + sizeof(*swhdr), 0)) |
| | | 1524 | break; |
| | | 1525 | } |
| | | 1526 | |
| | | 1527 | hwhdr->frmlen = le16toh(hwhdr->frmlen); |
| | | 1528 | hwhdr->cksum = le16toh(hwhdr->cksum); |
| | | 1529 | |
| | | 1530 | if (hwhdr->frmlen == 0 && hwhdr->cksum == 0) |
| | | 1531 | break; |
| | | 1532 | |
| | | 1533 | if ((hwhdr->frmlen ^ hwhdr->cksum) != 0xffff) { |
| | | 1534 | printf("%s: checksum error\n", DEVNAME(sc)); |
| | | 1535 | break; |
| | | 1536 | } |
| | | 1537 | |
| | | 1538 | if (hwhdr->frmlen < sizeof(*hwhdr) + sizeof(*swhdr)) { |
| | | 1539 | printf("%s: length error\n", DEVNAME(sc)); |
| | | 1540 | break; |
| | | 1541 | } |
| | | 1542 | |
| | | 1543 | if (nextlen && hwhdr->frmlen > nextlen) { |
| | | 1544 | printf("%s: read ahead length error (%u > %u)\n", |
| | | 1545 | DEVNAME(sc), hwhdr->frmlen, nextlen); |
| | | 1546 | break; |
| | | 1547 | } |
451 | | | 1548 | |
452 | DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); | | 1549 | sc->sc_tx_max_seq = swhdr->maxseqnr; |
453 | | | 1550 | |
454 | return ret; | | 1551 | flen = hwhdr->frmlen - (sizeof(*hwhdr) + sizeof(*swhdr)); |
| | | 1552 | if (flen == 0) { |
| | | 1553 | nextlen = swhdr->nextlen << 4; |
| | | 1554 | continue; |
| | | 1555 | } |
| | | 1556 | |
| | | 1557 | if (!nextlen) { |
| | | 1558 | KASSERT(roundup(flen, 4) <= sc->sc_bounce_size - |
| | | 1559 | (sizeof(*hwhdr) + sizeof(*swhdr))); |
| | | 1560 | if (bwfm_sdio_frame_read_write(sc, data, |
| | | 1561 | roundup(flen, 4), 0)) |
| | | 1562 | break; |
| | | 1563 | } |
| | | 1564 | |
| | | 1565 | if (swhdr->dataoff < (sizeof(*hwhdr) + sizeof(*swhdr))) { |
| | | 1566 | printf("%s: data offset %u in header\n", |
| | | 1567 | DEVNAME(sc), swhdr->dataoff); |
| | | 1568 | break; |
| | | 1569 | } |
| | | 1570 | |
| | | 1571 | off = swhdr->dataoff - (sizeof(*hwhdr) + sizeof(*swhdr)); |
| | | 1572 | if (off > flen) { |
| | | 1573 | printf("%s: offset %zu beyond end %zu\n", |
| | | 1574 | DEVNAME(sc), off, flen); |
| | | 1575 | break; |
| | | 1576 | } |
| | | 1577 | |
| | | 1578 | switch (swhdr->chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) { |
| | | 1579 | case BWFM_SDIO_SWHDR_CHANNEL_CONTROL: |
| | | 1580 | m = bwfm_sdio_newbuf(); |
| | | 1581 | if (m == NULL) |
| | | 1582 | break; |
| | | 1583 | if (flen - off > m->m_len) { |
| | | 1584 | printf("%s: ctl bigger than anticipated\n", |
| | | 1585 | DEVNAME(sc)); |
| | | 1586 | m_freem(m); |
| | | 1587 | break; |
| | | 1588 | } |
| | | 1589 | m->m_len = m->m_pkthdr.len = flen - off; |
| | | 1590 | memcpy(mtod(m, char *), data + off, flen - off); |
| | | 1591 | bwfm_qput(&sc->sc_rxctl_queue, m); |
| | | 1592 | cv_broadcast(&sc->sc_rxctl_cv); |
| | | 1593 | nextlen = swhdr->nextlen << 4; |
| | | 1594 | break; |
| | | 1595 | case BWFM_SDIO_SWHDR_CHANNEL_EVENT: |
| | | 1596 | case BWFM_SDIO_SWHDR_CHANNEL_DATA: |
| | | 1597 | m = bwfm_sdio_newbuf(); |
| | | 1598 | if (m == NULL) |
| | | 1599 | break; |
| | | 1600 | if (flen - off > m->m_len) { |
| | | 1601 | printf("%s: frame bigger than anticipated\n", |
| | | 1602 | DEVNAME(sc)); |
| | | 1603 | m_freem(m); |
| | | 1604 | break; |
| | | 1605 | } |
| | | 1606 | m->m_len = m->m_pkthdr.len = flen - off; |
| | | 1607 | memcpy(mtod(m, char *), data + off, flen - off); |
| | | 1608 | bcdc = mtod(m, struct bwfm_proto_bcdc_hdr *); |
| | | 1609 | hoff = sizeof(*bcdc) + ((size_t)bcdc->data_offset << 2); |
| | | 1610 | if (m->m_len < hoff) { |
| | | 1611 | printf("%s: short bcdc packet %d < %zu\n", |
| | | 1612 | DEVNAME(sc), m->m_len, hoff); |
| | | 1613 | m_freem(m); |
| | | 1614 | break; |
| | | 1615 | } |
| | | 1616 | m_adj(m, hoff); |
| | | 1617 | bwfm_rx(&sc->sc_sc, m); |
| | | 1618 | nextlen = swhdr->nextlen << 4; |
| | | 1619 | break; |
| | | 1620 | case BWFM_SDIO_SWHDR_CHANNEL_GLOM: |
| | | 1621 | if ((flen % sizeof(uint16_t)) != 0) { |
| | | 1622 | printf("%s: odd length (%zu) glom table\n", |
| | | 1623 | DEVNAME(sc), flen); |
| | | 1624 | break; |
| | | 1625 | } |
| | | 1626 | nsub = flen / sizeof(uint16_t); |
| | | 1627 | sublen = kmem_zalloc(nsub * sizeof(uint16_t), KM_SLEEP); |
| | | 1628 | memcpy(sublen, data, nsub * sizeof(uint16_t)); |
| | | 1629 | bwfm_sdio_rx_glom(sc, sublen, nsub, &nextlen); |
| | | 1630 | kmem_free(sublen, nsub * sizeof(uint16_t)); |
| | | 1631 | break; |
| | | 1632 | default: |
| | | 1633 | printf("%s: unknown channel\n", DEVNAME(sc)); |
| | | 1634 | break; |
| | | 1635 | } |
| | | 1636 | } |
455 | } | | 1637 | } |
| | | 1638 | |
| | | 1639 | void |
| | | 1640 | bwfm_sdio_rx_glom(struct bwfm_sdio_softc *sc, uint16_t *sublen, int nsub, |
| | | 1641 | uint16_t *nextlen) |
| | | 1642 | { |
| | | 1643 | struct bwfm_sdio_hwhdr hwhdr; |
| | | 1644 | struct bwfm_sdio_swhdr swhdr; |
| | | 1645 | struct bwfm_proto_bcdc_hdr *bcdc; |
| | | 1646 | struct mbuf *m, *m0; |
| | | 1647 | size_t flen, off, hoff; |
| | | 1648 | int i; |
| | | 1649 | |
| | | 1650 | if (nsub == 0) |
| | | 1651 | return; |
| | | 1652 | |
| | | 1653 | m0 = NULL; |
| | | 1654 | for (i = 0; i < nsub; i++) { |
| | | 1655 | m = bwfm_sdio_newbuf(); |
| | | 1656 | if (m == NULL) { |
| | | 1657 | m_freem(m0); |
| | | 1658 | return; |
| | | 1659 | } |
| | | 1660 | bwfm_qput(&m0, m); |
| | | 1661 | if (le16toh(sublen[i]) > m->m_len) { |
| | | 1662 | m_freem(m0); |
| | | 1663 | return; |
| | | 1664 | } |
| | | 1665 | if (bwfm_sdio_frame_read_write(sc, mtod(m, char *), |
| | | 1666 | le16toh(sublen[i]), 0)) { |
| | | 1667 | m_freem(m0); |
| | | 1668 | return; |
| | | 1669 | } |
| | | 1670 | m->m_len = m->m_pkthdr.len = le16toh(sublen[i]); |
| | | 1671 | } |
| | | 1672 | |
| | | 1673 | if (m0->m_len >= sizeof(hwhdr) + sizeof(swhdr)) { |
| | | 1674 | m_copydata(m0, 0, sizeof(hwhdr), &hwhdr); |
| | | 1675 | m_copydata(m0, sizeof(hwhdr), sizeof(swhdr), &swhdr); |
| | | 1676 | |
| | | 1677 | /* TODO: Verify actual superframe header */ |
| | | 1678 | |
| | | 1679 | /* remove superframe header */ |
| | | 1680 | if (m0->m_len >= swhdr.dataoff) |
| | | 1681 | m_adj(m0, swhdr.dataoff); |
| | | 1682 | } |
| | | 1683 | |
| | | 1684 | *nextlen = 0; |
| | | 1685 | while ((m = bwfm_qget(&m0)) != NULL) { |
| | | 1686 | if (m->m_len < sizeof(hwhdr) + sizeof(swhdr)) { |
| | | 1687 | printf("%s: tiny mbuf %d < %zu\n", DEVNAME(sc), |
| | | 1688 | m->m_len, sizeof(hwhdr) + sizeof(swhdr)); |
| | | 1689 | goto drop; |
| | | 1690 | } |
| | | 1691 | |
| | | 1692 | m_copydata(m, 0, sizeof(hwhdr), &hwhdr); |
| | | 1693 | m_copydata(m, sizeof(hwhdr), sizeof(swhdr), &swhdr); |
| | | 1694 | |
| | | 1695 | hwhdr.frmlen = le16toh(hwhdr.frmlen); |
| | | 1696 | hwhdr.cksum = le16toh(hwhdr.cksum); |
| | | 1697 | |
| | | 1698 | if (hwhdr.frmlen == 0 && hwhdr.cksum == 0) |
| | | 1699 | goto drop; |
| | | 1700 | |
| | | 1701 | if ((hwhdr.frmlen ^ hwhdr.cksum) != 0xffff) { |
| | | 1702 | printf("%s: checksum error\n", DEVNAME(sc)); |
| | | 1703 | goto drop; |
| | | 1704 | } |
| | | 1705 | |
| | | 1706 | |
| | | 1707 | if (hwhdr.frmlen < sizeof(hwhdr) + sizeof(swhdr)) { |
| | | 1708 | printf("%s: length error\n", DEVNAME(sc)); |
| | | 1709 | goto drop; |
| | | 1710 | } |
| | | 1711 | |
| | | 1712 | flen = hwhdr.frmlen - (sizeof(hwhdr) + sizeof(swhdr)); |
| | | 1713 | if (flen == 0) |
| | | 1714 | goto drop; |
| | | 1715 | |
| | | 1716 | if (hwhdr.frmlen > m->m_len) { |
| | | 1717 | printf("%s: short mbuf %d < %zu\n", |
| | | 1718 | DEVNAME(sc),m->m_len,flen); |
| | | 1719 | goto drop; |
| | | 1720 | } |
| | | 1721 | |
| | | 1722 | if (swhdr.dataoff < (sizeof(hwhdr) + sizeof(swhdr))) { |
| | | 1723 | printf("%s: data offset %u in header\n", |
| | | 1724 | DEVNAME(sc), swhdr.dataoff); |
| | | 1725 | goto drop; |
| | | 1726 | } |
| | | 1727 | |
| | | 1728 | off = swhdr.dataoff - (sizeof(hwhdr) + sizeof(swhdr)); |
| | | 1729 | if (off > flen) { |
| | | 1730 | printf("%s: offset %zu beyond end %zu\n", |
| | | 1731 | DEVNAME(sc), off, flen); |
| | | 1732 | goto drop; |
| | | 1733 | } |
| | | 1734 | |
| | | 1735 | m_adj(m, (int)hwhdr.frmlen - m->m_len); |
| | | 1736 | *nextlen = swhdr.nextlen << 4; |
| | | 1737 | |
| | | 1738 | switch (swhdr.chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) { |
| | | 1739 | case BWFM_SDIO_SWHDR_CHANNEL_CONTROL: |
| | | 1740 | printf("%s: control channel not allowed in glom\n", |
| | | 1741 | DEVNAME(sc)); |
| | | 1742 | goto drop; |
| | | 1743 | case BWFM_SDIO_SWHDR_CHANNEL_EVENT: |
| | | 1744 | case BWFM_SDIO_SWHDR_CHANNEL_DATA: |
| | | 1745 | m_adj(m, swhdr.dataoff); |
| | | 1746 | bcdc = mtod(m, struct bwfm_proto_bcdc_hdr *); |
| | | 1747 | hoff = sizeof(*bcdc) + ((size_t)bcdc->data_offset << 2); |
| | | 1748 | if (m->m_len < hoff) { |
| | | 1749 | printf("%s: short bcdc packet %d < %zu\n", |
| | | 1750 | DEVNAME(sc), m->m_len, hoff); |
| | | 1751 | m_freem(m); |
| | | 1752 | break; |
| | | 1753 | } |
| | | 1754 | m_adj(m, hoff); |
| | | 1755 | bwfm_rx(&sc->sc_sc, m); |
| | | 1756 | break; |
| | | 1757 | case BWFM_SDIO_SWHDR_CHANNEL_GLOM: |
| | | 1758 | printf("%s: glom not allowed in glom\n", |
| | | 1759 | DEVNAME(sc)); |
| | | 1760 | goto drop; |
| | | 1761 | default: |
| | | 1762 | printf("%s: unknown channel\n", DEVNAME(sc)); |
| | | 1763 | goto drop; |
| | | 1764 | } |
| | | 1765 | |
| | | 1766 | continue; |
| | | 1767 | drop: |
| | | 1768 | printf("rx dropped %p len %d\n",mtod(m, char *),m->m_pkthdr.len); |
| | | 1769 | m_free(m); |
| | | 1770 | } |
| | | 1771 | } |
| | | 1772 | |
| | | 1773 | #ifdef BWFM_DEBUG |
| | | 1774 | void |
| | | 1775 | bwfm_sdio_debug_console(struct bwfm_sdio_softc *sc) |
| | | 1776 | { |
| | | 1777 | struct bwfm_sdio_console c; |
| | | 1778 | uint32_t newidx; |
| | | 1779 | int err; |
| | | 1780 | |
| | | 1781 | if (!sc->sc_console_addr) |
| | | 1782 | return; |
| | | 1783 | |
| | | 1784 | err = bwfm_sdio_ram_read_write(sc, sc->sc_console_addr, |
| | | 1785 | (char *)&c, sizeof(c), 0); |
| | | 1786 | if (err) |
| | | 1787 | return; |
| | | 1788 | |
| | | 1789 | c.log_buf = le32toh(c.log_buf); |
| | | 1790 | c.log_bufsz = le32toh(c.log_bufsz); |
| | | 1791 | c.log_idx = le32toh(c.log_idx); |
| | | 1792 | |
| | | 1793 | if (sc->sc_console_buf == NULL) { |
| | | 1794 | sc->sc_console_buf = malloc(c.log_bufsz, M_DEVBUF, |
| | | 1795 | M_WAITOK|M_ZERO); |
| | | 1796 | sc->sc_console_buf_size = c.log_bufsz; |
| | | 1797 | } |
| | | 1798 | |
| | | 1799 | newidx = c.log_idx; |
| | | 1800 | if (newidx >= sc->sc_console_buf_size) |
| | | 1801 | return; |
| | | 1802 | |
| | | 1803 | err = bwfm_sdio_ram_read_write(sc, c.log_buf, sc->sc_console_buf, |
| | | 1804 | sc->sc_console_buf_size, 0); |
| | | 1805 | if (err) |
| | | 1806 | return; |
| | | 1807 | |
| | | 1808 | if (newidx != sc->sc_console_readidx) |
| | | 1809 | DPRINTFN(3, ("BWFM CONSOLE: ")); |
| | | 1810 | while (newidx != sc->sc_console_readidx) { |
| | | 1811 | uint8_t ch = sc->sc_console_buf[sc->sc_console_readidx]; |
| | | 1812 | sc->sc_console_readidx++; |
| | | 1813 | if (sc->sc_console_readidx == sc->sc_console_buf_size) |
| | | 1814 | sc->sc_console_readidx = 0; |
| | | 1815 | if (ch == '\r') |
| | | 1816 | continue; |
| | | 1817 | DPRINTFN(3, ("%c", ch)); |
| | | 1818 | } |
| | | 1819 | } |
| | | 1820 | #endif |