| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: if_bwfm_sdio.c,v 1.9 2019/10/28 06:37:52 mlelstv Exp $ */ | | 1 | /* $NetBSD: if_bwfm_sdio.c,v 1.10 2019/12/30 16:28:14 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 |
| @@ -106,26 +106,27 @@ struct bwfm_sdio_softc { | | | @@ -106,26 +106,27 @@ struct bwfm_sdio_softc { |
106 | char *sc_console_buf; | | 106 | char *sc_console_buf; |
107 | size_t sc_console_buf_size; | | 107 | size_t sc_console_buf_size; |
108 | uint32_t sc_console_readidx; | | 108 | uint32_t sc_console_readidx; |
109 | | | 109 | |
110 | int sc_phandle; | | 110 | int sc_phandle; |
111 | void *sc_fdtih; | | 111 | void *sc_fdtih; |
112 | }; | | 112 | }; |
113 | | | 113 | |
114 | static int bwfm_sdio_match(device_t, cfdata_t, void *); | | 114 | static int bwfm_sdio_match(device_t, cfdata_t, void *); |
115 | static void bwfm_sdio_attach(device_t, device_t, void *); | | 115 | static void bwfm_sdio_attach(device_t, device_t, void *); |
116 | static int bwfm_sdio_detach(device_t, int); | | 116 | static int bwfm_sdio_detach(device_t, int); |
117 | static void bwfm_sdio_attachhook(device_t); | | 117 | static void bwfm_sdio_attachhook(device_t); |
118 | static int bwfm_fdt_find_phandle(device_t, device_t); | | 118 | static int bwfm_fdt_find_phandle(device_t, device_t); |
| | | 119 | static const char *bwfm_fdt_get_model(void); |
119 | | | 120 | |
120 | static void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); | | 121 | static void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); |
121 | static uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); | | 122 | static uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); |
122 | static uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); | | 123 | static uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); |
123 | static void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, | | 124 | static void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, |
124 | uint8_t); | | 125 | uint8_t); |
125 | static void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, | | 126 | static void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, |
126 | uint32_t); | | 127 | uint32_t); |
127 | | | 128 | |
128 | static uint32_t bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t); | | 129 | static uint32_t bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t); |
129 | static void bwfm_sdio_dev_write(struct bwfm_sdio_softc *, uint32_t, | | 130 | static void bwfm_sdio_dev_write(struct bwfm_sdio_softc *, uint32_t, |
130 | uint32_t); | | 131 | uint32_t); |
131 | | | 132 | |
| @@ -367,27 +368,28 @@ bwfm_sdio_attach(device_t parent, device | | | @@ -367,27 +368,28 @@ bwfm_sdio_attach(device_t parent, device |
367 | | | 368 | |
368 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); | | 369 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); |
369 | sc->sc_clkstate = CLK_SDONLY; | | 370 | sc->sc_clkstate = CLK_SDONLY; |
370 | | | 371 | |
371 | config_mountroot(self, bwfm_sdio_attachhook); | | 372 | config_mountroot(self, bwfm_sdio_attachhook); |
372 | } | | 373 | } |
373 | | | 374 | |
374 | static void | | 375 | static void |
375 | bwfm_sdio_attachhook(device_t self) | | 376 | bwfm_sdio_attachhook(device_t self) |
376 | { | | 377 | { |
377 | struct bwfm_sdio_softc *sc = device_private(self); | | 378 | struct bwfm_sdio_softc *sc = device_private(self); |
378 | struct bwfm_softc *bwfm = &sc->sc_sc; | | 379 | struct bwfm_softc *bwfm = &sc->sc_sc; |
379 | firmware_handle_t fwh; | | 380 | firmware_handle_t fwh; |
380 | const char *name, *nvname; | | 381 | const char *name, *nvname, *model; |
| | | 382 | char *nvnamebuf; |
381 | u_char *ucode, *nvram; | | 383 | u_char *ucode, *nvram; |
382 | size_t size, nvlen, nvsize; | | 384 | size_t size, nvlen, nvsize; |
383 | uint32_t reg, clk; | | 385 | uint32_t reg, clk; |
384 | int error; | | 386 | int error; |
385 | | | 387 | |
386 | DPRINTF(("%s: chip 0x%08x rev %u\n", DEVNAME(sc), | | 388 | DPRINTF(("%s: chip 0x%08x rev %u\n", DEVNAME(sc), |
387 | bwfm->sc_chip.ch_chip, bwfm->sc_chip.ch_chiprev)); | | 389 | bwfm->sc_chip.ch_chip, bwfm->sc_chip.ch_chiprev)); |
388 | switch (bwfm->sc_chip.ch_chip) { | | 390 | switch (bwfm->sc_chip.ch_chip) { |
389 | case BRCM_CC_4330_CHIP_ID: | | 391 | case BRCM_CC_4330_CHIP_ID: |
390 | name = "brcmfmac4330-sdio.bin"; | | 392 | name = "brcmfmac4330-sdio.bin"; |
391 | nvname = "brcmfmac4330-sdio.txt"; | | 393 | nvname = "brcmfmac4330-sdio.txt"; |
392 | break; | | 394 | break; |
393 | case BRCM_CC_4334_CHIP_ID: | | 395 | case BRCM_CC_4334_CHIP_ID: |
| @@ -425,48 +427,64 @@ bwfm_sdio_attachhook(device_t self) | | | @@ -425,48 +427,64 @@ bwfm_sdio_attachhook(device_t self) |
425 | nvname = "brcmfmac43430-sdio.txt"; | | 427 | nvname = "brcmfmac43430-sdio.txt"; |
426 | } | | 428 | } |
427 | break; | | 429 | break; |
428 | case BRCM_CC_4356_CHIP_ID: | | 430 | case BRCM_CC_4356_CHIP_ID: |
429 | name = "brcmfmac4356-sdio.bin"; | | 431 | name = "brcmfmac4356-sdio.bin"; |
430 | nvname = "brcmfmac4356-sdio.txt"; | | 432 | nvname = "brcmfmac4356-sdio.txt"; |
431 | break; | | 433 | break; |
432 | default: | | 434 | default: |
433 | printf("%s: unknown firmware for chip %s\n", | | 435 | printf("%s: unknown firmware for chip %s\n", |
434 | DEVNAME(sc), bwfm->sc_chip.ch_name); | | 436 | DEVNAME(sc), bwfm->sc_chip.ch_name); |
435 | goto err; | | 437 | goto err; |
436 | } | | 438 | } |
437 | | | 439 | |
| | | 440 | /* compute a model specific filename for the NV config */ |
| | | 441 | nvnamebuf = NULL; |
| | | 442 | model = bwfm_fdt_get_model(); |
| | | 443 | if (model != NULL) { |
| | | 444 | /* assume nvname ends in ".txt" */ |
| | | 445 | nvnamebuf = kmem_asprintf("%.*s.%s.txt", |
| | | 446 | (int)(strlen(nvname) - 4), |
| | | 447 | nvname, model); |
| | | 448 | } |
| | | 449 | |
| | | 450 | aprint_verbose_dev(self, "Firmware %s\n", name); |
| | | 451 | aprint_verbose_dev(self, "Default Config %s\n", nvname); |
| | | 452 | if (nvnamebuf != NULL) |
| | | 453 | aprint_verbose_dev(self, "Model Config %s\n", nvnamebuf); |
| | | 454 | |
438 | if (firmware_open("if_bwfm", name, &fwh) != 0) { | | 455 | if (firmware_open("if_bwfm", name, &fwh) != 0) { |
439 | printf("%s: failed firmware_open of file %s\n", | | 456 | printf("%s: failed firmware_open of file %s\n", |
440 | DEVNAME(sc), name); | | 457 | DEVNAME(sc), name); |
441 | goto err; | | 458 | goto err; |
442 | } | | 459 | } |
443 | size = firmware_get_size(fwh); | | 460 | size = firmware_get_size(fwh); |
444 | ucode = firmware_malloc(size); | | 461 | ucode = firmware_malloc(size); |
445 | if (ucode == NULL) { | | 462 | if (ucode == NULL) { |
446 | printf("%s: failed firmware_open of file %s\n", | | 463 | printf("%s: failed firmware_open of file %s\n", |
447 | DEVNAME(sc), name); | | 464 | DEVNAME(sc), name); |
448 | firmware_close(fwh); | | 465 | firmware_close(fwh); |
449 | goto err; | | 466 | goto err; |
450 | } | | 467 | } |
451 | error = firmware_read(fwh, 0, ucode, size); | | 468 | error = firmware_read(fwh, 0, ucode, size); |
452 | firmware_close(fwh); | | 469 | firmware_close(fwh); |
453 | if (error != 0) { | | 470 | if (error != 0) { |
454 | printf("%s: failed to read firmware (error %d)\n", | | 471 | printf("%s: failed to read firmware (error %d)\n", |
455 | DEVNAME(sc), error); | | 472 | DEVNAME(sc), error); |
456 | goto err1; | | 473 | goto err1; |
457 | } | | 474 | } |
458 | | | 475 | |
459 | if (firmware_open("if_bwfm", nvname, &fwh) != 0) { | | 476 | if ((nvnamebuf == NULL || firmware_open("if_bwfm", nvnamebuf, &fwh) != 0) |
| | | 477 | && firmware_open("if_bwfm", nvname, &fwh) != 0) { |
460 | printf("%s: failed firmware_open of file %s\n", | | 478 | printf("%s: failed firmware_open of file %s\n", |
461 | DEVNAME(sc), nvname); | | 479 | DEVNAME(sc), nvname); |
462 | goto err1; | | 480 | goto err1; |
463 | } | | 481 | } |
464 | nvlen = firmware_get_size(fwh); | | 482 | nvlen = firmware_get_size(fwh); |
465 | nvram = firmware_malloc(nvlen); | | 483 | nvram = firmware_malloc(nvlen); |
466 | if (nvram == NULL) { | | 484 | if (nvram == NULL) { |
467 | printf("%s: failed firmware_open of file %s\n", | | 485 | printf("%s: failed firmware_open of file %s\n", |
468 | DEVNAME(sc), name); | | 486 | DEVNAME(sc), name); |
469 | firmware_close(fwh); | | 487 | firmware_close(fwh); |
470 | goto err1; | | 488 | goto err1; |
471 | } | | 489 | } |
472 | error = firmware_read(fwh, 0, nvram, nvlen); | | 490 | error = firmware_read(fwh, 0, nvram, nvlen); |
| @@ -482,26 +500,28 @@ bwfm_sdio_attachhook(device_t self) | | | @@ -482,26 +500,28 @@ bwfm_sdio_attachhook(device_t self) |
482 | goto err2; | | 500 | goto err2; |
483 | } | | 501 | } |
484 | | | 502 | |
485 | sc->sc_alp_only = true; | | 503 | sc->sc_alp_only = true; |
486 | if (bwfm_sdio_load_microcode(sc, ucode, size, nvram, nvsize) != 0) { | | 504 | if (bwfm_sdio_load_microcode(sc, ucode, size, nvram, nvsize) != 0) { |
487 | printf("%s: could not load microcode\n", | | 505 | printf("%s: could not load microcode\n", |
488 | DEVNAME(sc)); | | 506 | DEVNAME(sc)); |
489 | goto err2; | | 507 | goto err2; |
490 | } | | 508 | } |
491 | sc->sc_alp_only = false; | | 509 | sc->sc_alp_only = false; |
492 | | | 510 | |
493 | firmware_free(nvram, nvlen); | | 511 | firmware_free(nvram, nvlen); |
494 | firmware_free(ucode, size); | | 512 | firmware_free(ucode, size); |
| | | 513 | if (nvnamebuf != NULL) |
| | | 514 | kmem_free(nvnamebuf, strlen(nvnamebuf)+1); |
495 | | | 515 | |
496 | sdmmc_pause(hztoms(1)*1000, NULL); | | 516 | sdmmc_pause(hztoms(1)*1000, NULL); |
497 | | | 517 | |
498 | bwfm_sdio_clkctl(sc, CLK_AVAIL, false); | | 518 | bwfm_sdio_clkctl(sc, CLK_AVAIL, false); |
499 | if (sc->sc_clkstate != CLK_AVAIL) { | | 519 | if (sc->sc_clkstate != CLK_AVAIL) { |
500 | printf("%s: could not access clock\n", | | 520 | printf("%s: could not access clock\n", |
501 | DEVNAME(sc)); | | 521 | DEVNAME(sc)); |
502 | goto err; | | 522 | goto err; |
503 | } | | 523 | } |
504 | | | 524 | |
505 | clk = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); | | 525 | clk = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); |
506 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, | | 526 | bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, |
507 | clk | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); | | 527 | clk | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); |
| @@ -555,26 +575,28 @@ bwfm_sdio_attachhook(device_t self) | | | @@ -555,26 +575,28 @@ bwfm_sdio_attachhook(device_t self) |
555 | sdmmc_pause(100000, NULL); | | 575 | sdmmc_pause(100000, NULL); |
556 | | | 576 | |
557 | sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops; | | 577 | sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops; |
558 | sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops; | | 578 | sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops; |
559 | bwfm_attach(&sc->sc_sc); | | 579 | bwfm_attach(&sc->sc_sc); |
560 | sc->sc_bwfm_attached = true; | | 580 | sc->sc_bwfm_attached = true; |
561 | | | 581 | |
562 | return; | | 582 | return; |
563 | | | 583 | |
564 | err2: | | 584 | err2: |
565 | firmware_free(nvram, nvlen); | | 585 | firmware_free(nvram, nvlen); |
566 | err1: | | 586 | err1: |
567 | firmware_free(ucode, size); | | 587 | firmware_free(ucode, size); |
| | | 588 | if (nvnamebuf != NULL) |
| | | 589 | kmem_free(nvnamebuf, strlen(nvnamebuf)+1); |
568 | err: | | 590 | err: |
569 | return; | | 591 | return; |
570 | } | | 592 | } |
571 | | | 593 | |
572 | static int | | 594 | static int |
573 | bwfm_fdt_find_phandle(device_t self, device_t parent) | | 595 | bwfm_fdt_find_phandle(device_t self, device_t parent) |
574 | { | | 596 | { |
575 | prop_dictionary_t dict; | | 597 | prop_dictionary_t dict; |
576 | device_t dev; | | 598 | device_t dev; |
577 | const char *str; | | 599 | const char *str; |
578 | int phandle; | | 600 | int phandle; |
579 | | | 601 | |
580 | /* locate in FDT */ | | 602 | /* locate in FDT */ |
| @@ -593,26 +615,35 @@ bwfm_fdt_find_phandle(device_t self, dev | | | @@ -593,26 +615,35 @@ bwfm_fdt_find_phandle(device_t self, dev |
593 | if (!prop_dictionary_get_cstring_nocopy(dict, "fdt-path", &str)) | | 615 | if (!prop_dictionary_get_cstring_nocopy(dict, "fdt-path", &str)) |
594 | return -1; | | 616 | return -1; |
595 | | | 617 | |
596 | /* are we the only FDT child ? */ | | 618 | /* are we the only FDT child ? */ |
597 | phandle = OF_child(OF_finddevice(str)); | | 619 | phandle = OF_child(OF_finddevice(str)); |
598 | } | | 620 | } |
599 | | | 621 | |
600 | if (!of_match_compatible(phandle, compatible)) | | 622 | if (!of_match_compatible(phandle, compatible)) |
601 | return -1; | | 623 | return -1; |
602 | | | 624 | |
603 | return phandle; | | 625 | return phandle; |
604 | } | | 626 | } |
605 | | | 627 | |
| | | 628 | static const char * |
| | | 629 | bwfm_fdt_get_model(void) |
| | | 630 | { |
| | | 631 | int phandle; |
| | | 632 | |
| | | 633 | phandle = OF_finddevice("/"); |
| | | 634 | return fdtbus_get_string_index(phandle, "compatible", 0); |
| | | 635 | } |
| | | 636 | |
606 | static int | | 637 | static int |
607 | bwfm_sdio_detach(device_t self, int flags) | | 638 | bwfm_sdio_detach(device_t self, int flags) |
608 | { | | 639 | { |
609 | struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; | | 640 | struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; |
610 | | | 641 | |
611 | #ifdef BWFM_DEBUG | | 642 | #ifdef BWFM_DEBUG |
612 | bwfm_sdio_debug_console(sc); | | 643 | bwfm_sdio_debug_console(sc); |
613 | #endif | | 644 | #endif |
614 | | | 645 | |
615 | if (sc->sc_ih || sc->sc_fdtih) { | | 646 | if (sc->sc_ih || sc->sc_fdtih) { |
616 | sdmmc_intr_disable(sc->sc_sf[1]); | | 647 | sdmmc_intr_disable(sc->sc_sf[1]); |
617 | if (sc->sc_ih) | | 648 | if (sc->sc_ih) |
618 | sdmmc_intr_disestablish(sc->sc_ih); | | 649 | sdmmc_intr_disestablish(sc->sc_ih); |