diff options
author | Arend van Spriel <arend@broadcom.com> | 2014-05-27 06:56:22 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-05-29 13:10:23 -0400 |
commit | bd0e1b1d380efe713a612c91417b948e7ebea58d (patch) | |
tree | c36ee65b9e613784c4d3cb4d7bcb0db5ea0edef7 /drivers/net/wireless/brcm80211 | |
parent | c1416e77a6166766c847f03f21cca18d8ac54dd3 (diff) |
brcmfmac: use asynchronous firmware request in SDIO
This patch adds use of asynchronous firmware request to
the driver SDIO layer.
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/brcm80211')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 271 |
1 files changed, 124 insertions, 147 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index efb8c4febe65..506ef0d4a52b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | |||
@@ -632,43 +632,28 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { | |||
632 | { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } | 632 | { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } |
633 | }; | 633 | }; |
634 | 634 | ||
635 | 635 | static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, | |
636 | static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus, | 636 | enum brcmf_firmware_type type) |
637 | enum brcmf_firmware_type type) | ||
638 | { | 637 | { |
639 | const struct firmware *fw; | 638 | int i; |
640 | const char *name; | ||
641 | int err, i; | ||
642 | 639 | ||
643 | for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { | 640 | for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { |
644 | if (brcmf_fwname_data[i].chipid == bus->ci->chip && | 641 | if (brcmf_fwname_data[i].chipid == ci->chip && |
645 | brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) { | 642 | brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { |
646 | switch (type) { | 643 | switch (type) { |
647 | case BRCMF_FIRMWARE_BIN: | 644 | case BRCMF_FIRMWARE_BIN: |
648 | name = brcmf_fwname_data[i].bin; | 645 | return brcmf_fwname_data[i].bin; |
649 | break; | ||
650 | case BRCMF_FIRMWARE_NVRAM: | 646 | case BRCMF_FIRMWARE_NVRAM: |
651 | name = brcmf_fwname_data[i].nv; | 647 | return brcmf_fwname_data[i].nv; |
652 | break; | ||
653 | default: | 648 | default: |
654 | brcmf_err("invalid firmware type (%d)\n", type); | 649 | brcmf_err("invalid firmware type (%d)\n", type); |
655 | return NULL; | 650 | return NULL; |
656 | } | 651 | } |
657 | goto found; | ||
658 | } | 652 | } |
659 | } | 653 | } |
660 | brcmf_err("Unknown chipid %d [%d]\n", | 654 | brcmf_err("Unknown chipid %d [%d]\n", |
661 | bus->ci->chip, bus->ci->chiprev); | 655 | ci->chip, ci->chiprev); |
662 | return NULL; | 656 | return NULL; |
663 | |||
664 | found: | ||
665 | err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev); | ||
666 | if ((err) || (!fw)) { | ||
667 | brcmf_err("fail to request firmware %s (%d)\n", name, err); | ||
668 | return NULL; | ||
669 | } | ||
670 | |||
671 | return fw; | ||
672 | } | 657 | } |
673 | 658 | ||
674 | static void pkt_align(struct sk_buff *p, int len, int align) | 659 | static void pkt_align(struct sk_buff *p, int len, int align) |
@@ -3278,20 +3263,13 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, | |||
3278 | } | 3263 | } |
3279 | 3264 | ||
3280 | static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, | 3265 | static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, |
3281 | const struct firmware *nv) | 3266 | void *vars, u32 varsz) |
3282 | { | 3267 | { |
3283 | void *vars; | ||
3284 | u32 varsz; | ||
3285 | int address; | 3268 | int address; |
3286 | int err; | 3269 | int err; |
3287 | 3270 | ||
3288 | brcmf_dbg(TRACE, "Enter\n"); | 3271 | brcmf_dbg(TRACE, "Enter\n"); |
3289 | 3272 | ||
3290 | vars = brcmf_fw_nvram_strip(nv, &varsz); | ||
3291 | |||
3292 | if (vars == NULL) | ||
3293 | return -EINVAL; | ||
3294 | |||
3295 | address = bus->ci->ramsize - varsz + bus->ci->rambase; | 3273 | address = bus->ci->ramsize - varsz + bus->ci->rambase; |
3296 | err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); | 3274 | err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); |
3297 | if (err) | 3275 | if (err) |
@@ -3300,15 +3278,14 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, | |||
3300 | else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) | 3278 | else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) |
3301 | err = -EIO; | 3279 | err = -EIO; |
3302 | 3280 | ||
3303 | brcmf_fw_nvram_free(vars); | ||
3304 | |||
3305 | return err; | 3281 | return err; |
3306 | } | 3282 | } |
3307 | 3283 | ||
3308 | static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) | 3284 | static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, |
3285 | const struct firmware *fw, | ||
3286 | void *nvram, u32 nvlen) | ||
3309 | { | 3287 | { |
3310 | int bcmerror = -EFAULT; | 3288 | int bcmerror = -EFAULT; |
3311 | const struct firmware *fw; | ||
3312 | u32 rstvec; | 3289 | u32 rstvec; |
3313 | 3290 | ||
3314 | sdio_claim_host(bus->sdiodev->func[1]); | 3291 | sdio_claim_host(bus->sdiodev->func[1]); |
@@ -3317,12 +3294,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) | |||
3317 | /* Keep arm in reset */ | 3294 | /* Keep arm in reset */ |
3318 | brcmf_chip_enter_download(bus->ci); | 3295 | brcmf_chip_enter_download(bus->ci); |
3319 | 3296 | ||
3320 | fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); | ||
3321 | if (fw == NULL) { | ||
3322 | bcmerror = -ENOENT; | ||
3323 | goto err; | ||
3324 | } | ||
3325 | |||
3326 | rstvec = get_unaligned_le32(fw->data); | 3297 | rstvec = get_unaligned_le32(fw->data); |
3327 | brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); | 3298 | brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); |
3328 | 3299 | ||
@@ -3330,17 +3301,12 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) | |||
3330 | release_firmware(fw); | 3301 | release_firmware(fw); |
3331 | if (bcmerror) { | 3302 | if (bcmerror) { |
3332 | brcmf_err("dongle image file download failed\n"); | 3303 | brcmf_err("dongle image file download failed\n"); |
3304 | brcmf_fw_nvram_free(nvram); | ||
3333 | goto err; | 3305 | goto err; |
3334 | } | 3306 | } |
3335 | 3307 | ||
3336 | fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); | 3308 | bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); |
3337 | if (fw == NULL) { | 3309 | brcmf_fw_nvram_free(nvram); |
3338 | bcmerror = -ENOENT; | ||
3339 | goto err; | ||
3340 | } | ||
3341 | |||
3342 | bcmerror = brcmf_sdio_download_nvram(bus, fw); | ||
3343 | release_firmware(fw); | ||
3344 | if (bcmerror) { | 3310 | if (bcmerror) { |
3345 | brcmf_err("dongle nvram file download failed\n"); | 3311 | brcmf_err("dongle nvram file download failed\n"); |
3346 | goto err; | 3312 | goto err; |
@@ -3490,97 +3456,6 @@ done: | |||
3490 | return err; | 3456 | return err; |
3491 | } | 3457 | } |
3492 | 3458 | ||
3493 | static int brcmf_sdio_bus_init(struct device *dev) | ||
3494 | { | ||
3495 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); | ||
3496 | struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | ||
3497 | struct brcmf_sdio *bus = sdiodev->bus; | ||
3498 | int err, ret = 0; | ||
3499 | u8 saveclk; | ||
3500 | |||
3501 | brcmf_dbg(TRACE, "Enter\n"); | ||
3502 | |||
3503 | /* try to download image and nvram to the dongle */ | ||
3504 | if (bus_if->state == BRCMF_BUS_DOWN) { | ||
3505 | bus->alp_only = true; | ||
3506 | err = brcmf_sdio_download_firmware(bus); | ||
3507 | if (err) | ||
3508 | return err; | ||
3509 | bus->alp_only = false; | ||
3510 | } | ||
3511 | |||
3512 | if (!bus->sdiodev->bus_if->drvr) | ||
3513 | return 0; | ||
3514 | |||
3515 | /* Start the watchdog timer */ | ||
3516 | bus->sdcnt.tickcnt = 0; | ||
3517 | brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||
3518 | |||
3519 | sdio_claim_host(bus->sdiodev->func[1]); | ||
3520 | |||
3521 | /* Make sure backplane clock is on, needed to generate F2 interrupt */ | ||
3522 | brcmf_sdio_clkctl(bus, CLK_AVAIL, false); | ||
3523 | if (bus->clkstate != CLK_AVAIL) | ||
3524 | goto exit; | ||
3525 | |||
3526 | /* Force clocks on backplane to be sure F2 interrupt propagates */ | ||
3527 | saveclk = brcmf_sdiod_regrb(bus->sdiodev, | ||
3528 | SBSDIO_FUNC1_CHIPCLKCSR, &err); | ||
3529 | if (!err) { | ||
3530 | brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | ||
3531 | (saveclk | SBSDIO_FORCE_HT), &err); | ||
3532 | } | ||
3533 | if (err) { | ||
3534 | brcmf_err("Failed to force clock for F2: err %d\n", err); | ||
3535 | goto exit; | ||
3536 | } | ||
3537 | |||
3538 | /* Enable function 2 (frame transfers) */ | ||
3539 | w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, | ||
3540 | offsetof(struct sdpcmd_regs, tosbmailboxdata)); | ||
3541 | err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]); | ||
3542 | |||
3543 | |||
3544 | brcmf_dbg(INFO, "enable F2: err=%d\n", err); | ||
3545 | |||
3546 | /* If F2 successfully enabled, set core and enable interrupts */ | ||
3547 | if (!err) { | ||
3548 | /* Set up the interrupt mask and enable interrupts */ | ||
3549 | bus->hostintmask = HOSTINTMASK; | ||
3550 | w_sdreg32(bus, bus->hostintmask, | ||
3551 | offsetof(struct sdpcmd_regs, hostintmask)); | ||
3552 | |||
3553 | brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); | ||
3554 | } else { | ||
3555 | /* Disable F2 again */ | ||
3556 | sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); | ||
3557 | ret = -ENODEV; | ||
3558 | } | ||
3559 | |||
3560 | if (brcmf_chip_sr_capable(bus->ci)) { | ||
3561 | brcmf_sdio_sr_init(bus); | ||
3562 | } else { | ||
3563 | /* Restore previous clock setting */ | ||
3564 | brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | ||
3565 | saveclk, &err); | ||
3566 | } | ||
3567 | |||
3568 | if (ret == 0) { | ||
3569 | ret = brcmf_sdiod_intr_register(bus->sdiodev); | ||
3570 | if (ret != 0) | ||
3571 | brcmf_err("intr register failed:%d\n", ret); | ||
3572 | } | ||
3573 | |||
3574 | /* If we didn't come up, turn off backplane clock */ | ||
3575 | if (ret != 0) | ||
3576 | brcmf_sdio_clkctl(bus, CLK_NONE, false); | ||
3577 | |||
3578 | exit: | ||
3579 | sdio_release_host(bus->sdiodev->func[1]); | ||
3580 | |||
3581 | return ret; | ||
3582 | } | ||
3583 | |||
3584 | void brcmf_sdio_isr(struct brcmf_sdio *bus) | 3459 | void brcmf_sdio_isr(struct brcmf_sdio *bus) |
3585 | { | 3460 | { |
3586 | brcmf_dbg(TRACE, "Enter\n"); | 3461 | brcmf_dbg(TRACE, "Enter\n"); |
@@ -4026,6 +3901,108 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = { | |||
4026 | .gettxq = brcmf_sdio_bus_gettxq, | 3901 | .gettxq = brcmf_sdio_bus_gettxq, |
4027 | }; | 3902 | }; |
4028 | 3903 | ||
3904 | static void brcmf_sdio_firmware_callback(struct device *dev, | ||
3905 | const struct firmware *code, | ||
3906 | void *nvram, u32 nvram_len) | ||
3907 | { | ||
3908 | struct brcmf_bus *bus_if = dev_get_drvdata(dev); | ||
3909 | struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | ||
3910 | struct brcmf_sdio *bus = sdiodev->bus; | ||
3911 | int err = 0; | ||
3912 | u8 saveclk; | ||
3913 | |||
3914 | brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); | ||
3915 | |||
3916 | /* try to download image and nvram to the dongle */ | ||
3917 | if (bus_if->state == BRCMF_BUS_DOWN) { | ||
3918 | bus->alp_only = true; | ||
3919 | err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); | ||
3920 | if (err) | ||
3921 | goto fail; | ||
3922 | bus->alp_only = false; | ||
3923 | } | ||
3924 | |||
3925 | if (!bus_if->drvr) | ||
3926 | return; | ||
3927 | |||
3928 | /* Start the watchdog timer */ | ||
3929 | bus->sdcnt.tickcnt = 0; | ||
3930 | brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); | ||
3931 | |||
3932 | sdio_claim_host(sdiodev->func[1]); | ||
3933 | |||
3934 | /* Make sure backplane clock is on, needed to generate F2 interrupt */ | ||
3935 | brcmf_sdio_clkctl(bus, CLK_AVAIL, false); | ||
3936 | if (bus->clkstate != CLK_AVAIL) | ||
3937 | goto release; | ||
3938 | |||
3939 | /* Force clocks on backplane to be sure F2 interrupt propagates */ | ||
3940 | saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); | ||
3941 | if (!err) { | ||
3942 | brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | ||
3943 | (saveclk | SBSDIO_FORCE_HT), &err); | ||
3944 | } | ||
3945 | if (err) { | ||
3946 | brcmf_err("Failed to force clock for F2: err %d\n", err); | ||
3947 | goto release; | ||
3948 | } | ||
3949 | |||
3950 | /* Enable function 2 (frame transfers) */ | ||
3951 | w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, | ||
3952 | offsetof(struct sdpcmd_regs, tosbmailboxdata)); | ||
3953 | err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); | ||
3954 | |||
3955 | |||
3956 | brcmf_dbg(INFO, "enable F2: err=%d\n", err); | ||
3957 | |||
3958 | /* If F2 successfully enabled, set core and enable interrupts */ | ||
3959 | if (!err) { | ||
3960 | /* Set up the interrupt mask and enable interrupts */ | ||
3961 | bus->hostintmask = HOSTINTMASK; | ||
3962 | w_sdreg32(bus, bus->hostintmask, | ||
3963 | offsetof(struct sdpcmd_regs, hostintmask)); | ||
3964 | |||
3965 | brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); | ||
3966 | } else { | ||
3967 | /* Disable F2 again */ | ||
3968 | sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); | ||
3969 | goto release; | ||
3970 | } | ||
3971 | |||
3972 | if (brcmf_chip_sr_capable(bus->ci)) { | ||
3973 | brcmf_sdio_sr_init(bus); | ||
3974 | } else { | ||
3975 | /* Restore previous clock setting */ | ||
3976 | brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | ||
3977 | saveclk, &err); | ||
3978 | } | ||
3979 | |||
3980 | if (err == 0) { | ||
3981 | err = brcmf_sdiod_intr_register(sdiodev); | ||
3982 | if (err != 0) | ||
3983 | brcmf_err("intr register failed:%d\n", err); | ||
3984 | } | ||
3985 | |||
3986 | /* If we didn't come up, turn off backplane clock */ | ||
3987 | if (err != 0) | ||
3988 | brcmf_sdio_clkctl(bus, CLK_NONE, false); | ||
3989 | |||
3990 | sdio_release_host(sdiodev->func[1]); | ||
3991 | |||
3992 | err = brcmf_bus_start(dev); | ||
3993 | if (err != 0) { | ||
3994 | brcmf_err("dongle is not responding\n"); | ||
3995 | goto fail; | ||
3996 | } | ||
3997 | return; | ||
3998 | |||
3999 | release: | ||
4000 | sdio_release_host(sdiodev->func[1]); | ||
4001 | fail: | ||
4002 | brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); | ||
4003 | device_release_driver(dev); | ||
4004 | } | ||
4005 | |||
4029 | struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) | 4006 | struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) |
4030 | { | 4007 | { |
4031 | int ret; | 4008 | int ret; |
@@ -4149,14 +4126,14 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) | |||
4149 | brcmf_sdio_debugfs_create(bus); | 4126 | brcmf_sdio_debugfs_create(bus); |
4150 | brcmf_dbg(INFO, "completed!!\n"); | 4127 | brcmf_dbg(INFO, "completed!!\n"); |
4151 | 4128 | ||
4152 | ret = brcmf_sdio_bus_init(sdiodev->dev); | 4129 | ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, |
4153 | if (ret) | 4130 | brcmf_sdio_get_fwname(bus->ci, |
4154 | goto fail; | 4131 | BRCMF_FIRMWARE_BIN), |
4155 | 4132 | brcmf_sdio_get_fwname(bus->ci, | |
4156 | /* if firmware path present try to download and bring up bus */ | 4133 | BRCMF_FIRMWARE_NVRAM), |
4157 | ret = brcmf_bus_start(bus->sdiodev->dev); | 4134 | brcmf_sdio_firmware_callback); |
4158 | if (ret != 0) { | 4135 | if (ret != 0) { |
4159 | brcmf_err("dongle is not responding\n"); | 4136 | brcmf_err("async firmware request failed: %d\n", ret); |
4160 | goto fail; | 4137 | goto fail; |
4161 | } | 4138 | } |
4162 | 4139 | ||