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

Commit c597f665 authored by David S. Miller's avatar David S. Miller
Browse files
parents 669d67bf 341352d1
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1122,12 +1122,12 @@ exit:
static void at76_dump_mib_local(struct at76_priv *priv)
{
	int ret;
	struct mib_local *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL);
	struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL);

	if (!m)
		return;

	ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(struct mib_local));
	ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m));
	if (ret < 0) {
		wiphy_err(priv->hw->wiphy,
			  "at76_get_mib (LOCAL) failed: %d\n", ret);
+9 −0
Original line number Diff line number Diff line
@@ -36,6 +36,15 @@ config BRCMFMAC_SDIO
	  IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to
	  use the driver for a SDIO wireless card.

config BRCMFMAC_SDIO_OOB
	bool "Out of band interrupt support for SDIO interface chipset"
	depends on BRCMFMAC_SDIO
	---help---
	  This option enables out-of-band interrupt support for Broadcom
	  SDIO Wifi chipset using fullmac in order to gain better
	  performance and deep sleep wake up capability on certain
	  platforms. Say N if you are unsure.

config BRCMFMAC_USB
	bool "USB bus interface support for FullMAC driver"
	depends on USB
+87 −10
Original line number Diff line number Diff line
@@ -39,37 +39,113 @@

#define SDIOH_API_ACCESS_RETRY_LIMIT	2

static void brcmf_sdioh_irqhandler(struct sdio_func *func)
#ifdef CONFIG_BRCMFMAC_SDIO_OOB
static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id)
{
	struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev);
	struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(dev_id);

	brcmf_dbg(TRACE, "***IRQHandler\n");
	brcmf_dbg(INTR, "oob intr triggered\n");

	sdio_release_host(func);
	/*
	 * out-of-band interrupt is level-triggered which won't
	 * be cleared until dpc
	 */
	if (sdiodev->irq_en) {
		disable_irq_nosync(irq);
		sdiodev->irq_en = false;
	}

	brcmf_sdbrcm_isr(sdiodev->bus);

	sdio_claim_host(func);
	return IRQ_HANDLED;
}

int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
{
	int ret = 0;
	u8 data;
	unsigned long flags;

	brcmf_dbg(TRACE, "Entering\n");

	brcmf_dbg(ERROR, "requesting irq %d\n", sdiodev->irq);
	ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler,
			  sdiodev->irq_flags, "brcmf_oob_intr",
			  &sdiodev->func[1]->card->dev);
	if (ret != 0)
		return ret;
	spin_lock_init(&sdiodev->irq_en_lock);
	spin_lock_irqsave(&sdiodev->irq_en_lock, flags);
	sdiodev->irq_en = true;
	spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags);

	ret = enable_irq_wake(sdiodev->irq);
	if (ret != 0)
		return ret;
	sdiodev->irq_wake = true;

	/* must configure SDIO_CCCR_IENx to enable irq */
	data = brcmf_sdcard_cfg_read(sdiodev, SDIO_FUNC_0,
				     SDIO_CCCR_IENx, &ret);
	data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
	brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_IENx,
			       data, &ret);

	/* redirect, configure ane enable io for interrupt signal */
	data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
	if (sdiodev->irq_flags | IRQF_TRIGGER_HIGH)
		data |= SDIO_SEPINT_ACT_HI;
	brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_BRCM_SEPINT,
			       data, &ret);

	return 0;
}

int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
{
	brcmf_dbg(TRACE, "Entering\n");

	brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_BRCM_SEPINT,
			       0, NULL);
	brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_0, SDIO_CCCR_IENx, 0, NULL);

	if (sdiodev->irq_wake) {
		disable_irq_wake(sdiodev->irq);
		sdiodev->irq_wake = false;
	}
	free_irq(sdiodev->irq, &sdiodev->func[1]->card->dev);
	sdiodev->irq_en = false;

	return 0;
}
#else		/* CONFIG_BRCMFMAC_SDIO_OOB */
static void brcmf_sdio_irqhandler(struct sdio_func *func)
{
	struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev);

	brcmf_dbg(INTR, "ib intr triggered\n");

	brcmf_sdbrcm_isr(sdiodev->bus);
}

/* dummy handler for SDIO function 2 interrupt */
static void brcmf_sdioh_dummy_irq_handler(struct sdio_func *func)
static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func)
{
}

int brcmf_sdcard_intr_reg(struct brcmf_sdio_dev *sdiodev)
int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
{
	brcmf_dbg(TRACE, "Entering\n");

	sdio_claim_host(sdiodev->func[1]);
	sdio_claim_irq(sdiodev->func[1], brcmf_sdioh_irqhandler);
	sdio_claim_irq(sdiodev->func[2], brcmf_sdioh_dummy_irq_handler);
	sdio_claim_irq(sdiodev->func[1], brcmf_sdio_irqhandler);
	sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler);
	sdio_release_host(sdiodev->func[1]);

	return 0;
}

int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev)
int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
{
	brcmf_dbg(TRACE, "Entering\n");

@@ -80,6 +156,7 @@ int brcmf_sdcard_intr_dereg(struct brcmf_sdio_dev *sdiodev)

	return 0;
}
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */

u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_dev *sdiodev, uint fnc_num, u32 addr,
			 int *err)
+104 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include <linux/errno.h>
#include <linux/sched.h>	/* request_irq() */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <net/cfg80211.h>

#include <defs.h>
@@ -55,6 +56,15 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);

#ifdef CONFIG_BRCMFMAC_SDIO_OOB
static struct list_head oobirq_lh;
struct brcmf_sdio_oobirq {
	unsigned int irq;
	unsigned long flags;
	struct list_head list;
};
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */

static bool
brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
{
@@ -107,7 +117,8 @@ static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev,
			}
			sdio_release_host(sdfunc);
		}
	} else if (regaddr == SDIO_CCCR_ABORT) {
	} else if ((regaddr == SDIO_CCCR_ABORT) ||
		   (regaddr == SDIO_CCCR_IENx)) {
		sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func),
				 GFP_KERNEL);
		if (!sdfunc)
@@ -467,12 +478,40 @@ void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev)

}

#ifdef CONFIG_BRCMFMAC_SDIO_OOB
static int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev)
{
	struct brcmf_sdio_oobirq *oobirq_entry;

	if (list_empty(&oobirq_lh)) {
		brcmf_dbg(ERROR, "no valid oob irq resource\n");
		return -ENXIO;
	}

	oobirq_entry = list_first_entry(&oobirq_lh, struct brcmf_sdio_oobirq,
					list);

	sdiodev->irq = oobirq_entry->irq;
	sdiodev->irq_flags = oobirq_entry->flags;
	list_del(&oobirq_entry->list);
	kfree(oobirq_entry);

	return 0;
}
#else
static inline int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev)
{
	return 0;
}
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */

static int brcmf_ops_sdio_probe(struct sdio_func *func,
			      const struct sdio_device_id *id)
{
	int ret = 0;
	struct brcmf_sdio_dev *sdiodev;
	struct brcmf_bus *bus_if;

	brcmf_dbg(TRACE, "Enter\n");
	brcmf_dbg(TRACE, "func->class=%x\n", func->class);
	brcmf_dbg(TRACE, "sdio_vendor: 0x%04x\n", func->vendor);
@@ -511,6 +550,10 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
		sdiodev = dev_get_drvdata(&func->card->dev);
		if ((!sdiodev) || (sdiodev->func[1]->card != func->card))
			return -ENODEV;

		ret = brcmf_sdio_getintrcfg(sdiodev);
		if (ret)
			return ret;
		sdiodev->func[2] = func;

		bus_if = sdiodev->bus_if;
@@ -603,6 +646,65 @@ static struct sdio_driver brcmf_sdmmc_driver = {
#endif	/* CONFIG_PM_SLEEP */
};

#ifdef CONFIG_BRCMFMAC_SDIO_OOB
static int brcmf_sdio_pd_probe(struct platform_device *pdev)
{
	struct resource *res;
	struct brcmf_sdio_oobirq *oobirq_entry;
	int i, ret;

	INIT_LIST_HEAD(&oobirq_lh);

	for (i = 0; ; i++) {
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
		if (!res)
			break;

		oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
				       GFP_KERNEL);
		oobirq_entry->irq = res->start;
		oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK;
		list_add_tail(&oobirq_entry->list, &oobirq_lh);
	}
	if (i == 0)
		return -ENXIO;

	ret = sdio_register_driver(&brcmf_sdmmc_driver);

	if (ret)
		brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret);

	return ret;
}

static struct platform_driver brcmf_sdio_pd = {
	.probe		= brcmf_sdio_pd_probe,
	.driver		= {
		.name	= "brcmf_sdio_pd"
	}
};

void brcmf_sdio_exit(void)
{
	brcmf_dbg(TRACE, "Enter\n");

	sdio_unregister_driver(&brcmf_sdmmc_driver);

	platform_driver_unregister(&brcmf_sdio_pd);
}

void brcmf_sdio_init(void)
{
	int ret;

	brcmf_dbg(TRACE, "Enter\n");

	ret = platform_driver_register(&brcmf_sdio_pd);

	if (ret)
		brcmf_dbg(ERROR, "platform_driver_register failed: %d\n", ret);
}
#else
void brcmf_sdio_exit(void)
{
	brcmf_dbg(TRACE, "Enter\n");
@@ -621,3 +723,4 @@ void brcmf_sdio_init(void)
	if (ret)
		brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret);
}
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */
+28 −11
Original line number Diff line number Diff line
@@ -2352,6 +2352,24 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
	up(&bus->sdsem);
}

#ifdef CONFIG_BRCMFMAC_SDIO_OOB
static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
{
	unsigned long flags;

	spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
	if (!bus->sdiodev->irq_en && !bus->ipend) {
		enable_irq(bus->sdiodev->irq);
		bus->sdiodev->irq_en = true;
	}
	spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
}
#else
static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
{
}
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */

static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
{
	u32 intstatus, newstatus = 0;
@@ -2509,6 +2527,8 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
	bus->intstatus = intstatus;

clkwait:
	brcmf_sdbrcm_clrintr(bus);

	if (data_ok(bus) && bus->ctrl_frame_stat &&
		(bus->clkstate == CLK_AVAIL)) {
		int ret, i;
@@ -3508,8 +3528,14 @@ static int brcmf_sdbrcm_bus_init(struct device *dev)
	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
			       SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);

	if (ret == 0) {
		ret = brcmf_sdio_intr_register(bus->sdiodev);
		if (ret != 0)
			brcmf_dbg(ERROR, "intr register failed:%d\n", ret);
	}

	/* If we didn't come up, turn off backplane clock */
	if (!ret)
	if (bus_if->state != BRCMF_BUS_DATA)
		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);

exit:
@@ -3867,7 +3893,7 @@ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus)

	if (bus) {
		/* De-register interrupt handler */
		brcmf_sdcard_intr_dereg(bus->sdiodev);
		brcmf_sdio_intr_unregister(bus->sdiodev);

		if (bus->sdiodev->bus_if->drvr) {
			brcmf_detach(bus->sdiodev->dev);
@@ -3968,15 +3994,6 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
		goto fail;
	}

	/* Register interrupt callback, but mask it (not operational yet). */
	brcmf_dbg(INTR, "disable SDIO interrupts (not interested yet)\n");
	ret = brcmf_sdcard_intr_reg(bus->sdiodev);
	if (ret != 0) {
		brcmf_dbg(ERROR, "FAILED: sdcard_intr_reg returned %d\n", ret);
		goto fail;
	}
	brcmf_dbg(INTR, "registered SDIO interrupt function ok\n");

	brcmf_dbg(INFO, "completed!!\n");

	/* if firmware path present try to download and bring up bus */
Loading