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

Commit 668761ac authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville
Browse files

brcmfmac: define and use platform specific data for SDIO.



This patch adds support for platform specific data for SDIO
fullmac devices. Currently OOB interrupts are configured by Kconfig
BRCMFMAC_SDIO_OOB but that is now determined dynamically by checking
availibility of platform data.

Cc: Hauke Mehrtens <hauke@hauke-m.de>
Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarPiotr Haber <phaber@broadcom.com>
Signed-off-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 979c2920
Loading
Loading
Loading
Loading
+0 −9
Original line number Diff line number Diff line
@@ -37,15 +37,6 @@ 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
+78 −77
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/platform_data/brcmfmac-sdio.h>

#include <defs.h>
#include <brcm_hw_ids.h>
@@ -37,16 +38,15 @@

#define SDIOH_API_ACCESS_RETRY_LIMIT	2

#ifdef CONFIG_BRCMFMAC_SDIO_OOB
static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id)

static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id)
{
	struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;

	brcmf_dbg(INTR, "oob intr triggered\n");
	brcmf_dbg(INTR, "OOB intr triggered\n");

	/*
	 * out-of-band interrupt is level-triggered which won't
	/* out-of-band interrupt is level-triggered which won't
	 * be cleared until dpc
	 */
	if (sdiodev->irq_en) {
@@ -59,27 +59,50 @@ static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id)
	return IRQ_HANDLED;
}

static void brcmf_sdio_ib_irqhandler(struct sdio_func *func)
{
	struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev);
	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;

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

	brcmf_sdbrcm_isr(sdiodev->bus);
}

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

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

	brcmf_dbg(SDIO, "Entering: irq %d\n", sdiodev->irq);

	ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler,
			  sdiodev->irq_flags, "brcmf_oob_intr",
	if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
		brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
			  sdiodev->pdata->oob_irq_nr);
		ret = request_irq(sdiodev->pdata->oob_irq_nr,
				  brcmf_sdio_oob_irqhandler,
				  sdiodev->pdata->oob_irq_flags,
				  "brcmf_oob_intr",
				  &sdiodev->func[1]->dev);
	if (ret != 0)
		if (ret != 0) {
			brcmf_err("request_irq failed %d\n", ret);
			return ret;
		}
		sdiodev->oob_irq_requested = true;
		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)
		ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr);
		if (ret != 0) {
			brcmf_err("enable_irq_wake failed %d\n", ret);
			return ret;
		}
		sdiodev->irq_wake = true;

		sdio_claim_host(sdiodev->func[1]);
@@ -91,11 +114,18 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)

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

		sdio_release_host(sdiodev->func[1]);
	} else {
		brcmf_dbg(SDIO, "Entering\n");
		sdio_claim_host(sdiodev->func[1]);
		sdio_claim_irq(sdiodev->func[1], brcmf_sdio_ib_irqhandler);
		sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler);
		sdio_release_host(sdiodev->func[1]);
	}

	return 0;
}
@@ -104,60 +134,31 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
{
	brcmf_dbg(SDIO, "Entering\n");

	if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
		sdio_claim_host(sdiodev->func[1]);
		brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
		brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
		sdio_release_host(sdiodev->func[1]);

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

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

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

	brcmf_sdbrcm_isr(sdiodev->bus);
}

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

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

	sdio_claim_host(sdiodev->func[1]);
	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_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
{
	brcmf_dbg(SDIO, "Entering\n");

	} else {
		sdio_claim_host(sdiodev->func[1]);
		sdio_release_irq(sdiodev->func[2]);
		sdio_release_irq(sdiodev->func[1]);
		sdio_release_host(sdiodev->func[1]);
	}

	return 0;
}
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */

int
brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
+31 −83
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/sched.h>	/* request_irq() */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/platform_data/brcmfmac-sdio.h>
#include <net/cfg80211.h>

#include <defs.h>
@@ -62,14 +63,8 @@ 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 struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;


static bool
brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
@@ -428,33 +423,6 @@ 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_err("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)
{
@@ -495,15 +463,13 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
	dev_set_drvdata(&func->dev, bus_if);
	dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
	sdiodev->dev = &sdiodev->func[1]->dev;
	sdiodev->pdata = brcmfmac_sdio_pdata;

	atomic_set(&sdiodev->suspend, false);
	init_waitqueue_head(&sdiodev->request_byte_wait);
	init_waitqueue_head(&sdiodev->request_word_wait);
	init_waitqueue_head(&sdiodev->request_chain_wait);
	init_waitqueue_head(&sdiodev->request_buffer_wait);
	err = brcmf_sdio_getintrcfg(sdiodev);
	if (err)
		goto fail;

	brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n");
	err = brcmf_sdio_probe(sdiodev);
@@ -598,7 +564,7 @@ static const struct dev_pm_ops brcmf_sdio_pm_ops = {
static struct sdio_driver brcmf_sdmmc_driver = {
	.probe = brcmf_ops_sdio_probe,
	.remove = brcmf_ops_sdio_remove,
	.name = "brcmfmac",
	.name = BRCMFMAC_SDIO_PDATA_NAME,
	.id_table = brcmf_sdmmc_ids,
#ifdef CONFIG_PM_SLEEP
	.drv = {
@@ -607,71 +573,50 @@ 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;
	int ret;

	INIT_LIST_HEAD(&oobirq_lh);
	brcmf_dbg(SDIO, "Enter\n");

	for (i = 0; ; i++) {
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
		if (!res)
			break;
	brcmfmac_sdio_pdata = pdev->dev.platform_data;

		oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
				       GFP_KERNEL);
		if (!oobirq_entry)
			return -ENOMEM;
		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;
	if (brcmfmac_sdio_pdata->power_on)
		brcmfmac_sdio_pdata->power_on();

	ret = sdio_register_driver(&brcmf_sdmmc_driver);

	if (ret)
		brcmf_err("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)
static int brcmf_sdio_pd_remove(struct platform_device *pdev)
{
	brcmf_dbg(SDIO, "Enter\n");

	if (brcmfmac_sdio_pdata->power_off)
		brcmfmac_sdio_pdata->power_off();

	sdio_unregister_driver(&brcmf_sdmmc_driver);

	platform_driver_unregister(&brcmf_sdio_pd);
	return 0;
}

void brcmf_sdio_init(void)
{
	int ret;

	brcmf_dbg(SDIO, "Enter\n");

	ret = platform_driver_register(&brcmf_sdio_pd);

	if (ret)
		brcmf_err("platform_driver_register failed: %d\n", ret);
static struct platform_driver brcmf_sdio_pd = {
	.remove		= brcmf_sdio_pd_remove,
	.driver		= {
		.name	= BRCMFMAC_SDIO_PDATA_NAME
	}
#else
};

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

	if (brcmfmac_sdio_pdata)
		platform_driver_unregister(&brcmf_sdio_pd);
	else
		sdio_unregister_driver(&brcmf_sdmmc_driver);
}

@@ -681,9 +626,12 @@ void brcmf_sdio_init(void)

	brcmf_dbg(SDIO, "Enter\n");

	ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
	if (ret == -ENODEV) {
		brcmf_dbg(SDIO, "No platform data available, registering without.\n");
		ret = sdio_register_driver(&brcmf_sdmmc_driver);
	}

	if (ret)
		brcmf_err("sdio_register_driver failed: %d\n", ret);
		brcmf_err("driver registration failed: %d\n", ret);
}
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */
+15 −14
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/bcma/bcma.h>
#include <linux/debugfs.h>
#include <linux/vmalloc.h>
#include <linux/platform_data/brcmfmac-sdio.h>
#include <asm/unaligned.h>
#include <defs.h>
#include <brcmu_wifi.h>
@@ -517,7 +518,7 @@ static int qcount[NUMPRIO];
static int tx_packets[NUMPRIO];
#endif				/* DEBUG */

#define SDIO_DRIVE_STRENGTH	6	/* in milliamps */
#define DEFAULT_SDIO_DRIVE_STRENGTH	6	/* in milliamps */

#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)

@@ -2046,23 +2047,19 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
	bus->tx_seq = bus->rx_seq = 0;
}

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

	if (bus->sdiodev->oob_irq_requested) {
		spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
		if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
		enable_irq(bus->sdiodev->irq);
			enable_irq(bus->sdiodev->pdata->oob_irq_nr);
			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 inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
{
@@ -3639,6 +3636,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
	int err = 0;
	int reg_addr;
	u32 reg_val;
	u32 drivestrength;

	bus->alp_only = true;

@@ -3679,8 +3677,11 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
		goto fail;
	}

	brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci,
					  SDIO_DRIVE_STRENGTH);
	if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength))
		drivestrength = bus->sdiodev->pdata->drive_strength;
	else
		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
	brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);

	/* Get info on the SOCRAM cores... */
	bus->ramsize = bus->ci->ramsize;
+2 −4
Original line number Diff line number Diff line
@@ -174,13 +174,11 @@ struct brcmf_sdio_dev {
	wait_queue_head_t request_buffer_wait;
	struct device *dev;
	struct brcmf_bus *bus_if;
#ifdef CONFIG_BRCMFMAC_SDIO_OOB
	unsigned int irq;		/* oob interrupt number */
	unsigned long irq_flags;	/* board specific oob flags */
	struct brcmfmac_sdio_platform_data *pdata;
	bool oob_irq_requested;
	bool irq_en;			/* irq enable flags */
	spinlock_t irq_en_lock;
	bool irq_wake;			/* irq wake enable flags */
#endif		/* CONFIG_BRCMFMAC_SDIO_OOB */
};

/* Register/deregister interrupt handler. */
Loading