aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/brcm80211/brcmfmac
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile3
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c243
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c118
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/btcoex.c497
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/btcoex.h29
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd.h1
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c11
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c659
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c256
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/p2p.c98
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c369
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h101
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h32
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c197
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h14
15 files changed, 1895 insertions, 733 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index 598c8e2f8d2..8e9b1221b32 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -30,7 +30,8 @@ brcmfmac-objs += \
p2p.o \
dhd_cdc.o \
dhd_common.o \
- dhd_linux.o
+ dhd_linux.o \
+ btcoex.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
dhd_sdio.o \
bcmsdh.o \
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index f3149debede..4891e3df205 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -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,72 +59,12 @@ static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id)
return IRQ_HANDLED;
}
-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",
- &sdiodev->func[1]->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;
-
- sdio_claim_host(sdiodev->func[1]);
-
- /* must configure SDIO_CCCR_IENx to enable irq */
- data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
- data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
- brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
-
- /* redirect, configure and 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_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
-
- sdio_release_host(sdiodev->func[1]);
-
- return 0;
-}
-
-int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
-{
- brcmf_dbg(SDIO, "Entering\n");
-
- 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->irq_wake) {
- disable_irq_wake(sdiodev->irq);
- sdiodev->irq_wake = false;
- }
- free_irq(sdiodev->irq, &sdiodev->func[1]->dev);
- sdiodev->irq_en = false;
-
- return 0;
-}
-#else /* CONFIG_BRCMFMAC_SDIO_OOB */
-static void brcmf_sdio_irqhandler(struct sdio_func *func)
+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_dbg(INTR, "IB intr triggered\n");
brcmf_sdbrcm_isr(sdiodev->bus);
}
@@ -136,12 +76,56 @@ 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");
+ int ret = 0;
+ u8 data;
+ unsigned long flags;
- 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]);
+ 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) {
+ 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->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]);
+
+ /* must configure SDIO_CCCR_IENx to enable irq */
+ data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
+ data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
+ brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
+
+ /* redirect, configure and enable io for interrupt signal */
+ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
+ 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;
}
@@ -150,14 +134,31 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
{
brcmf_dbg(SDIO, "Entering\n");
- sdio_claim_host(sdiodev->func[1]);
- sdio_release_irq(sdiodev->func[2]);
- sdio_release_irq(sdiodev->func[1]);
- sdio_release_host(sdiodev->func[1]);
+ 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->pdata->oob_irq_nr);
+ sdiodev->irq_wake = false;
+ }
+ free_irq(sdiodev->pdata->oob_irq_nr,
+ &sdiodev->func[1]->dev);
+ sdiodev->irq_en = false;
+ }
+ } 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)
@@ -457,36 +458,80 @@ done:
return err;
}
-int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
- u8 *buf, uint nbytes)
+int
+brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+ u8 *data, uint size)
{
- struct sk_buff *mypkt;
- bool write = rw ? SDIOH_WRITE : SDIOH_READ;
- int err;
+ int bcmerror = 0;
+ struct sk_buff *pkt;
+ u32 sdaddr;
+ uint dsize;
+
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ pkt = dev_alloc_skb(dsize);
+ if (!pkt) {
+ brcmf_err("dev_alloc_skb failed: len %d\n", dsize);
+ return -EIO;
+ }
+ pkt->priority = 0;
- addr &= SBSDIO_SB_OFT_ADDR_MASK;
- addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+ /* Determine initial transfer parameters */
+ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+ if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+ dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+ else
+ dsize = size;
- mypkt = brcmu_pkt_buf_get_skb(nbytes);
- if (!mypkt) {
- brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
- nbytes);
- return -EIO;
+ sdio_claim_host(sdiodev->func[1]);
+
+ /* Do the transfer(s) */
+ while (size) {
+ /* Set the backplane window to include the start address */
+ bcmerror = brcmf_sdcard_set_sbaddr_window(sdiodev, address);
+ if (bcmerror)
+ break;
+
+ brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
+ write ? "write" : "read", dsize,
+ sdaddr, address & SBSDIO_SBWINDOW_MASK);
+
+ sdaddr &= SBSDIO_SB_OFT_ADDR_MASK;
+ sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+ skb_put(pkt, dsize);
+ if (write)
+ memcpy(pkt->data, data, dsize);
+ bcmerror = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC,
+ write, SDIO_FUNC_1,
+ sdaddr, pkt);
+ if (bcmerror) {
+ brcmf_err("membytes transfer failed\n");
+ break;
+ }
+ if (!write)
+ memcpy(data, pkt->data, dsize);
+ skb_trim(pkt, dsize);
+
+ /* Adjust for next transfer (if any) */
+ size -= dsize;
+ if (size) {
+ data += dsize;
+ address += dsize;
+ sdaddr = 0;
+ dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+ }
}
- /* For a write, copy the buffer data into the packet. */
- if (write)
- memcpy(mypkt->data, buf, nbytes);
+ dev_kfree_skb(pkt);
- err = brcmf_sdioh_request_buffer(sdiodev, SDIOH_DATA_INC, write,
- SDIO_FUNC_1, addr, mypkt);
+ /* Return the window to backplane enumeration space for core access */
+ if (brcmf_sdcard_set_sbaddr_window(sdiodev, sdiodev->sbwad))
+ brcmf_err("FAILED to set window back to 0x%x\n",
+ sdiodev->sbwad);
- /* For a read, copy the packet data back to the buffer. */
- if (!err && !write)
- memcpy(buf, mypkt->data, nbytes);
+ sdio_release_host(sdiodev->func[1]);
- brcmu_pkt_buf_free_skb(mypkt);
- return err;
+ return bcmerror;
}
int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 716548989e4..44fa0cdbf97 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -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>
@@ -40,32 +41,30 @@
#define DMA_ALIGN_MASK 0x03
+#define SDIO_DEVICE_ID_BROADCOM_43143 43143
#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
+#define SDIO_DEVICE_ID_BROADCOM_4335 0x4335
#define SDIO_FUNC1_BLOCKSIZE 64
#define SDIO_FUNC2_BLOCKSIZE 512
/* devices we support, null terminated */
static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335)},
{ /* end: all zeroes */ },
};
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)
@@ -424,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)
{
@@ -491,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);
@@ -594,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 = {
@@ -603,72 +573,51 @@ 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);
+static struct platform_driver brcmf_sdio_pd = {
+ .remove = brcmf_sdio_pd_remove,
+ .driver = {
+ .name = BRCMFMAC_SDIO_PDATA_NAME
+ }
+};
- if (ret)
- brcmf_err("platform_driver_register failed: %d\n", ret);
-}
-#else
void brcmf_sdio_exit(void)
{
brcmf_dbg(SDIO, "Enter\n");
- sdio_unregister_driver(&brcmf_sdmmc_driver);
+ if (brcmfmac_sdio_pdata)
+ platform_driver_unregister(&brcmf_sdio_pd);
+ else
+ sdio_unregister_driver(&brcmf_sdmmc_driver);
}
void brcmf_sdio_init(void)
@@ -677,9 +626,12 @@ void brcmf_sdio_init(void)
brcmf_dbg(SDIO, "Enter\n");
- ret = sdio_register_driver(&brcmf_sdmmc_driver);
+ 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 */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
new file mode 100644
index 00000000000..0cb591b050b
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <net/cfg80211.h>
+
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <defs.h>
+#include <dhd.h>
+#include <dhd_dbg.h>
+#include "fwil.h"
+#include "fwil_types.h"
+#include "btcoex.h"
+#include "p2p.h"
+#include "wl_cfg80211.h"
+
+/* T1 start SCO/eSCO priority suppression */
+#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000
+
+/* BT registers values during DHCP */
+#define BRCMF_BT_DHCP_REG50 0x8022
+#define BRCMF_BT_DHCP_REG51 0
+#define BRCMF_BT_DHCP_REG64 0
+#define BRCMF_BT_DHCP_REG65 0
+#define BRCMF_BT_DHCP_REG71 0
+#define BRCMF_BT_DHCP_REG66 0x2710
+#define BRCMF_BT_DHCP_REG41 0x33
+#define BRCMF_BT_DHCP_REG68 0x190
+
+/* number of samples for SCO detection */
+#define BRCMF_BT_SCO_SAMPLES 12
+
+/**
+* enum brcmf_btcoex_state - BT coex DHCP state machine states
+* @BRCMF_BT_DHCP_IDLE: DCHP is idle
+* @BRCMF_BT_DHCP_START: DHCP started, wait before
+* boosting wifi priority
+* @BRCMF_BT_DHCP_OPPR_WIN: graceful DHCP opportunity ended,
+* boost wifi priority
+* @BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: wifi priority boost end,
+* restore defaults
+*/
+enum brcmf_btcoex_state {
+ BRCMF_BT_DHCP_IDLE,
+ BRCMF_BT_DHCP_START,
+ BRCMF_BT_DHCP_OPPR_WIN,
+ BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT
+};
+
+/**
+ * struct brcmf_btcoex_info - BT coex related information
+ * @vif: interface for which request was done.
+ * @timer: timer for DHCP state machine
+ * @timeout: configured timeout.
+ * @timer_on: DHCP timer active
+ * @dhcp_done: DHCP finished before T1/T2 timer expiration
+ * @bt_state: DHCP state machine state
+ * @work: DHCP state machine work
+ * @cfg: driver private data for cfg80211 interface
+ * @reg66: saved value of btc_params 66
+ * @reg41: saved value of btc_params 41
+ * @reg68: saved value of btc_params 68
+ * @saved_regs_part1: flag indicating regs 66,41,68
+ * have been saved
+ * @reg51: saved value of btc_params 51
+ * @reg64: saved value of btc_params 64
+ * @reg65: saved value of btc_params 65
+ * @reg71: saved value of btc_params 71
+ * @saved_regs_part1: flag indicating regs 50,51,64,65,71
+ * have been saved
+ */
+struct brcmf_btcoex_info {
+ struct brcmf_cfg80211_vif *vif;
+ struct timer_list timer;
+ u16 timeout;
+ bool timer_on;
+ bool dhcp_done;
+ enum brcmf_btcoex_state bt_state;
+ struct work_struct work;
+ struct brcmf_cfg80211_info *cfg;
+ u32 reg66;
+ u32 reg41;
+ u32 reg68;
+ bool saved_regs_part1;
+ u32 reg50;
+ u32 reg51;
+ u32 reg64;
+ u32 reg65;
+ u32 reg71;
+ bool saved_regs_part2;
+};
+
+/**
+ * brcmf_btcoex_params_write() - write btc_params firmware variable
+ * @ifp: interface
+ * @addr: btc_params register number
+ * @data: data to write
+ */
+static s32 brcmf_btcoex_params_write(struct brcmf_if *ifp, u32 addr, u32 data)
+{
+ struct {
+ __le32 addr;
+ __le32 data;
+ } reg_write;
+
+ reg_write.addr = cpu_to_le32(addr);
+ reg_write.data = cpu_to_le32(data);
+ return brcmf_fil_iovar_data_set(ifp, "btc_params",
+ &reg_write, sizeof(reg_write));
+}
+
+/**
+ * brcmf_btcoex_params_read() - read btc_params firmware variable
+ * @ifp: interface
+ * @addr: btc_params register number
+ * @data: read data
+ */
+static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data)
+{
+ *data = addr;
+
+ return brcmf_fil_iovar_int_get(ifp, "btc_params", data);
+}
+
+/**
+ * brcmf_btcoex_boost_wifi() - control BT SCO/eSCO parameters
+ * @btci: BT coex info
+ * @trump_sco:
+ * true - set SCO/eSCO parameters for compatibility
+ * during DHCP window
+ * false - restore saved parameter values
+ *
+ * Enhanced BT COEX settings for eSCO compatibility during DHCP window
+ */
+static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
+ bool trump_sco)
+{
+ struct brcmf_if *ifp = btci->cfg->pub->iflist[0];
+
+ if (trump_sco && !btci->saved_regs_part2) {
+ /* this should reduce eSCO agressive
+ * retransmit w/o breaking it
+ */
+
+ /* save current */
+ brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n");
+ brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
+ brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
+ brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
+ brcmf_btcoex_params_read(ifp, 65, &btci->reg65);
+ brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
+
+ btci->saved_regs_part2 = true;
+ brcmf_dbg(TRACE,
+ "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ btci->reg50, btci->reg51, btci->reg64,
+ btci->reg65, btci->reg71);
+
+ /* pacify the eSco */
+ brcmf_btcoex_params_write(ifp, 50, BRCMF_BT_DHCP_REG50);
+ brcmf_btcoex_params_write(ifp, 51, BRCMF_BT_DHCP_REG51);
+ brcmf_btcoex_params_write(ifp, 64, BRCMF_BT_DHCP_REG64);
+ brcmf_btcoex_params_write(ifp, 65, BRCMF_BT_DHCP_REG65);
+ brcmf_btcoex_params_write(ifp, 71, BRCMF_BT_DHCP_REG71);
+
+ } else if (btci->saved_regs_part2) {
+ /* restore previously saved bt params */
+ brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n");
+ brcmf_btcoex_params_write(ifp, 50, btci->reg50);
+ brcmf_btcoex_params_write(ifp, 51, btci->reg51);
+ brcmf_btcoex_params_write(ifp, 64, btci->reg64);
+ brcmf_btcoex_params_write(ifp, 65, btci->reg65);
+ brcmf_btcoex_params_write(ifp, 71, btci->reg71);
+
+ brcmf_dbg(TRACE,
+ "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ btci->reg50, btci->reg51, btci->reg64,
+ btci->reg65, btci->reg71);
+
+ btci->saved_regs_part2 = false;
+ } else {
+ brcmf_err("attempted to restore not saved BTCOEX params\n");
+ }
+}
+
+/**
+ * brcmf_btcoex_is_sco_active() - check if SCO/eSCO is active
+ * @ifp: interface
+ *
+ * return: true if SCO/eSCO session is active
+ */
+static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
+{
+ int ioc_res = 0;
+ bool res = false;
+ int sco_id_cnt = 0;
+ u32 param27;
+ int i;
+
+ for (i = 0; i < BRCMF_BT_SCO_SAMPLES; i++) {
+ ioc_res = brcmf_btcoex_params_read(ifp, 27, &param27);
+
+ if (ioc_res < 0) {
+ brcmf_err("ioc read btc params error\n");
+ break;
+ }
+
+ brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27);
+
+ if ((param27 & 0x6) == 2) { /* count both sco & esco */
+ sco_id_cnt++;
+ }
+
+ if (sco_id_cnt > 2) {
+ brcmf_dbg(TRACE,
+ "sco/esco detected, pkt id_cnt:%d samples:%d\n",
+ sco_id_cnt, i);
+ res = true;
+ break;
+ }
+ }
+ brcmf_dbg(TRACE, "exit: result=%d\n", res);
+ return res;
+}
+
+/**
+ * btcmf_btcoex_save_part1() - save first step parameters.
+ */
+static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp = btci->vif->ifp;
+
+ if (!btci->saved_regs_part1) {
+ /* Retrieve and save original reg value */
+ brcmf_btcoex_params_read(ifp, 66, &btci->reg66);
+ brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
+ brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
+ btci->saved_regs_part1 = true;
+ brcmf_dbg(TRACE,
+ "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
+ btci->reg66, btci->reg41,
+ btci->reg68);
+ }
+}
+
+/**
+ * brcmf_btcoex_restore_part1() - restore first step parameters.
+ */
+static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp;
+
+ if (btci->saved_regs_part1) {
+ btci->saved_regs_part1 = false;
+ ifp = btci->vif->ifp;
+ brcmf_btcoex_params_write(ifp, 66, btci->reg66);
+ brcmf_btcoex_params_write(ifp, 41, btci->reg41);
+ brcmf_btcoex_params_write(ifp, 68, btci->reg68);
+ brcmf_dbg(TRACE,
+ "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
+ btci->reg66, btci->reg41,
+ btci->reg68);
+ }
+}
+
+/**
+ * brcmf_btcoex_timerfunc() - BT coex timer callback
+ */
+static void brcmf_btcoex_timerfunc(ulong data)
+{
+ struct brcmf_btcoex_info *bt_local = (struct brcmf_btcoex_info *)data;
+ brcmf_dbg(TRACE, "enter\n");
+
+ bt_local->timer_on = false;
+ schedule_work(&bt_local->work);
+}
+
+/**
+ * brcmf_btcoex_handler() - BT coex state machine work handler
+ * @work: work
+ */
+static void brcmf_btcoex_handler(struct work_struct *work)
+{
+ struct brcmf_btcoex_info *btci;
+ btci = container_of(work, struct brcmf_btcoex_info, work);
+ if (btci->timer_on) {
+ btci->timer_on = false;
+ del_timer_sync(&btci->timer);
+ }
+
+ switch (btci->bt_state) {
+ case BRCMF_BT_DHCP_START:
+ /* DHCP started provide OPPORTUNITY window
+ to get DHCP address
+ */
+ brcmf_dbg(TRACE, "DHCP started\n");
+ btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
+ if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
+ mod_timer(&btci->timer, btci->timer.expires);
+ } else {
+ btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME;
+ mod_timer(&btci->timer,
+ jiffies +
+ msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME));
+ }
+ btci->timer_on = true;
+ break;
+
+ case BRCMF_BT_DHCP_OPPR_WIN:
+ if (btci->dhcp_done) {
+ brcmf_dbg(TRACE, "DHCP done before T1 expiration\n");
+ goto idle;
+ }
+
+ /* DHCP is not over yet, start lowering BT priority */
+ brcmf_dbg(TRACE, "DHCP T1:%d expired\n",
+ BRCMF_BTCOEX_OPPR_WIN_TIME);
+ brcmf_btcoex_boost_wifi(btci, true);
+
+ btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT;
+ mod_timer(&btci->timer,
+ jiffies + msecs_to_jiffies(btci->timeout));
+ btci->timer_on = true;
+ break;
+
+ case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
+ if (btci->dhcp_done)
+ brcmf_dbg(TRACE, "DHCP done before T2 expiration\n");
+ else
+ brcmf_dbg(TRACE, "DHCP T2:%d expired\n",
+ BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
+
+ goto idle;
+
+ default:
+ brcmf_err("invalid state=%d !!!\n", btci->bt_state);
+ goto idle;
+ }
+
+ return;
+
+idle:
+ btci->bt_state = BRCMF_BT_DHCP_IDLE;
+ btci->timer_on = false;
+ brcmf_btcoex_boost_wifi(btci, false);
+ cfg80211_crit_proto_stopped(&btci->vif->wdev, GFP_KERNEL);
+ brcmf_btcoex_restore_part1(btci);
+ btci->vif = NULL;
+}
+
+/**
+ * brcmf_btcoex_attach() - initialize BT coex data
+ * @cfg: driver private cfg80211 data
+ *
+ * return: 0 on success
+ */
+int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg)
+{
+ struct brcmf_btcoex_info *btci = NULL;
+ brcmf_dbg(TRACE, "enter\n");
+
+ btci = kmalloc(sizeof(struct brcmf_btcoex_info), GFP_KERNEL);
+ if (!btci)
+ return -ENOMEM;
+
+ btci->bt_state = BRCMF_BT_DHCP_IDLE;
+
+ /* Set up timer for BT */
+ btci->timer_on = false;
+ btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME;
+ init_timer(&btci->timer);
+ btci->timer.data = (ulong)btci;
+ btci->timer.function = brcmf_btcoex_timerfunc;
+ btci->cfg = cfg;
+ btci->saved_regs_part1 = false;
+ btci->saved_regs_part2 = false;
+
+ INIT_WORK(&btci->work, brcmf_btcoex_handler);
+
+ cfg->btcoex = btci;
+ return 0;
+}
+
+/**
+ * brcmf_btcoex_detach - clean BT coex data
+ * @cfg: driver private cfg80211 data
+ */
+void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg)
+{
+ brcmf_dbg(TRACE, "enter\n");
+
+ if (!cfg->btcoex)
+ return;
+
+ if (cfg->btcoex->timer_on) {
+ cfg->btcoex->timer_on = false;
+ del_timer_sync(&cfg->btcoex->timer);
+ }
+
+ cancel_work_sync(&cfg->btcoex->work);
+
+ brcmf_btcoex_boost_wifi(cfg->btcoex, false);
+ brcmf_btcoex_restore_part1(cfg->btcoex);
+
+ kfree(cfg->btcoex);
+ cfg->btcoex = NULL;
+}
+
+static void brcmf_btcoex_dhcp_start(struct brcmf_btcoex_info *btci)
+{
+ struct brcmf_if *ifp = btci->vif->ifp;
+
+ btcmf_btcoex_save_part1(btci);
+ /* set new regs values */
+ brcmf_btcoex_params_write(ifp, 66, BRCMF_BT_DHCP_REG66);
+ brcmf_btcoex_params_write(ifp, 41, BRCMF_BT_DHCP_REG41);
+ brcmf_btcoex_params_write(ifp, 68, BRCMF_BT_DHCP_REG68);
+ btci->dhcp_done = false;
+ btci->bt_state = BRCMF_BT_DHCP_START;
+ schedule_work(&btci->work);
+ brcmf_dbg(TRACE, "enable BT DHCP Timer\n");
+}
+
+static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
+{
+ /* Stop any bt timer because DHCP session is done */
+ btci->dhcp_done = true;
+ if (btci->timer_on) {
+ brcmf_dbg(TRACE, "disable BT DHCP Timer\n");
+ btci->timer_on = false;
+ del_timer_sync(&btci->timer);
+
+ /* schedule worker if transition to IDLE is needed */
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
+ brcmf_dbg(TRACE, "bt_state:%d\n",
+ btci->bt_state);
+ schedule_work(&btci->work);
+ }
+ } else {
+ /* Restore original values */
+ brcmf_btcoex_restore_part1(btci);
+ }
+}
+
+/**
+ * brcmf_btcoex_set_mode - set BT coex mode
+ * @cfg: driver private cfg80211 data
+ * @mode: Wifi-Bluetooth coexistence mode
+ *
+ * return: 0 on success
+ */
+int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
+ enum brcmf_btcoex_mode mode, u16 duration)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy);
+ struct brcmf_btcoex_info *btci = cfg->btcoex;
+ struct brcmf_if *ifp = cfg->pub->iflist[0];
+
+ switch (mode) {
+ case BRCMF_BTCOEX_DISABLED:
+ brcmf_dbg(TRACE, "DHCP session starts\n");
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
+ return -EBUSY;
+ /* Start BT timer only for SCO connection */
+ if (brcmf_btcoex_is_sco_active(ifp)) {
+ btci->timeout = duration;
+ btci->vif = vif;
+ brcmf_btcoex_dhcp_start(btci);
+ }
+ break;
+
+ case BRCMF_BTCOEX_ENABLED:
+ brcmf_dbg(TRACE, "DHCP session ends\n");
+ if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
+ vif == btci->vif) {
+ brcmf_btcoex_dhcp_end(btci);
+ }
+ break;
+ default:
+ brcmf_dbg(TRACE, "Unknown mode, ignored\n");
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h
new file mode 100644
index 00000000000..19647c68aa9
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2013 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef WL_BTCOEX_H_
+#define WL_BTCOEX_H_
+
+enum brcmf_btcoex_mode {
+ BRCMF_BTCOEX_DISABLED,
+ BRCMF_BTCOEX_ENABLED
+};
+
+int brcmf_btcoex_attach(struct brcmf_cfg80211_info *cfg);
+void brcmf_btcoex_detach(struct brcmf_cfg80211_info *cfg);
+int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
+ enum brcmf_btcoex_mode mode, u16 duration);
+
+#endif /* WL_BTCOEX_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 5249c67b466..28db9cf3967 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -28,6 +28,7 @@
/*******************************************************************************
* IO codes that are interpreted by dongle firmware
******************************************************************************/
+#define BRCMF_C_GET_VERSION 1
#define BRCMF_C_UP 2
#define BRCMF_C_DOWN 3
#define BRCMF_C_SET_PROMISC 10
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 763a84eba21..59c25463e42 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -754,14 +754,14 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
/* this is P2P_DEVICE interface */
brcmf_dbg(INFO, "allocate non-netdev interface\n");
ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
+ if (!ifp)
+ return ERR_PTR(-ENOMEM);
} else {
brcmf_dbg(INFO, "allocate netdev interface\n");
/* Allocate netdev, including space for private structure */
ndev = alloc_netdev(sizeof(*ifp), name, ether_setup);
- if (!ndev) {
- brcmf_err("OOM - alloc_netdev\n");
+ if (!ndev)
return ERR_PTR(-ENOMEM);
- }
ifp = netdev_priv(ndev);
ifp->ndev = ndev;
@@ -899,7 +899,10 @@ int brcmf_bus_start(struct device *dev)
goto fail;
drvr->fw_signals = true;
- (void)brcmf_fws_init(drvr);
+ ret = brcmf_fws_init(drvr);
+ if (ret < 0)
+ goto fail;
+
brcmf_fws_add_interface(ifp);
drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 4ff2d3c52ee..d2487518bd2 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -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>
@@ -324,6 +325,9 @@ MODULE_FIRMWARE(BRCMF_SDIO_NV_NAME);
*/
#define BRCMF_IDLE_INTERVAL 1
+#define KSO_WAIT_US 50
+#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
+
/*
* Conversion of 802.1D priority to precedence level
*/
@@ -333,95 +337,6 @@ static uint prio2prec(u32 prio)
(prio^2) : prio;
}
-/* core registers */
-struct sdpcmd_regs {
- u32 corecontrol; /* 0x00, rev8 */
- u32 corestatus; /* rev8 */
- u32 PAD[1];
- u32 biststatus; /* rev8 */
-
- /* PCMCIA access */
- u16 pcmciamesportaladdr; /* 0x010, rev8 */
- u16 PAD[1];
- u16 pcmciamesportalmask; /* rev8 */
- u16 PAD[1];
- u16 pcmciawrframebc; /* rev8 */
- u16 PAD[1];
- u16 pcmciaunderflowtimer; /* rev8 */
- u16 PAD[1];
-
- /* interrupt */
- u32 intstatus; /* 0x020, rev8 */
- u32 hostintmask; /* rev8 */
- u32 intmask; /* rev8 */
- u32 sbintstatus; /* rev8 */
- u32 sbintmask; /* rev8 */
- u32 funcintmask; /* rev4 */
- u32 PAD[2];
- u32 tosbmailbox; /* 0x040, rev8 */
- u32 tohostmailbox; /* rev8 */
- u32 tosbmailboxdata; /* rev8 */
- u32 tohostmailboxdata; /* rev8 */
-
- /* synchronized access to registers in SDIO clock domain */
- u32 sdioaccess; /* 0x050, rev8 */
- u32 PAD[3];
-
- /* PCMCIA frame control */
- u8 pcmciaframectrl; /* 0x060, rev8 */
- u8 PAD[3];
- u8 pcmciawatermark; /* rev8 */
- u8 PAD[155];
-
- /* interrupt batching control */
- u32 intrcvlazy; /* 0x100, rev8 */
- u32 PAD[3];
-
- /* counters */
- u32 cmd52rd; /* 0x110, rev8 */
- u32 cmd52wr; /* rev8 */
- u32 cmd53rd; /* rev8 */
- u32 cmd53wr; /* rev8 */
- u32 abort; /* rev8 */
- u32 datacrcerror; /* rev8 */
- u32 rdoutofsync; /* rev8 */
- u32 wroutofsync; /* rev8 */
- u32 writebusy; /* rev8 */
- u32 readwait; /* rev8 */
- u32 readterm; /* rev8 */
- u32 writeterm; /* rev8 */
- u32 PAD[40];
- u32 clockctlstatus; /* rev8 */
- u32 PAD[7];
-
- u32 PAD[128]; /* DMA engines */
-
- /* SDIO/PCMCIA CIS region */
- char cis[512]; /* 0x400-0x5ff, rev6 */
-
- /* PCMCIA function control registers */
- char pcmciafcr[256]; /* 0x600-6ff, rev6 */
- u16 PAD[55];
-
- /* PCMCIA backplane access */
- u16 backplanecsr; /* 0x76E, rev6 */
- u16 backplaneaddr0; /* rev6 */
- u16 backplaneaddr1; /* rev6 */
- u16 backplaneaddr2; /* rev6 */
- u16 backplaneaddr3; /* rev6 */
- u16 backplanedata0; /* rev6 */
- u16 backplanedata1; /* rev6 */
- u16 backplanedata2; /* rev6 */
- u16 backplanedata3; /* rev6 */
- u16 PAD[31];
-
- /* sprom "size" & "blank" info */
- u16 spromstatus; /* 0x7BE, rev2 */
- u32 PAD[464];
-
- u16 PAD[0x80];
-};
-
#ifdef DEBUG
/* Device console log buffer state */
struct brcmf_console {
@@ -588,12 +503,14 @@ struct brcmf_sdio {
bool txoff; /* Transmit flow-controlled */
struct brcmf_sdio_count sdcnt;
+ bool sr_enabled; /* SaveRestore enabled */
+ bool sleeping; /* SDIO bus sleeping */
};
/* clkstate */
#define CLK_NONE 0
#define CLK_SDONLY 1
-#define CLK_PENDING 2 /* Not used yet */
+#define CLK_PENDING 2
#define CLK_AVAIL 3
#ifdef DEBUG
@@ -601,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)
@@ -665,6 +582,62 @@ w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
return ret;
}
+static int
+brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on)
+{
+ u8 wr_val = 0, rd_val, cmp_val, bmask;
+ int err = 0;
+ int try_cnt = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ /* 1st KSO write goes to AOS wake up core if device is asleep */
+ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ wr_val, &err);
+ if (err) {
+ brcmf_err("SDIO_AOS KSO write error: %d\n", err);
+ return err;
+ }
+
+ if (on) {
+ /* device WAKEUP through KSO:
+ * write bit 0 & read back until
+ * both bits 0 (kso bit) & 1 (dev on status) are set
+ */
+ cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK |
+ SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK;
+ bmask = cmp_val;
+ usleep_range(2000, 3000);
+ } else {
+ /* Put device to sleep, turn off KSO */
+ cmp_val = 0;
+ /* only check for bit0, bit1(dev on status) may not
+ * get cleared right away
+ */
+ bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK;
+ }
+
+ do {
+ /* reliable KSO bit set/clr:
+ * the sdiod sleep write access is synced to PMU 32khz clk
+ * just one write attempt may fail,
+ * read it back until it matches written value
+ */
+ rd_val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ &err);
+ if (((rd_val & bmask) == cmp_val) && !err)
+ break;
+ brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n",
+ try_cnt, MAX_KSO_ATTEMPTS, err);
+ udelay(KSO_WAIT_US);
+ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ wr_val, &err);
+ } while (try_cnt++ < MAX_KSO_ATTEMPTS);
+
+ return err;
+}
+
#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
@@ -680,6 +653,11 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
clkctl = 0;
+ if (bus->sr_enabled) {
+ bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY);
+ return 0;
+ }
+
if (on) {
/* Request HT Avail */
clkreq =
@@ -856,6 +834,63 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
return 0;
}
+static int
+brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
+{
+ int err = 0;
+ brcmf_dbg(TRACE, "Enter\n");
+ brcmf_dbg(SDIO, "request %s currently %s\n",
+ (sleep ? "SLEEP" : "WAKE"),
+ (bus->sleeping ? "SLEEP" : "WAKE"));
+
+ /* If SR is enabled control bus state with KSO */
+ if (bus->sr_enabled) {
+ /* Done if we're already in the requested state */
+ if (sleep == bus->sleeping)
+ goto end;
+
+ /* Going to sleep */
+ if (sleep) {
+ /* Don't sleep if something is pending */
+ if (atomic_read(&bus->intstatus) ||
+ atomic_read(&bus->ipend) > 0 ||
+ (!atomic_read(&bus->fcstate) &&
+ brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
+ data_ok(bus)))
+ return -EBUSY;
+ err = brcmf_sdbrcm_kso_control(bus, false);
+ /* disable watchdog */
+ if (!err)
+ brcmf_sdbrcm_wd_timer(bus, 0);
+ } else {
+ bus->idlecount = 0;
+ err = brcmf_sdbrcm_kso_control(bus, true);
+ }
+ if (!err) {
+ /* Change state */
+ bus->sleeping = sleep;
+ brcmf_dbg(SDIO, "new state %s\n",
+ (sleep ? "SLEEP" : "WAKE"));
+ } else {
+ brcmf_err("error while changing bus sleep state %d\n",
+ err);
+ return err;
+ }
+ }
+
+end:
+ /* control clocks */
+ if (sleep) {
+ if (!bus->sr_enabled)
+ brcmf_sdbrcm_clkctl(bus, CLK_NONE, pendok);
+ } else {
+ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, pendok);
+ }
+
+ return err;
+
+}
+
static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
{
u32 intstatus = 0;
@@ -1960,7 +1995,7 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
sdio_claim_host(bus->sdiodev->func[1]);
/* Enable clock for device interrupts */
- brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ brcmf_sdbrcm_bus_sleep(bus, false, false);
/* Disable and clear interrupts at the chip level also */
w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
@@ -2012,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;
- spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags);
- if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) {
- enable_irq(bus->sdiodev->irq);
- bus->sdiodev->irq_en = true;
+ 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->pdata->oob_irq_nr);
+ bus->sdiodev->irq_en = true;
+ }
+ spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags);
}
- 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)
{
@@ -2096,7 +2127,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
sdio_claim_host(bus->sdiodev->func[1]);
/* If waiting for HTAVAIL, check status */
- if (bus->clkstate == CLK_PENDING) {
+ if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) {
u8 clkctl, devctl = 0;
#ifdef DEBUG
@@ -2142,7 +2173,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
}
/* Make sure backplane clock is on */
- brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
+ brcmf_sdbrcm_bus_sleep(bus, false, true);
/* Pending interrupt indicates new device status */
if (atomic_read(&bus->ipend) > 0) {
@@ -2288,8 +2319,9 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
if ((bus->clkstate != CLK_PENDING)
&& bus->idletime == BRCMF_IDLE_IMMEDIATE) {
bus->activity = false;
+ brcmf_dbg(SDIO, "idle state\n");
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+ brcmf_sdbrcm_bus_sleep(bus, true, false);
sdio_release_host(bus->sdiodev->func[1]);
}
}
@@ -2362,69 +2394,6 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
return ret;
}
-static int
-brcmf_sdbrcm_membytes(struct brcmf_sdio *bus, bool write, u32 address, u8 *data,
- uint size)
-{
- int bcmerror = 0;
- u32 sdaddr;
- uint dsize;
-
- /* Determine initial transfer parameters */
- sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
- if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
- dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
- else
- dsize = size;
-
- sdio_claim_host(bus->sdiodev->func[1]);
-
- /* Set the backplane window to include the start address */
- bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address);
- if (bcmerror) {
- brcmf_err("window change failed\n");
- goto xfer_done;
- }
-
- /* Do the transfer(s) */
- while (size) {
- brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
- write ? "write" : "read", dsize,
- sdaddr, address & SBSDIO_SBWINDOW_MASK);
- bcmerror = brcmf_sdcard_rwdata(bus->sdiodev, write,
- sdaddr, data, dsize);
- if (bcmerror) {
- brcmf_err("membytes transfer failed\n");
- break;
- }
-
- /* Adjust for next transfer (if any) */
- size -= dsize;
- if (size) {
- data += dsize;
- address += dsize;
- bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev,
- address);
- if (bcmerror) {
- brcmf_err("window change failed\n");
- break;
- }
- sdaddr = 0;
- dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
- }
- }
-
-xfer_done:
- /* Return the window to backplane enumeration space for core access */
- if (brcmf_sdcard_set_sbaddr_window(bus->sdiodev, bus->sdiodev->sbwad))
- brcmf_err("FAILED to set window back to 0x%x\n",
- bus->sdiodev->sbwad);
-
- sdio_release_host(bus->sdiodev->func[1]);
-
- return bcmerror;
-}
-
#ifdef DEBUG
#define CONSOLE_LINE_MAX 192
@@ -2441,8 +2410,8 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)
/* Read console log struct */
addr = bus->console_addr + offsetof(struct rte_console, log_le);
- rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&c->log_le,
- sizeof(c->log_le));
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
+ sizeof(c->log_le));
if (rv < 0)
return rv;
@@ -2467,7 +2436,7 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)
/* Read the console buffer */
addr = le32_to_cpu(c->log_le.buf);
- rv = brcmf_sdbrcm_membytes(bus, false, addr, c->buf, c->bufsize);
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
if (rv < 0)
return rv;
@@ -2592,7 +2561,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
/* Make sure backplane clock is on */
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ brcmf_sdbrcm_bus_sleep(bus, false, false);
sdio_release_host(bus->sdiodev->func[1]);
/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
@@ -2650,6 +2619,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
bus->activity = false;
sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_dbg(INFO, "idle\n");
brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
sdio_release_host(bus->sdiodev->func[1]);
} else {
@@ -2679,16 +2649,15 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
struct sdpcm_shared_le sh_le;
__le32 addr_le;
- shaddr = bus->ramsize - 4;
+ shaddr = bus->ci->rambase + bus->ramsize - 4;
/*
* Read last word in socram to determine
* address of sdpcm_shared structure
*/
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
- rv = brcmf_sdbrcm_membytes(bus, false, shaddr,
- (u8 *)&addr_le, 4);
+ brcmf_sdbrcm_bus_sleep(bus, false, false);
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
sdio_release_host(bus->sdiodev->func[1]);
if (rv < 0)
return rv;
@@ -2708,8 +2677,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
}
/* Read hndrte_shared structure */
- rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_le,
- sizeof(struct sdpcm_shared_le));
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
+ sizeof(struct sdpcm_shared_le));
if (rv < 0)
return rv;
@@ -2745,22 +2714,22 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
/* obtain console information from device memory */
addr = sh->console_addr + offsetof(struct rte_console, log_le);
- rv = brcmf_sdbrcm_membytes(bus, false, addr,
- (u8 *)&sh_val, sizeof(u32));
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
if (rv < 0)
return rv;
console_ptr = le32_to_cpu(sh_val);
addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
- rv = brcmf_sdbrcm_membytes(bus, false, addr,
- (u8 *)&sh_val, sizeof(u32));
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
if (rv < 0)
return rv;
console_size = le32_to_cpu(sh_val);
addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
- rv = brcmf_sdbrcm_membytes(bus, false, addr,
- (u8 *)&sh_val, sizeof(u32));
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
if (rv < 0)
return rv;
console_index = le32_to_cpu(sh_val);
@@ -2774,8 +2743,8 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
/* obtain the console data from device */
conbuf[console_size] = '\0';
- rv = brcmf_sdbrcm_membytes(bus, false, console_ptr, (u8 *)conbuf,
- console_size);
+ rv = brcmf_sdio_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
+ console_size);
if (rv < 0)
goto done;
@@ -2812,8 +2781,8 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
return 0;
}
- error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr,
- sizeof(struct brcmf_trap_info));
+ error = brcmf_sdio_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
+ sizeof(struct brcmf_trap_info));
if (error < 0)
return error;
@@ -2856,14 +2825,14 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
sdio_claim_host(bus->sdiodev->func[1]);
if (sh->assert_file_addr != 0) {
- error = brcmf_sdbrcm_membytes(bus, false, sh->assert_file_addr,
- (u8 *)file, 80);
+ error = brcmf_sdio_ramrw(bus->sdiodev, false,
+ sh->assert_file_addr, (u8 *)file, 80);
if (error < 0)
return error;
}
if (sh->assert_exp_addr != 0) {
- error = brcmf_sdbrcm_membytes(bus, false, sh->assert_exp_addr,
- (u8 *)expr, 80);
+ error = brcmf_sdio_ramrw(bus->sdiodev, false,
+ sh->assert_exp_addr, (u8 *)expr, 80);
if (error < 0)
return error;
}
@@ -3021,84 +2990,8 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
return rxlen ? (int)rxlen : -ETIMEDOUT;
}
-static int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus)
-{
- int bcmerror = 0;
- u32 varaddr;
- u32 varsizew;
- __le32 varsizew_le;
-#ifdef DEBUG
- char *nvram_ularray;
-#endif /* DEBUG */
-
- /* Even if there are no vars are to be written, we still
- need to set the ramsize. */
- varaddr = (bus->ramsize - 4) - bus->varsz;
-
- if (bus->vars) {
- /* Write the vars list */
- bcmerror = brcmf_sdbrcm_membytes(bus, true, varaddr,
- bus->vars, bus->varsz);
-#ifdef DEBUG
- /* Verify NVRAM bytes */
- brcmf_dbg(INFO, "Compare NVRAM dl & ul; varsize=%d\n",
- bus->varsz);
- nvram_ularray = kmalloc(bus->varsz, GFP_ATOMIC);
- if (!nvram_ularray)
- return -ENOMEM;
-
- /* Upload image to verify downloaded contents. */
- memset(nvram_ularray, 0xaa, bus->varsz);
-
- /* Read the vars list to temp buffer for comparison */
- bcmerror = brcmf_sdbrcm_membytes(bus, false, varaddr,
- nvram_ularray, bus->varsz);
- if (bcmerror) {
- brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n",
- bcmerror, bus->varsz, varaddr);
- }
- /* Compare the org NVRAM with the one read from RAM */
- if (memcmp(bus->vars, nvram_ularray, bus->varsz))
- brcmf_err("Downloaded NVRAM image is corrupted\n");
- else
- brcmf_err("Download/Upload/Compare of NVRAM ok\n");
-
- kfree(nvram_ularray);
-#endif /* DEBUG */
- }
-
- /* adjust to the user specified RAM */
- brcmf_dbg(INFO, "Physical memory size: %d\n", bus->ramsize);
- brcmf_dbg(INFO, "Vars are at %d, orig varsize is %d\n",
- varaddr, bus->varsz);
-
- /*
- * Determine the length token:
- * Varsize, converted to words, in lower 16-bits, checksum
- * in upper 16-bits.
- */
- if (bcmerror) {
- varsizew = 0;
- varsizew_le = cpu_to_le32(0);
- } else {
- varsizew = bus->varsz / 4;
- varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
- varsizew_le = cpu_to_le32(varsizew);
- }
-
- brcmf_dbg(INFO, "New varsize is %d, length token=0x%08x\n",
- bus->varsz, varsizew);
-
- /* Write the length token to the last word */
- bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->ramsize - 4),
- (u8 *)&varsizew_le, 4);
-
- return bcmerror;
-}
-
-static int brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
+static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
{
- int bcmerror = 0;
struct chip_info *ci = bus->ci;
/* To enter download state, disable ARM and reset SOCRAM.
@@ -3107,41 +3000,19 @@ static int brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
if (enter) {
bus->alp_only = true;
- ci->coredisable(bus->sdiodev, ci, BCMA_CORE_ARM_CM3);
-
- ci->resetcore(bus->sdiodev, ci, BCMA_CORE_INTERNAL_MEM);
-
- /* Clear the top bit of memory */
- if (bus->ramsize) {
- u32 zeros = 0;
- brcmf_sdbrcm_membytes(bus, true, bus->ramsize - 4,
- (u8 *)&zeros, 4);
- }
+ brcmf_sdio_chip_enter_download(bus->sdiodev, ci);
} else {
- if (!ci->iscoreup(bus->sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
- brcmf_err("SOCRAM core is down after reset?\n");
- bcmerror = -EBADE;
- goto fail;
- }
-
- bcmerror = brcmf_sdbrcm_write_vars(bus);
- if (bcmerror) {
- brcmf_err("no vars written to RAM\n");
- bcmerror = 0;
- }
-
- w_sdreg32(bus, 0xFFFFFFFF,
- offsetof(struct sdpcmd_regs, intstatus));
-
- ci->resetcore(bus->sdiodev, ci, BCMA_CORE_ARM_CM3);
+ if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci, bus->vars,
+ bus->varsz))
+ return false;
/* Allow HT Clock now that the ARM is running. */
bus->alp_only = false;
bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD;
}
-fail:
- return bcmerror;
+
+ return true;
}
static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus)
@@ -3156,10 +3027,11 @@ static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus)
static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
{
- int offset = 0;
+ int offset;
uint len;
u8 *memblock = NULL, *memptr;
int ret;
+ u8 idx;
brcmf_dbg(INFO, "Enter\n");
@@ -3180,10 +3052,15 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
memptr += (BRCMF_SDALIGN -
((u32)(unsigned long)memblock % BRCMF_SDALIGN));
+ offset = bus->ci->rambase;
+
/* Download image */
- while ((len =
- brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus))) {
- ret = brcmf_sdbrcm_membytes(bus, true, offset, memptr, len);
+ len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus);
+ idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4);
+ if (BRCMF_MAX_CORENUM != idx)
+ memcpy(&bus->ci->rst_vec, memptr, sizeof(bus->ci->rst_vec));
+ while (len) {
+ ret = brcmf_sdio_ramrw(bus->sdiodev, true, offset, memptr, len);
if (ret) {
brcmf_err("error %d on writing %d membytes at 0x%08x\n",
ret, MEMBLOCK, offset);
@@ -3191,6 +3068,7 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
}
offset += MEMBLOCK;
+ len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus);
}
err:
@@ -3298,7 +3176,7 @@ static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
int bcmerror = -1;
/* Keep arm in reset */
- if (brcmf_sdbrcm_download_state(bus, true)) {
+ if (!brcmf_sdbrcm_download_state(bus, true)) {
brcmf_err("error placing ARM core in reset\n");
goto err;
}
@@ -3314,7 +3192,7 @@ static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
}
/* Take arm out of reset */
- if (brcmf_sdbrcm_download_state(bus, false)) {
+ if (!brcmf_sdbrcm_download_state(bus, false)) {
brcmf_err("error getting out of ARM core reset\n");
goto err;
}
@@ -3325,6 +3203,103 @@ err:
return bcmerror;
}
+static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus)
+{
+ u32 addr, reg;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* old chips with PMU version less than 17 don't support save restore */
+ if (bus->ci->pmurev < 17)
+ return false;
+
+ /* read PMU chipcontrol register 3*/
+ addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr);
+ brcmf_sdio_regwl(bus->sdiodev, addr, 3, NULL);
+ addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data);
+ reg = brcmf_sdio_regrl(bus->sdiodev, addr, NULL);
+
+ return (bool)reg;
+}
+
+static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus)
+{
+ int err = 0;
+ u8 val;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL,
+ &err);
+ if (err) {
+ brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
+ return;
+ }
+
+ val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
+ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL,
+ val, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
+ return;
+ }
+
+ /* Add CMD14 Support */
+ brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
+ (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
+ SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
+ &err);
+ if (err) {
+ brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
+ return;
+ }
+
+ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ SBSDIO_FORCE_HT, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
+ return;
+ }
+
+ /* set flag */
+ bus->sr_enabled = true;
+ brcmf_dbg(INFO, "SR enabled\n");
+}
+
+/* enable KSO bit */
+static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)
+{
+ u8 val;
+ int err = 0;
+
+ brcmf_dbg(TRACE, "Enter\n");
+
+ /* KSO bit added in SDIO core rev 12 */
+ if (bus->ci->c_inf[1].rev < 12)
+ return 0;
+
+ val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ &err);
+ if (err) {
+ brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
+ return err;
+ }
+
+ if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
+ val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
+ SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
+ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+ val, &err);
+ if (err) {
+ brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
static bool
brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
{
@@ -3423,8 +3398,13 @@ static int brcmf_sdbrcm_bus_init(struct device *dev)
ret = -ENODEV;
}
- /* Restore previous clock setting */
- brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
+ if (brcmf_sdbrcm_sr_capable(bus)) {
+ brcmf_sdbrcm_sr_init(bus);
+ } else {
+ /* Restore previous clock setting */
+ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+ saveclk, &err);
+ }
if (ret == 0) {
ret = brcmf_sdio_intr_register(bus->sdiodev);
@@ -3485,7 +3465,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
brcmf_dbg(TIMER, "Enter\n");
/* Poll period: check device if appropriate. */
- if (bus->poll && (++bus->polltick >= bus->pollrate)) {
+ if (!bus->sr_enabled &&
+ bus->poll && (++bus->polltick >= bus->pollrate)) {
u32 intstatus = 0;
/* Reset poll tick */
@@ -3536,7 +3517,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
bus->console.count -= bus->console_interval;
sdio_claim_host(bus->sdiodev->func[1]);
/* Make sure backplane clock is on */
- brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+ brcmf_sdbrcm_bus_sleep(bus, false, false);
if (brcmf_sdbrcm_readconsole(bus) < 0)
/* stop on error */
bus->console_interval = 0;
@@ -3553,8 +3534,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
bus->activity = false;
brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
} else {
+ brcmf_dbg(SDIO, "idle\n");
sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+ brcmf_sdbrcm_bus_sleep(bus, true, false);
sdio_release_host(bus->sdiodev->func[1]);
}
}
@@ -3565,6 +3547,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
static bool brcmf_sdbrcm_chipmatch(u16 chipid)
{
+ if (chipid == BCM43143_CHIP_ID)
+ return true;
if (chipid == BCM43241_CHIP_ID)
return true;
if (chipid == BCM4329_CHIP_ID)
@@ -3573,6 +3557,8 @@ static bool brcmf_sdbrcm_chipmatch(u16 chipid)
return true;
if (chipid == BCM4334_CHIP_ID)
return true;
+ if (chipid == BCM4335_CHIP_ID)
+ return true;
return false;
}
@@ -3650,7 +3636,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
int err = 0;
int reg_addr;
u32 reg_val;
- u8 idx;
+ u32 drivestrength;
bus->alp_only = true;
@@ -3686,8 +3672,16 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
goto fail;
}
- brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci,
- SDIO_DRIVE_STRENGTH);
+ if (brcmf_sdbrcm_kso_init(bus)) {
+ brcmf_err("error enabling KSO\n");
+ goto fail;
+ }
+
+ 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;
@@ -3696,12 +3690,37 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
goto fail;
}
- /* Set core control so an SDIO reset does a backplane reset */
- idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
- reg_addr = bus->ci->c_inf[idx].base +
- offsetof(struct sdpcmd_regs, corecontrol);
- reg_val = brcmf_sdio_regrl(bus->sdiodev, reg_addr, NULL);
- brcmf_sdio_regwl(bus->sdiodev, reg_addr, reg_val | CC_BPRESEN, NULL);
+ /* Set card control so an SDIO card reset does a WLAN backplane reset */
+ reg_val = brcmf_sdio_regrb(bus->sdiodev,
+ SDIO_CCCR_BRCM_CARDCTRL, &err);
+ if (err)
+ goto fail;
+
+ reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
+
+ brcmf_sdio_regwb(bus->sdiodev,
+ SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
+ if (err)
+ goto fail;
+
+ /* set PMUControl so a backplane reset does PMU state reload */
+ reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base,
+ pmucontrol);
+ reg_val = brcmf_sdio_regrl(bus->sdiodev,
+ reg_addr,
+ &err);
+ if (err)
+ goto fail;
+
+ reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
+
+ brcmf_sdio_regwl(bus->sdiodev,
+ reg_addr,
+ reg_val,
+ &err);
+ if (err)
+ goto fail;
+
sdio_release_host(bus->sdiodev->func[1]);
@@ -3755,6 +3774,10 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus)
bus->use_rxchain = false;
bus->sd_rxchain = false;
+ /* SR state */
+ bus->sleeping = false;
+ bus->sr_enabled = false;
+
return true;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index b3c608ee37c..5352dc1fdf3 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -21,6 +21,7 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/err.h>
+#include <linux/jiffies.h>
#include <uapi/linux/nl80211.h>
#include <net/cfg80211.h>
@@ -31,8 +32,11 @@
#include "dhd_dbg.h"
#include "dhd_bus.h"
#include "fwil.h"
+#include "fwil_types.h"
#include "fweh.h"
#include "fwsignal.h"
+#include "p2p.h"
+#include "wl_cfg80211.h"
/**
* DOC: Firmware Signalling
@@ -144,6 +148,9 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01
#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02
+#define BRCMF_FWS_RET_OK_NOSCHEDULE 0
+#define BRCMF_FWS_RET_OK_SCHEDULE 1
+
/**
* enum brcmf_fws_skb_state - indicates processing state of skb.
*
@@ -265,6 +272,9 @@ struct brcmf_skbuff_cb {
brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \
BRCMF_FWS_TXSTAT_ ## field ## _SHIFT)
+/* How long to defer borrowing in jiffies */
+#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
+
/**
* enum brcmf_fws_fifo - fifo indices used by dongle firmware.
*
@@ -419,9 +429,11 @@ struct brcmf_fws_info {
struct work_struct fws_dequeue_work;
u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
int fifo_credit[BRCMF_FWS_FIFO_COUNT];
+ int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
u32 fifo_credit_map;
u32 fifo_delay_map;
+ unsigned long borrow_defer_timestamp;
};
/*
@@ -678,28 +690,28 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
}
static struct brcmf_fws_mac_descriptor*
-brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da)
+brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp,
+ u8 *da)
{
struct brcmf_fws_mac_descriptor *entry = &fws->desc.other;
- struct brcmf_if *ifp;
bool multicast;
+ enum nl80211_iftype iftype;
- brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx);
+ brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
multicast = is_multicast_ether_addr(da);
- ifp = fws->drvr->iflist[ifidx ? ifidx + 1 : 0];
- if (WARN_ON(!ifp))
- goto done;
+ iftype = brcmf_cfg80211_get_iftype(ifp);
/* Multicast destination and P2P clients get the interface entry.
* STA gets the interface entry if there is no exact match. For
* example, TDLS destinations have their own entry.
*/
entry = NULL;
- if (multicast && ifp->fws_desc)
+ if ((multicast || iftype == NL80211_IFTYPE_STATION ||
+ iftype == NL80211_IFTYPE_P2P_CLIENT) && ifp->fws_desc)
entry = ifp->fws_desc;
- if (entry != NULL && multicast)
+ if (entry != NULL && iftype != NL80211_IFTYPE_STATION)
goto done;
entry = brcmf_fws_mac_descriptor_lookup(fws, da);
@@ -711,26 +723,29 @@ done:
return entry;
}
-static bool brcmf_fws_mac_desc_ready(struct brcmf_fws_mac_descriptor *entry,
- int fifo)
+static bool brcmf_fws_mac_desc_closed(struct brcmf_fws_info *fws,
+ struct brcmf_fws_mac_descriptor *entry,
+ int fifo)
{
- bool ready;
+ struct brcmf_fws_mac_descriptor *if_entry;
+ bool closed;
- /*
- * destination entry is ready when firmware says it is OPEN
- * and there are no packets enqueued for it.
+ /* for unique destination entries the related interface
+ * may be closed.
*/
- ready = entry->state == BRCMF_FWS_STATE_OPEN &&
- !entry->suppressed &&
- brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0;
-
- /*
- * Or when the destination entry is CLOSED, but firmware has
- * specifically requested packets for this entry.
+ if (entry->mac_handle) {
+ if_entry = &fws->desc.iface[entry->interface_id];
+ if (if_entry->state == BRCMF_FWS_STATE_CLOSE)
+ return true;
+ }
+ /* an entry is closed when the state is closed and
+ * the firmware did not request anything.
*/
- ready = ready || (entry->state == BRCMF_FWS_STATE_CLOSE &&
- (entry->requested_credit + entry->requested_packet));
- return ready;
+ closed = entry->state == BRCMF_FWS_STATE_CLOSE &&
+ !entry->requested_credit && !entry->requested_packet;
+
+ /* Or firmware does not allow traffic for given fifo */
+ return closed || !(entry->ac_bitmap & BIT(fifo));
}
static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws,
@@ -825,11 +840,12 @@ brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq,
{
struct brcmf_if *ifp = fws->drvr->iflist[if_id];
- brcmf_dbg(TRACE,
- "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx);
if (WARN_ON(!ifp))
return;
+ brcmf_dbg(TRACE,
+ "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx);
+
if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) &&
pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER)
brcmf_txflowblock_if(ifp,
@@ -861,9 +877,10 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
entry = &fws->desc.nodes[mac_handle & 0x1F];
if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx);
- if (entry->occupied)
+ if (entry->occupied) {
+ brcmf_fws_mac_desc_cleanup(fws, entry, -1);
brcmf_fws_clear_mac_descriptor(entry);
- else
+ } else
fws->stats.mac_update_failed++;
return 0;
}
@@ -914,12 +931,13 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws,
entry->requested_credit = 0;
if (type == BRCMF_FWS_TYPE_MAC_OPEN) {
entry->state = BRCMF_FWS_STATE_OPEN;
+ return BRCMF_FWS_RET_OK_SCHEDULE;
} else {
entry->state = BRCMF_FWS_STATE_CLOSE;
for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++)
brcmf_fws_tim_update(fws, entry, i);
}
- return 0;
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
}
static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
@@ -946,10 +964,10 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws,
switch (type) {
case BRCMF_FWS_TYPE_INTERFACE_OPEN:
entry->state = BRCMF_FWS_STATE_OPEN;
- return 0;
+ return BRCMF_FWS_RET_OK_SCHEDULE;
case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
entry->state = BRCMF_FWS_STATE_CLOSE;
- return 0;
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
default:
ret = -EINVAL;
break;
@@ -979,15 +997,40 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type,
entry->requested_packet = data[0];
entry->ac_bitmap = data[2];
- return 0;
+ return BRCMF_FWS_RET_OK_SCHEDULE;
}
static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
u8 fifo, u8 credits)
{
+ int lender_ac;
+ int *borrowed;
+ int *fifo_credit;
+
if (!credits)
return;
+ if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
+ (fws->credits_borrowed[0])) {
+ for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
+ lender_ac--) {
+ borrowed = &fws->credits_borrowed[lender_ac];
+ if (*borrowed) {
+ fws->fifo_credit_map |= (1 << lender_ac);
+ fifo_credit = &fws->fifo_credit[lender_ac];
+ if (*borrowed >= credits) {
+ *borrowed -= credits;
+ *fifo_credit += credits;
+ return;
+ } else {
+ credits -= *borrowed;
+ *fifo_credit += *borrowed;
+ *borrowed = 0;
+ }
+ }
+ }
+ }
+
fws->fifo_credit_map |= 1 << fifo;
fws->fifo_credit[fifo] += credits;
}
@@ -1074,7 +1117,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
int num_nodes;
int node_pos;
int prec_out;
- int pmsk = 3;
+ int pmsk;
int i;
table = (struct brcmf_fws_mac_descriptor *)&fws->desc;
@@ -1083,11 +1126,14 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo)
for (i = 0; i < num_nodes; i++) {
entry = &table[(node_pos + i) % num_nodes];
- if (!entry->occupied)
+ if (!entry->occupied ||
+ brcmf_fws_mac_desc_closed(fws, entry, fifo))
continue;
if (entry->suppressed)
pmsk = 2;
+ else
+ pmsk = 3;
p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out);
if (p == NULL) {
if (entry->suppressed) {
@@ -1250,7 +1296,7 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
brcmf_dbg(INFO, "ignored\n");
- return 0;
+ return BRCMF_FWS_RET_OK_NOSCHEDULE;
}
brcmf_dbg(TRACE, "enter: data %pM\n", data);
@@ -1259,8 +1305,7 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map,
fws->fifo_delay_map);
- brcmf_fws_schedule_deq(fws);
- return 0;
+ return BRCMF_FWS_RET_OK_SCHEDULE;
}
static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
@@ -1344,6 +1389,8 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
u8 type;
u8 len;
u8 *data;
+ s32 status;
+ s32 err;
brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n",
ifidx, skb->len, signal_len);
@@ -1363,6 +1410,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
data_len = signal_len;
signal_data = skb->data;
+ status = BRCMF_FWS_RET_OK_NOSCHEDULE;
while (data_len > 0) {
/* extract tlv info */
type = signal_data[0];
@@ -1388,6 +1436,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
if (len != brcmf_fws_get_tlv_len(fws, type))
break;
+ err = BRCMF_FWS_RET_OK_NOSCHEDULE;
switch (type) {
case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
case BRCMF_FWS_TYPE_COMP_TXSTATUS:
@@ -1398,21 +1447,22 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
break;
case BRCMF_FWS_TYPE_MAC_OPEN:
case BRCMF_FWS_TYPE_MAC_CLOSE:
- brcmf_fws_macdesc_state_indicate(fws, type, data);
+ err = brcmf_fws_macdesc_state_indicate(fws, type, data);
break;
case BRCMF_FWS_TYPE_INTERFACE_OPEN:
case BRCMF_FWS_TYPE_INTERFACE_CLOSE:
- brcmf_fws_interface_state_indicate(fws, type, data);
+ err = brcmf_fws_interface_state_indicate(fws, type,
+ data);
break;
case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT:
case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
- brcmf_fws_request_indicate(fws, type, data);
+ err = brcmf_fws_request_indicate(fws, type, data);
break;
case BRCMF_FWS_TYPE_TXSTATUS:
brcmf_fws_txstatus_indicate(fws, data);
break;
case BRCMF_FWS_TYPE_FIFO_CREDITBACK:
- brcmf_fws_fifocreditback_indicate(fws, data);
+ err = brcmf_fws_fifocreditback_indicate(fws, data);
break;
case BRCMF_FWS_TYPE_RSSI:
brcmf_fws_rssi_indicate(fws, *data);
@@ -1426,7 +1476,8 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
fws->stats.tlv_invalid_type++;
break;
}
-
+ if (err == BRCMF_FWS_RET_OK_SCHEDULE)
+ status = BRCMF_FWS_RET_OK_SCHEDULE;
signal_data += len + 2;
data_len -= len + 2;
}
@@ -1434,6 +1485,9 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
if (data_len != 0)
fws->stats.tlv_parse_failed++;
+ if (status == BRCMF_FWS_RET_OK_SCHEDULE)
+ brcmf_fws_schedule_deq(fws);
+
/* signalling processing result does
* not affect the actual ethernet packet.
*/
@@ -1553,7 +1607,7 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
return rc;
}
-static int
+static void
brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
{
/*
@@ -1595,7 +1649,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
/* free the hanger slot */
brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
&pktout, true);
- brcmf_txfinalize(fws->drvr, skb, false);
rc = -EINVAL;
goto fail;
}
@@ -1629,11 +1682,31 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb)
fail:
- if (rc)
+ if (rc) {
+ brcmf_txfinalize(fws->drvr, skb, false);
fws->stats.rollback_failed++;
- else
+ } else
fws->stats.rollback_success++;
- return rc;
+}
+
+static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
+{
+ int lender_ac;
+
+ if (time_after(fws->borrow_defer_timestamp, jiffies))
+ return -ENAVAIL;
+
+ for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
+ if (fws->fifo_credit[lender_ac]) {
+ fws->credits_borrowed[lender_ac]++;
+ fws->fifo_credit[lender_ac]--;
+ if (fws->fifo_credit[lender_ac] == 0)
+ fws->fifo_credit_map &= ~(1 << lender_ac);
+ brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac);
+ return 0;
+ }
+ }
+ return -ENAVAIL;
}
static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
@@ -1669,8 +1742,17 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo,
return 0;
}
+ if (fifo != BRCMF_FWS_FIFO_AC_BE)
+ fws->borrow_defer_timestamp = jiffies +
+ BRCMF_FWS_BORROW_DEFER_PERIOD;
+
if (!(*credit)) {
- brcmf_dbg(TRACE, "exit: credits depleted\n");
+ /* Try to borrow a credit from other queue */
+ if (fifo == BRCMF_FWS_FIFO_AC_BE &&
+ brcmf_fws_borrow_credit(fws) == 0)
+ return 0;
+
+ brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo);
return -ENAVAIL;
}
(*credit)--;
@@ -1712,17 +1794,17 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
return rc;
rollback:
- rc = brcmf_fws_rollback_toq(fws, skb);
+ brcmf_fws_rollback_toq(fws, skb);
return rc;
}
int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
{
struct brcmf_pub *drvr = ifp->drvr;
+ struct brcmf_fws_info *fws = drvr->fws;
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
struct ethhdr *eh = (struct ethhdr *)(skb->data);
ulong flags;
- u8 ifidx = ifp->ifidx;
int fifo = BRCMF_FWS_FIFO_BCMC;
bool multicast = is_multicast_ether_addr(eh->h_dest);
@@ -1734,9 +1816,9 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
if (ntohs(eh->h_proto) == ETH_P_PAE)
atomic_inc(&ifp->pend_8021x_cnt);
- if (!brcmf_fws_fc_active(drvr->fws)) {
+ if (!brcmf_fws_fc_active(fws)) {
/* If the protocol uses a data header, apply it */
- brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
+ brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);
/* Use bus module to send data frame */
return brcmf_bus_txdata(drvr->bus_if, skb);
@@ -1744,9 +1826,9 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
/* set control buffer information */
skcb->if_flags = 0;
- skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifidx, eh->h_dest);
+ skcb->mac = brcmf_fws_find_mac_desc(fws, ifp, eh->h_dest);
skcb->state = BRCMF_FWS_SKBSTATE_NEW;
- brcmf_skb_if_flags_set_field(skb, INDEX, ifidx);
+ brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx);
if (!multicast)
fifo = brcmf_fws_prio2fifo[skb->priority];
brcmf_skb_if_flags_set_field(skb, FIFO, fifo);
@@ -1755,14 +1837,18 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
multicast, fifo);
brcmf_fws_lock(drvr, flags);
- if (!brcmf_fws_mac_desc_ready(skcb->mac, fifo) ||
+ if (skcb->mac->suppressed ||
+ brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) ||
+ brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) ||
(!multicast &&
- brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) {
+ brcmf_fws_consume_credit(fws, fifo, skb) < 0)) {
/* enqueue the packet in delayQ */
drvr->fws->fifo_delay_map |= 1 << fifo;
- brcmf_fws_enq(drvr->fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
+ brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
} else {
- brcmf_fws_commit_skb(drvr->fws, fifo, skb);
+ if (brcmf_fws_commit_skb(fws, fifo, skb))
+ if (!multicast)
+ brcmf_skb_pick_up_credit(fws, fifo, skb);
}
brcmf_fws_unlock(drvr, flags);
return 0;
@@ -1799,14 +1885,17 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
void brcmf_fws_del_interface(struct brcmf_if *ifp)
{
struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
+ ulong flags;
brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx);
if (!entry)
return;
+ brcmf_fws_lock(ifp->drvr, flags);
ifp->fws_desc = NULL;
brcmf_fws_clear_mac_descriptor(entry);
brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
+ brcmf_fws_unlock(ifp->drvr, flags);
}
static void brcmf_fws_dequeue_worker(struct work_struct *worker)
@@ -1826,14 +1915,29 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
fws->fifo_credit[fifo]);
for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) {
skb = brcmf_fws_deq(fws, fifo);
- if (!skb)
+ if (!skb || brcmf_fws_commit_skb(fws, fifo, skb))
break;
- if (!brcmf_fws_commit_skb(fws, fifo, skb) &&
- brcmf_skbcb(skb)->if_flags &
+ if (brcmf_skbcb(skb)->if_flags &
BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)
credit++;
}
- fws->fifo_credit[fifo] -= credit;
+ if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
+ (credit == fws->fifo_credit[fifo])) {
+ fws->fifo_credit[fifo] -= credit;
+ while (brcmf_fws_borrow_credit(fws) == 0) {
+ skb = brcmf_fws_deq(fws, fifo);
+ if (!skb) {
+ brcmf_fws_return_credits(fws, fifo, 1);
+ break;
+ }
+ if (brcmf_fws_commit_skb(fws, fifo, skb)) {
+ brcmf_fws_return_credits(fws, fifo, 1);
+ break;
+ }
+ }
+ } else {
+ fws->fifo_credit[fifo] -= credit;
+ }
}
brcmf_fws_unlock(fws->drvr, flags);
}
@@ -1872,16 +1976,20 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
- rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv);
+ rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
+ brcmf_fws_notify_credit_map);
if (rc < 0) {
- brcmf_err("failed to set bdcv2 tlv signaling\n");
+ brcmf_err("register credit map handler failed\n");
goto fail;
}
- if (brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
- brcmf_fws_notify_credit_map)) {
- brcmf_err("register credit map handler failed\n");
- goto fail;
+ /* setting the iovar may fail if feature is unsupported
+ * so leave the rc as is so driver initialization can
+ * continue.
+ */
+ if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
+ brcmf_err("failed to set bdcv2 tlv signaling\n");
+ goto fail_event;
}
brcmf_fws_hanger_init(&drvr->fws->hanger);
@@ -1897,9 +2005,9 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
drvr->fw_signals ? "enabled" : "disabled", tlv);
return 0;
+fail_event:
+ brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
fail:
- /* disable flow control entirely */
- drvr->fw_signals = false;
brcmf_fws_deinit(drvr);
return rc;
}
@@ -1912,6 +2020,14 @@ void brcmf_fws_deinit(struct brcmf_pub *drvr)
if (!fws)
return;
+ /* disable firmware signalling entirely
+ * to avoid using the workqueue.
+ */
+ drvr->fw_signals = false;
+
+ if (drvr->fws->fws_wq)
+ destroy_workqueue(drvr->fws->fws_wq);
+
/* cleanup */
brcmf_fws_lock(drvr, flags);
brcmf_fws_cleanup(fws, -1);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index 94ff045df2b..2b90da0d85f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -424,29 +424,6 @@ static void brcmf_p2p_print_actframe(bool tx, void *frame, u32 frame_len)
/**
- * brcmf_p2p_chnr_to_chspec() - convert channel number to chanspec.
- *
- * @channel: channel number
- */
-static u16 brcmf_p2p_chnr_to_chspec(u16 channel)
-{
- u16 chanspec;
-
- chanspec = channel & WL_CHANSPEC_CHAN_MASK;
-
- if (channel <= CH_MAX_2G_CHANNEL)
- chanspec |= WL_CHANSPEC_BAND_2G;
- else
- chanspec |= WL_CHANSPEC_BAND_5G;
-
- chanspec |= WL_CHANSPEC_BW_20;
- chanspec |= WL_CHANSPEC_CTL_SB_NONE;
-
- return chanspec;
-}
-
-
-/**
* brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation.
*
* @ifp: ifp to use for iovars (primary).
@@ -837,7 +814,8 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
IEEE80211_CHAN_PASSIVE_SCAN))
continue;
- chanspecs[i] = channel_to_chanspec(chan);
+ chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf,
+ chan);
brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n",
num_nodfs, chan->hw_value, chanspecs[i]);
num_nodfs++;
@@ -945,8 +923,8 @@ static s32
brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
{
struct brcmf_cfg80211_vif *vif;
+ struct brcmu_chan ch;
s32 err = 0;
- u16 chanspec;
vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
if (!vif) {
@@ -961,9 +939,11 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
goto exit;
}
- chanspec = brcmf_p2p_chnr_to_chspec(channel);
+ ch.chnum = channel;
+ ch.bw = BRCMU_CHAN_BW_20;
+ p2p->cfg->d11inf.encchspec(&ch);
err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN,
- chanspec, (u16)duration);
+ ch.chspec, (u16)duration);
if (!err) {
set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status);
p2p->remain_on_channel_cookie++;
@@ -1075,6 +1055,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
u32 channel_cnt;
u16 *default_chan_list;
u32 i;
+ struct brcmu_chan ch;
brcmf_dbg(TRACE, "Enter\n");
@@ -1089,15 +1070,23 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
err = -ENOMEM;
goto exit;
}
+ ch.bw = BRCMU_CHAN_BW_20;
if (channel) {
+ ch.chnum = channel;
+ p2p->cfg->d11inf.encchspec(&ch);
/* insert same channel to the chan_list */
for (i = 0; i < channel_cnt; i++)
- default_chan_list[i] =
- brcmf_p2p_chnr_to_chspec(channel);
+ default_chan_list[i] = ch.chspec;
} else {
- default_chan_list[0] = brcmf_p2p_chnr_to_chspec(SOCIAL_CHAN_1);
- default_chan_list[1] = brcmf_p2p_chnr_to_chspec(SOCIAL_CHAN_2);
- default_chan_list[2] = brcmf_p2p_chnr_to_chspec(SOCIAL_CHAN_3);
+ ch.chnum = SOCIAL_CHAN_1;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[0] = ch.chspec;
+ ch.chnum = SOCIAL_CHAN_2;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[1] = ch.chspec;
+ ch.chnum = SOCIAL_CHAN_3;
+ p2p->cfg->d11inf.encchspec(&ch);
+ default_chan_list[2] = ch.chspec;
}
err = brcmf_p2p_escan(p2p, channel_cnt, default_chan_list,
WL_P2P_DISC_ST_SEARCH, WL_ESCAN_ACTION_START,
@@ -1227,6 +1216,7 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
{
struct brcmf_p2p_info *p2p = &cfg->p2p;
struct afx_hdl *afx_hdl = &p2p->afx_hdl;
+ struct brcmu_chan ch;
u8 *ie;
s32 err;
u8 p2p_dev_addr[ETH_ALEN];
@@ -1252,8 +1242,12 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
p2p_dev_addr, sizeof(p2p_dev_addr));
if ((err >= 0) &&
(!memcmp(p2p_dev_addr, afx_hdl->tx_dst_addr, ETH_ALEN))) {
- afx_hdl->peer_chan = bi->ctl_ch ? bi->ctl_ch :
- CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+ if (!bi->ctl_ch) {
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ bi->ctl_ch = ch.chnum;
+ }
+ afx_hdl->peer_chan = bi->ctl_ch;
brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n",
afx_hdl->tx_dst_addr, afx_hdl->peer_chan);
complete(&afx_hdl->act_frm_scan);
@@ -1360,12 +1354,14 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
u8 *frame = (u8 *)(rxframe + 1);
struct brcmf_p2p_pub_act_frame *act_frm;
struct brcmf_p2psd_gas_pub_act_frame *sd_act_frm;
- u16 chanspec = be16_to_cpu(rxframe->chanspec);
+ struct brcmu_chan ch;
struct ieee80211_mgmt *mgmt_frame;
s32 freq;
u16 mgmt_type;
u8 action;
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
/* Check if wpa_supplicant has registered for this frame */
brcmf_dbg(INFO, "ifp->vif->mgmt_rx_reg %04x\n", ifp->vif->mgmt_rx_reg);
mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4;
@@ -1384,7 +1380,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
&p2p->status) &&
(memcmp(afx_hdl->tx_dst_addr, e->addr,
ETH_ALEN) == 0)) {
- afx_hdl->peer_chan = CHSPEC_CHANNEL(chanspec);
+ afx_hdl->peer_chan = ch.chnum;
brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n",
afx_hdl->peer_chan);
complete(&afx_hdl->act_frm_scan);
@@ -1427,8 +1423,8 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
memcpy(&mgmt_frame->u, frame, mgmt_frame_len);
mgmt_frame_len += offsetof(struct ieee80211_mgmt, u);
- freq = ieee80211_channel_to_frequency(CHSPEC_CHANNEL(chanspec),
- CHSPEC_IS2G(chanspec) ?
+ freq = ieee80211_channel_to_frequency(ch.chnum,
+ ch.band == BRCMU_CHAN_BAND_2G ?
IEEE80211_BAND_2GHZ :
IEEE80211_BAND_5GHZ);
@@ -1854,6 +1850,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
struct brcmf_cfg80211_vif *vif = ifp->vif;
struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
u16 chanspec = be16_to_cpu(rxframe->chanspec);
+ struct brcmu_chan ch;
u8 *mgmt_frame;
u32 mgmt_frame_len;
s32 freq;
@@ -1862,9 +1859,12 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
e->reason);
+ ch.chspec = be16_to_cpu(rxframe->chanspec);
+ cfg->d11inf.decchspec(&ch);
+
if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) &&
(memcmp(afx_hdl->tx_dst_addr, e->addr, ETH_ALEN) == 0)) {
- afx_hdl->peer_chan = CHSPEC_CHANNEL(chanspec);
+ afx_hdl->peer_chan = ch.chnum;
brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n",
afx_hdl->peer_chan);
complete(&afx_hdl->act_frm_scan);
@@ -1889,8 +1889,8 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
mgmt_frame = (u8 *)(rxframe + 1);
mgmt_frame_len = e->datalen - sizeof(*rxframe);
- freq = ieee80211_channel_to_frequency(CHSPEC_CHANNEL(chanspec),
- CHSPEC_IS2G(chanspec) ?
+ freq = ieee80211_channel_to_frequency(ch.chnum,
+ ch.band == BRCMU_CHAN_BAND_2G ?
IEEE80211_BAND_2GHZ :
IEEE80211_BAND_5GHZ);
@@ -2014,21 +2014,19 @@ static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p,
{
struct brcmf_if *ifp;
struct brcmf_fil_chan_info_le ci;
+ struct brcmu_chan ch;
s32 err;
ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
- *chanspec = 11 & WL_CHANSPEC_CHAN_MASK;
+ ch.chnum = 11;
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CHANNEL, &ci, sizeof(ci));
- if (!err) {
- *chanspec = le32_to_cpu(ci.hw_channel) & WL_CHANSPEC_CHAN_MASK;
- if (*chanspec < CH_MAX_2G_CHANNEL)
- *chanspec |= WL_CHANSPEC_BAND_2G;
- else
- *chanspec |= WL_CHANSPEC_BAND_5G;
- }
- *chanspec |= WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
+ if (!err)
+ ch.chnum = le32_to_cpu(ci.hw_channel);
+ ch.bw = BRCMU_CHAN_BW_20;
+ p2p->cfg->d11inf.encchspec(&ch);
+ *chanspec = ch.chspec;
}
/**
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
index 14be2d5530c..ca72177388b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
@@ -40,6 +40,15 @@
#define BCM4329_CORE_ARM_BASE 0x18002000
#define BCM4329_RAMSIZE 0x48000
+/* bcm43143 */
+/* SDIO device core */
+#define BCM43143_CORE_BUS_BASE 0x18002000
+/* internal memory core */
+#define BCM43143_CORE_SOCRAM_BASE 0x18004000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM43143_CORE_ARM_BASE 0x18003000
+#define BCM43143_RAMSIZE 0x70000
+
#define SBCOREREV(sbidh) \
((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \
((sbidh) & SSB_IDHIGH_RCLO))
@@ -52,6 +61,9 @@
#define CIB_REV_MASK 0xff000000
#define CIB_REV_SHIFT 24
+/* ARM CR4 core specific control flag bits */
+#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020
+
#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
/* SDIO Pad drive strength to select value mappings */
struct sdiod_drive_str {
@@ -70,6 +82,14 @@ static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
{0, 0x1}
};
+/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
+static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
+ {16, 0x7},
+ {12, 0x5},
+ {8, 0x3},
+ {4, 0x1}
+};
+
u8
brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid)
{
@@ -149,7 +169,7 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
static void
brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
- struct chip_info *ci, u16 coreid)
+ struct chip_info *ci, u16 coreid, u32 core_bits)
{
u32 regdata, base;
u8 idx;
@@ -235,7 +255,7 @@ brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
static void
brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
- struct chip_info *ci, u16 coreid)
+ struct chip_info *ci, u16 coreid, u32 core_bits)
{
u8 idx;
u32 regdata;
@@ -249,19 +269,36 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
if ((regdata & BCMA_RESET_CTL_RESET) != 0)
return;
- brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, 0, NULL);
- regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+ /* ensure no pending backplane operation
+ * 300uc should be sufficient for backplane ops to be finish
+ * extra 10ms is taken into account for firmware load stage
+ * after 10300us carry on disabling the core anyway
+ */
+ SPINWAIT(brcmf_sdio_regrl(sdiodev,
+ ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
+ NULL), 10300);
+ regdata = brcmf_sdio_regrl(sdiodev,
+ ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
NULL);
- udelay(10);
+ if (regdata)
+ brcmf_err("disabling core 0x%x with reset status %x\n",
+ coreid, regdata);
brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
BCMA_RESET_CTL_RESET, NULL);
udelay(1);
+
+ brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+ core_bits, NULL);
+ regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+ NULL);
+ usleep_range(10, 20);
+
}
static void
brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
- struct chip_info *ci, u16 coreid)
+ struct chip_info *ci, u16 coreid, u32 core_bits)
{
u32 regdata;
u8 idx;
@@ -272,7 +309,7 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
* Must do the disable sequence first to work for
* arbitrary current core state.
*/
- brcmf_sdio_sb_coredisable(sdiodev, ci, coreid);
+ brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0);
/*
* Now do the initialization sequence.
@@ -325,7 +362,7 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
static void
brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
- struct chip_info *ci, u16 coreid)
+ struct chip_info *ci, u16 coreid, u32 core_bits)
{
u8 idx;
u32 regdata;
@@ -333,31 +370,69 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
idx = brcmf_sdio_chip_getinfidx(ci, coreid);
/* must disable first to work for arbitrary current core state */
- brcmf_sdio_ai_coredisable(sdiodev, ci, coreid);
+ brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits);
/* now do initialization sequence */
brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
- BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
+ core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
NULL);
brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
0, NULL);
+ regdata = brcmf_sdio_regrl(sdiodev,
+ ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+ NULL);
udelay(1);
brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
- BCMA_IOCTL_CLK, NULL);
+ core_bits | BCMA_IOCTL_CLK, NULL);
regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
NULL);
udelay(1);
}
+#ifdef DEBUG
+/* safety check for chipinfo */
+static int brcmf_sdio_chip_cichk(struct chip_info *ci)
+{
+ u8 core_idx;
+
+ /* check RAM core presence for ARM CM3 core */
+ core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
+ if (BRCMF_MAX_CORENUM != core_idx) {
+ core_idx = brcmf_sdio_chip_getinfidx(ci,
+ BCMA_CORE_INTERNAL_MEM);
+ if (BRCMF_MAX_CORENUM == core_idx) {
+ brcmf_err("RAM core not provided with ARM CM3 core\n");
+ return -ENODEV;
+ }
+ }
+
+ /* check RAM base for ARM CR4 core */
+ core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4);
+ if (BRCMF_MAX_CORENUM != core_idx) {
+ if (ci->rambase == 0) {
+ brcmf_err("RAM base not provided with ARM CR4 core\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+#else /* DEBUG */
+static inline int brcmf_sdio_chip_cichk(struct chip_info *ci)
+{
+ return 0;
+}
+#endif
+
static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
struct chip_info *ci, u32 regs)
{
u32 regdata;
+ int ret;
- /*
- * Get CC core rev
+ /* Get CC core rev
* Chipid is assume to be at offset 0 from regs arg
* For different chiptypes or old sdio hosts w/o chipcommon,
* other ways of recognition should be added here.
@@ -375,6 +450,23 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
/* Address of cores for new chips should be added here */
switch (ci->chip) {
+ case BCM43143_CHIP_ID:
+ ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000;
+ ci->c_inf[0].cib = 0x2b000000;
+ ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
+ ci->c_inf[1].base = BCM43143_CORE_BUS_BASE;
+ ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000;
+ ci->c_inf[1].cib = 0x18000000;
+ ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
+ ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE;
+ ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000;
+ ci->c_inf[2].cib = 0x14000000;
+ ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
+ ci->c_inf[3].base = BCM43143_CORE_ARM_BASE;
+ ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000;
+ ci->c_inf[3].cib = 0x07000000;
+ ci->ramsize = BCM43143_RAMSIZE;
+ break;
case BCM43241_CHIP_ID:
ci->c_inf[0].wrapbase = 0x18100000;
ci->c_inf[0].cib = 0x2a084411;
@@ -435,11 +527,29 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
ci->c_inf[3].cib = 0x07004211;
ci->ramsize = 0x80000;
break;
+ case BCM4335_CHIP_ID:
+ ci->c_inf[0].wrapbase = 0x18100000;
+ ci->c_inf[0].cib = 0x2b084411;
+ ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
+ ci->c_inf[1].base = 0x18005000;
+ ci->c_inf[1].wrapbase = 0x18105000;
+ ci->c_inf[1].cib = 0x0f004211;
+ ci->c_inf[2].id = BCMA_CORE_ARM_CR4;
+ ci->c_inf[2].base = 0x18002000;
+ ci->c_inf[2].wrapbase = 0x18102000;
+ ci->c_inf[2].cib = 0x01084411;
+ ci->ramsize = 0xc0000;
+ ci->rambase = 0x180000;
+ break;
default:
brcmf_err("chipid 0x%x is not supported\n", ci->chip);
return -ENODEV;
}
+ ret = brcmf_sdio_chip_cichk(ci);
+ if (ret)
+ return ret;
+
switch (ci->socitype) {
case SOCI_SB:
ci->iscoreup = brcmf_sdio_sb_iscoreup;
@@ -539,7 +649,7 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
* Make sure any on-chip ARM is off (in case strapping is wrong),
* or downloaded code was already running.
*/
- ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3);
+ ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
}
int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
@@ -600,21 +710,37 @@ void
brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
struct chip_info *ci, u32 drivestrength)
{
- struct sdiod_drive_str *str_tab = NULL;
- u32 str_mask = 0;
- u32 str_shift = 0;
+ const struct sdiod_drive_str *str_tab = NULL;
+ u32 str_mask;
+ u32 str_shift;
char chn[8];
u32 base = ci->c_inf[0].base;
+ u32 i;
+ u32 drivestrength_sel = 0;
+ u32 cc_data_temp;
+ u32 addr;
if (!(ci->c_inf[0].caps & CC_CAP_PMU))
return;
switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
- str_tab = (struct sdiod_drive_str *)&sdiod_drvstr_tab1_1v8;
+ str_tab = sdiod_drvstr_tab1_1v8;
str_mask = 0x00003800;
str_shift = 11;
break;
+ case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
+ /* note: 43143 does not support tristate */
+ i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
+ if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
+ str_tab = sdiod_drvstr_tab2_3v3;
+ str_mask = 0x00000007;
+ str_shift = 0;
+ } else
+ brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
+ brcmf_sdio_chip_name(ci->chip, chn, 8),
+ drivestrength);
+ break;
default:
brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
brcmf_sdio_chip_name(ci->chip, chn, 8),
@@ -623,30 +749,207 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
}
if (str_tab != NULL) {
- u32 drivestrength_sel = 0;
- u32 cc_data_temp;
- int i;
-
for (i = 0; str_tab[i].strength != 0; i++) {
if (drivestrength >= str_tab[i].strength) {
drivestrength_sel = str_tab[i].sel;
break;
}
}
-
- brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
- 1, NULL);
- cc_data_temp =
- brcmf_sdio_regrl(sdiodev,
- CORE_CC_REG(base, chipcontrol_addr),
- NULL);
+ addr = CORE_CC_REG(base, chipcontrol_addr);
+ brcmf_sdio_regwl(sdiodev, addr, 1, NULL);
+ cc_data_temp = brcmf_sdio_regrl(sdiodev, addr, NULL);
cc_data_temp &= ~str_mask;
drivestrength_sel <<= str_shift;
cc_data_temp |= drivestrength_sel;
- brcmf_sdio_regwl(sdiodev, CORE_CC_REG(base, chipcontrol_addr),
- cc_data_temp, NULL);
+ brcmf_sdio_regwl(sdiodev, addr, cc_data_temp, NULL);
- brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n",
- drivestrength, cc_data_temp);
+ brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
+ str_tab[i].strength, drivestrength, cc_data_temp);
}
}
+
+#ifdef DEBUG
+static bool
+brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
+ char *nvram_dat, uint nvram_sz)
+{
+ char *nvram_ularray;
+ int err;
+ bool ret = true;
+
+ /* read back and verify */
+ brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz);
+ nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL);
+ /* do not proceed while no memory but */
+ if (!nvram_ularray)
+ return true;
+
+ /* Upload image to verify downloaded contents. */
+ memset(nvram_ularray, 0xaa, nvram_sz);
+
+ /* Read the vars list to temp buffer for comparison */
+ err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray,
+ nvram_sz);
+ if (err) {
+ brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n",
+ err, nvram_sz, nvram_addr);
+ } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) {
+ brcmf_err("Downloaded NVRAM image is corrupted\n");
+ ret = false;
+ }
+ kfree(nvram_ularray);
+
+ return ret;
+}
+#else /* DEBUG */
+static inline bool
+brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
+ char *nvram_dat, uint nvram_sz)
+{
+ return true;
+}
+#endif /* DEBUG */
+
+static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci,
+ char *nvram_dat, uint nvram_sz)
+{
+ int err;
+ u32 nvram_addr;
+ u32 token;
+ __le32 token_le;
+
+ nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase;
+
+ /* Write the vars list */
+ err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
+ if (err) {
+ brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
+ err, nvram_sz, nvram_addr);
+ return false;
+ }
+
+ if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr,
+ nvram_dat, nvram_sz))
+ return false;
+
+ /* generate token:
+ * nvram size, converted to words, in lower 16-bits, checksum
+ * in upper 16-bits.
+ */
+ token = nvram_sz / 4;
+ token = (~token << 16) | (token & 0x0000FFFF);
+ token_le = cpu_to_le32(token);
+
+ brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize);
+ brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n",
+ nvram_addr, nvram_sz, token);
+
+ /* Write the length token to the last word */
+ if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase),
+ (u8 *)&token_le, 4))
+ return false;
+
+ return true;
+}
+
+static void
+brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci)
+{
+ u32 zeros = 0;
+
+ ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
+ ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0);
+
+ /* clear length token */
+ brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
+}
+
+static bool
+brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
+ char *nvram_dat, uint nvram_sz)
+{
+ u8 core_idx;
+ u32 reg_addr;
+
+ if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
+ brcmf_err("SOCRAM core is down after reset?\n");
+ return false;
+ }
+
+ if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
+ return false;
+
+ /* clear all interrupts */
+ core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
+ reg_addr = ci->c_inf[core_idx].base;
+ reg_addr += offsetof(struct sdpcmd_regs, intstatus);
+ brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+ ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
+
+ return true;
+}
+
+static inline void
+brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci)
+{
+ ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4,
+ ARMCR4_BCMA_IOCTL_CPUHALT);
+}
+
+static bool
+brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
+ char *nvram_dat, uint nvram_sz)
+{
+ u8 core_idx;
+ u32 reg_addr;
+
+ if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz))
+ return false;
+
+ /* clear all interrupts */
+ core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
+ reg_addr = ci->c_inf[core_idx].base;
+ reg_addr += offsetof(struct sdpcmd_regs, intstatus);
+ brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+
+ /* Write reset vector to address 0 */
+ brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec,
+ sizeof(ci->rst_vec));
+
+ /* restore ARM */
+ ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0);
+
+ return true;
+}
+
+void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci)
+{
+ u8 arm_core_idx;
+
+ arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
+ if (BRCMF_MAX_CORENUM != arm_core_idx) {
+ brcmf_sdio_chip_cm3_enterdl(sdiodev, ci);
+ return;
+ }
+
+ brcmf_sdio_chip_cr4_enterdl(sdiodev, ci);
+}
+
+bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci, char *nvram_dat,
+ uint nvram_sz)
+{
+ u8 arm_core_idx;
+
+ arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3);
+ if (BRCMF_MAX_CORENUM != arm_core_idx)
+ return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat,
+ nvram_sz);
+
+ return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
index ce974d76bd9..83c041f1bf4 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
@@ -73,15 +73,17 @@ struct chip_info {
u32 pmurev;
u32 pmucaps;
u32 ramsize;
+ u32 rambase;
+ u32 rst_vec; /* reset vertor for ARM CR4 core */
bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
u16 coreid);
u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
u16 coreid);
void (*coredisable)(struct brcmf_sdio_dev *sdiodev,
- struct chip_info *ci, u16 coreid);
+ struct chip_info *ci, u16 coreid, u32 core_bits);
void (*resetcore)(struct brcmf_sdio_dev *sdiodev,
- struct chip_info *ci, u16 coreid);
+ struct chip_info *ci, u16 coreid, u32 core_bits);
};
struct sbconfig {
@@ -124,6 +126,95 @@ struct sbconfig {
u32 sbidhigh; /* identification */
};
+/* sdio core registers */
+struct sdpcmd_regs {
+ u32 corecontrol; /* 0x00, rev8 */
+ u32 corestatus; /* rev8 */
+ u32 PAD[1];
+ u32 biststatus; /* rev8 */
+
+ /* PCMCIA access */
+ u16 pcmciamesportaladdr; /* 0x010, rev8 */
+ u16 PAD[1];
+ u16 pcmciamesportalmask; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciawrframebc; /* rev8 */
+ u16 PAD[1];
+ u16 pcmciaunderflowtimer; /* rev8 */
+ u16 PAD[1];
+
+ /* interrupt */
+ u32 intstatus; /* 0x020, rev8 */
+ u32 hostintmask; /* rev8 */
+ u32 intmask; /* rev8 */
+ u32 sbintstatus; /* rev8 */
+ u32 sbintmask; /* rev8 */
+ u32 funcintmask; /* rev4 */
+ u32 PAD[2];
+ u32 tosbmailbox; /* 0x040, rev8 */
+ u32 tohostmailbox; /* rev8 */
+ u32 tosbmailboxdata; /* rev8 */
+ u32 tohostmailboxdata; /* rev8 */
+
+ /* synchronized access to registers in SDIO clock domain */
+ u32 sdioaccess; /* 0x050, rev8 */
+ u32 PAD[3];
+
+ /* PCMCIA frame control */
+ u8 pcmciaframectrl; /* 0x060, rev8 */
+ u8 PAD[3];
+ u8 pcmciawatermark; /* rev8 */
+ u8 PAD[155];
+
+ /* interrupt batching control */
+ u32 intrcvlazy; /* 0x100, rev8 */
+ u32 PAD[3];
+
+ /* counters */
+ u32 cmd52rd; /* 0x110, rev8 */
+ u32 cmd52wr; /* rev8 */
+ u32 cmd53rd; /* rev8 */
+ u32 cmd53wr; /* rev8 */
+ u32 abort; /* rev8 */
+ u32 datacrcerror; /* rev8 */
+ u32 rdoutofsync; /* rev8 */
+ u32 wroutofsync; /* rev8 */
+ u32 writebusy; /* rev8 */
+ u32 readwait; /* rev8 */
+ u32 readterm; /* rev8 */
+ u32 writeterm; /* rev8 */
+ u32 PAD[40];
+ u32 clockctlstatus; /* rev8 */
+ u32 PAD[7];
+
+ u32 PAD[128]; /* DMA engines */
+
+ /* SDIO/PCMCIA CIS region */
+ char cis[512]; /* 0x400-0x5ff, rev6 */
+
+ /* PCMCIA function control registers */
+ char pcmciafcr[256]; /* 0x600-6ff, rev6 */
+ u16 PAD[55];
+
+ /* PCMCIA backplane access */
+ u16 backplanecsr; /* 0x76E, rev6 */
+ u16 backplaneaddr0; /* rev6 */
+ u16 backplaneaddr1; /* rev6 */
+ u16 backplaneaddr2; /* rev6 */
+ u16 backplaneaddr3; /* rev6 */
+ u16 backplanedata0; /* rev6 */
+ u16 backplanedata1; /* rev6 */
+ u16 backplanedata2; /* rev6 */
+ u16 backplanedata3; /* rev6 */
+ u16 PAD[31];
+
+ /* sprom "size" & "blank" info */
+ u16 spromstatus; /* 0x7BE, rev2 */
+ u32 PAD[464];
+
+ u16 PAD[0x80];
+};
+
extern int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
struct chip_info **ci_ptr, u32 regs);
extern void brcmf_sdio_chip_detach(struct chip_info **ci_ptr);
@@ -131,6 +222,10 @@ extern void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
struct chip_info *ci,
u32 drivestrength);
extern u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid);
-
+extern void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci);
+extern bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev,
+ struct chip_info *ci, char *nvram_dat,
+ uint nvram_sz);
#endif /* _BRCMFMAC_SDIO_CHIP_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
index 0d30afd8c67..7c1b6332747 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -48,7 +48,13 @@
#define SBSDIO_NUM_FUNCTION 3
/* function 0 vendor specific CCCR registers */
-#define SDIO_CCCR_BRCM_SEPINT 0xf2
+#define SDIO_CCCR_BRCM_CARDCAP 0xf0
+#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02
+#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04
+#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08
+#define SDIO_CCCR_BRCM_CARDCTRL 0xf1
+#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02
+#define SDIO_CCCR_BRCM_SEPINT 0xf2
#define SDIO_SEPINT_MASK 0x01
#define SDIO_SEPINT_OE 0x02
@@ -97,9 +103,23 @@
#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B
/* Read Frame Byte Count High */
#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C
+/* MesBusyCtl (rev 11) */
+#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
+/* Sdio Core Rev 12 */
+#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
+#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
+#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0
+#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2
+#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1
+#define SBSDIO_FUNC1_SLEEPCSR 0x1001F
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0
+#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1
+#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2
+#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1
#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */
-#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */
/* function 1 OCP space */
@@ -154,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. */
@@ -224,6 +242,8 @@ brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
*/
extern int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw,
u32 addr, u8 *buf, uint nbytes);
+extern int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write,
+ u32 address, u8 *data, uint size);
/* Issue an abort to the specified function */
extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index e4f1f3c9575..6d758f28535 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -29,6 +29,7 @@
#include "tracepoint.h"
#include "fwil_types.h"
#include "p2p.h"
+#include "btcoex.h"
#include "wl_cfg80211.h"
#include "fwil.h"
@@ -334,22 +335,16 @@ static u8 brcmf_mw_to_qdbm(u16 mw)
return qdbm;
}
-u16 channel_to_chanspec(struct ieee80211_channel *ch)
+u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct ieee80211_channel *ch)
{
- u16 chanspec;
-
- chanspec = ieee80211_frequency_to_channel(ch->center_freq);
- chanspec &= WL_CHANSPEC_CHAN_MASK;
-
- if (ch->band == IEEE80211_BAND_2GHZ)
- chanspec |= WL_CHANSPEC_BAND_2G;
- else
- chanspec |= WL_CHANSPEC_BAND_5G;
+ struct brcmu_chan ch_inf;
- chanspec |= WL_CHANSPEC_BW_20;
- chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+ ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
+ ch_inf.bw = BRCMU_CHAN_BW_20;
+ d11inf->encchspec(&ch_inf);
- return chanspec;
+ return ch_inf.chspec;
}
/* Traverse a string of 1-byte tag/1-byte length/variable-length value
@@ -680,7 +675,8 @@ done:
return err;
}
-static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
+static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_scan_params_le *params_le,
struct cfg80211_scan_request *request)
{
u32 n_ssids;
@@ -712,7 +708,8 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
n_channels);
if (n_channels > 0) {
for (i = 0; i < n_channels; i++) {
- chanspec = channel_to_chanspec(request->channels[i]);
+ chanspec = channel_to_chanspec(&cfg->d11inf,
+ request->channels[i]);
brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
request->channels[i]->hw_value, chanspec);
params_le->channel_list[i] = cpu_to_le16(chanspec);
@@ -784,7 +781,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
goto exit;
}
BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
- brcmf_escan_prep(&params->params_le, request);
+ brcmf_escan_prep(cfg, &params->params_le, request);
params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
params->action = cpu_to_le16(action);
params->sync_id = cpu_to_le16(0x1234);
@@ -860,6 +857,11 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
cfg->scan_status);
return -EAGAIN;
}
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
return -EAGAIN;
@@ -1050,6 +1052,7 @@ static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
@@ -1063,6 +1066,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
}
clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
+ clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+ brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
brcmf_dbg(TRACE, "Exit\n");
}
@@ -1182,7 +1187,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
params->chandef.chan->center_freq);
if (params->channel_fixed) {
/* adding chanspec */
- chanspec = channel_to_chanspec(params->chandef.chan);
+ chanspec = channel_to_chanspec(&cfg->d11inf,
+ params->chandef.chan);
join_params.params_le.chanspec_list[0] =
cpu_to_le16(chanspec);
join_params.params_le.chanspec_num = cpu_to_le32(1);
@@ -1572,7 +1578,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
if (chan) {
cfg->channel =
ieee80211_frequency_to_channel(chan->center_freq);
- chanspec = channel_to_chanspec(chan);
+ chanspec = channel_to_chanspec(&cfg->d11inf, chan);
brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
cfg->channel, chan->center_freq, chanspec);
} else {
@@ -2231,6 +2237,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
struct ieee80211_channel *notify_channel;
struct cfg80211_bss *bss;
struct ieee80211_supported_band *band;
+ struct brcmu_chan ch;
s32 err = 0;
u16 channel;
u32 freq;
@@ -2245,8 +2252,12 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
return 0;
}
- channel = bi->ctl_ch ? bi->ctl_ch :
- CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+ if (!bi->ctl_ch) {
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ bi->ctl_ch = ch.chnum;
+ }
+ channel = bi->ctl_ch;
if (channel <= CH_MAX_2G_CHANNEL)
band = wiphy->bands[IEEE80211_BAND_2GHZ];
@@ -2321,9 +2332,9 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
struct brcmf_bss_info_le *bi = NULL;
struct ieee80211_supported_band *band;
struct cfg80211_bss *bss;
+ struct brcmu_chan ch;
u8 *buf = NULL;
s32 err = 0;
- u16 channel;
u32 freq;
u16 notify_capability;
u16 notify_interval;
@@ -2350,15 +2361,15 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
bi = (struct brcmf_bss_info_le *)(buf + 4);
- channel = bi->ctl_ch ? bi->ctl_ch :
- CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
- if (channel <= CH_MAX_2G_CHANNEL)
+ if (ch.band == BRCMU_CHAN_BAND_2G)
band = wiphy->bands[IEEE80211_BAND_2GHZ];
else
band = wiphy->bands[IEEE80211_BAND_5GHZ];
- freq = ieee80211_channel_to_frequency(channel, band->band);
+ freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
notify_channel = ieee80211_get_channel(wiphy, freq);
notify_capability = le16_to_cpu(bi->capability);
@@ -2367,7 +2378,7 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
notify_ielen = le32_to_cpu(bi->ie_length);
notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
- brcmf_dbg(CONN, "channel: %d(%d)\n", channel, freq);
+ brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
brcmf_dbg(CONN, "capability: %X\n", notify_capability);
brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
brcmf_dbg(CONN, "signal: %d\n", notify_signal);
@@ -2490,12 +2501,19 @@ static void brcmf_escan_timeout(unsigned long data)
}
static s32
-brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
+brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
+ struct brcmf_bss_info_le *bss,
struct brcmf_bss_info_le *bss_info_le)
{
+ struct brcmu_chan ch_bss, ch_bss_info_le;
+
+ ch_bss.chspec = le16_to_cpu(bss->chanspec);
+ cfg->d11inf.decchspec(&ch_bss);
+ ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
+ cfg->d11inf.decchspec(&ch_bss_info_le);
+
if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
- (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
- CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
+ ch_bss.band == ch_bss_info_le.band &&
bss_info_le->SSID_len == bss->SSID_len &&
!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
@@ -2593,7 +2611,8 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
bss = bss ? (struct brcmf_bss_info_le *)
((unsigned char *)bss +
le32_to_cpu(bss->length)) : list->bss_info_le;
- if (brcmf_compare_update_same_bss(bss, bss_info_le))
+ if (brcmf_compare_update_same_bss(cfg, bss,
+ bss_info_le))
goto exit;
}
memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
@@ -3007,6 +3026,11 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
return -EAGAIN;
}
+ if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
+ brcmf_err("Scanning suppressed: status (%lu)\n",
+ cfg->scan_status);
+ return -EAGAIN;
+ }
if (!request->n_ssids || !request->n_match_sets) {
brcmf_err("Invalid sched scan req!! n_ssids:%d\n",
@@ -3993,6 +4017,39 @@ exit:
return err;
}
+static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_crit_proto_id proto,
+ u16 duration)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ /* only DHCP support for now */
+ if (proto != NL80211_CRIT_PROTO_DHCP)
+ return -EINVAL;
+
+ /* suppress and abort scanning */
+ set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+ brcmf_abort_scanning(cfg);
+
+ return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
+}
+
+static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_cfg80211_vif *vif;
+
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+ brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
+ clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
+}
+
static struct cfg80211_ops wl_cfg80211_ops = {
.add_virtual_intf = brcmf_cfg80211_add_iface,
.del_virtual_intf = brcmf_cfg80211_del_iface,
@@ -4029,6 +4086,8 @@ static struct cfg80211_ops wl_cfg80211_ops = {
.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
.start_p2p_device = brcmf_p2p_start_device,
.stop_p2p_device = brcmf_p2p_stop_device,
+ .crit_proto_start = brcmf_cfg80211_crit_proto_start,
+ .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
#ifdef CONFIG_NL80211_TESTMODE
.testmode_cmd = brcmf_cfg80211_testmode
#endif
@@ -4337,9 +4396,9 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
struct ieee80211_channel *notify_channel = NULL;
struct ieee80211_supported_band *band;
struct brcmf_bss_info_le *bi;
+ struct brcmu_chan ch;
u32 freq;
s32 err = 0;
- u32 target_channel;
u8 *buf;
brcmf_dbg(TRACE, "Enter\n");
@@ -4363,15 +4422,15 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
goto done;
bi = (struct brcmf_bss_info_le *)(buf + 4);
- target_channel = bi->ctl_ch ? bi->ctl_ch :
- CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
- if (target_channel <= CH_MAX_2G_CHANNEL)
+ if (ch.band == BRCMU_CHAN_BAND_2G)
band = wiphy->bands[IEEE80211_BAND_2GHZ];
else
band = wiphy->bands[IEEE80211_BAND_5GHZ];
- freq = ieee80211_channel_to_frequency(target_channel, band->band);
+ freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
notify_channel = ieee80211_get_channel(wiphy, freq);
done:
@@ -4725,6 +4784,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
struct brcmf_cfg80211_vif *vif;
struct brcmf_if *ifp;
s32 err = 0;
+ s32 io_type;
if (!ndev) {
brcmf_err("ndev is invalid\n");
@@ -4765,6 +4825,21 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
brcmf_err("P2P initilisation failed (%d)\n", err);
goto cfg80211_p2p_attach_out;
}
+ err = brcmf_btcoex_attach(cfg);
+ if (err) {
+ brcmf_err("BT-coex initialisation failed (%d)\n", err);
+ brcmf_p2p_detach(&cfg->p2p);
+ goto cfg80211_p2p_attach_out;
+ }
+
+ err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
+ &io_type);
+ if (err) {
+ brcmf_err("Failed to get D11 version (%d)\n", err);
+ goto cfg80211_p2p_attach_out;
+ }
+ cfg->d11inf.io_type = (u8)io_type;
+ brcmu_d11_attach(&cfg->d11inf);
return cfg;
@@ -4783,6 +4858,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
struct brcmf_cfg80211_vif *tmp;
wl_deinit_priv(cfg);
+ brcmf_btcoex_detach(cfg);
list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
brcmf_free_vif(vif);
}
@@ -4885,11 +4961,11 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
struct ieee80211_channel *band_chan_arr;
struct brcmf_chanspec_list *list;
+ struct brcmu_chan ch;
s32 err;
u8 *pbuf;
u32 i, j;
u32 total;
- u16 chanspec;
enum ieee80211_band band;
u32 channel;
u32 *n_cnt;
@@ -4918,42 +4994,30 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
total = le32_to_cpu(list->count);
for (i = 0; i < total; i++) {
- chanspec = (u16)le32_to_cpu(list->element[i]);
- channel = CHSPEC_CHANNEL(chanspec);
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
- if (CHSPEC_IS40(chanspec)) {
- if (CHSPEC_SB_UPPER(chanspec))
- channel += CH_10MHZ_APART;
- else
- channel -= CH_10MHZ_APART;
- } else if (CHSPEC_IS80(chanspec)) {
- brcmf_dbg(INFO, "HT80 center channel : %d\n",
- channel);
- continue;
- }
- if (CHSPEC_IS2G(chanspec) && (channel >= CH_MIN_2G_CHANNEL) &&
- (channel <= CH_MAX_2G_CHANNEL)) {
+ if (ch.band == BRCMU_CHAN_BAND_2G) {
band_chan_arr = __wl_2ghz_channels;
array_size = ARRAY_SIZE(__wl_2ghz_channels);
n_cnt = &__wl_band_2ghz.n_channels;
band = IEEE80211_BAND_2GHZ;
ht40_allowed = (bw_cap == WLC_N_BW_40ALL);
- } else if (CHSPEC_IS5G(chanspec) &&
- channel >= CH_MIN_5G_CHANNEL) {
+ } else if (ch.band == BRCMU_CHAN_BAND_5G) {
band_chan_arr = __wl_5ghz_a_channels;
array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
n_cnt = &__wl_band_5ghz_a.n_channels;
band = IEEE80211_BAND_5GHZ;
ht40_allowed = !(bw_cap == WLC_N_BW_20ALL);
} else {
- brcmf_err("Invalid channel Sepc. 0x%x.\n", chanspec);
+ brcmf_err("Invalid channel Sepc. 0x%x.\n", ch.chspec);
continue;
}
- if (!ht40_allowed && CHSPEC_IS40(chanspec))
+ if (!ht40_allowed && ch.bw == BRCMU_CHAN_BW_40)
continue;
update = false;
for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
- if (band_chan_arr[j].hw_value == channel) {
+ if (band_chan_arr[j].hw_value == ch.chnum) {
update = true;
break;
}
@@ -4964,16 +5028,16 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
index = *n_cnt;
if (index < array_size) {
band_chan_arr[index].center_freq =
- ieee80211_channel_to_frequency(channel, band);
- band_chan_arr[index].hw_value = channel;
+ ieee80211_channel_to_frequency(ch.chnum, band);
+ band_chan_arr[index].hw_value = ch.chnum;
- if (CHSPEC_IS40(chanspec) && ht40_allowed) {
+ if (ch.bw == BRCMU_CHAN_BW_40 && ht40_allowed) {
/* assuming the order is HT20, HT40 Upper,
* HT40 lower from chanspecs
*/
ht40_flag = band_chan_arr[index].flags &
IEEE80211_CHAN_NO_HT40;
- if (CHSPEC_SB_UPPER(chanspec)) {
+ if (ch.sb == BRCMU_CHAN_SB_U) {
if (ht40_flag == IEEE80211_CHAN_NO_HT40)
band_chan_arr[index].flags &=
~IEEE80211_CHAN_NO_HT40;
@@ -4993,11 +5057,9 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
} else {
band_chan_arr[index].flags =
IEEE80211_CHAN_NO_HT40;
- if (band == IEEE80211_BAND_2GHZ)
- channel |= WL_CHANSPEC_BAND_2G;
- else
- channel |= WL_CHANSPEC_BAND_5G;
- channel |= WL_CHANSPEC_BW_20;
+ ch.bw = BRCMU_CHAN_BW_20;
+ cfg->d11inf.encchspec(&ch);
+ channel = ch.chspec;
err = brcmf_fil_bsscfg_int_get(ifp,
"per_chan_info",
&channel);
@@ -5226,6 +5288,13 @@ s32 brcmf_cfg80211_down(struct net_device *ndev)
return err;
}
+enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
+{
+ struct wireless_dev *wdev = &ifp->vif->wdev;
+
+ return wdev->iftype;
+}
+
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
{
struct brcmf_cfg80211_vif *vif;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index 2907437ef43..a71cff84cdc 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -17,6 +17,9 @@
#ifndef _wl_cfg80211_h_
#define _wl_cfg80211_h_
+/* for brcmu_d11inf */
+#include <brcmu_d11.h>
+
#define WL_NUM_SCAN_MAX 10
#define WL_NUM_PMKIDS_MAX MAXPMKID
#define WL_TLV_INFO_MAX 1024
@@ -74,14 +77,16 @@
/**
- * enum brcmf_scan_status - dongle scan status
+ * enum brcmf_scan_status - scan engine status
*
* @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle.
* @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle.
+ * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver.
*/
enum brcmf_scan_status {
BRCMF_SCAN_STATUS_BUSY,
BRCMF_SCAN_STATUS_ABORT,
+ BRCMF_SCAN_STATUS_SUPPRESS,
};
/**
@@ -346,6 +351,7 @@ struct brcmf_cfg80211_vif_event {
* @wiphy: wiphy object for cfg80211 interface.
* @conf: dongle configuration.
* @p2p: peer-to-peer specific information.
+ * @btcoex: Bluetooth coexistence information.
* @scan_request: cfg80211 scan request object.
* @usr_sync: mainly for dongle up/down synchronization.
* @bss_list: bss_list holding scanned ap information.
@@ -379,6 +385,7 @@ struct brcmf_cfg80211_info {
struct wiphy *wiphy;
struct brcmf_cfg80211_conf *conf;
struct brcmf_p2p_info p2p;
+ struct brcmf_btcoex_info *btcoex;
struct cfg80211_scan_request *scan_request;
struct mutex usr_sync;
struct brcmf_scan_results *bss_list;
@@ -408,6 +415,7 @@ struct brcmf_cfg80211_info {
u8 vif_cnt;
struct brcmf_cfg80211_vif_event vif_event;
struct completion vif_disabled;
+ struct brcmu_d11inf d11inf;
};
/**
@@ -474,6 +482,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg);
s32 brcmf_cfg80211_up(struct net_device *ndev);
s32 brcmf_cfg80211_down(struct net_device *ndev);
+enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
enum nl80211_iftype type,
@@ -484,7 +493,8 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
const u8 *vndr_ie_buf, u32 vndr_ie_len);
s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif);
struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key);
-u16 channel_to_chanspec(struct ieee80211_channel *ch);
+u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
+ struct ieee80211_channel *ch);
u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif);