1 /* $NetBSD: if_bwi_sdio.c,v 1.1 2025/01/19 00:29:29 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 31 __KERNEL_RCSID(0, "$NetBSD: if_bwi_sdio.c,v 1.1 2025/01/19 00:29:29 jmcneill Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/device.h> 36 #include <sys/mutex.h> 37 #include <sys/systm.h> 38 39 #include <net/if.h> 40 #include <net/if_dl.h> 41 #include <net/if_ether.h> 42 #include <net/if_media.h> 43 44 #include <netinet/in.h> 45 46 #include <net80211/ieee80211_node.h> 47 #include <net80211/ieee80211_amrr.h> 48 #include <net80211/ieee80211_radiotap.h> 49 #include <net80211/ieee80211_var.h> 50 51 #include <dev/ic/bwireg.h> 52 #include <dev/ic/bwivar.h> 53 54 #include <dev/pcmcia/pcmciareg.h> 55 56 #include <dev/sdmmc/sdmmcdevs.h> 57 #include <dev/sdmmc/sdmmcvar.h> 58 59 #define BWI_SDIO_FUNC1_SBADDRLOW 0x1000a 60 #define BWI_SDIO_FUNC1_SBADDRMID 0x1000b 61 #define BWI_SDIO_FUNC1_SBADDRHI 0x1000c 62 63 #define BWI_CISTPL_VENDOR 0x80 64 #define BWI_VENDOR_SROMREV 0 65 #define BWI_VENDOR_ID 1 66 #define BWI_VENDOR_BOARDREV 2 67 #define BWI_VENDOR_PA 3 68 #define BWI_VENDOR_OEMNAME 4 69 #define BWI_VENDOR_CCODE 5 70 #define BWI_VENDOR_ANTENNA 6 71 #define BWI_VENDOR_ANTGAIN 7 72 #define BWI_VENDOR_BFLAGS 8 73 #define BWI_VENDOR_LEDS 9 74 75 #define BWI_SDIO_REG_OFFSET(ssc, reg) \ 76 ((reg) | ((ssc)->sc_sel_regwin & 0x7000)) 77 78 #define BWI_SDIO_REG_32BIT_ACCESS 0x8000 79 80 static const struct bwi_sdio_product { 81 uint16_t vendor; 82 uint16_t product; 83 } bwi_sdio_products[] = { 84 { SDMMC_VENDOR_BROADCOM, SDMMC_PRODUCT_BROADCOM_NINTENDO_WII }, 85 }; 86 87 struct bwi_sdio_sprom { 88 uint16_t pa_params[3]; 89 uint16_t board_vendor; 90 uint16_t card_flags; 91 uint8_t srom_rev; 92 uint8_t board_rev; 93 uint8_t idle_tssi; 94 uint8_t max_txpwr; 95 uint8_t country_code; 96 uint8_t ant_avail; 97 uint8_t ant_gain; 98 uint8_t gpio[4]; 99 }; 100 101 struct bwi_sdio_softc { 102 struct bwi_softc sc_base; 103 104 struct sdmmc_function *sc_sf; 105 struct bwi_sdio_sprom sc_sprom; 106 uint32_t sc_sel_regwin; 107 kmutex_t sc_lock; 108 }; 109 110 static int bwi_sdio_match(device_t, cfdata_t, void *); 111 static void bwi_sdio_attach(device_t, device_t, void *); 112 113 static void bwi_sdio_parse_cis(struct bwi_sdio_softc *); 114 115 static int bwi_sdio_intr(void *); 116 117 static void bwi_sdio_conf_write(void *, uint32_t, uint32_t); 118 static uint32_t bwi_sdio_conf_read(void *, uint32_t); 119 static void bwi_sdio_reg_write_2(void *, uint32_t, uint16_t); 120 static uint16_t bwi_sdio_reg_read_2(void *, uint32_t); 121 static void bwi_sdio_reg_write_4(void *, uint32_t, uint32_t); 122 static uint32_t bwi_sdio_reg_read_4(void *, uint32_t); 123 static void bwi_sdio_reg_write_multi_4(void *, uint32_t, const uint32_t *, 124 size_t); 125 static void bwi_sdio_reg_read_multi_4(void *, uint32_t, uint32_t *, 126 size_t); 127 128 CFATTACH_DECL_NEW(bwi_sdio, sizeof(struct bwi_sdio_softc), 129 bwi_sdio_match, bwi_sdio_attach, NULL, NULL); 130 131 static int 132 bwi_sdio_match(device_t parent, cfdata_t cf, void *aux) 133 { 134 struct sdmmc_attach_args * const saa = aux; 135 struct sdmmc_function *sf = saa->sf; 136 struct sdmmc_cis *cis; 137 u_int n; 138 139 if (sf == NULL) { 140 return 0; 141 } 142 cis = &sf->sc->sc_fn0->cis; 143 144 for (n = 0; n < __arraycount(bwi_sdio_products); n++) { 145 const struct bwi_sdio_product *bsp = &bwi_sdio_products[n]; 146 147 if (bsp->vendor == cis->manufacturer && 148 bsp->product == cis->product) { 149 return 1; 150 } 151 } 152 153 return 0; 154 } 155 156 static void 157 bwi_sdio_attach(device_t parent, device_t self, void *aux) 158 { 159 struct bwi_sdio_softc * const ssc = device_private(self); 160 struct bwi_softc * const sc = &ssc->sc_base; 161 struct sdmmc_attach_args * const saa = aux; 162 struct sdmmc_function *sf = saa->sf; 163 struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis; 164 int error; 165 void *ih; 166 167 aprint_naive("\n"); 168 aprint_normal(": Broadcom Wireless\n"); 169 170 sc->sc_dev = self; 171 sc->sc_flags = BWI_F_SDIO | BWI_F_PIO; 172 sc->sc_conf_write = bwi_sdio_conf_write; 173 sc->sc_conf_read = bwi_sdio_conf_read; 174 sc->sc_reg_write_multi_4 = bwi_sdio_reg_write_multi_4; 175 sc->sc_reg_read_multi_4 = bwi_sdio_reg_read_multi_4; 176 sc->sc_reg_write_2 = bwi_sdio_reg_write_2; 177 sc->sc_reg_read_2 = bwi_sdio_reg_read_2; 178 sc->sc_reg_write_4 = bwi_sdio_reg_write_4; 179 sc->sc_reg_read_4 = bwi_sdio_reg_read_4; 180 sc->sc_pci_revid = 0; /* XXX can this come from CIS? */ 181 sc->sc_pci_did = cis->product; 182 sc->sc_pci_subvid = cis->manufacturer; 183 sc->sc_pci_subdid = cis->product; 184 185 ssc->sc_sf = sf; 186 mutex_init(&ssc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 187 188 sdmmc_io_set_blocklen(ssc->sc_sf, 64); 189 if (sdmmc_io_function_enable(ssc->sc_sf) != 0) { 190 aprint_error_dev(self, "couldn't enable function\n"); 191 return; 192 } 193 194 bwi_sdio_parse_cis(ssc); 195 196 ih = sdmmc_intr_establish(parent, bwi_sdio_intr, ssc, 197 device_xname(self)); 198 if (ih == NULL) { 199 aprint_error_dev(self, "couldn't establish interrupt\n"); 200 return; 201 } 202 203 error = bwi_attach(sc); 204 if (error != 0) { 205 sdmmc_intr_disestablish(ih); 206 return; 207 } 208 209 sdmmc_intr_enable(ssc->sc_sf); 210 } 211 212 static void 213 bwi_sdio_parse_cis(struct bwi_sdio_softc *ssc) 214 { 215 struct sdmmc_function *sf0 = ssc->sc_sf->sc->sc_fn0; 216 struct bwi_sdio_sprom *sprom = &ssc->sc_sprom; 217 uint32_t reg; 218 uint8_t tplcode, tpllen; 219 220 reg = sdmmc_cisptr(ssc->sc_sf); 221 for (;;) { 222 tplcode = sdmmc_io_read_1(sf0, reg++); 223 if (tplcode == PCMCIA_CISTPL_NULL) { 224 continue; 225 } 226 tpllen = sdmmc_io_read_1(sf0, reg++); 227 if (tplcode == PCMCIA_CISTPL_END || tpllen == 0) { 228 break; 229 } 230 if (tplcode != BWI_CISTPL_VENDOR) { 231 reg += tpllen; 232 continue; 233 } 234 235 switch (sdmmc_io_read_1(sf0, reg)) { 236 case BWI_VENDOR_SROMREV: 237 sprom->srom_rev = sdmmc_io_read_1(sf0, reg + 1); 238 break; 239 case BWI_VENDOR_ID: 240 sprom->board_vendor = 241 sdmmc_io_read_1(sf0, reg + 1) | 242 ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8); 243 break; 244 case BWI_VENDOR_BOARDREV: 245 sprom->board_rev = 246 sdmmc_io_read_1(sf0, reg + 1); 247 break; 248 case BWI_VENDOR_PA: 249 sprom->pa_params[0] = 250 sdmmc_io_read_1(sf0, reg + 1) | 251 ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8); 252 sprom->pa_params[1] = 253 sdmmc_io_read_1(sf0, reg + 3) | 254 ((uint16_t)sdmmc_io_read_1(sf0, reg + 4) << 8); 255 sprom->pa_params[2] = 256 sdmmc_io_read_1(sf0, reg + 5) | 257 ((uint16_t)sdmmc_io_read_1(sf0, reg + 6) << 8); 258 sprom->idle_tssi = 259 sdmmc_io_read_1(sf0, reg + 7); 260 sprom->max_txpwr = 261 sdmmc_io_read_1(sf0, reg + 8); 262 break; 263 case BWI_VENDOR_CCODE: 264 sprom->country_code = 265 sdmmc_io_read_1(sf0, reg + 1); 266 break; 267 case BWI_VENDOR_ANTGAIN: 268 sprom->ant_gain = sdmmc_io_read_1(sf0, reg + 1); 269 break; 270 case BWI_VENDOR_BFLAGS: 271 sprom->card_flags = 272 sdmmc_io_read_1(sf0, reg + 1) | 273 ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8); 274 break; 275 case BWI_VENDOR_LEDS: 276 sprom->gpio[0] = sdmmc_io_read_1(sf0, reg + 1); 277 sprom->gpio[1] = sdmmc_io_read_1(sf0, reg + 2); 278 sprom->gpio[2] = sdmmc_io_read_1(sf0, reg + 3); 279 sprom->gpio[3] = sdmmc_io_read_1(sf0, reg + 4); 280 break; 281 } 282 283 reg += tpllen; 284 } 285 } 286 287 static int 288 bwi_sdio_intr(void *priv) 289 { 290 struct bwi_sdio_softc * const ssc = priv; 291 292 bwi_intr(&ssc->sc_base); 293 294 return 1; 295 } 296 297 static void 298 bwi_sdio_conf_write(void *priv, uint32_t reg, uint32_t val) 299 { 300 struct bwi_sdio_softc * const ssc = priv; 301 302 KASSERT(reg == BWI_PCIR_SEL_REGWIN); 303 304 mutex_enter(&ssc->sc_lock); 305 if (reg == BWI_PCIR_SEL_REGWIN && ssc->sc_sel_regwin != val) { 306 sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRLOW, 307 (val >> 8) & 0x80); 308 sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRMID, 309 (val >> 16) & 0xff); 310 sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRHI, 311 (val >> 24) & 0xff); 312 ssc->sc_sel_regwin = val; 313 } 314 mutex_exit(&ssc->sc_lock); 315 } 316 317 static uint32_t 318 bwi_sdio_conf_read(void *priv, uint32_t reg) 319 { 320 struct bwi_sdio_softc * const ssc = priv; 321 322 KASSERT(reg == BWI_PCIR_SEL_REGWIN); 323 324 if (reg == BWI_PCIR_SEL_REGWIN) { 325 return ssc->sc_sel_regwin; 326 } else { 327 return 0; 328 } 329 } 330 331 static void 332 bwi_sdio_reg_write_multi_4(void *priv, uint32_t reg, const uint32_t *datap, 333 size_t count) 334 { 335 struct bwi_sdio_softc * const ssc = priv; 336 337 mutex_enter(&ssc->sc_lock); 338 sdmmc_io_write_multi_1(ssc->sc_sf, 339 BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, 340 (uint8_t *)__UNCONST(datap), count * sizeof(uint32_t)); 341 mutex_exit(&ssc->sc_lock); 342 } 343 344 static void 345 bwi_sdio_reg_read_multi_4(void *priv, uint32_t reg, uint32_t *datap, 346 size_t count) 347 { 348 struct bwi_sdio_softc * const ssc = priv; 349 350 mutex_enter(&ssc->sc_lock); 351 sdmmc_io_read_multi_1(ssc->sc_sf, 352 BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, 353 (uint8_t *)datap, count * sizeof(uint32_t)); 354 mutex_exit(&ssc->sc_lock); 355 } 356 357 static void 358 bwi_sdio_reg_write_2(void *priv, uint32_t reg, uint16_t val) 359 { 360 struct bwi_sdio_softc * const ssc = priv; 361 362 val = htole16(val); 363 364 mutex_enter(&ssc->sc_lock); 365 sdmmc_io_write_2(ssc->sc_sf, BWI_SDIO_REG_OFFSET(ssc, reg), val); 366 mutex_exit(&ssc->sc_lock); 367 } 368 369 static uint16_t 370 bwi_sdio_reg_read_sprom(struct bwi_sdio_softc *ssc, uint32_t reg) 371 { 372 struct bwi_sdio_sprom *sprom = &ssc->sc_sprom; 373 struct sdmmc_cis *cis = &ssc->sc_sf->cis; 374 375 switch (reg) { 376 case BWI_SPROM_11BG_EADDR ... BWI_SPROM_11BG_EADDR + 4: 377 return *(uint16_t *)&cis->lan_nid[reg - BWI_SPROM_11BG_EADDR]; 378 case BWI_SPROM_11A_EADDR ... BWI_SPROM_11A_EADDR + 4: 379 return *(uint16_t *)&cis->lan_nid[reg - BWI_SPROM_11A_EADDR]; 380 case BWI_SPROM_CARD_INFO: 381 return (uint16_t)sprom->country_code << 8; 382 case BWI_SPROM_PA_PARAM_11BG ... BWI_SPROM_PA_PARAM_11BG + 4: 383 return sprom->pa_params[(reg - BWI_SPROM_PA_PARAM_11BG) / 2]; 384 case BWI_SPROM_PA_PARAM_11A ... BWI_SPROM_PA_PARAM_11A + 4: 385 return sprom->pa_params[(reg - BWI_SPROM_PA_PARAM_11A) / 2]; 386 case BWI_SPROM_GPIO01: 387 return sprom->gpio[0] | ((uint16_t)sprom->gpio[1] << 8); 388 case BWI_SPROM_GPIO23: 389 return sprom->gpio[2] | ((uint16_t)sprom->gpio[3] << 8); 390 case BWI_SPROM_MAX_TXPWR: 391 return sprom->max_txpwr | ((uint16_t)sprom->max_txpwr << 8); 392 case BWI_SPROM_IDLE_TSSI: 393 return sprom->idle_tssi | ((uint16_t)sprom->idle_tssi << 8); 394 case BWI_SPROM_CARD_FLAGS: 395 return sprom->card_flags; 396 case BWI_SPROM_ANT_GAIN: 397 return sprom->ant_gain | ((uint16_t)sprom->ant_gain << 8); 398 default: 399 return 0xffff; 400 } 401 } 402 403 static uint16_t 404 bwi_sdio_reg_read_2(void *priv, uint32_t reg) 405 { 406 struct bwi_sdio_softc * const ssc = priv; 407 uint16_t val; 408 409 /* Emulate SPROM reads */ 410 if (reg >= BWI_SPROM_START && 411 reg <= BWI_SPROM_START + BWI_SPROM_ANT_GAIN) { 412 return bwi_sdio_reg_read_sprom(ssc, reg - BWI_SPROM_START); 413 } 414 415 mutex_enter(&ssc->sc_lock); 416 val = sdmmc_io_read_2(ssc->sc_sf, BWI_SDIO_REG_OFFSET(ssc, reg)); 417 mutex_exit(&ssc->sc_lock); 418 419 val = le16toh(val); 420 421 return val; 422 } 423 424 static void 425 bwi_sdio_reg_write_4(void *priv, uint32_t reg, uint32_t val) 426 { 427 struct bwi_sdio_softc * const ssc = priv; 428 429 val = htole32(val); 430 431 mutex_enter(&ssc->sc_lock); 432 sdmmc_io_write_4(ssc->sc_sf, 433 BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, val); 434 /* SDIO cards require a read after a 32-bit write */ 435 sdmmc_io_read_4(ssc->sc_sf, 0); 436 mutex_exit(&ssc->sc_lock); 437 } 438 439 static uint32_t 440 bwi_sdio_reg_read_4(void *priv, uint32_t reg) 441 { 442 struct bwi_sdio_softc * const ssc = priv; 443 uint32_t val; 444 445 mutex_enter(&ssc->sc_lock); 446 val = sdmmc_io_read_4(ssc->sc_sf, 447 BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS); 448 mutex_exit(&ssc->sc_lock); 449 450 val = le32toh(val); 451 452 return val; 453 } 454