diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-02 20:53:45 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-02 20:53:45 -0700 |
commit | cd6362befe4cc7bf589a5236d2a780af2d47bcc9 (patch) | |
tree | 3bd4e13ec3f92a00dc4f6c3d65e820b54dbfe46e /drivers/net/wireless/brcm80211 | |
parent | 0f1b1e6d73cb989ce2c071edc57deade3b084dfe (diff) | |
parent | b1586f099ba897542ece36e8a23c1a62907261ef (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
"Here is my initial pull request for the networking subsystem during
this merge window:
1) Support for ESN in AH (RFC 4302) from Fan Du.
2) Add full kernel doc for ethtool command structures, from Ben
Hutchings.
3) Add BCM7xxx PHY driver, from Florian Fainelli.
4) Export computed TCP rate information in netlink socket dumps, from
Eric Dumazet.
5) Allow IPSEC SA to be dumped partially using a filter, from Nicolas
Dichtel.
6) Convert many drivers to pci_enable_msix_range(), from Alexander
Gordeev.
7) Record SKB timestamps more efficiently, from Eric Dumazet.
8) Switch to microsecond resolution for TCP round trip times, also
from Eric Dumazet.
9) Clean up and fix 6lowpan fragmentation handling by making use of
the existing inet_frag api for it's implementation.
10) Add TX grant mapping to xen-netback driver, from Zoltan Kiss.
11) Auto size SKB lengths when composing netlink messages based upon
past message sizes used, from Eric Dumazet.
12) qdisc dumps can take a long time, add a cond_resched(), From Eric
Dumazet.
13) Sanitize netpoll core and drivers wrt. SKB handling semantics.
Get rid of never-used-in-tree netpoll RX handling. From Eric W
Biederman.
14) Support inter-address-family and namespace changing in VTI tunnel
driver(s). From Steffen Klassert.
15) Add Altera TSE driver, from Vince Bridgers.
16) Optimizing csum_replace2() so that it doesn't adjust the checksum
by checksumming the entire header, from Eric Dumazet.
17) Expand BPF internal implementation for faster interpreting, more
direct translations into JIT'd code, and much cleaner uses of BPF
filtering in non-socket ocntexts. From Daniel Borkmann and Alexei
Starovoitov"
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1976 commits)
netpoll: Use skb_irq_freeable to make zap_completion_queue safe.
net: Add a test to see if a skb is freeable in irq context
qlcnic: Fix build failure due to undefined reference to `vxlan_get_rx_port'
net: ptp: move PTP classifier in its own file
net: sxgbe: make "core_ops" static
net: sxgbe: fix logical vs bitwise operation
net: sxgbe: sxgbe_mdio_register() frees the bus
Call efx_set_channels() before efx->type->dimension_resources()
xen-netback: disable rogue vif in kthread context
net/mlx4: Set proper build dependancy with vxlan
be2net: fix build dependency on VxLAN
mac802154: make csma/cca parameters per-wpan
mac802154: allow only one WPAN to be up at any given time
net: filter: minor: fix kdoc in __sk_run_filter
netlink: don't compare the nul-termination in nla_strcmp
can: c_can: Avoid led toggling for every packet.
can: c_can: Simplify TX interrupt cleanup
can: c_can: Store dlc private
can: c_can: Reduce register access
can: c_can: Make the code readable
...
Diffstat (limited to 'drivers/net/wireless/brcm80211')
18 files changed, 2061 insertions, 1806 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 57cddee03252..1d2ceac3a221 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -24,6 +24,7 @@ ccflags-y += -D__CHECK_ENDIAN__ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ wl_cfg80211.o \ + chip.o \ fwil.o \ fweh.o \ fwsignal.o \ @@ -36,8 +37,7 @@ brcmfmac-objs += \ btcoex.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ - bcmsdh.o \ - sdio_chip.o + bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o brcmfmac-$(CONFIG_BRCMDBG) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index fa35b23bbaa7..a16e644e7c08 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -43,7 +43,6 @@ #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" -#include "sdio_chip.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -54,6 +53,12 @@ /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 +#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ +#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ + +static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; +module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); +MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) { @@ -264,26 +269,17 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, break; } - if (ret) { - /* - * SleepCSR register access can fail when - * waking up the device so reduce this noise - * in the logs. - */ - if (addr != SBSDIO_FUNC1_SLEEPCSR) - brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - else - brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - } + if (ret) + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", fn, addr, ret); + return ret; } static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { - u8 func_num; + u8 func; s32 retry = 0; int ret; @@ -297,9 +293,9 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, * The rest: function 1 silicon backplane core registers */ if ((addr & ~REG_F0_REG_MASK) == 0) - func_num = SDIO_FUNC_0; + func = SDIO_FUNC_0; else - func_num = SDIO_FUNC_1; + func = SDIO_FUNC_1; do { if (!write) @@ -307,16 +303,26 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, /* for retry wait for 1 ms till bus get settled down */ if (retry) usleep_range(1000, 2000); - ret = brcmf_sdiod_request_data(sdiodev, func_num, addr, regsz, + ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz, data, write); } while (ret != 0 && ret != -ENOMEDIUM && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret == -ENOMEDIUM) brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); - else if (ret != 0) - brcmf_err("failed with %d\n", ret); - + else if (ret != 0) { + /* + * SleepCSR register access can fail when + * waking up the device so reduce this noise + * in the logs. + */ + if (addr != SBSDIO_FUNC1_SLEEPCSR) + brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + else + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + } return ret; } @@ -488,7 +494,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, struct mmc_request mmc_req; struct mmc_command mmc_cmd; struct mmc_data mmc_dat; - struct sg_table st; struct scatterlist *sgl; int ret = 0; @@ -533,16 +538,11 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, pkt_offset = 0; pkt_next = target_list->next; - if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) { - ret = -ENOMEM; - goto exit; - } - memset(&mmc_req, 0, sizeof(struct mmc_request)); memset(&mmc_cmd, 0, sizeof(struct mmc_command)); memset(&mmc_dat, 0, sizeof(struct mmc_data)); - mmc_dat.sg = st.sgl; + mmc_dat.sg = sdiodev->sgtable.sgl; mmc_dat.blksz = func_blk_sz; mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; mmc_cmd.opcode = SD_IO_RW_EXTENDED; @@ -558,7 +558,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, while (seg_sz) { req_sz = 0; sg_cnt = 0; - sgl = st.sgl; + sgl = sdiodev->sgtable.sgl; /* prep sg table */ while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; @@ -640,7 +640,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, } exit: - sg_free_table(&st); + sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents); while ((pkt_next = __skb_dequeue(&local_list)) != NULL) brcmu_pkt_buf_free_skb(pkt_next); @@ -827,7 +827,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, } if (!write) memcpy(data, pkt->data, dsize); - skb_trim(pkt, dsize); + skb_trim(pkt, 0); /* Adjust for next transfer (if any) */ size -= dsize; @@ -864,6 +864,29 @@ int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) return 0; } +static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) +{ + uint nents; + int err; + + if (!sdiodev->sg_support) + return; + + nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); + nents += (nents >> 4) + 1; + + WARN_ON(nents > sdiodev->max_segment_count); + + brcmf_dbg(TRACE, "nents=%d\n", nents); + err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL); + if (err < 0) { + brcmf_err("allocation failed: disable scatter-gather"); + sdiodev->sg_support = false; + } + + sdiodev->txglomsz = brcmf_sdiod_txglomsz; +} + static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) { if (sdiodev->bus) { @@ -881,6 +904,7 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) sdio_disable_func(sdiodev->func[1]); sdio_release_host(sdiodev->func[1]); + sg_free_table(&sdiodev->sgtable); sdiodev->sbwad = 0; return 0; @@ -936,6 +960,11 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) SG_MAX_SINGLE_ALLOC); sdiodev->max_segment_size = host->max_seg_size; + /* allocate scatter-gather table. sg support + * will be disabled upon allocation failure. + */ + brcmf_sdiod_sgtable_alloc(sdiodev); + /* try to attach to the target device */ sdiodev->bus = brcmf_sdio_probe(sdiodev); if (!sdiodev->bus) { @@ -960,6 +989,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335_4339)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)}, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -1073,9 +1103,7 @@ static int brcmf_ops_sdio_suspend(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; int ret = 0; - brcmf_dbg(SDIO, "\n"); - - atomic_set(&sdiodev->suspend, true); + brcmf_dbg(SDIO, "Enter\n"); sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { @@ -1083,9 +1111,12 @@ static int brcmf_ops_sdio_suspend(struct device *dev) return -EINVAL; } + atomic_set(&sdiodev->suspend, true); + ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); if (ret) { brcmf_err("Failed to set pm_flags\n"); + atomic_set(&sdiodev->suspend, false); return ret; } @@ -1099,6 +1130,7 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + brcmf_dbg(SDIO, "Enter\n"); brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); atomic_set(&sdiodev->suspend, false); return 0; @@ -1115,14 +1147,15 @@ static struct sdio_driver brcmf_sdmmc_driver = { .remove = brcmf_ops_sdio_remove, .name = BRCMFMAC_SDIO_PDATA_NAME, .id_table = brcmf_sdmmc_ids, -#ifdef CONFIG_PM_SLEEP .drv = { + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP .pm = &brcmf_sdio_pm_ops, - }, #endif /* CONFIG_PM_SLEEP */ + }, }; -static int brcmf_sdio_pd_probe(struct platform_device *pdev) +static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) { brcmf_dbg(SDIO, "Enter\n"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c new file mode 100644 index 000000000000..df130ef53d1c --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -0,0 +1,1034 @@ +/* + * Copyright (c) 2014 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/kernel.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/ssb/ssb_regs.h> +#include <linux/bcma/bcma.h> +#include <linux/bcma/bcma_regs.h> + +#include <defs.h> +#include <soc.h> +#include <brcm_hw_ids.h> +#include <brcmu_utils.h> +#include <chipcommon.h> +#include "dhd_dbg.h" +#include "chip.h" + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 + +/* PL-368 DMP definitions */ +#define DMP_DESC_TYPE_MSK 0x0000000F +#define DMP_DESC_EMPTY 0x00000000 +#define DMP_DESC_VALID 0x00000001 +#define DMP_DESC_COMPONENT 0x00000001 +#define DMP_DESC_MASTER_PORT 0x00000003 +#define DMP_DESC_ADDRESS 0x00000005 +#define DMP_DESC_ADDRSIZE_GT32 0x00000008 +#define DMP_DESC_EOT 0x0000000F + +#define DMP_COMP_DESIGNER 0xFFF00000 +#define DMP_COMP_DESIGNER_S 20 +#define DMP_COMP_PARTNUM 0x000FFF00 +#define DMP_COMP_PARTNUM_S 8 +#define DMP_COMP_CLASS 0x000000F0 +#define DMP_COMP_CLASS_S 4 +#define DMP_COMP_REVISION 0xFF000000 +#define DMP_COMP_REVISION_S 24 +#define DMP_COMP_NUM_SWRAP 0x00F80000 +#define DMP_COMP_NUM_SWRAP_S 19 +#define DMP_COMP_NUM_MWRAP 0x0007C000 +#define DMP_COMP_NUM_MWRAP_S 14 +#define DMP_COMP_NUM_SPORT 0x00003E00 +#define DMP_COMP_NUM_SPORT_S 9 +#define DMP_COMP_NUM_MPORT 0x000001F0 +#define DMP_COMP_NUM_MPORT_S 4 + +#define DMP_MASTER_PORT_UID 0x0000FF00 +#define DMP_MASTER_PORT_UID_S 8 +#define DMP_MASTER_PORT_NUM 0x000000F0 +#define DMP_MASTER_PORT_NUM_S 4 + +#define DMP_SLAVE_ADDR_BASE 0xFFFFF000 +#define DMP_SLAVE_ADDR_BASE_S 12 +#define DMP_SLAVE_PORT_NUM 0x00000F00 +#define DMP_SLAVE_PORT_NUM_S 8 +#define DMP_SLAVE_TYPE 0x000000C0 +#define DMP_SLAVE_TYPE_S 6 +#define DMP_SLAVE_TYPE_SLAVE 0 +#define DMP_SLAVE_TYPE_BRIDGE 1 +#define DMP_SLAVE_TYPE_SWRAP 2 +#define DMP_SLAVE_TYPE_MWRAP 3 +#define DMP_SLAVE_SIZE_TYPE 0x00000030 +#define DMP_SLAVE_SIZE_TYPE_S 4 +#define DMP_SLAVE_SIZE_4K 0 +#define DMP_SLAVE_SIZE_8K 1 +#define DMP_SLAVE_SIZE_16K 2 +#define DMP_SLAVE_SIZE_DESC 3 + +/* EROM CompIdentB */ +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 + +/* ARM CR4 core specific control flag bits */ +#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 + +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + +/* chip core base & ramsize */ +/* bcm4329 */ +/* SDIO device core, ID 0x829 */ +#define BCM4329_CORE_BUS_BASE 0x18011000 +/* internal memory core, ID 0x80e */ +#define BCM4329_CORE_SOCRAM_BASE 0x18003000 +/* ARM Cortex M3 core, ID 0x82a */ +#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 CORE_SB(base, field) \ + (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) +#define SBCOREREV(sbidh) \ + ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ + ((sbidh) & SSB_IDHIGH_RCLO)) + +struct sbconfig { + u32 PAD[2]; + u32 sbipsflag; /* initiator port ocp slave flag */ + u32 PAD[3]; + u32 sbtpsflag; /* target port ocp slave flag */ + u32 PAD[11]; + u32 sbtmerrloga; /* (sonics >= 2.3) */ + u32 PAD; + u32 sbtmerrlog; /* (sonics >= 2.3) */ + u32 PAD[3]; + u32 sbadmatch3; /* address match3 */ + u32 PAD; + u32 sbadmatch2; /* address match2 */ + u32 PAD; + u32 sbadmatch1; /* address match1 */ + u32 PAD[7]; + u32 sbimstate; /* initiator agent state */ + u32 sbintvec; /* interrupt mask */ + u32 sbtmstatelow; /* target state */ + u32 sbtmstatehigh; /* target state */ + u32 sbbwa0; /* bandwidth allocation table0 */ + u32 PAD; + u32 sbimconfiglow; /* initiator configuration */ + u32 sbimconfighigh; /* initiator configuration */ + u32 sbadmatch0; /* address match0 */ + u32 PAD; + u32 sbtmconfiglow; /* target configuration */ + u32 sbtmconfighigh; /* target configuration */ + u32 sbbconfig; /* broadcast configuration */ + u32 PAD; + u32 sbbstate; /* broadcast state */ + u32 PAD[3]; + u32 sbactcnfg; /* activate configuration */ + u32 PAD[3]; + u32 sbflagst; /* current sbflags */ + u32 PAD[3]; + u32 sbidlow; /* identification */ + u32 sbidhigh; /* identification */ +}; + +struct brcmf_core_priv { + struct brcmf_core pub; + u32 wrapbase; + struct list_head list; + struct brcmf_chip_priv *chip; +}; + +/* ARM CR4 core specific control flag bits */ +#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 + +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + +struct brcmf_chip_priv { + struct brcmf_chip pub; + const struct brcmf_buscore_ops *ops; + void *ctx; + /* assured first core is chipcommon, second core is buscore */ + struct list_head cores; + u16 num_cores; + + bool (*iscoreup)(struct brcmf_core_priv *core); + void (*coredisable)(struct brcmf_core_priv *core, u32 prereset, + u32 reset); + void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset, + u32 postreset); +}; + +static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci, + struct brcmf_core *core) +{ + u32 regdata; + + regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh)); + core->rev = SBCOREREV(regdata); +} + +static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + u32 address; + + ci = core->chip; + address = CORE_SB(core->pub.base, sbtmstatelow); + regdata = ci->ops->read32(ci->ctx, address); + regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | + SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); + return SSB_TMSLOW_CLOCK == regdata; +} + +static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + bool ret; + + ci = core->chip; + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; + + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); + ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); + + return ret; +} + +static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) +{ + struct brcmf_chip_priv *ci; + u32 val, base; + + ci = core->chip; + base = core->pub.base; + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if (val & SSB_TMSLOW_RESET) + return; + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if ((val & SSB_TMSLOW_CLOCK) != 0) { + /* + * set target reject and spin until busy is clear + * (preserve core-specific bits) + */ + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + val | SSB_TMSLOW_REJECT); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)) + & SSB_TMSHIGH_BUSY), 100000); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (val & SSB_TMSHIGH_BUSY) + brcmf_err("core state still busy\n"); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val |= SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + udelay(1); + SPINWAIT((ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)) & + SSB_IMSTATE_BUSY), 100000); + } + + /* set reset and reject while enabling the clocks */ + val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(10); + + /* clear the initiator reject bit */ + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val &= ~SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + } + } + + /* leave reset and reject asserted */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); + udelay(1); +} + +static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + + ci = core->chip; + + /* if core is already in reset, just return */ + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); + if ((regdata & BCMA_RESET_CTL_RESET) != 0) + return; + + /* configure reset */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + + /* put in reset */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, + BCMA_RESET_CTL_RESET); + usleep_range(10, 20); + + /* wait till reset is 1 */ + SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) != + BCMA_RESET_CTL_RESET, 300); + + /* in-reset configure */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + u32 base; + + ci = core->chip; + base = core->pub.base; + /* + * Must do the disable sequence first to work for + * arbitrary current core state. + */ + brcmf_chip_sb_coredisable(core, 0, 0); + + /* + * Now do the initialization sequence. + * set reset while enabling the clock and + * forcing them on throughout the core + */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_RESET); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + + /* clear any serror */ + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (regdata & SSB_TMSHIGH_SERR) + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0); + + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate)); + if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { + regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); + ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata); + } + + /* clear reset and allow it to propagate throughout the core */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + + /* leave clock enabled */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); +} + +static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) +{ + struct brcmf_chip_priv *ci; + int count; + + ci = core->chip; + + /* must disable first to work for arbitrary current core state */ + brcmf_chip_ai_coredisable(core, prereset, reset); + + count = 0; + while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & + BCMA_RESET_CTL_RESET) { + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0); + count++; + if (count > 50) + break; + usleep_range(40, 60); + } + + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + postreset | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static char *brcmf_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, + u16 coreid, u32 base, + u32 wrapbase) +{ + struct brcmf_core_priv *core; + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) + return ERR_PTR(-ENOMEM); + + core->pub.id = coreid; + core->pub.base = base; + core->chip = ci; + core->wrapbase = wrapbase; + + list_add_tail(&core->list, &ci->cores); + return &core->pub; +} + +#ifdef DEBUG +/* safety check for chipinfo */ +static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) +{ + struct brcmf_core_priv *core; + bool need_socram = false; + bool has_socram = false; + int idx = 1; + + list_for_each_entry(core, &ci->cores, list) { + brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n", + idx++, core->pub.id, core->pub.rev, core->pub.base, + core->wrapbase); + + switch (core->pub.id) { + case BCMA_CORE_ARM_CM3: + need_socram = true; + break; + case BCMA_CORE_INTERNAL_MEM: + has_socram = true; + break; + case BCMA_CORE_ARM_CR4: + if (ci->pub.rambase == 0) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -ENOMEM; + } + break; + default: + break; + } + } + + /* check RAM core presence for ARM CM3 core */ + if (need_socram && !has_socram) { + brcmf_err("RAM core not provided with ARM CM3 core\n"); + return -ENODEV; + } + return 0; +} +#else /* DEBUG */ +static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) +{ + return 0; +} +#endif + +static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +{ + switch (ci->pub.chip) { + case BCM4329_CHIP_ID: + ci->pub.ramsize = BCM4329_RAMSIZE; + break; + case BCM43143_CHIP_ID: + ci->pub.ramsize = BCM43143_RAMSIZE; + break; + case BCM43241_CHIP_ID: + ci->pub.ramsize = 0x90000; + break; + case BCM4330_CHIP_ID: + ci->pub.ramsize = 0x48000; + break; + case BCM4334_CHIP_ID: + ci->pub.ramsize = 0x80000; + break; + case BCM4335_CHIP_ID: + ci->pub.ramsize = 0xc0000; + ci->pub.rambase = 0x180000; + break; + case BCM43362_CHIP_ID: + ci->pub.ramsize = 0x3c000; + break; + case BCM4339_CHIP_ID: + case BCM4354_CHIP_ID: + ci->pub.ramsize = 0xc0000; + ci->pub.rambase = 0x180000; + break; + default: + brcmf_err("unknown chip: %s\n", ci->pub.name); + break; + } +} + +static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, + u8 *type) +{ + u32 val; + + /* read next descriptor */ + val = ci->ops->read32(ci->ctx, *eromaddr); + *eromaddr += 4; + + if (!type) + return val; + + /* determine descriptor type */ + *type = (val & DMP_DESC_TYPE_MSK); + if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS) + *type = DMP_DESC_ADDRESS; + + return val; +} + +static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, + u32 *regbase, u32 *wrapbase) +{ + u8 desc; + u32 val; + u8 mpnum = 0; + u8 stype, sztype, wraptype; + + *regbase = 0; + *wrapbase = 0; + + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + if (desc == DMP_DESC_MASTER_PORT) { + mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; + wraptype = DMP_SLAVE_TYPE_MWRAP; + } else if (desc == DMP_DESC_ADDRESS) { + /* revert erom address */ + *eromaddr -= 4; + wraptype = DMP_SLAVE_TYPE_SWRAP; + } else { + *eromaddr -= 4; + return -EILSEQ; + } + + do { + /* locate address descriptor */ + do { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + /* unexpected table end */ + if (desc == DMP_DESC_EOT) { + *eromaddr -= 4; + return -EFAULT; + } + } while (desc != DMP_DESC_ADDRESS); + + /* skip upper 32-bit address descriptor */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + + sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S; + + /* next size descriptor can be skipped */ + if (sztype == DMP_SLAVE_SIZE_DESC) { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + /* skip upper size descriptor if present */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + } + + /* only look for 4K register regions */ + if (sztype != DMP_SLAVE_SIZE_4K) + continue; + + stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S; + + /* only regular slave and wrapper */ + if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE) + *regbase = val & DMP_SLAVE_ADDR_BASE; + if (*wrapbase == 0 && stype == wraptype) + *wrapbase = val & DMP_SLAVE_ADDR_BASE; + } while (*regbase == 0 || *wrapbase == 0); + + return 0; +} + +static +int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 eromaddr; + u8 desc_type = 0; + u32 val; + u16 id; + u8 nmp, nsp, nmw, nsw, rev; + u32 base, wrap; + int err; + + eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr)); + + while (desc_type != DMP_DESC_EOT) { + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (!(val & DMP_DESC_VALID)) + continue; + + if (desc_type == DMP_DESC_EMPTY) + continue; + + /* need a component descriptor */ + if (desc_type != DMP_DESC_COMPONENT) + continue; + + id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S; + + /* next descriptor must be component as well */ + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT)) + return -EFAULT; + + /* only look at cores with master port(s) */ + nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; + nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; + nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; + nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; + rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; + + /* need core with ports */ + if (nmw + nsw == 0) + continue; + + /* try to obtain register address info */ + err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap); + if (err) + continue; + + /* finally a core to be added */ + core = brcmf_chip_add_core(ci, id, base, wrap); + if (IS_ERR(core)) + return PTR_ERR(core); + + core->rev = rev; + } + + return 0; +} + +static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 regdata; + u32 socitype; + + /* Get CC core rev + * Chipid is assume to be at offset 0 from SI_ENUM_BASE + * For different chiptypes or old sdio hosts w/o chipcommon, + * other ways of recognition should be added here. + */ + regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid)); + ci->pub.chip = regdata & CID_ID_MASK; + ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; + socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + + brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name)); + brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n", + socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name, + ci->pub.chiprev); + + if (socitype == SOCI_SB) { + if (ci->pub.chip != BCM4329_CHIP_ID) { + brcmf_err("SB chip is not supported\n"); + return -ENODEV; + } + ci->iscoreup = brcmf_chip_sb_iscoreup; + ci->coredisable = brcmf_chip_sb_coredisable; + ci->resetcore = brcmf_chip_sb_resetcore; + + core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, + SI_ENUM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + BCM4329_CORE_BUS_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + BCM4329_CORE_SOCRAM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + BCM4329_CORE_ARM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + + core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0); + brcmf_chip_sb_corerev(ci, core); + } else if (socitype == SOCI_AI) { + ci->iscoreup = brcmf_chip_ai_iscoreup; + ci->coredisable = brcmf_chip_ai_coredisable; + ci->resetcore = brcmf_chip_ai_resetcore; + + brcmf_chip_dmp_erom_scan(ci); + } else { + brcmf_err("chip backplane type %u is not supported\n", + socitype); + return -ENODEV; + } + + brcmf_chip_get_raminfo(ci); + + return brcmf_chip_cores_check(ci); +} + +static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) +{ + struct brcmf_core *core; + struct brcmf_core_priv *cr4; + u32 val; + + + core = brcmf_chip_get_core(&chip->pub, id); + if (!core) + return; + + switch (id) { + case BCMA_CORE_ARM_CM3: + brcmf_chip_coredisable(core, 0, 0); + break; + case BCMA_CORE_ARM_CR4: + cr4 = container_of(core, struct brcmf_core_priv, pub); + + /* clear all IOCTL bits except HALT bit */ + val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL); + val &= ARMCR4_BCMA_IOCTL_CPUHALT; + brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, + ARMCR4_BCMA_IOCTL_CPUHALT); + break; + default: + brcmf_err("unknown id: %u\n", id); + break; + } +} + +static int brcmf_chip_setup(struct brcmf_chip_priv *chip) +{ + struct brcmf_chip *pub; + struct brcmf_core_priv *cc; + u32 base; + u32 val; + int ret = 0; + + pub = &chip->pub; + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + base = cc->pub.base; + + /* get chipcommon capabilites */ + pub->cc_caps = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, capabilities)); + + /* get pmu caps & rev */ + if (pub->cc_caps & CC_CAP_PMU) { + val = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, pmucapabilities)); + pub->pmurev = val & PCAP_REV_MASK; + pub->pmucaps = val; + } + + brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n", + cc->pub.rev, pub->pmurev, pub->pmucaps); + + /* execute bus core specific setup */ + if (chip->ops->setup) + ret = chip->ops->setup(chip->ctx, pub); + + /* + * Make sure any on-chip ARM is off (in case strapping is wrong), + * or downloaded code was already running. + */ + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + return ret; +} + +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops) +{ + struct brcmf_chip_priv *chip; + int err = 0; + + if (WARN_ON(!ops->read32)) + err = -EINVAL; + if (WARN_ON(!ops->write32)) + err = -EINVAL; + if (WARN_ON(!ops->prepare)) + err = -EINVAL; + if (WARN_ON(!ops->exit_dl)) + err = -EINVAL; + if (err < 0) + return ERR_PTR(-EINVAL); + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&chip->cores); + chip->num_cores = 0; + chip->ops = ops; + chip->ctx = ctx; + + err = ops->prepare(ctx); + if (err < 0) + goto fail; + + err = brcmf_chip_recognition(chip); + if (err < 0) + goto fail; + + err = brcmf_chip_setup(chip); + if (err < 0) + goto fail; + + return &chip->pub; + +fail: + brcmf_chip_detach(&chip->pub); + return ERR_PTR(err); +} + +void brcmf_chip_detach(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + struct brcmf_core_priv *tmp; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry_safe(core, tmp, &chip->cores, list) { + list_del(&core->list); + kfree(core); + } + kfree(chip); +} + +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry(core, &chip->cores, list) + if (core->pub.id == coreid) + return &core->pub; + + return NULL; +} + +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *cc; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON)) + return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON); + return &cc->pub; +} + +bool brcmf_chip_iscoreup(struct brcmf_core *pub) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + return core->chip->iscoreup(core); +} + +void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->coredisable(core, prereset, reset); +} + +void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, + u32 postreset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->resetcore(core, prereset, reset, postreset); +} + +static void +brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); +} + +static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + if (!brcmf_chip_iscoreup(core)) { + brcmf_err("SOCRAM core is down after reset?\n"); + return false; + } + + chip->ops->exit_dl(chip->ctx, &chip->pub, 0); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); + brcmf_chip_resetcore(core, 0, 0, 0); + + return true; +} + +static inline void +brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + +void brcmf_chip_enter_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); + if (arm) { + brcmf_chip_cr4_enterdl(chip); + return; + } + + brcmf_chip_cm3_enterdl(chip); +} + +bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); + if (arm) + return brcmf_chip_cr4_exitdl(chip, rstvec); + + return brcmf_chip_cm3_exitdl(chip); +} + +bool brcmf_chip_sr_capable(struct brcmf_chip *pub) +{ + u32 base, addr, reg, pmu_cc3_mask = ~0; + struct brcmf_chip_priv *chip; + + brcmf_dbg(TRACE, "Enter\n"); + + /* old chips with PMU version less than 17 don't support save restore */ + if (pub->pmurev < 17) + return false; + + base = brcmf_chip_get_chipcommon(pub)->base; + chip = container_of(pub, struct brcmf_chip_priv, pub); + + switch (pub->chip) { + case BCM4354_CHIP_ID: + /* explicitly check SR engine enable bit */ + pmu_cc3_mask = BIT(2); + /* fall-through */ + case BCM43241_CHIP_ID: + case BCM4335_CHIP_ID: + case BCM4339_CHIP_ID: + /* read PMU chipcontrol register 3 */ + addr = CORE_CC_REG(base, chipcontrol_addr); + chip->ops->write32(chip->ctx, addr, 3); + addr = CORE_CC_REG(base, chipcontrol_data); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & pmu_cc3_mask) != 0; + default: + addr = CORE_CC_REG(base, pmucapabilities_ext); + reg = chip->ops->read32(chip->ctx, addr); + if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) + return false; + + addr = CORE_CC_REG(base, retention_ctl); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | + PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h new file mode 100644 index 000000000000..c32908da90c8 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 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 BRCMF_CHIP_H +#define BRCMF_CHIP_H + +#include <linux/types.h> + +#define CORE_CC_REG(base, field) \ + (base + offsetof(struct chipcregs, field)) + +/** + * struct brcmf_chip - chip level information. + * + * @chip: chip identifier. + * @chiprev: chip revision. + * @cc_caps: chipcommon core capabilities. + * @pmucaps: PMU capabilities. + * @pmurev: PMU revision. + * @rambase: RAM base address (only applicable for ARM CR4 chips). + * @ramsize: amount of RAM on chip. + * @name: string representation of the chip identifier. + */ +struct brcmf_chip { + u32 chip; + u32 chiprev; + u32 cc_caps; + u32 pmucaps; + u32 pmurev; + u32 rambase; + u32 ramsize; + char name[8]; +}; + +/** + * struct brcmf_core - core related information. + * + * @id: core identifier. + * @rev: core revision. + * @base: base address of core register space. + */ +struct brcmf_core { + u16 id; + u16 rev; + u32 base; +}; + +/** + * struct brcmf_buscore_ops - buscore specific callbacks. + * + * @read32: read 32-bit value over bus. + * @write32: write 32-bit value over bus. + * @prepare: prepare bus for core configuration. + * @setup: bus-specific core setup. + * @exit_dl: exit download state. + * The callback should use the provided @rstvec when non-zero. + */ +struct brcmf_buscore_ops { + u32 (*read32)(void *ctx, u32 addr); + void (*write32)(void *ctx, u32 addr, u32 value); + int (*prepare)(void *ctx); + int (*setup)(void *ctx, struct brcmf_chip *chip); + void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec); +}; + +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops); +void brcmf_chip_detach(struct brcmf_chip *chip); +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); +bool brcmf_chip_iscoreup(struct brcmf_core *core); +void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); +void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, + u32 postreset); +void brcmf_chip_enter_download(struct brcmf_chip *ci); +bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec); +bool brcmf_chip_sr_capable(struct brcmf_chip *pub); + +#endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index d4d966beb840..7d28cd385092 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -1040,12 +1040,12 @@ void brcmf_detach(struct device *dev) brcmf_cfg80211_detach(drvr->config); + brcmf_fws_deinit(drvr); + brcmf_bus_detach(drvr); brcmf_proto_detach(drvr); - brcmf_fws_deinit(drvr); - brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; kfree(drvr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ddaa9efd053d..13c89a0c4ba7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> #include <linux/semaphore.h> @@ -40,7 +41,7 @@ #include <brcm_hw_ids.h> #include <soc.h> #include "sdio_host.h" -#include "sdio_chip.h" +#include "chip.h" #include "nvram.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ @@ -112,8 +113,6 @@ struct rte_console { #define BRCMF_TXBOUND 20 /* Default for max tx frames in one scheduling */ -#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ - #define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ #define MEMBLOCK 2048 /* Block size used for downloading @@ -156,6 +155,34 @@ struct rte_console { /* manfid tuple length, include tuple, link bytes */ #define SBSDIO_CIS_MANFID_TUPLE_LEN 6 +#define CORE_BUS_REG(base, field) \ + (base + offsetof(struct sdpcmd_regs, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP 0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT 0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 +/* Status: ALP is ready */ +#define SBSDIO_ALP_AVAIL 0x40 +/* Status: HT is ready */ +#define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_CSR_MASK 0x1F +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ + (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) + /* intstatus */ #define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ #define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ @@ -276,7 +303,6 @@ struct rte_console { /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -#define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */ #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle */ @@ -433,10 +459,11 @@ struct brcmf_sdio { bool alp_only; /* Don't use HT clock (ALP only) */ u8 *ctrl_frame_buf; - u32 ctrl_frame_len; + u16 ctrl_frame_len; bool ctrl_frame_stat; - spinlock_t txqlock; + spinlock_t txq_lock; /* protect bus->txq */ + struct semaphore tx_seq_lock; /* protect bus->tx_seq */ wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; @@ -483,16 +510,58 @@ static const uint max_roundup = 512; #define ALIGNMENT 4 -static int brcmf_sdio_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; -module_param_named(txglomsz, brcmf_sdio_txglomsz, int, 0); -MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); - enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_NORMAL, BRCMF_SDIO_FT_SUPER, BRCMF_SDIO_FT_SUB, }; +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { + u8 strength; /* Pad Drive Strength in mA */ + u8 sel; /* Chip-specific select value */ +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* 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} +}; + #define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" #define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" #define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" @@ -511,6 +580,8 @@ enum brcmf_sdio_frmtype { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43143_NVRAM_NAME); @@ -530,6 +601,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); struct brcmf_firmware_names { u32 chipid; @@ -555,7 +628,8 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) } + { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; @@ -618,27 +692,24 @@ static bool data_ok(struct brcmf_sdio *bus) * Reads a register in the SDIO hardware block. This block occupies a series of * adresses on the 32 bit backplane bus. */ -static int -r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - *regvar = brcmf_sdiod_regrl(bus->sdiodev, - bus->ci->c_inf[idx].base + offset, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); return ret; } -static int -w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - brcmf_sdiod_regwl(bus->sdiodev, - bus->ci->c_inf[idx].base + reg_offset, - regval, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); return ret; } @@ -650,16 +721,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) int err = 0; int try_cnt = 0; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter: on=%d\n", on); wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */ brcmf_sdiod_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: @@ -689,18 +756,22 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) &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_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); } while (try_cnt++ < MAX_KSO_ATTEMPTS); + if (try_cnt > 2) + brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, + rd_val, err); + + if (try_cnt > MAX_KSO_ATTEMPTS) + brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); + return err; } -#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND) - #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) /* Turn backplane clock on or off */ @@ -799,7 +870,6 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } #endif /* defined (DEBUG) */ - bus->activity = true; } else { clkreq = 0; @@ -899,8 +969,9 @@ static int brcmf_sdio_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", + u8 clkcsr; + + brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); @@ -917,8 +988,20 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) - return -EBUSY; + data_ok(bus))) { + err = -EBUSY; + goto done; + } + + clkcsr = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if ((clkcsr & SBSDIO_CSR_MASK) == 0) { + brcmf_dbg(SDIO, "no clock, set ALP\n"); + brcmf_sdiod_regwb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + } err = brcmf_sdio_kso_control(bus, false); /* disable watchdog */ if (!err) @@ -935,7 +1018,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_err("error while changing bus sleep state %d\n", err); - return err; + goto done; } } @@ -947,11 +1030,92 @@ end: } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; } +#ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ + return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + 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_sdio_bus_sleep(bus, false, false); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); + sdio_release_host(bus->sdiodev->func[1]); + if (rv < 0) + return rv; + + addr = le32_to_cpu(addr_le); + + brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_err("invalid sdpcm_shared address 0x%08X\n", + addr); + return -EINVAL; + } + + /* Read hndrte_shared structure */ + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + return rv; + + /* Endianness */ + sh->flags = le32_to_cpu(sh_le.flags); + sh->trap_addr = le32_to_cpu(sh_le.trap_addr); + sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); + sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); + sh->assert_line = le32_to_cpu(sh_le.assert_line); + sh->console_addr = le32_to_cpu(sh_le.console_addr); + sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { + brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", + SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK); + return -EPROTO; + } + + return 0; +} + +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ + struct sdpcm_shared sh; + + if (brcmf_sdio_readshared(bus, &sh) == 0) + bus->console_addr = sh.console_addr; +} +#else +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) { u32 intstatus = 0; @@ -995,6 +1159,12 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) else brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", bus->sdpcm_ver); + + /* + * Retrieve console state address now that firmware should have + * updated it. + */ + brcmf_sdio_get_console_addr(bus); } /* @@ -1083,6 +1253,28 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) bus->cur_read.len = 0; } +static void brcmf_sdio_txfail(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + u8 i, hi, lo; + + /* On failure, abort the command and terminate the frame */ + brcmf_err("sdio error, abort command and terminate frame\n"); + bus->sdcnt.tx_sderrs++; + + brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); + bus->sdcnt.f1regdata++; + + for (i = 0; i < 3; i++) { + hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->sdcnt.f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } +} + /* return total length of buffer chain */ static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) { @@ -1955,7 +2147,7 @@ static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, memcpy(pkt_pad->data, pkt->data + pkt->len - tail_chop, tail_chop); - *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; skb_trim(pkt, pkt->len - tail_chop); skb_trim(pkt_pad, tail_pad + tail_chop); __skb_queue_after(pktq, pkt, pkt_pad); @@ -2003,7 +2195,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, * already properly aligned and does not * need an sdpcm header. */ - if (*(u32 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) continue; /* align packet data pointer */ @@ -2037,10 +2229,10 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, if (BRCMF_BYTES_ON() && ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) - brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, + brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, "Tx Frame:\n"); else if (BRCMF_HDRS_ON()) - brcmf_dbg_hex_dump(true, pkt_next, + brcmf_dbg_hex_dump(true, pkt_next->data, head_pad + bus->tx_hdrlen, "Tx Header:\n"); } @@ -2067,11 +2259,11 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) u8 *hdr; u32 dat_offset; u16 tail_pad; - u32 dummy_flags, chop_len; + u16 dummy_flags, chop_len; struct sk_buff *pkt_next, *tmp, *pkt_prev; skb_queue_walk_safe(pktq, pkt_next, tmp) { - dummy_flags = *(u32 *)(pkt_next->cb); + dummy_flags = *(u16 *)(pkt_next->cb); if (dummy_flags & ALIGN_SKB_FLAG) { chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; if (chop_len) { @@ -2100,7 +2292,6 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, uint chan) { int ret; - int i; struct sk_buff *pkt_next, *tmp; brcmf_dbg(TRACE, "Enter\n"); @@ -2113,28 +2304,9 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); bus->sdcnt.f2txdata++; - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; + if (ret < 0) + brcmf_sdio_txfail(bus); - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } - } sdio_release_host(bus->sdiodev->func[1]); done: @@ -2164,13 +2336,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { pkt_num = 1; - __skb_queue_head_init(&pktq); + if (down_interruptible(&bus->tx_seq_lock)) + return cnt; if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, - brcmf_sdio_txglomsz); + bus->sdiodev->txglomsz); pkt_num = min_t(u32, pkt_num, brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); - spin_lock_bh(&bus->txqlock); + __skb_queue_head_init(&pktq); + spin_lock_bh(&bus->txq_lock); for (i = 0; i < pkt_num; i++) { pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); @@ -2178,15 +2352,19 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) break; __skb_queue_tail(&pktq, pkt); } - spin_unlock_bh(&bus->txqlock); - if (i == 0) + spin_unlock_bh(&bus->txq_lock); + if (i == 0) { + up(&bus->tx_seq_lock); break; + } ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + up(&bus->tx_seq_lock); + cnt += i; /* In poll mode, need to check for other events */ - if (!bus->intr && cnt) { + if (!bus->intr) { /* Check device status, signal pending interrupt */ sdio_claim_host(bus->sdiodev->func[1]); ret = r_sdreg32(bus, &intstatus, @@ -2211,6 +2389,68 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) return cnt; } +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ + u8 doff; + u16 pad; + uint retries = 0; + struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Back the pointer to make room for bus header */ + frame -= bus->tx_hdrlen; + len += bus->tx_hdrlen; + + /* Add alignment padding (optional for ctl frames) */ + doff = ((unsigned long)frame % bus->head_align); + if (doff) { + frame -= doff; + len += doff; + memset(frame + bus->tx_hdrlen, 0, doff); + } + + /* Round send length to next SDIO block */ + pad = 0; + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + pad = bus->blocksize - (len % bus->blocksize); + if ((pad > bus->roundup) || (pad >= bus->blocksize)) + pad = 0; + } else if (len % bus->head_align) { + pad = bus->head_align - (len % bus->head_align); + } + len += pad; + + hd_info.len = len - pad; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff + bus->tx_hdrlen; + hd_info.seq_num = bus->tx_seq; + hd_info.lastfrm = true; + hd_info.tail_pad = pad; + brcmf_sdio_hdpack(bus, frame, &hd_info); + + if (bus->txglom) + brcmf_sdio_update_hwhdr(frame, len); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + frame, len, "Tx Frame:\n"); + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && + BRCMF_HDRS_ON(), + frame, min_t(u16, len, 16), "TxHdr:\n"); + + do { + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + } while (ret < 0 && retries++ < TXRETRIES); + + return ret; +} + static void brcmf_sdio_bus_stop(struct device *dev) { u32 local_hostintmask; @@ -2292,21 +2532,29 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) } } +static void atomic_orr(int val, atomic_t *v) +{ + int old_val; + + old_val = atomic_read(v); + while (atomic_cmpxchg(v, old_val, val | old_val) != old_val) + old_val = atomic_read(v); +} + static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { - u8 idx; + struct brcmf_core *buscore; u32 addr; unsigned long val; - int n, ret; + int ret; - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - addr = bus->ci->c_inf[idx].base + - offsetof(struct sdpcmd_regs, intstatus); + buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - val = 0; + return ret; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2315,13 +2563,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - } - - if (ret) { - atomic_set(&bus->intstatus, 0); - } else if (val) { - for_each_set_bit(n, &val, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); + atomic_orr(val, &bus->intstatus); } return ret; @@ -2331,10 +2573,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; unsigned long intstatus; - uint rxlimit = bus->rxbound; /* Rx frames to read before resched */ uint txlimit = bus->txbound; /* Tx frames to send before resched */ - uint framecnt = 0; /* Temporary counter of tx/rx frames */ - int err = 0, n; + uint framecnt; /* Temporary counter of tx/rx frames */ + int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -2431,70 +2672,38 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) intstatus &= ~I_HMB_FRAME_IND; /* On frame indication, read available frames */ - if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { - framecnt = brcmf_sdio_readframes(bus, rxlimit); + if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { + brcmf_sdio_readframes(bus, bus->rxbound); if (!bus->rxpending) intstatus &= ~I_HMB_FRAME_IND; - rxlimit -= min(framecnt, rxlimit); } /* Keep still-pending events for next scheduling */ - if (intstatus) { - for_each_set_bit(n, &intstatus, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); - } + if (intstatus) + atomic_orr(intstatus, &bus->intstatus); brcmf_sdio_clrintr(bus); - if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - int i; - - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, - (u32)bus->ctrl_frame_len); - - if (err < 0) { - /* On failure, abort the command and - terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - err); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, &err); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, - &err); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, - &err); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } + if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && + (down_interruptible(&bus->tx_seq_lock) == 0)) { + if (data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); - } else { - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); } - sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); + up(&bus->tx_seq_lock); } /* Send queued frames (limit 1 if rx may still be pending) */ - else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit - && data_ok(bus)) { + if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && + data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; - framecnt = brcmf_sdio_sendfromq(bus, framecnt); - txlimit -= framecnt; + brcmf_sdio_sendfromq(bus, framecnt); } if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { @@ -2504,19 +2713,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus)) || PKT_AVAILABLE()) { + data_ok(bus))) { atomic_inc(&bus->dpc_tskcnt); } - - /* If we're done for now, turn off clock request. */ - 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_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } } static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) @@ -2531,15 +2730,12 @@ static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; - uint datalen, prec; + uint prec; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - ulong flags; - brcmf_dbg(TRACE, "Enter\n"); - - datalen = pkt->len; + brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); /* Add space for the header */ skb_push(pkt, bus->tx_hdrlen); @@ -2553,7 +2749,9 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->sdcnt.fcqueued++; /* Priority based enq */ - spin_lock_irqsave(&bus->txqlock, flags); + spin_lock_bh(&bus->txq_lock); + /* reset bus_flags in packet cb */ + *(u16 *)(pkt->cb) = 0; if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); @@ -2566,7 +2764,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } - spin_unlock_irqrestore(&bus->txqlock, flags); + spin_unlock_bh(&bus->txq_lock); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) @@ -2661,110 +2859,27 @@ break2: } #endif /* DEBUG */ -static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - int i; - int ret; - - bus->ctrl_frame_stat = false; - ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if (hi == 0 && lo == 0) - break; - } - return ret; - } - - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - - return ret; -} - static int brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { - u8 *frame; - u16 len, pad; - uint retries = 0; - u8 doff = 0; - int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret = -1; brcmf_dbg(TRACE, "Enter\n"); - /* Back the pointer to make a room for bus header */ - frame = msg - bus->tx_hdrlen; - len = (msglen += bus->tx_hdrlen); - - /* Add alignment padding (optional for ctl frames) */ - doff = ((unsigned long)frame % bus->head_align); - if (doff) { - frame -= doff; - len += doff; - msglen += doff; - memset(frame, 0, doff + bus->tx_hdrlen); - } - /* precondition: doff < bus->head_align */ - doff += bus->tx_hdrlen; - - /* Round send length to next SDIO block */ - pad = 0; - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - pad = bus->blocksize - (len % bus->blocksize); - if ((pad > bus->roundup) || (pad >= bus->blocksize)) - pad = 0; - } else if (len % bus->head_align) { - pad = bus->head_align - (len % bus->head_align); - } - len += pad; - - /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - - /* Make sure backplane clock is on */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - sdio_release_host(bus->sdiodev->func[1]); - - hd_info.len = (u16)msglen; - hd_info.channel = SDPCM_CONTROL_CHANNEL; - hd_info.dat_offset = doff; - hd_info.seq_num = bus->tx_seq; - hd_info.lastfrm = true; - hd_info.tail_pad = pad; - brcmf_sdio_hdpack(bus, frame, &hd_info); - - if (bus->txglom) - brcmf_sdio_update_hwhdr(frame, len); + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; if (!data_ok(bus)) { brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", bus->tx_max, bus->tx_seq); - bus->ctrl_frame_stat = true; + up(&bus->tx_seq_lock); /* Send from dpc */ - bus->ctrl_frame_buf = frame; - bus->ctrl_frame_len = len; + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, @@ -2775,31 +2890,18 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) ret = 0; } else { brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); + bus->ctrl_frame_stat = false; + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; ret = -1; } } - if (ret == -1) { - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - do { - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdio_tx_frame(bus, frame, len); - sdio_release_host(bus->sdiodev->func[1]); - } while (ret < 0 && retries++ < TXRETRIES); - } - - if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && - atomic_read(&bus->dpc_tskcnt) == 0) { - bus->activity = false; sdio_claim_host(bus->sdiodev->func[1]); - brcmf_dbg(INFO, "idle\n"); - brcmf_sdio_clkctl(bus, CLK_NONE, true); + brcmf_sdio_bus_sleep(bus, false, false); + ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); sdio_release_host(bus->sdiodev->func[1]); + up(&bus->tx_seq_lock); } if (ret) @@ -2811,72 +2913,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #ifdef DEBUG -static inline bool brcmf_sdio_valid_shared_address(u32 addr) -{ - return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); -} - -static int brcmf_sdio_readshared(struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr; - int rv; - u32 shaddr = 0; - struct sdpcm_shared_le sh_le; - __le32 addr_le; - - 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_sdio_bus_sleep(bus, false, false); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); - if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); - - /* - * Check if addr is valid. - * NVRAM length at the end of memory should have been overwritten. - */ - if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; - } - - /* Read hndrte_shared structure */ - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); - if (rv < 0) - return rv; - - /* Endianness */ - sh->flags = le32_to_cpu(sh_le.flags); - sh->trap_addr = le32_to_cpu(sh_le.trap_addr); - sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); - sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); - sh->assert_line = le32_to_cpu(sh_le.assert_line); - sh->console_addr = le32_to_cpu(sh_le.console_addr); - sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - - if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { - brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", - SDPCM_SHARED_VERSION, - sh->flags & SDPCM_SHARED_VERSION_MASK); - return -EPROTO; - } - - return 0; -} - static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) @@ -3106,6 +3142,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) debugfs_create_file("forensics", S_IRUGO, dentry, bus, &brcmf_sdio_forensic_ops); brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); + debugfs_create_u32("console_interval", 0644, dentry, + &bus->console_interval); } #else static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) @@ -3224,32 +3262,17 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, const struct firmware *fw) { int err; - int offset; - int address; - int len; brcmf_dbg(TRACE, "Enter\n"); - err = 0; - offset = 0; - address = bus->ci->rambase; - while (offset < fw->size) { - len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK : - fw->size - offset; - err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, - (u8 *)&fw->data[offset], len); - if (err) { - brcmf_err("error %d on writing %d membytes at 0x%08x\n", - err, len, address); - return err; - } - offset += len; - address += len; - } - if (!err) - if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, - (u8 *)fw->data, fw->size)) - err = -EIO; + err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, + (u8 *)fw->data, fw->size); + if (err) + brcmf_err("error %d on writing %d membytes at 0x%08x\n", + err, (int)fw->size, bus->ci->rambase); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; return err; } @@ -3292,7 +3315,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci); + brcmf_chip_enter_download(bus->ci); fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); if (fw == NULL) { @@ -3324,7 +3347,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) } /* Take arm out of reset */ - if (!brcmf_sdio_chip_exit_download(bus->sdiodev, bus->ci, rstvec)) { + if (!brcmf_chip_exit_download(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } @@ -3339,40 +3362,6 @@ err: return bcmerror; } -static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus) -{ - u32 addr, reg, pmu_cc3_mask = ~0; - int err; - - 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; - - switch (bus->ci->chip) { - case BCM43241_CHIP_ID: - case BCM4335_CHIP_ID: - case BCM4339_CHIP_ID: - /* read PMU chipcontrol register 3 */ - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); - brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL); - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); - return (reg & pmu_cc3_mask) != 0; - default: - addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucapabilities_ext); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, &err); - if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) - return false; - - addr = CORE_CC_REG(bus->ci->c_inf[0].base, retention_ctl); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); - return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | - PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; - } -} - static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) { int err = 0; @@ -3424,7 +3413,7 @@ static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); /* KSO bit added in SDIO core rev 12 */ - if (bus->ci->c_inf[1].rev < 12) + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) return 0; val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); @@ -3455,15 +3444,13 @@ static int brcmf_sdio_bus_preinit(struct device *dev) struct brcmf_sdio *bus = sdiodev->bus; uint pad_size; u32 value; - u8 idx; int err; /* the commands below use the terms tx and rx from * a device perspective, ie. bus:txglom affects the * bus transfers from device to host. */ - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - if (bus->ci->c_inf[idx].rev < 12) { + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { /* for sdio core rev < 12, disable txgloming */ value = 0; err = brcmf_iovar_data_set(dev, "bus:txglom", &value, @@ -3570,7 +3557,7 @@ static int brcmf_sdio_bus_init(struct device *dev) ret = -ENODEV; } - if (brcmf_sdio_sr_capable(bus)) { + if (brcmf_chip_sr_capable(bus->ci)) { brcmf_sdio_sr_init(bus); } else { /* Restore previous clock setting */ @@ -3714,11 +3701,175 @@ static void brcmf_sdio_dataworker(struct work_struct *work) datawork); while (atomic_read(&bus->dpc_tskcnt)) { + atomic_set(&bus->dpc_tskcnt, 0); brcmf_sdio_dpc(bus); - atomic_dec(&bus->dpc_tskcnt); } } +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 drivestrength) +{ + const struct sdiod_drive_str *str_tab = NULL; + u32 str_mask; + u32 str_shift; + u32 base; + u32 i; + u32 drivestrength_sel = 0; + u32 cc_data_temp; + u32 addr; + + if (!(ci->cc_caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + str_tab = sdiod_drvstr_tab1_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + str_tab = sdiod_drvstr_tab6_1v8; + str_mask = 0x00001800; + 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", + ci->name, drivestrength); + break; + case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + str_tab = sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + ci->name, ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + base = brcmf_chip_get_chipcommon(ci)->base; + addr = CORE_CC_REG(base, chipcontrol_addr); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + + 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); + } +} + +static int brcmf_sdio_buscoreprep(void *ctx) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + int err = 0; + u8 clkval, clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (err) { + brcmf_err("error writing for HT off\n"); + return err; + } + + /* If register supported, wait for ALPAvail and then force ALP */ + /* This may take up to 15 milliseconds */ + clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); + + if ((clkval & ~SBSDIO_AVBITS) != clkset) { + brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", + clkset, clkval); + return -EACCES; + } + + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + struct brcmf_core *core; + u32 reg_addr; + + /* clear all interrupts */ + core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); + reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + if (rstvec) + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + u32 val, rev; + + val = brcmf_sdiod_regrl(sdiodev, addr, NULL); + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { + rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; + if (rev >= 2) { + val &= ~CID_ID_MASK; + val |= BCM4339_CHIP_ID; + } + } + return val; +} + +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + + brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { + .prepare = brcmf_sdio_buscoreprep, + .exit_dl = brcmf_sdio_buscore_exitdl, + .read32 = brcmf_sdio_buscore_read32, + .write32 = brcmf_sdio_buscore_write32, +}; + static bool brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { @@ -3734,7 +3885,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); /* - * Force PLL off until brcmf_sdio_chip_attach() + * Force PLL off until brcmf_chip_attach() * programs PLL control regs */ @@ -3755,8 +3906,10 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) */ brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); - if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) { - brcmf_err("brcmf_sdio_chip_attach failed!\n"); + bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + if (IS_ERR(bus->ci)) { + brcmf_err("brcmf_chip_attach failed!\n"); + bus->ci = NULL; goto fail; } @@ -3769,7 +3922,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) drivestrength = bus->sdiodev->pdata->drive_strength; else drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); /* Get info on the SOCRAM cores... */ bus->ramsize = bus->ci->ramsize; @@ -3792,24 +3945,18 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, + reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, pmucontrol); - reg_val = brcmf_sdiod_regrl(bus->sdiodev, - reg_addr, - &err); + reg_val = brcmf_sdiod_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_sdiod_regwl(bus->sdiodev, - reg_addr, - reg_val, - &err); + brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); if (err) goto fail; - sdio_release_host(bus->sdiodev->func[1]); brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); @@ -3849,6 +3996,7 @@ brcmf_sdio_watchdog_thread(void *data) brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); } else break; } @@ -3925,7 +4073,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) } spin_lock_init(&bus->rxctl_lock); - spin_lock_init(&bus->txqlock); + spin_lock_init(&bus->txq_lock); + sema_init(&bus->tx_seq_lock, 1); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); @@ -4024,14 +4173,14 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) /* De-register interrupt handler */ brcmf_sdiod_intr_unregister(bus->sdiodev); - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); - if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); } + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + if (bus->ci) { if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { sdio_claim_host(bus->sdiodev->func[1]); @@ -4042,12 +4191,11 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) * all necessary cores. */ msleep(20); - brcmf_sdio_chip_enter_download(bus->sdiodev, - bus->ci); + brcmf_chip_enter_download(bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); } - brcmf_sdio_chip_detach(&bus->ci); + brcmf_chip_detach(bus->ci); } kfree(bus->rxbuf); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 22adbe311d20..59a5af5bf994 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -124,7 +124,8 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) } static u32 -brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) +brcmf_create_iovar(char *name, const char *data, u32 datalen, + char *buf, u32 buflen) { u32 len; @@ -144,7 +145,7 @@ brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) s32 -brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 77eae86e55c2..a30be683f4a1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -83,7 +83,7 @@ s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); -s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len); s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, u32 len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index af17a5bc8b83..614e4888504f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -48,6 +48,11 @@ #define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ +/* OBSS Coex Auto/On/Off */ +#define BRCMF_OBSS_COEX_AUTO (-1) +#define BRCMF_OBSS_COEX_OFF 0 +#define BRCMF_OBSS_COEX_ON 1 + enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_CLIENT, BRCMF_FIL_P2P_IF_GO, @@ -87,6 +92,11 @@ struct brcmf_fil_bss_enable_le { __le32 enable; }; +struct brcmf_fil_bwcap_le { + __le32 band; + __le32 bw_cap; +}; + /** * struct tdls_iovar - common structure for tdls iovars. * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index fc4f98b275d7..f3445ac627e4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -797,7 +797,8 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, /* SOCIAL CHANNELS 1, 6, 11 */ search_state = WL_P2P_DISC_ST_SEARCH; brcmf_dbg(INFO, "P2P SEARCH PHASE START\n"); - } else if (dev != NULL && vif->mode == WL_MODE_AP) { + } else if (dev != NULL && + vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { /* If you are already a GO, then do SEARCH only */ brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n"); search_state = WL_P2P_DISC_ST_SEARCH; @@ -2256,7 +2257,6 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct brcmf_cfg80211_vif *vif; enum brcmf_fil_p2p_if_types iftype; - enum wl_mode mode; int err; if (brcmf_cfg80211_vif_event_armed(cfg)) @@ -2267,11 +2267,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, switch (type) { case NL80211_IFTYPE_P2P_CLIENT: iftype = BRCMF_FIL_P2P_IF_CLIENT; - mode = WL_MODE_BSS; break; case NL80211_IFTYPE_P2P_GO: iftype = BRCMF_FIL_P2P_IF_GO; - mode = WL_MODE_AP; break; case NL80211_IFTYPE_P2P_DEVICE: return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c deleted file mode 100644 index 82bf3c5d3cdc..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ /dev/null @@ -1,972 +0,0 @@ -/* - * Copyright (c) 2011 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. - */ -/* ***** SDIO interface chip backplane handle functions ***** */ - -#include <linux/types.h> -#include <linux/netdevice.h> -#include <linux/mmc/card.h> -#include <linux/mmc/sdio_func.h> -#include <linux/mmc/sdio_ids.h> -#include <linux/ssb/ssb_regs.h> -#include <linux/bcma/bcma.h> - -#include <chipcommon.h> -#include <brcm_hw_ids.h> -#include <brcmu_wifi.h> -#include <brcmu_utils.h> -#include <soc.h> -#include "dhd_dbg.h" -#include "sdio_host.h" -#include "sdio_chip.h" - -/* chip core base & ramsize */ -/* bcm4329 */ -/* SDIO device core, ID 0x829 */ -#define BCM4329_CORE_BUS_BASE 0x18011000 -/* internal memory core, ID 0x80e */ -#define BCM4329_CORE_SOCRAM_BASE 0x18003000 -/* ARM Cortex M3 core, ID 0x82a */ -#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 - -/* All D11 cores, ID 0x812 */ -#define BCM43xx_CORE_D11_BASE 0x18001000 - -#define SBCOREREV(sbidh) \ - ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ - ((sbidh) & SSB_IDHIGH_RCLO)) - -/* SOC Interconnect types (aka chip types) */ -#define SOCI_SB 0 -#define SOCI_AI 1 - -/* EROM CompIdentB */ -#define CIB_REV_MASK 0xff000000 -#define CIB_REV_SHIFT 24 - -/* ARM CR4 core specific control flag bits */ -#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 - -/* D11 core specific control flag bits */ -#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 -#define D11_BCMA_IOCTL_PHYRESET 0x0008 - -#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) -/* SDIO Pad drive strength to select value mappings */ -struct sdiod_drive_str { - u8 strength; /* Pad Drive Strength in mA */ - u8 sel; /* Chip-specific select value */ -}; -/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { - {32, 0x6}, - {26, 0x7}, - {22, 0x4}, - {16, 0x5}, - {12, 0x2}, - {8, 0x3}, - {4, 0x0}, - {0, 0x1} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ -static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { - {6, 0x7}, - {5, 0x6}, - {4, 0x5}, - {3, 0x4}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ -static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { - {3, 0x3}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} }; - -/* 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 brcmf_chip *ci, u16 coreid) -{ - u8 idx; - - for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) - if (coreid == ci->c_inf[idx].id) - return idx; - - return BRCMF_MAX_CORENUM; -} - -static u32 -brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) -{ - u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbidhigh), - NULL); - return SBCOREREV(regdata); -} - -static u32 -brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) -{ - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; -} - -static bool -brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) -{ - u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return false; - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | - SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); - return (SSB_TMSLOW_CLOCK == regdata); -} - -static bool -brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) -{ - u32 regdata; - u8 idx; - bool ret; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return false; - - regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; - - regdata = brcmf_sdiod_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); - ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); - - return ret; -} - -static void -brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits) -{ - u32 regdata, base; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - base = ci->c_inf[idx].base; - - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); - if (regdata & SSB_TMSLOW_RESET) - return; - - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); - if ((regdata & SSB_TMSLOW_CLOCK) != 0) { - /* - * set target reject and spin until busy is clear - * (preserve core-specific bits) - */ - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatelow), NULL); - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata | SSB_TMSLOW_REJECT, NULL); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatelow), NULL); - udelay(1); - SPINWAIT((brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL) & - SSB_TMSHIGH_BUSY), 100000); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL); - if (regdata & SSB_TMSHIGH_BUSY) - brcmf_err("core state still busy\n"); - - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); - if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - regdata |= SSB_IMSTATE_REJECT; - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - udelay(1); - SPINWAIT((brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL) & - SSB_IMSTATE_BUSY), 100000); - } - - /* set reset and reject while enabling the clocks */ - regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | - SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatelow), NULL); - udelay(10); - - /* clear the initiator reject bit */ - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); - if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - regdata &= ~SSB_IMSTATE_REJECT; - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); - } - } - - /* leave reset and reject asserted */ - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); - udelay(1); -} - -static void -brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits) -{ - u8 idx; - u32 regdata; - u32 wrapbase; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return; - - wrapbase = ci->c_inf[idx].wrapbase; - - /* if core is already in reset, just return */ - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL); - if ((regdata & BCMA_RESET_CTL_RESET) != 0) - return; - - /* configure reset */ - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | - BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); - - /* put in reset */ - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, - BCMA_RESET_CTL_RESET, NULL); - usleep_range(10, 20); - - /* wait till reset is 1 */ - SPINWAIT(brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) != - BCMA_RESET_CTL_RESET, 300); - - /* post reset configure */ - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | - BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); -} - -static void -brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits, u32 post_resetbits) -{ - u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return; - - /* - * Must do the disable sequence first to work for - * arbitrary current core state. - */ - brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, pre_resetbits, - in_resetbits); - - /* - * Now do the initialization sequence. - * set reset while enabling the clock and - * forcing them on throughout the core - */ - brcmf_sdiod_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, - NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - udelay(1); - - /* clear any serror */ - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - NULL); - if (regdata & SSB_TMSHIGH_SERR) - brcmf_sdiod_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - 0, NULL); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - NULL); - if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) - brcmf_sdiod_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), - NULL); - - /* clear reset and allow it to propagate throughout the core */ - brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - udelay(1); - - /* leave clock enabled */ - brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - udelay(1); -} - -static void -brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits, u32 post_resetbits) -{ - u8 idx; - u32 regdata; - u32 wrapbase; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return; - - wrapbase = ci->c_inf[idx].wrapbase; - - /* must disable first to work for arbitrary current core state */ - brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, pre_resetbits, - in_resetbits); - - while (brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) & - BCMA_RESET_CTL_RESET) { - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, 0, NULL); - usleep_range(40, 60); - } - - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, post_resetbits | - BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); -} - -#ifdef DEBUG -/* safety check for chipinfo */ -static int brcmf_sdio_chip_cichk(struct brcmf_chip *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 brcmf_chip *ci) -{ - return 0; -} -#endif - -static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) -{ - u32 regdata; - u32 socitype; - - /* Get CC core rev - * Chipid is assume to be at offset 0 from SI_ENUM_BASE - * For different chiptypes or old sdio hosts w/o chipcommon, - * other ways of recognition should be added here. - */ - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(SI_ENUM_BASE, chipid), - NULL); - ci->chip = regdata & CID_ID_MASK; - ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; - if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && - ci->chiprev >= 2) - ci->chip = BCM4339_CHIP_ID; - socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; - - brcmf_dbg(INFO, "found %s chip: id=0x%x, rev=%d\n", - socitype == SOCI_SB ? "SB" : "AXI", ci->chip, ci->chiprev); - - if (socitype == SOCI_SB) { - if (ci->chip != BCM4329_CHIP_ID) { - brcmf_err("SB chip is not supported\n"); - return -ENODEV; - } - ci->iscoreup = brcmf_sdio_sb_iscoreup; - ci->corerev = brcmf_sdio_sb_corerev; - ci->coredisable = brcmf_sdio_sb_coredisable; - ci->resetcore = brcmf_sdio_sb_resetcore; - - ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = SI_ENUM_BASE; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->ramsize = BCM4329_RAMSIZE; - } else if (socitype == SOCI_AI) { - ci->iscoreup = brcmf_sdio_ai_iscoreup; - ci->corerev = brcmf_sdio_ai_corerev; - ci->coredisable = brcmf_sdio_ai_coredisable; - ci->resetcore = brcmf_sdio_ai_resetcore; - - ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = SI_ENUM_BASE; - - /* 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->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = BCM43143_RAMSIZE; - break; - case BCM43241_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2a084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0e004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x14080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x90000; - break; - case BCM4330_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x07004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x0d080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x48000; - break; - case BCM4334_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x29004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0d004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x13080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - 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->c_inf[3].id = BCMA_CORE_80211; - ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; - break; - case BCM43362_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0a004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x08080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x3C000; - break; - case BCM4339_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2e084411; - 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 = 0x15004211; - 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 = 0x04084411; - ci->c_inf[3].id = BCMA_CORE_80211; - ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; - break; - default: - brcmf_err("AXI chip is not supported\n"); - return -ENODEV; - } - } else { - brcmf_err("chip backplane type %u is not supported\n", - socitype); - return -ENODEV; - } - - return brcmf_sdio_chip_cichk(ci); -} - -static int -brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) -{ - int err = 0; - u8 clkval, clkset; - - /* Try forcing SDIO core to do ALPAvail request only */ - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - if (err) { - brcmf_err("error writing for HT off\n"); - return err; - } - - /* If register supported, wait for ALPAvail and then force ALP */ - /* This may take up to 15 milliseconds */ - clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); - - if ((clkval & ~SBSDIO_AVBITS) != clkset) { - brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", - clkset, clkval); - return -EACCES; - } - - SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL)), - !SBSDIO_ALPAV(clkval)), - PMU_MAX_TRANSITION_DLY); - if (!SBSDIO_ALPAV(clkval)) { - brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", - clkval); - return -EBUSY; - } - - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - udelay(65); - - /* Also, disable the extra SDIO pull-ups */ - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); - - return 0; -} - -static void -brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) -{ - u32 base = ci->c_inf[0].base; - - /* get chipcommon rev */ - ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); - - /* get chipcommon capabilites */ - ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(base, capabilities), - NULL); - - /* get pmu caps & rev */ - if (ci->c_inf[0].caps & CC_CAP_PMU) { - ci->pmucaps = - brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(base, pmucapabilities), - NULL); - ci->pmurev = ci->pmucaps & PCAP_REV_MASK; - } - - ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); - - brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", - ci->c_inf[0].rev, ci->pmurev, - ci->c_inf[1].rev, ci->c_inf[1].id); - - /* - * 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, 0, 0); -} - -int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip **ci_ptr) -{ - int ret; - struct brcmf_chip *ci; - - brcmf_dbg(TRACE, "Enter\n"); - - ci = kzalloc(sizeof(*ci), GFP_ATOMIC); - if (!ci) - return -ENOMEM; - - ret = brcmf_sdio_chip_buscoreprep(sdiodev); - if (ret != 0) - goto err; - - ret = brcmf_sdio_chip_recognition(sdiodev, ci); - if (ret != 0) - goto err; - - brcmf_sdio_chip_buscoresetup(sdiodev, ci); - - brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), - 0, NULL); - brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), - 0, NULL); - - *ci_ptr = ci; - return 0; - -err: - kfree(ci); - return ret; -} - -void -brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr) -{ - brcmf_dbg(TRACE, "Enter\n"); - - kfree(*ci_ptr); - *ci_ptr = NULL; -} - -static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - -void -brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 drivestrength) -{ - 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 = sdiod_drvstr_tab1_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): - str_tab = sdiod_drvstr_tab6_1v8; - str_mask = 0x00001800; - 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; - case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): - str_tab = sdiod_drive_strength_tab5_1v8; - str_mask = 0x00003800; - str_shift = 11; - 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), - ci->chiprev, ci->pmurev); - break; - } - - if (str_tab != NULL) { - for (i = 0; str_tab[i].strength != 0; i++) { - if (drivestrength >= str_tab[i].strength) { - drivestrength_sel = str_tab[i].sel; - break; - } - } - addr = CORE_CC_REG(base, chipcontrol_addr); - brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); - cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); - cc_data_temp &= ~str_mask; - drivestrength_sel <<= str_shift; - cc_data_temp |= drivestrength_sel; - brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); - - 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); - } -} - -static void -brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) -{ - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); - ci->resetcore(sdiodev, ci, BCMA_CORE_80211, - D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); - ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0); -} - -static bool brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) -{ - 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; - } - - /* 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_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0, 0); - - return true; -} - -static inline void -brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) -{ - u8 idx; - u32 regdata; - u32 wrapbase; - idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); - - if (idx == BRCMF_MAX_CORENUM) - return; - - wrapbase = ci->c_inf[idx].wrapbase; - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); - regdata &= ARMCR4_BCMA_IOCTL_CPUHALT; - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, regdata, - ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); - ci->resetcore(sdiodev, ci, BCMA_CORE_80211, - D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); -} - -static bool brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 rstvec) -{ - u8 core_idx; - u32 reg_addr; - - /* 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_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - /* Write reset vector to address 0 */ - brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, - sizeof(rstvec)); - - /* restore ARM */ - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT, - 0, 0); - - return true; -} - -void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *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 brcmf_chip *ci, u32 rstvec) -{ - 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); - - return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, rstvec); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h deleted file mode 100644 index fb0614329ede..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2011 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 _BRCMFMAC_SDIO_CHIP_H_ -#define _BRCMFMAC_SDIO_CHIP_H_ - -/* - * Core reg address translation. - * Both macro's returns a 32 bits byte address on the backplane bus. - */ -#define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) -#define CORE_BUS_REG(base, field) \ - (base + offsetof(struct sdpcmd_regs, field)) -#define CORE_SB(base, field) \ - (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) - -/* SDIO function 1 register CHIPCLKCSR */ -/* Force ALP request to backplane */ -#define SBSDIO_FORCE_ALP 0x01 -/* Force HT request to backplane */ -#define SBSDIO_FORCE_HT 0x02 -/* Force ILP request to backplane */ -#define SBSDIO_FORCE_ILP 0x04 -/* Make ALP ready (power up xtal) */ -#define SBSDIO_ALP_AVAIL_REQ 0x08 -/* Make HT ready (power up PLL) */ -#define SBSDIO_HT_AVAIL_REQ 0x10 -/* Squelch clock requests from HW */ -#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 -/* Status: ALP is ready */ -#define SBSDIO_ALP_AVAIL 0x40 -/* Status: HT is ready */ -#define SBSDIO_HT_AVAIL 0x80 -#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) -#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) -#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) -#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) -#define SBSDIO_CLKAV(regval, alponly) \ - (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) - -#define BRCMF_MAX_CORENUM 6 - -struct brcmf_core { - u16 id; - u16 rev; - u32 base; - u32 wrapbase; - u32 caps; - u32 cib; -}; - -struct brcmf_chip { - u32 chip; - u32 chiprev; - /* core info */ - /* always put chipcommon core at 0, bus core at 1 */ - struct brcmf_core c_inf[BRCMF_MAX_CORENUM]; - 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 brcmf_chip *ci, - u16 coreid); - u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci, - u16 coreid); - void (*coredisable)(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits); - void (*resetcore)(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits, u32 post_resetbits); -}; - -struct sbconfig { - u32 PAD[2]; - u32 sbipsflag; /* initiator port ocp slave flag */ - u32 PAD[3]; - u32 sbtpsflag; /* target port ocp slave flag */ - u32 PAD[11]; - u32 sbtmerrloga; /* (sonics >= 2.3) */ - u32 PAD; - u32 sbtmerrlog; /* (sonics >= 2.3) */ - u32 PAD[3]; - u32 sbadmatch3; /* address match3 */ - u32 PAD; - u32 sbadmatch2; /* address match2 */ - u32 PAD; - u32 sbadmatch1; /* address match1 */ - u32 PAD[7]; - u32 sbimstate; /* initiator agent state */ - u32 sbintvec; /* interrupt mask */ - u32 sbtmstatelow; /* target state */ - u32 sbtmstatehigh; /* target state */ - u32 sbbwa0; /* bandwidth allocation table0 */ - u32 PAD; - u32 sbimconfiglow; /* initiator configuration */ - u32 sbimconfighigh; /* initiator configuration */ - u32 sbadmatch0; /* address match0 */ - u32 PAD; - u32 sbtmconfiglow; /* target configuration */ - u32 sbtmconfighigh; /* target configuration */ - u32 sbbconfig; /* broadcast configuration */ - u32 PAD; - u32 sbbstate; /* broadcast state */ - u32 PAD[3]; - u32 sbactcnfg; /* activate configuration */ - u32 PAD[3]; - u32 sbflagst; /* current sbflags */ - u32 PAD[3]; - u32 sbidlow; /* identification */ - 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]; -}; - -int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip **ci_ptr); -void brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr); -void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, - u32 drivestrength); -u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid); -void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci); -bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 rstvec); - -#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 092e9c824992..3deab7959a0d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -180,6 +180,97 @@ struct brcmf_sdio_dev { uint max_request_size; ushort max_segment_count; uint max_segment_size; + uint txglomsz; + struct sg_table sgtable; +}; + +/* 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]; }; /* Register/deregister interrupt handler. */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index d7718a5fa2f0..afb3d15e38ff 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -18,6 +18,7 @@ #include <linux/kernel.h> #include <linux/etherdevice.h> +#include <linux/module.h> #include <net/cfg80211.h> #include <net/netlink.h> @@ -190,6 +191,7 @@ static struct ieee80211_supported_band __wl_band_2ghz = { .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size, + .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true}, }; static struct ieee80211_supported_band __wl_band_5ghz_a = { @@ -251,6 +253,10 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +static int brcmf_roamoff; +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); +MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); + /* Quarter dBm units to mW * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 * Table is offset so the last entry is largest mW value that fits in @@ -351,13 +357,11 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, * triples, returning a pointer to the substring whose first element * matches tag */ -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key) { - struct brcmf_tlv *elt; - int totlen; - - elt = (struct brcmf_tlv *)buf; - totlen = buflen; + const struct brcmf_tlv *elt = buf; + int totlen = buflen; /* find tagged parameter */ while (totlen >= TLV_HDR_LEN) { @@ -378,8 +382,8 @@ struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) * not update the tlvs buffer pointer/length. */ static bool -brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, - u8 *oui, u32 oui_len, u8 type) +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) { /* If the contents match the OUI and the type */ if (ie[TLV_LEN_OFF] >= oui_len + 1 && @@ -401,12 +405,12 @@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, } static struct brcmf_vs_tlv * -brcmf_find_wpaie(u8 *parse, u32 len) +brcmf_find_wpaie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, + if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) return (struct brcmf_vs_tlv *)ie; } @@ -414,9 +418,9 @@ brcmf_find_wpaie(u8 *parse, u32 len) } static struct brcmf_vs_tlv * -brcmf_find_wpsie(u8 *parse, u32 len) +brcmf_find_wpsie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, @@ -491,6 +495,19 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) return err; } +static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) +{ + enum nl80211_iftype iftype; + + iftype = vif->wdev.iftype; + return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; +} + +static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) +{ + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; +} + static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, enum nl80211_iftype type, @@ -651,7 +668,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, type); return -EOPNOTSUPP; case NL80211_IFTYPE_ADHOC: - vif->mode = WL_MODE_IBSS; infra = 0; break; case NL80211_IFTYPE_STATION: @@ -667,12 +683,10 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, */ return 0; } - vif->mode = WL_MODE_BSS; infra = 1; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - vif->mode = WL_MODE_AP; ap = 1; break; default: @@ -696,7 +710,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, err = -EAGAIN; goto done; } - brcmf_dbg(INFO, "IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ? + brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? "Adhoc" : "Infra"); } ndev->ieee80211_ptr->iftype = type; @@ -1340,13 +1354,14 @@ static s32 brcmf_set_auth_type(struct net_device *ndev, } static s32 -brcmf_set_set_cipher(struct net_device *ndev, - struct cfg80211_connect_params *sme) +brcmf_set_wsec_mode(struct net_device *ndev, + struct cfg80211_connect_params *sme, bool mfp) { struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); struct brcmf_cfg80211_security *sec; s32 pval = 0; s32 gval = 0; + s32 wsec; s32 err = 0; if (sme->crypto.n_ciphers_pairwise) { @@ -1398,7 +1413,12 @@ brcmf_set_set_cipher(struct net_device *ndev, if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && sme->privacy) pval = AES_ENABLED; - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", pval | gval); + + if (mfp) + wsec = pval | gval | MFP_CAPABLE; + else + wsec = pval | gval; + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); if (err) { brcmf_err("error (%d)\n", err); return err; @@ -1562,13 +1582,12 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; - void *ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; u32 ie_len; struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -1591,7 +1610,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ie_len = wpa_ie->len + TLV_HDR_LEN; } else { /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len, + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, + sme->ie_len, WLAN_EID_RSN); if (rsn_ie) { ie = rsn_ie; @@ -1636,7 +1656,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = brcmf_set_set_cipher(ndev, sme); + err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); if (err) { brcmf_err("wl_set_set_cipher failed (%d)\n", err); goto done; @@ -1678,22 +1698,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); - /*increase dwell time to receive probe response or detect Beacon - * from target AP at a noisy air only during connect command - */ - ext_join_params->scan_le.active_time = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); - ext_join_params->scan_le.passive_time = - cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* Set up join scan parameters */ ext_join_params->scan_le.scan_type = -1; - /* to sync with presence period of VSDB GO. - * Send probe request more frequently. Probe request will be stopped - * when it gets probe response from target AP/GO. - */ - ext_join_params->scan_le.nprobes = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / - BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); ext_join_params->scan_le.home_time = cpu_to_le32(-1); if (sme->bssid) @@ -1706,6 +1713,25 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->assoc_le.chanspec_list[0] = cpu_to_le16(chanspec); + /* Increase dwell time to receive probe response or detect + * beacon from target AP at a noisy air only during connect + * command. + */ + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* To sync with presence period of VSDB GO send probe request + * more frequently. Probe request will be stopped when it gets + * probe response from target AP/GO. + */ + ext_join_params->scan_le.nprobes = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / + BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); + } else { + ext_join_params->scan_le.active_time = cpu_to_le32(-1); + ext_join_params->scan_le.passive_time = cpu_to_le32(-1); + ext_join_params->scan_le.nprobes = cpu_to_le32(-1); } err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, @@ -1913,7 +1939,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "Setting the key index %d\n", key.index); memcpy(key.data, params->key, key.len); - if ((ifp->vif->mode != WL_MODE_AP) && + if (!brcmf_is_apmode(ifp->vif) && (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); @@ -1981,7 +2007,9 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (mac_addr) { + if (mac_addr && + (params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { brcmf_dbg(TRACE, "Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } @@ -2010,7 +2038,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: - if (ifp->vif->mode != WL_MODE_AP) { + if (!brcmf_is_apmode(ifp->vif)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); @@ -2164,12 +2192,14 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, s32 err = 0; u8 *bssid = profile->bssid; struct brcmf_sta_info_le sta_info_le; + u32 beacon_period; + u32 dtim_period; brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac); if (!check_vif_up(ifp->vif)) return -EIO; - if (ifp->vif->mode == WL_MODE_AP) { + if (brcmf_is_apmode(ifp->vif)) { memcpy(&sta_info_le, mac, ETH_ALEN); err = brcmf_fil_iovar_data_get(ifp, "sta_info", &sta_info_le, @@ -2186,7 +2216,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, } brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", sinfo->inactive_time, sinfo->connected_time); - } else if (ifp->vif->mode == WL_MODE_BSS) { + } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) { if (memcmp(mac, bssid, ETH_ALEN)) { brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n", mac, bssid); @@ -2218,6 +2248,30 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, sinfo->signal = rssi; brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD, + &beacon_period); + if (err) { + brcmf_err("Could not get beacon period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.beacon_interval = + beacon_period; + brcmf_dbg(CONN, "Beacon peroid %d\n", + beacon_period); + } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD, + &dtim_period); + if (err) { + brcmf_err("Could not get DTIM period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.dtim_period = dtim_period; + brcmf_dbg(CONN, "DTIM peroid %d\n", + dtim_period); + } + sinfo->filled |= STATION_INFO_BSS_PARAM; } } else err = -EPERM; @@ -2444,18 +2498,13 @@ CleanUp: return err; } -static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) -{ - return vif->mode == WL_MODE_IBSS; -} - static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); struct brcmf_bss_info_le *bi; struct brcmf_ssid *ssid; - struct brcmf_tlv *tim; + const struct brcmf_tlv *tim; u16 beacon_interval; u8 dtim_period; size_t ie_len; @@ -3220,8 +3269,9 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) } static s32 -brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie) +brcmf_configure_wpaie(struct net_device *ndev, + const struct brcmf_vs_tlv *wpa_ie, + bool is_rsn_ie) { struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ @@ -3707,11 +3757,11 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, s32 ie_offset; struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *ssid_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; struct brcmf_join_params join_params; enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; @@ -4220,32 +4270,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode) }; -static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type) -{ - switch (type) { - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return -ENOTSUPP; - case NL80211_IFTYPE_ADHOC: - return WL_MODE_IBSS; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - return WL_MODE_BSS; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - return WL_MODE_AP; - case NL80211_IFTYPE_P2P_DEVICE: - return WL_MODE_P2P; - case NL80211_IFTYPE_UNSPECIFIED: - default: - break; - } - - return -EINVAL; -} - static void brcmf_wiphy_pno_params(struct wiphy *wiphy) { /* scheduled scan settings */ @@ -4370,7 +4394,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->wdev.wiphy = cfg->wiphy; vif->wdev.iftype = type; - vif->mode = brcmf_nl80211_iftype_to_mode(type); vif->pm_block = pm_block; vif->roam_off = -1; @@ -4416,7 +4439,9 @@ static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) u32 event = e->event_code; u16 flags = e->flags; - if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) { + if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DISASSOC_IND) || + ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { brcmf_dbg(CONN, "Processing link down\n"); return true; } @@ -4658,16 +4683,19 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct net_device *ndev = ifp->ndev; struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan; s32 err = 0; + u16 reason; - if (ifp->vif->mode == WL_MODE_AP) { + if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(e)) { brcmf_dbg(CONN, "Linkup\n"); if (brcmf_is_ibssmode(ifp->vif)) { + chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); memcpy(profile->bssid, e->addr, ETH_ALEN); wl_inform_ibss(cfg, ndev, e->addr); - cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL); + cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); set_bit(BRCMF_VIF_STATUS_CONNECTED, @@ -4679,9 +4707,15 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) - cfg80211_disconnected(ndev, 0, NULL, 0, + &ifp->vif->sme_state)) { + reason = 0; + if (((e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DISASSOC_IND)) && + (e->reason != WLAN_REASON_UNSPECIFIED)) + reason = e->reason; + cfg80211_disconnected(ndev, reason, NULL, 0, GFP_KERNEL); + } } brcmf_link_down(ifp->vif); brcmf_init_prof(ndev_to_prof(ndev)); @@ -4875,11 +4909,8 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) cfg->scan_request = NULL; cfg->pwr_save = true; - cfg->roam_on = true; /* roam on & off switch. - we enable roam per default */ - cfg->active_scan = true; /* we do active scan for - specific scan per default */ - cfg->dongle_up = false; /* dongle is not up yet */ + cfg->active_scan = true; /* we do active scan per default */ + cfg->dongle_up = false; /* dongle is not up yet */ err = brcmf_init_priv_mem(cfg); if (err) return err; @@ -4904,6 +4935,30 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } +static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) +{ + struct brcmf_fil_bwcap_le band_bwcap; + u32 val; + int err; + + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_40MHZ_BIT); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } + return err; +} + struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { @@ -4961,6 +5016,17 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto cfg80211_p2p_attach_out; } + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + err = brcmf_enable_bw40_2g(ifp); + if (!err) + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + } + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); @@ -4999,7 +5065,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) } static s32 -brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) +brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { s32 err = 0; __le32 roamtrigger[2]; @@ -5009,7 +5075,7 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) * Setup timeout if Beacons are lost and roam is * off to report link down */ - if (roamvar) { + if (brcmf_roamoff) { err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); if (err) { brcmf_err("bcn_timeout error (%d)\n", err); @@ -5021,8 +5087,9 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) * Enable/Disable built-in roaming to allow supplicant * to take care of roaming */ - brcmf_dbg(INFO, "Internal Roaming = %s\n", roamvar ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar); + brcmf_dbg(INFO, "Internal Roaming = %s\n", + brcmf_roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); if (err) { brcmf_err("roam_off error (%d)\n", err); goto dongle_rom_out; @@ -5164,9 +5231,6 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, ieee80211_channel_to_frequency(ch.chnum, band); band_chan_arr[index].hw_value = ch.chnum; - brcmf_err("channel %d: f=%d bw=%d sb=%d\n", - ch.chnum, band_chan_arr[index].center_freq, - ch.bw, ch.sb); if (ch.bw == BRCMU_CHAN_BW_40) { /* assuming the order is HT20, HT40 Upper, * HT40 lower from chanspecs @@ -5267,6 +5331,8 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) u32 band_list[3]; u32 nmode; u32 bw_cap[2] = { 0, 0 }; + u32 rxchain; + u32 nchain; s8 phy; s32 err; u32 nband; @@ -5303,6 +5369,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode, bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]); + err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); + if (err) { + brcmf_err("rxchain error (%d)\n", err); + nchain = 1; + } else { + for (nchain = 0; rxchain; nchain++) + rxchain = rxchain & (rxchain - 1); + } + brcmf_dbg(INFO, "nchain=%d\n", nchain); + err = brcmf_construct_reginfo(cfg, bw_cap); if (err) { brcmf_err("brcmf_construct_reginfo failed (%d)\n", err); @@ -5331,10 +5407,7 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) band->ht_cap.ht_supported = true; band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - /* An HT shall support all EQM rates for one spatial - * stream - */ - band->ht_cap.mcs.rx_mask[0] = 0xff; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; bands[band->band] = band; } @@ -5381,7 +5454,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "power save set to %s\n", (power_mode ? "enabled" : "disabled")); - err = brcmf_dongle_roam(ifp, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT); + err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 2dc6a074e8ed..283c525a44f7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -89,21 +89,6 @@ enum brcmf_scan_status { BRCMF_SCAN_STATUS_SUPPRESS, }; -/** - * enum wl_mode - driver mode of virtual interface. - * - * @WL_MODE_BSS: connects to BSS. - * @WL_MODE_IBSS: operate as ad-hoc. - * @WL_MODE_AP: operate as access-point. - * @WL_MODE_P2P: provide P2P discovery. - */ -enum wl_mode { - WL_MODE_BSS, - WL_MODE_IBSS, - WL_MODE_AP, - WL_MODE_P2P -}; - /* dongle configuration */ struct brcmf_cfg80211_conf { u32 frag_threshold; @@ -193,7 +178,6 @@ struct vif_saved_ie { * @ifp: lower layer interface pointer * @wdev: wireless device. * @profile: profile information. - * @mode: operating mode. * @roam_off: roaming state. * @sme_state: SME state using enum brcmf_vif_status bits. * @pm_block: power-management blocked. @@ -204,7 +188,6 @@ struct brcmf_cfg80211_vif { struct brcmf_if *ifp; struct wireless_dev wdev; struct brcmf_cfg80211_profile profile; - s32 mode; s32 roam_off; unsigned long sme_state; bool pm_block; @@ -402,7 +385,6 @@ struct brcmf_cfg80211_info { bool ibss_starter; bool pwr_save; bool dongle_up; - bool roam_on; bool scan_tried; u8 *dcmd_buf; u8 *extra_buf; @@ -491,7 +473,8 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); 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); +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key); 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); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 925034b80e9c..8c5fa4e58139 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -426,6 +426,12 @@ static int brcms_ops_start(struct ieee80211_hw *hw) bool blocked; int err; + if (!wl->ucode.bcm43xx_bomminor) { + err = brcms_request_fw(wl, wl->wlc->hw->d11core); + if (err) + return -ENOENT; + } + ieee80211_wake_queues(hw); spin_lock_bh(&wl->lock); blocked = brcms_rfkill_set_hw_state(wl); @@ -433,14 +439,6 @@ static int brcms_ops_start(struct ieee80211_hw *hw) if (!blocked) wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); - if (!wl->ucode.bcm43xx_bomminor) { - err = brcms_request_fw(wl, wl->wlc->hw->d11core); - if (err) { - brcms_remove(wl->wlc->hw->d11core); - return -ENOENT; - } - } - spin_lock_bh(&wl->lock); /* avoid acknowledging frames before a non-monitor device is added */ wl->mute_tx = true; @@ -1094,12 +1092,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw) * Attach to the WL device identified by vendor and device parameters. * regs is a host accessible memory address pointing to WL device registers. * - * brcms_attach is not defined as static because in the case where no bus - * is defined, wl_attach will never be called, and thus, gcc will issue - * a warning that this function is defined but not used if we declare - * it as static. - * - * * is called in brcms_bcma_probe() context, therefore no locking required. */ static struct brcms_info *brcms_attach(struct bcma_device *pdev) diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 6fa5d4863782..d816270db3be 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -43,5 +43,6 @@ #define BCM4335_CHIP_ID 0x4335 #define BCM43362_CHIP_ID 43362 #define BCM4339_CHIP_ID 0x4339 +#define BCM4354_CHIP_ID 0x4354 #endif /* _BRCM_HW_IDS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h index 7ca2aa1035b2..74419d4bd123 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h @@ -217,6 +217,9 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WSEC_SWFLAG 0x0008 /* to go into transition mode without setting wep */ #define SES_OW_ENABLED 0x0040 +/* MFP */ +#define MFP_CAPABLE 0x0200 +#define MFP_REQUIRED 0x0400 /* WPA authentication mode bitvec */ #define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ |