mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-04 00:06:36 +00:00
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>
This commit is contained in:
parent
c1416e77a6
commit
bd0e1b1d38
1 changed files with 124 additions and 147 deletions
|
@ -632,43 +632,28 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = {
|
|||
{ BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
|
||||
};
|
||||
|
||||
|
||||
static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus,
|
||||
enum brcmf_firmware_type type)
|
||||
static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci,
|
||||
enum brcmf_firmware_type type)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const char *name;
|
||||
int err, i;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
|
||||
if (brcmf_fwname_data[i].chipid == bus->ci->chip &&
|
||||
brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) {
|
||||
if (brcmf_fwname_data[i].chipid == ci->chip &&
|
||||
brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) {
|
||||
switch (type) {
|
||||
case BRCMF_FIRMWARE_BIN:
|
||||
name = brcmf_fwname_data[i].bin;
|
||||
break;
|
||||
return brcmf_fwname_data[i].bin;
|
||||
case BRCMF_FIRMWARE_NVRAM:
|
||||
name = brcmf_fwname_data[i].nv;
|
||||
break;
|
||||
return brcmf_fwname_data[i].nv;
|
||||
default:
|
||||
brcmf_err("invalid firmware type (%d)\n", type);
|
||||
return NULL;
|
||||
}
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
brcmf_err("Unknown chipid %d [%d]\n",
|
||||
bus->ci->chip, bus->ci->chiprev);
|
||||
ci->chip, ci->chiprev);
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev);
|
||||
if ((err) || (!fw)) {
|
||||
brcmf_err("fail to request firmware %s (%d)\n", name, err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fw;
|
||||
}
|
||||
|
||||
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,
|
|||
}
|
||||
|
||||
static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
|
||||
const struct firmware *nv)
|
||||
void *vars, u32 varsz)
|
||||
{
|
||||
void *vars;
|
||||
u32 varsz;
|
||||
int address;
|
||||
int err;
|
||||
|
||||
brcmf_dbg(TRACE, "Enter\n");
|
||||
|
||||
vars = brcmf_fw_nvram_strip(nv, &varsz);
|
||||
|
||||
if (vars == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
address = bus->ci->ramsize - varsz + bus->ci->rambase;
|
||||
err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
|
||||
if (err)
|
||||
|
@ -3300,15 +3278,14 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus,
|
|||
else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
|
||||
err = -EIO;
|
||||
|
||||
brcmf_fw_nvram_free(vars);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
|
||||
static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus,
|
||||
const struct firmware *fw,
|
||||
void *nvram, u32 nvlen)
|
||||
{
|
||||
int bcmerror = -EFAULT;
|
||||
const struct firmware *fw;
|
||||
u32 rstvec;
|
||||
|
||||
sdio_claim_host(bus->sdiodev->func[1]);
|
||||
|
@ -3317,12 +3294,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
|
|||
/* Keep arm in reset */
|
||||
brcmf_chip_enter_download(bus->ci);
|
||||
|
||||
fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
|
||||
if (fw == NULL) {
|
||||
bcmerror = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
rstvec = get_unaligned_le32(fw->data);
|
||||
brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
|
||||
|
||||
|
@ -3330,17 +3301,12 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
|
|||
release_firmware(fw);
|
||||
if (bcmerror) {
|
||||
brcmf_err("dongle image file download failed\n");
|
||||
brcmf_fw_nvram_free(nvram);
|
||||
goto err;
|
||||
}
|
||||
|
||||
fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
|
||||
if (fw == NULL) {
|
||||
bcmerror = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
bcmerror = brcmf_sdio_download_nvram(bus, fw);
|
||||
release_firmware(fw);
|
||||
bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen);
|
||||
brcmf_fw_nvram_free(nvram);
|
||||
if (bcmerror) {
|
||||
brcmf_err("dongle nvram file download failed\n");
|
||||
goto err;
|
||||
|
@ -3490,97 +3456,6 @@ done:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int brcmf_sdio_bus_init(struct device *dev)
|
||||
{
|
||||
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
||||
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
|
||||
struct brcmf_sdio *bus = sdiodev->bus;
|
||||
int err, ret = 0;
|
||||
u8 saveclk;
|
||||
|
||||
brcmf_dbg(TRACE, "Enter\n");
|
||||
|
||||
/* try to download image and nvram to the dongle */
|
||||
if (bus_if->state == BRCMF_BUS_DOWN) {
|
||||
bus->alp_only = true;
|
||||
err = brcmf_sdio_download_firmware(bus);
|
||||
if (err)
|
||||
return err;
|
||||
bus->alp_only = false;
|
||||
}
|
||||
|
||||
if (!bus->sdiodev->bus_if->drvr)
|
||||
return 0;
|
||||
|
||||
/* Start the watchdog timer */
|
||||
bus->sdcnt.tickcnt = 0;
|
||||
brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
|
||||
|
||||
sdio_claim_host(bus->sdiodev->func[1]);
|
||||
|
||||
/* Make sure backplane clock is on, needed to generate F2 interrupt */
|
||||
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
|
||||
if (bus->clkstate != CLK_AVAIL)
|
||||
goto exit;
|
||||
|
||||
/* Force clocks on backplane to be sure F2 interrupt propagates */
|
||||
saveclk = brcmf_sdiod_regrb(bus->sdiodev,
|
||||
SBSDIO_FUNC1_CHIPCLKCSR, &err);
|
||||
if (!err) {
|
||||
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
|
||||
(saveclk | SBSDIO_FORCE_HT), &err);
|
||||
}
|
||||
if (err) {
|
||||
brcmf_err("Failed to force clock for F2: err %d\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Enable function 2 (frame transfers) */
|
||||
w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
|
||||
offsetof(struct sdpcmd_regs, tosbmailboxdata));
|
||||
err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]);
|
||||
|
||||
|
||||
brcmf_dbg(INFO, "enable F2: err=%d\n", err);
|
||||
|
||||
/* If F2 successfully enabled, set core and enable interrupts */
|
||||
if (!err) {
|
||||
/* Set up the interrupt mask and enable interrupts */
|
||||
bus->hostintmask = HOSTINTMASK;
|
||||
w_sdreg32(bus, bus->hostintmask,
|
||||
offsetof(struct sdpcmd_regs, hostintmask));
|
||||
|
||||
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err);
|
||||
} else {
|
||||
/* Disable F2 again */
|
||||
sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
if (brcmf_chip_sr_capable(bus->ci)) {
|
||||
brcmf_sdio_sr_init(bus);
|
||||
} else {
|
||||
/* Restore previous clock setting */
|
||||
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
|
||||
saveclk, &err);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = brcmf_sdiod_intr_register(bus->sdiodev);
|
||||
if (ret != 0)
|
||||
brcmf_err("intr register failed:%d\n", ret);
|
||||
}
|
||||
|
||||
/* If we didn't come up, turn off backplane clock */
|
||||
if (ret != 0)
|
||||
brcmf_sdio_clkctl(bus, CLK_NONE, false);
|
||||
|
||||
exit:
|
||||
sdio_release_host(bus->sdiodev->func[1]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void brcmf_sdio_isr(struct brcmf_sdio *bus)
|
||||
{
|
||||
brcmf_dbg(TRACE, "Enter\n");
|
||||
|
@ -4026,6 +3901,108 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
|
|||
.gettxq = brcmf_sdio_bus_gettxq,
|
||||
};
|
||||
|
||||
static void brcmf_sdio_firmware_callback(struct device *dev,
|
||||
const struct firmware *code,
|
||||
void *nvram, u32 nvram_len)
|
||||
{
|
||||
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
||||
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
|
||||
struct brcmf_sdio *bus = sdiodev->bus;
|
||||
int err = 0;
|
||||
u8 saveclk;
|
||||
|
||||
brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev));
|
||||
|
||||
/* try to download image and nvram to the dongle */
|
||||
if (bus_if->state == BRCMF_BUS_DOWN) {
|
||||
bus->alp_only = true;
|
||||
err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
|
||||
if (err)
|
||||
goto fail;
|
||||
bus->alp_only = false;
|
||||
}
|
||||
|
||||
if (!bus_if->drvr)
|
||||
return;
|
||||
|
||||
/* Start the watchdog timer */
|
||||
bus->sdcnt.tickcnt = 0;
|
||||
brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
|
||||
|
||||
sdio_claim_host(sdiodev->func[1]);
|
||||
|
||||
/* Make sure backplane clock is on, needed to generate F2 interrupt */
|
||||
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
|
||||
if (bus->clkstate != CLK_AVAIL)
|
||||
goto release;
|
||||
|
||||
/* Force clocks on backplane to be sure F2 interrupt propagates */
|
||||
saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err);
|
||||
if (!err) {
|
||||
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
|
||||
(saveclk | SBSDIO_FORCE_HT), &err);
|
||||
}
|
||||
if (err) {
|
||||
brcmf_err("Failed to force clock for F2: err %d\n", err);
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Enable function 2 (frame transfers) */
|
||||
w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
|
||||
offsetof(struct sdpcmd_regs, tosbmailboxdata));
|
||||
err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]);
|
||||
|
||||
|
||||
brcmf_dbg(INFO, "enable F2: err=%d\n", err);
|
||||
|
||||
/* If F2 successfully enabled, set core and enable interrupts */
|
||||
if (!err) {
|
||||
/* Set up the interrupt mask and enable interrupts */
|
||||
bus->hostintmask = HOSTINTMASK;
|
||||
w_sdreg32(bus, bus->hostintmask,
|
||||
offsetof(struct sdpcmd_regs, hostintmask));
|
||||
|
||||
brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err);
|
||||
} else {
|
||||
/* Disable F2 again */
|
||||
sdio_disable_func(sdiodev->func[SDIO_FUNC_2]);
|
||||
goto release;
|
||||
}
|
||||
|
||||
if (brcmf_chip_sr_capable(bus->ci)) {
|
||||
brcmf_sdio_sr_init(bus);
|
||||
} else {
|
||||
/* Restore previous clock setting */
|
||||
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
|
||||
saveclk, &err);
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
err = brcmf_sdiod_intr_register(sdiodev);
|
||||
if (err != 0)
|
||||
brcmf_err("intr register failed:%d\n", err);
|
||||
}
|
||||
|
||||
/* If we didn't come up, turn off backplane clock */
|
||||
if (err != 0)
|
||||
brcmf_sdio_clkctl(bus, CLK_NONE, false);
|
||||
|
||||
sdio_release_host(sdiodev->func[1]);
|
||||
|
||||
err = brcmf_bus_start(dev);
|
||||
if (err != 0) {
|
||||
brcmf_err("dongle is not responding\n");
|
||||
goto fail;
|
||||
}
|
||||
return;
|
||||
|
||||
release:
|
||||
sdio_release_host(sdiodev->func[1]);
|
||||
fail:
|
||||
brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err);
|
||||
device_release_driver(dev);
|
||||
}
|
||||
|
||||
struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
|
||||
{
|
||||
int ret;
|
||||
|
@ -4149,14 +4126,14 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
|
|||
brcmf_sdio_debugfs_create(bus);
|
||||
brcmf_dbg(INFO, "completed!!\n");
|
||||
|
||||
ret = brcmf_sdio_bus_init(sdiodev->dev);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/* if firmware path present try to download and bring up bus */
|
||||
ret = brcmf_bus_start(bus->sdiodev->dev);
|
||||
ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
|
||||
brcmf_sdio_get_fwname(bus->ci,
|
||||
BRCMF_FIRMWARE_BIN),
|
||||
brcmf_sdio_get_fwname(bus->ci,
|
||||
BRCMF_FIRMWARE_NVRAM),
|
||||
brcmf_sdio_firmware_callback);
|
||||
if (ret != 0) {
|
||||
brcmf_err("dongle is not responding\n");
|
||||
brcmf_err("async firmware request failed: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue