Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd0e1b1d authored by Arend van Spriel's avatar Arend van Spriel Committed by John W. Linville
Browse files

brcmfmac: use asynchronous firmware request in SDIO



This patch adds use of asynchronous firmware request to
the driver SDIO layer.

Reviewed-by: default avatarHante Meuleman <meuleman@broadcom.com>
Reviewed-by: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarDaniel (Deognyoun) Kim <dekim@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent c1416e77
Loading
Loading
Loading
Loading
+124 −147
Original line number Diff line number Diff line
@@ -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,
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 @@ static int brcmf_sdio_bus_preinit(struct device *dev)
	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;
	}