diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-06-10 05:30:17 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-06-10 05:30:17 -0300 |
commit | d635711daa98be86d4c7fd01499c34f566b54ccb (patch) | |
tree | aa5cc3760a27c3d57146498cb82fa549547de06c /drivers/net/wireless | |
parent | c91265cd0efb83778f015b4d4b1129bd2cfd075e (diff) |
Linux-libre 4.6.2-gnu
Diffstat (limited to 'drivers/net/wireless')
225 files changed, 13456 insertions, 2969 deletions
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 03aa35f99..db1ca629c 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -15,6 +15,12 @@ config ATH10K_PCI ---help--- This module adds support for PCIE bus +config ATH10K_AHB + bool "Atheros ath10k AHB support" + depends on ATH10K_PCI && OF && RESET_CONTROLLER + ---help--- + This module adds support for AHB bus + config ATH10K_DEBUG bool "Atheros ath10k debugging" depends on ATH10K diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index c04fb00e7..930fadd94 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -25,5 +25,7 @@ obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o ath10k_pci-y += pci.o \ ce.o +ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o + # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c new file mode 100644 index 000000000..bd62bc19e --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -0,0 +1,933 @@ +/* + * Copyright (c) 2016 Qualcomm Atheros, Inc. All rights reserved. + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * 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/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk.h> +#include <linux/reset.h> +#include "core.h" +#include "debug.h" +#include "pci.h" +#include "ahb.h" + +static const struct of_device_id ath10k_ahb_of_match[] = { + /* TODO: enable this entry once everything in place. + * { .compatible = "qcom,ipq4019-wifi", + * .data = (void *)ATH10K_HW_QCA4019 }, + */ + { } +}; + +MODULE_DEVICE_TABLE(of, ath10k_ahb_of_match); + +static inline struct ath10k_ahb *ath10k_ahb_priv(struct ath10k *ar) +{ + return &((struct ath10k_pci *)ar->drv_priv)->ahb[0]; +} + +static void ath10k_ahb_write32(struct ath10k *ar, u32 offset, u32 value) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + iowrite32(value, ar_ahb->mem + offset); +} + +static u32 ath10k_ahb_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + return ioread32(ar_ahb->mem + offset); +} + +static u32 ath10k_ahb_gcc_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + return ioread32(ar_ahb->gcc_mem + offset); +} + +static void ath10k_ahb_tcsr_write32(struct ath10k *ar, u32 offset, u32 value) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + iowrite32(value, ar_ahb->tcsr_mem + offset); +} + +static u32 ath10k_ahb_tcsr_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + return ioread32(ar_ahb->tcsr_mem + offset); +} + +static u32 ath10k_ahb_soc_read32(struct ath10k *ar, u32 addr) +{ + return ath10k_ahb_read32(ar, RTC_SOC_BASE_ADDRESS + addr); +} + +static int ath10k_ahb_get_num_banks(struct ath10k *ar) +{ + if (ar->hw_rev == ATH10K_HW_QCA4019) + return 1; + + ath10k_warn(ar, "unknown number of banks, assuming 1\n"); + return 1; +} + +static int ath10k_ahb_clock_init(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + struct device *dev; + int ret; + + dev = &ar_ahb->pdev->dev; + + ar_ahb->cmd_clk = clk_get(dev, "wifi_wcss_cmd"); + if (IS_ERR_OR_NULL(ar_ahb->cmd_clk)) { + ath10k_err(ar, "failed to get cmd clk: %ld\n", + PTR_ERR(ar_ahb->cmd_clk)); + ret = ar_ahb->cmd_clk ? PTR_ERR(ar_ahb->cmd_clk) : -ENODEV; + goto out; + } + + ar_ahb->ref_clk = clk_get(dev, "wifi_wcss_ref"); + if (IS_ERR_OR_NULL(ar_ahb->ref_clk)) { + ath10k_err(ar, "failed to get ref clk: %ld\n", + PTR_ERR(ar_ahb->ref_clk)); + ret = ar_ahb->ref_clk ? PTR_ERR(ar_ahb->ref_clk) : -ENODEV; + goto err_cmd_clk_put; + } + + ar_ahb->rtc_clk = clk_get(dev, "wifi_wcss_rtc"); + if (IS_ERR_OR_NULL(ar_ahb->rtc_clk)) { + ath10k_err(ar, "failed to get rtc clk: %ld\n", + PTR_ERR(ar_ahb->rtc_clk)); + ret = ar_ahb->rtc_clk ? PTR_ERR(ar_ahb->rtc_clk) : -ENODEV; + goto err_ref_clk_put; + } + + return 0; + +err_ref_clk_put: + clk_put(ar_ahb->ref_clk); + +err_cmd_clk_put: + clk_put(ar_ahb->cmd_clk); + +out: + return ret; +} + +static void ath10k_ahb_clock_deinit(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + if (!IS_ERR_OR_NULL(ar_ahb->cmd_clk)) + clk_put(ar_ahb->cmd_clk); + + if (!IS_ERR_OR_NULL(ar_ahb->ref_clk)) + clk_put(ar_ahb->ref_clk); + + if (!IS_ERR_OR_NULL(ar_ahb->rtc_clk)) + clk_put(ar_ahb->rtc_clk); + + ar_ahb->cmd_clk = NULL; + ar_ahb->ref_clk = NULL; + ar_ahb->rtc_clk = NULL; +} + +static int ath10k_ahb_clock_enable(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + struct device *dev; + int ret; + + dev = &ar_ahb->pdev->dev; + + if (IS_ERR_OR_NULL(ar_ahb->cmd_clk) || + IS_ERR_OR_NULL(ar_ahb->ref_clk) || + IS_ERR_OR_NULL(ar_ahb->rtc_clk)) { + ath10k_err(ar, "clock(s) is/are not initialized\n"); + ret = -EIO; + goto out; + } + + ret = clk_prepare_enable(ar_ahb->cmd_clk); + if (ret) { + ath10k_err(ar, "failed to enable cmd clk: %d\n", ret); + goto out; + } + + ret = clk_prepare_enable(ar_ahb->ref_clk); + if (ret) { + ath10k_err(ar, "failed to enable ref clk: %d\n", ret); + goto err_cmd_clk_disable; + } + + ret = clk_prepare_enable(ar_ahb->rtc_clk); + if (ret) { + ath10k_err(ar, "failed to enable rtc clk: %d\n", ret); + goto err_ref_clk_disable; + } + + return 0; + +err_ref_clk_disable: + clk_disable_unprepare(ar_ahb->ref_clk); + +err_cmd_clk_disable: + clk_disable_unprepare(ar_ahb->cmd_clk); + +out: + return ret; +} + +static void ath10k_ahb_clock_disable(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + if (!IS_ERR_OR_NULL(ar_ahb->cmd_clk)) + clk_disable_unprepare(ar_ahb->cmd_clk); + + if (!IS_ERR_OR_NULL(ar_ahb->ref_clk)) + clk_disable_unprepare(ar_ahb->ref_clk); + + if (!IS_ERR_OR_NULL(ar_ahb->rtc_clk)) + clk_disable_unprepare(ar_ahb->rtc_clk); +} + +static int ath10k_ahb_rst_ctrl_init(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + struct device *dev; + int ret; + + dev = &ar_ahb->pdev->dev; + + ar_ahb->core_cold_rst = reset_control_get(dev, "wifi_core_cold"); + if (IS_ERR_OR_NULL(ar_ahb->core_cold_rst)) { + ath10k_err(ar, "failed to get core cold rst ctrl: %ld\n", + PTR_ERR(ar_ahb->core_cold_rst)); + ret = ar_ahb->core_cold_rst ? + PTR_ERR(ar_ahb->core_cold_rst) : -ENODEV; + goto out; + } + + ar_ahb->radio_cold_rst = reset_control_get(dev, "wifi_radio_cold"); + if (IS_ERR_OR_NULL(ar_ahb->radio_cold_rst)) { + ath10k_err(ar, "failed to get radio cold rst ctrl: %ld\n", + PTR_ERR(ar_ahb->radio_cold_rst)); + ret = ar_ahb->radio_cold_rst ? + PTR_ERR(ar_ahb->radio_cold_rst) : -ENODEV; + goto err_core_cold_rst_put; + } + + ar_ahb->radio_warm_rst = reset_control_get(dev, "wifi_radio_warm"); + if (IS_ERR_OR_NULL(ar_ahb->radio_warm_rst)) { + ath10k_err(ar, "failed to get radio warm rst ctrl: %ld\n", + PTR_ERR(ar_ahb->radio_warm_rst)); + ret = ar_ahb->radio_warm_rst ? + PTR_ERR(ar_ahb->radio_warm_rst) : -ENODEV; + goto err_radio_cold_rst_put; + } + + ar_ahb->radio_srif_rst = reset_control_get(dev, "wifi_radio_srif"); + if (IS_ERR_OR_NULL(ar_ahb->radio_srif_rst)) { + ath10k_err(ar, "failed to get radio srif rst ctrl: %ld\n", + PTR_ERR(ar_ahb->radio_srif_rst)); + ret = ar_ahb->radio_srif_rst ? + PTR_ERR(ar_ahb->radio_srif_rst) : -ENODEV; + goto err_radio_warm_rst_put; + } + + ar_ahb->cpu_init_rst = reset_control_get(dev, "wifi_cpu_init"); + if (IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) { + ath10k_err(ar, "failed to get cpu init rst ctrl: %ld\n", + PTR_ERR(ar_ahb->cpu_init_rst)); + ret = ar_ahb->cpu_init_rst ? + PTR_ERR(ar_ahb->cpu_init_rst) : -ENODEV; + goto err_radio_srif_rst_put; + } + + return 0; + +err_radio_srif_rst_put: + reset_control_put(ar_ahb->radio_srif_rst); + +err_radio_warm_rst_put: + reset_control_put(ar_ahb->radio_warm_rst); + +err_radio_cold_rst_put: + reset_control_put(ar_ahb->radio_cold_rst); + +err_core_cold_rst_put: + reset_control_put(ar_ahb->core_cold_rst); + +out: + return ret; +} + +static void ath10k_ahb_rst_ctrl_deinit(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + if (!IS_ERR_OR_NULL(ar_ahb->core_cold_rst)) + reset_control_put(ar_ahb->core_cold_rst); + + if (!IS_ERR_OR_NULL(ar_ahb->radio_cold_rst)) + reset_control_put(ar_ahb->radio_cold_rst); + + if (!IS_ERR_OR_NULL(ar_ahb->radio_warm_rst)) + reset_control_put(ar_ahb->radio_warm_rst); + + if (!IS_ERR_OR_NULL(ar_ahb->radio_srif_rst)) + reset_control_put(ar_ahb->radio_srif_rst); + + if (!IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) + reset_control_put(ar_ahb->cpu_init_rst); + + ar_ahb->core_cold_rst = NULL; + ar_ahb->radio_cold_rst = NULL; + ar_ahb->radio_warm_rst = NULL; + ar_ahb->radio_srif_rst = NULL; + ar_ahb->cpu_init_rst = NULL; +} + +static int ath10k_ahb_release_reset(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + int ret; + + if (IS_ERR_OR_NULL(ar_ahb->radio_cold_rst) || + IS_ERR_OR_NULL(ar_ahb->radio_warm_rst) || + IS_ERR_OR_NULL(ar_ahb->radio_srif_rst) || + IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) { + ath10k_err(ar, "rst ctrl(s) is/are not initialized\n"); + return -EINVAL; + } + + ret = reset_control_deassert(ar_ahb->radio_cold_rst); + if (ret) { + ath10k_err(ar, "failed to deassert radio cold rst: %d\n", ret); + return ret; + } + + ret = reset_control_deassert(ar_ahb->radio_warm_rst); + if (ret) { + ath10k_err(ar, "failed to deassert radio warm rst: %d\n", ret); + return ret; + } + + ret = reset_control_deassert(ar_ahb->radio_srif_rst); + if (ret) { + ath10k_err(ar, "failed to deassert radio srif rst: %d\n", ret); + return ret; + } + + ret = reset_control_deassert(ar_ahb->cpu_init_rst); + if (ret) { + ath10k_err(ar, "failed to deassert cpu init rst: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath10k_ahb_halt_axi_bus(struct ath10k *ar, u32 haltreq_reg, + u32 haltack_reg) +{ + unsigned long timeout; + u32 val; + + /* Issue halt axi bus request */ + val = ath10k_ahb_tcsr_read32(ar, haltreq_reg); + val |= AHB_AXI_BUS_HALT_REQ; + ath10k_ahb_tcsr_write32(ar, haltreq_reg, val); + + /* Wait for axi bus halted ack */ + timeout = jiffies + msecs_to_jiffies(ATH10K_AHB_AXI_BUS_HALT_TIMEOUT); + do { + val = ath10k_ahb_tcsr_read32(ar, haltack_reg); + if (val & AHB_AXI_BUS_HALT_ACK) + break; + + mdelay(1); + } while (time_before(jiffies, timeout)); + + if (!(val & AHB_AXI_BUS_HALT_ACK)) { + ath10k_err(ar, "failed to halt axi bus: %d\n", val); + return; + } + + ath10k_dbg(ar, ATH10K_DBG_AHB, "axi bus halted\n"); +} + +static void ath10k_ahb_halt_chip(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + u32 core_id, glb_cfg_reg, haltreq_reg, haltack_reg; + u32 val; + int ret; + + if (IS_ERR_OR_NULL(ar_ahb->core_cold_rst) || + IS_ERR_OR_NULL(ar_ahb->radio_cold_rst) || + IS_ERR_OR_NULL(ar_ahb->radio_warm_rst) || + IS_ERR_OR_NULL(ar_ahb->radio_srif_rst) || + IS_ERR_OR_NULL(ar_ahb->cpu_init_rst)) { + ath10k_err(ar, "rst ctrl(s) is/are not initialized\n"); + return; + } + + core_id = ath10k_ahb_read32(ar, ATH10K_AHB_WLAN_CORE_ID_REG); + + switch (core_id) { + case 0: + glb_cfg_reg = ATH10K_AHB_TCSR_WIFI0_GLB_CFG; + haltreq_reg = ATH10K_AHB_TCSR_WCSS0_HALTREQ; + haltack_reg = ATH10K_AHB_TCSR_WCSS0_HALTACK; + break; + case 1: + glb_cfg_reg = ATH10K_AHB_TCSR_WIFI1_GLB_CFG; + haltreq_reg = ATH10K_AHB_TCSR_WCSS1_HALTREQ; + haltack_reg = ATH10K_AHB_TCSR_WCSS1_HALTACK; + break; + default: + ath10k_err(ar, "invalid core id %d found, skipping reset sequence\n", + core_id); + return; + } + + ath10k_ahb_halt_axi_bus(ar, haltreq_reg, haltack_reg); + + val = ath10k_ahb_tcsr_read32(ar, glb_cfg_reg); + val |= TCSR_WIFIX_GLB_CFG_DISABLE_CORE_CLK; + ath10k_ahb_tcsr_write32(ar, glb_cfg_reg, val); + + ret = reset_control_assert(ar_ahb->core_cold_rst); + if (ret) + ath10k_err(ar, "failed to assert core cold rst: %d\n", ret); + msleep(1); + + ret = reset_control_assert(ar_ahb->radio_cold_rst); + if (ret) + ath10k_err(ar, "failed to assert radio cold rst: %d\n", ret); + msleep(1); + + ret = reset_control_assert(ar_ahb->radio_warm_rst); + if (ret) + ath10k_err(ar, "failed to assert radio warm rst: %d\n", ret); + msleep(1); + + ret = reset_control_assert(ar_ahb->radio_srif_rst); + if (ret) + ath10k_err(ar, "failed to assert radio srif rst: %d\n", ret); + msleep(1); + + ret = reset_control_assert(ar_ahb->cpu_init_rst); + if (ret) + ath10k_err(ar, "failed to assert cpu init rst: %d\n", ret); + msleep(10); + + /* Clear halt req and core clock disable req before + * deasserting wifi core reset. + */ + val = ath10k_ahb_tcsr_read32(ar, haltreq_reg); + val &= ~AHB_AXI_BUS_HALT_REQ; + ath10k_ahb_tcsr_write32(ar, haltreq_reg, val); + + val = ath10k_ahb_tcsr_read32(ar, glb_cfg_reg); + val &= ~TCSR_WIFIX_GLB_CFG_DISABLE_CORE_CLK; + ath10k_ahb_tcsr_write32(ar, glb_cfg_reg, val); + + ret = reset_control_deassert(ar_ahb->core_cold_rst); + if (ret) + ath10k_err(ar, "failed to deassert core cold rst: %d\n", ret); + + ath10k_dbg(ar, ATH10K_DBG_AHB, "core %d reset done\n", core_id); +} + +static irqreturn_t ath10k_ahb_interrupt_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + + ath10k_pci_disable_and_clear_legacy_irq(ar); + tasklet_schedule(&ar_pci->intr_tq); + + return IRQ_HANDLED; +} + +static int ath10k_ahb_request_irq_legacy(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + int ret; + + ret = request_irq(ar_ahb->irq, + ath10k_ahb_interrupt_handler, + IRQF_SHARED, "ath10k_ahb", ar); + if (ret) { + ath10k_warn(ar, "failed to request legacy irq %d: %d\n", + ar_ahb->irq, ret); + return ret; + } + + return 0; +} + +static void ath10k_ahb_release_irq_legacy(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + free_irq(ar_ahb->irq, ar); +} + +static void ath10k_ahb_irq_disable(struct ath10k *ar) +{ + ath10k_ce_disable_interrupts(ar); + ath10k_pci_disable_and_clear_legacy_irq(ar); +} + +static int ath10k_ahb_resource_init(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + struct platform_device *pdev; + struct device *dev; + struct resource *res; + int ret; + + pdev = ar_ahb->pdev; + dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ath10k_err(ar, "failed to get memory resource\n"); + ret = -ENXIO; + goto out; + } + + ar_ahb->mem = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ar_ahb->mem)) { + ath10k_err(ar, "mem ioremap error\n"); + ret = PTR_ERR(ar_ahb->mem); + goto out; + } + + ar_ahb->mem_len = resource_size(res); + + ar_ahb->gcc_mem = ioremap_nocache(ATH10K_GCC_REG_BASE, + ATH10K_GCC_REG_SIZE); + if (!ar_ahb->gcc_mem) { + ath10k_err(ar, "gcc mem ioremap error\n"); + ret = -ENOMEM; + goto err_mem_unmap; + } + + ar_ahb->tcsr_mem = ioremap_nocache(ATH10K_TCSR_REG_BASE, + ATH10K_TCSR_REG_SIZE); + if (!ar_ahb->tcsr_mem) { + ath10k_err(ar, "tcsr mem ioremap error\n"); + ret = -ENOMEM; + goto err_gcc_mem_unmap; + } + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + ath10k_err(ar, "failed to set 32-bit dma mask: %d\n", ret); + goto err_tcsr_mem_unmap; + } + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + ath10k_err(ar, "failed to set 32-bit consistent dma: %d\n", + ret); + goto err_tcsr_mem_unmap; + } + + ret = ath10k_ahb_clock_init(ar); + if (ret) + goto err_tcsr_mem_unmap; + + ret = ath10k_ahb_rst_ctrl_init(ar); + if (ret) + goto err_clock_deinit; + + ar_ahb->irq = platform_get_irq_byname(pdev, "legacy"); + if (ar_ahb->irq < 0) { + ath10k_err(ar, "failed to get irq number: %d\n", ar_ahb->irq); + goto err_clock_deinit; + } + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "irq: %d\n", ar_ahb->irq); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%p mem_len: %lu gcc mem: 0x%p tcsr_mem: 0x%p\n", + ar_ahb->mem, ar_ahb->mem_len, + ar_ahb->gcc_mem, ar_ahb->tcsr_mem); + return 0; + +err_clock_deinit: + ath10k_ahb_clock_deinit(ar); + +err_tcsr_mem_unmap: + iounmap(ar_ahb->tcsr_mem); + +err_gcc_mem_unmap: + ar_ahb->tcsr_mem = NULL; + iounmap(ar_ahb->gcc_mem); + +err_mem_unmap: + ar_ahb->gcc_mem = NULL; + devm_iounmap(&pdev->dev, ar_ahb->mem); + +out: + ar_ahb->mem = NULL; + return ret; +} + +static void ath10k_ahb_resource_deinit(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + struct device *dev; + + dev = &ar_ahb->pdev->dev; + + if (ar_ahb->mem) + devm_iounmap(dev, ar_ahb->mem); + + if (ar_ahb->gcc_mem) + iounmap(ar_ahb->gcc_mem); + + if (ar_ahb->tcsr_mem) + iounmap(ar_ahb->tcsr_mem); + + ar_ahb->mem = NULL; + ar_ahb->gcc_mem = NULL; + ar_ahb->tcsr_mem = NULL; + + ath10k_ahb_clock_deinit(ar); + ath10k_ahb_rst_ctrl_deinit(ar); +} + +static int ath10k_ahb_prepare_device(struct ath10k *ar) +{ + u32 val; + int ret; + + ret = ath10k_ahb_clock_enable(ar); + if (ret) { + ath10k_err(ar, "failed to enable clocks\n"); + return ret; + } + + /* Clock for the target is supplied from outside of target (ie, + * external clock module controlled by the host). Target needs + * to know what frequency target cpu is configured which is needed + * for target internal use. Read target cpu frequency info from + * gcc register and write into target's scratch register where + * target expects this information. + */ + val = ath10k_ahb_gcc_read32(ar, ATH10K_AHB_GCC_FEPLL_PLL_DIV); + ath10k_ahb_write32(ar, ATH10K_AHB_WIFI_SCRATCH_5_REG, val); + + ret = ath10k_ahb_release_reset(ar); + if (ret) + goto err_clk_disable; + + ath10k_ahb_irq_disable(ar); + + ath10k_ahb_write32(ar, FW_INDICATOR_ADDRESS, FW_IND_HOST_READY); + + ret = ath10k_pci_wait_for_target_init(ar); + if (ret) + goto err_halt_chip; + + return 0; + +err_halt_chip: + ath10k_ahb_halt_chip(ar); + +err_clk_disable: + ath10k_ahb_clock_disable(ar); + + return ret; +} + +static int ath10k_ahb_chip_reset(struct ath10k *ar) +{ + int ret; + + ath10k_ahb_halt_chip(ar); + ath10k_ahb_clock_disable(ar); + + ret = ath10k_ahb_prepare_device(ar); + if (ret) + return ret; + + return 0; +} + +static int ath10k_ahb_wake_target_cpu(struct ath10k *ar) +{ + u32 addr, val; + + addr = SOC_CORE_BASE_ADDRESS | CORE_CTRL_ADDRESS; + val = ath10k_ahb_read32(ar, addr); + val |= ATH10K_AHB_CORE_CTRL_CPU_INTR_MASK; + ath10k_ahb_write32(ar, addr, val); + + return 0; +} + +static int ath10k_ahb_hif_start(struct ath10k *ar) +{ + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif start\n"); + + ath10k_ce_enable_interrupts(ar); + ath10k_pci_enable_legacy_irq(ar); + + ath10k_pci_rx_post(ar); + + return 0; +} + +static void ath10k_ahb_hif_stop(struct ath10k *ar) +{ + struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif stop\n"); + + ath10k_ahb_irq_disable(ar); + synchronize_irq(ar_ahb->irq); + + ath10k_pci_flush(ar); +} + +static int ath10k_ahb_hif_power_up(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif power up\n"); + + ret = ath10k_ahb_chip_reset(ar); + if (ret) { + ath10k_err(ar, "failed to reset chip: %d\n", ret); + goto out; + } + + ret = ath10k_pci_init_pipes(ar); + if (ret) { + ath10k_err(ar, "failed to initialize CE: %d\n", ret); + goto out; + } + + ret = ath10k_pci_init_config(ar); + if (ret) { + ath10k_err(ar, "failed to setup init config: %d\n", ret); + goto err_ce_deinit; + } + + ret = ath10k_ahb_wake_target_cpu(ar); + if (ret) { + ath10k_err(ar, "could not wake up target CPU: %d\n", ret); + goto err_ce_deinit; + } + + return 0; + +err_ce_deinit: + ath10k_pci_ce_deinit(ar); +out: + return ret; +} + +static const struct ath10k_hif_ops ath10k_ahb_hif_ops = { + .tx_sg = ath10k_pci_hif_tx_sg, + .diag_read = ath10k_pci_hif_diag_read, + .diag_write = ath10k_pci_diag_write_mem, + .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, + .start = ath10k_ahb_hif_start, + .stop = ath10k_ahb_hif_stop, + .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, + .get_default_pipe = ath10k_pci_hif_get_default_pipe, + .send_complete_check = ath10k_pci_hif_send_complete_check, + .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, + .power_up = ath10k_ahb_hif_power_up, + .power_down = ath10k_pci_hif_power_down, + .read32 = ath10k_ahb_read32, + .write32 = ath10k_ahb_write32, +}; + +static const struct ath10k_bus_ops ath10k_ahb_bus_ops = { + .read32 = ath10k_ahb_read32, + .write32 = ath10k_ahb_write32, + .get_num_banks = ath10k_ahb_get_num_banks, +}; + +static int ath10k_ahb_probe(struct platform_device *pdev) +{ + struct ath10k *ar; + struct ath10k_ahb *ar_ahb; + struct ath10k_pci *ar_pci; + const struct of_device_id *of_id; + enum ath10k_hw_rev hw_rev; + size_t size; + int ret; + u32 chip_id; + + of_id = of_match_device(ath10k_ahb_of_match, &pdev->dev); + if (!of_id) { + dev_err(&pdev->dev, "failed to find matching device tree id\n"); + return -EINVAL; + } + + hw_rev = (enum ath10k_hw_rev)of_id->data; + + size = sizeof(*ar_pci) + sizeof(*ar_ahb); + ar = ath10k_core_create(size, &pdev->dev, ATH10K_BUS_AHB, + hw_rev, &ath10k_ahb_hif_ops); + if (!ar) { + dev_err(&pdev->dev, "failed to allocate core\n"); + return -ENOMEM; + } + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "ahb probe\n"); + + ar_pci = ath10k_pci_priv(ar); + ar_ahb = ath10k_ahb_priv(ar); + + ar_ahb->pdev = pdev; + platform_set_drvdata(pdev, ar); + + ret = ath10k_ahb_resource_init(ar); + if (ret) + goto err_core_destroy; + + ar->dev_id = 0; + ar_pci->mem = ar_ahb->mem; + ar_pci->mem_len = ar_ahb->mem_len; + ar_pci->ar = ar; + ar_pci->bus_ops = &ath10k_ahb_bus_ops; + + ret = ath10k_pci_setup_resource(ar); + if (ret) { + ath10k_err(ar, "failed to setup resource: %d\n", ret); + goto err_resource_deinit; + } + + ath10k_pci_init_irq_tasklets(ar); + + ret = ath10k_ahb_request_irq_legacy(ar); + if (ret) + goto err_free_pipes; + + ret = ath10k_ahb_prepare_device(ar); + if (ret) + goto err_free_irq; + + ath10k_pci_ce_deinit(ar); + + chip_id = ath10k_ahb_soc_read32(ar, SOC_CHIP_ID_ADDRESS); + if (chip_id == 0xffffffff) { + ath10k_err(ar, "failed to get chip id\n"); + goto err_halt_device; + } + + ret = ath10k_core_register(ar, chip_id); + if (ret) { + ath10k_err(ar, "failed to register driver core: %d\n", ret); + goto err_halt_device; + } + + return 0; + +err_halt_device: + ath10k_ahb_halt_chip(ar); + ath10k_ahb_clock_disable(ar); + +err_free_irq: + ath10k_ahb_release_irq_legacy(ar); + +err_free_pipes: + ath10k_pci_free_pipes(ar); + +err_resource_deinit: + ath10k_ahb_resource_deinit(ar); + +err_core_destroy: + ath10k_core_destroy(ar); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int ath10k_ahb_remove(struct platform_device *pdev) +{ + struct ath10k *ar = platform_get_drvdata(pdev); + struct ath10k_ahb *ar_ahb; + + if (!ar) + return -EINVAL; + + ar_ahb = ath10k_ahb_priv(ar); + + if (!ar_ahb) + return -EINVAL; + + ath10k_dbg(ar, ATH10K_DBG_AHB, "ahb remove\n"); + + ath10k_core_unregister(ar); + ath10k_ahb_irq_disable(ar); + ath10k_ahb_release_irq_legacy(ar); + ath10k_pci_release_resource(ar); + ath10k_ahb_halt_chip(ar); + ath10k_ahb_clock_disable(ar); + ath10k_ahb_resource_deinit(ar); + ath10k_core_destroy(ar); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ath10k_ahb_driver = { + .driver = { + .name = "ath10k_ahb", + .of_match_table = ath10k_ahb_of_match, + }, + .probe = ath10k_ahb_probe, + .remove = ath10k_ahb_remove, +}; + +int ath10k_ahb_init(void) +{ + int ret; + + printk(KERN_ERR "AHB support is still work in progress\n"); + + ret = platform_driver_register(&ath10k_ahb_driver); + if (ret) + printk(KERN_ERR "failed to register ath10k ahb driver: %d\n", + ret); + return ret; +} + +void ath10k_ahb_exit(void) +{ + platform_driver_unregister(&ath10k_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath10k/ahb.h b/drivers/net/wireless/ath/ath10k/ahb.h new file mode 100644 index 000000000..d43e37521 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/ahb.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Qualcomm Atheros, Inc. All rights reserved. + * Copyright (c) 2015 The Linux Foundation. All rights reserved. + * + * 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 _AHB_H_ +#define _AHB_H_ + +#include <linux/platform_device.h> + +struct ath10k_ahb { + struct platform_device *pdev; + void __iomem *mem; + unsigned long mem_len; + void __iomem *gcc_mem; + void __iomem *tcsr_mem; + + int irq; + + struct clk *cmd_clk; + struct clk *ref_clk; + struct clk *rtc_clk; + + struct reset_control *core_cold_rst; + struct reset_control *radio_cold_rst; + struct reset_control *radio_warm_rst; + struct reset_control *radio_srif_rst; + struct reset_control *cpu_init_rst; +}; + +#ifdef CONFIG_ATH10K_AHB + +#define ATH10K_GCC_REG_BASE 0x1800000 +#define ATH10K_GCC_REG_SIZE 0x60000 + +#define ATH10K_TCSR_REG_BASE 0x1900000 +#define ATH10K_TCSR_REG_SIZE 0x80000 + +#define ATH10K_AHB_GCC_FEPLL_PLL_DIV 0x2f020 +#define ATH10K_AHB_WIFI_SCRATCH_5_REG 0x4f014 + +#define ATH10K_AHB_WLAN_CORE_ID_REG 0x82030 + +#define ATH10K_AHB_TCSR_WIFI0_GLB_CFG 0x49000 +#define ATH10K_AHB_TCSR_WIFI1_GLB_CFG 0x49004 +#define TCSR_WIFIX_GLB_CFG_DISABLE_CORE_CLK BIT(25) + +#define ATH10K_AHB_TCSR_WCSS0_HALTREQ 0x52000 +#define ATH10K_AHB_TCSR_WCSS1_HALTREQ 0x52010 +#define ATH10K_AHB_TCSR_WCSS0_HALTACK 0x52004 +#define ATH10K_AHB_TCSR_WCSS1_HALTACK 0x52014 + +#define ATH10K_AHB_AXI_BUS_HALT_TIMEOUT 10 /* msec */ +#define AHB_AXI_BUS_HALT_REQ 1 +#define AHB_AXI_BUS_HALT_ACK 1 + +#define ATH10K_AHB_CORE_CTRL_CPU_INTR_MASK 1 + +int ath10k_ahb_init(void); +void ath10k_ahb_exit(void); + +#else /* CONFIG_ATH10K_AHB */ + +static inline int ath10k_ahb_init(void) +{ + return 0; +} + +static inline void ath10k_ahb_exit(void) +{ +} + +#endif /* CONFIG_ATH10K_AHB */ + +#endif /* _AHB_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 649be505d..3ede921da 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -156,6 +156,11 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .channel_counters_freq_hz = 150000, .max_probe_resp_desc_thres = 24, .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE, + .num_msdu_desc = 1424, + .qcache_active_peers = 50, + .tx_chain_mask = 0xf, + .rx_chain_mask = 0xf, + .max_spatial_stream = 4, .fw = { .dir = QCA99X0_HW_2_0_FW_DIR, .fw = QCA99X0_HW_2_0_FW_FILE, @@ -201,6 +206,31 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, }, + { + .id = QCA4019_HW_1_0_DEV_VERSION, + .dev_id = 0, + .name = "qca4019 hw1.0", + .patch_load_addr = QCA4019_HW_1_0_PATCH_LOAD_ADDR, + .uart_pin = 7, + .otp_exe_param = 0x0010000, + .continuous_frag_desc = true, + .channel_counters_freq_hz = 125000, + .max_probe_resp_desc_thres = 24, + .hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE, + .num_msdu_desc = 2500, + .qcache_active_peers = 35, + .tx_chain_mask = 0x3, + .rx_chain_mask = 0x3, + .max_spatial_stream = 2, + .fw = { + .dir = QCA4019_HW_1_0_FW_DIR, + .fw = QCA4019_HW_1_0_FW_FILE, + .otp = QCA4019_HW_1_0_OTP_FILE, + .board = QCA4019_HW_1_0_BOARD_DATA_FILE, + .board_size = QCA4019_BOARD_DATA_SZ, + .board_ext_size = QCA4019_BOARD_EXT_DATA_SZ, + }, + }, }; static const char *const ath10k_core_fw_feature_str[] = { @@ -217,6 +247,7 @@ static const char *const ath10k_core_fw_feature_str[] = { [ATH10K_FW_FEATURE_RAW_MODE_SUPPORT] = "raw-mode", [ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA] = "adaptive-cca", [ATH10K_FW_FEATURE_MFP_SUPPORT] = "mfp", + [ATH10K_FW_FEATURE_PEER_FLOW_CONTROL] = "peer-flow-ctrl", }; static unsigned int ath10k_core_get_fw_feature_str(char *buf, @@ -1478,8 +1509,13 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: case ATH10K_FW_WMI_OP_VERSION_10_2_4: - ar->max_num_peers = TARGET_10X_NUM_PEERS; - ar->max_num_stations = TARGET_10X_NUM_STATIONS; + if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) { + ar->max_num_peers = TARGET_10X_TX_STATS_NUM_PEERS; + ar->max_num_stations = TARGET_10X_TX_STATS_NUM_STATIONS; + } else { + ar->max_num_peers = TARGET_10X_NUM_PEERS; + ar->max_num_stations = TARGET_10X_NUM_STATIONS; + } ar->max_num_vdevs = TARGET_10X_NUM_VDEVS; ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; ar->fw_stats_req_mask = WMI_STAT_PEER; @@ -1502,9 +1538,9 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->num_active_peers = TARGET_10_4_ACTIVE_PEERS; ar->max_num_vdevs = TARGET_10_4_NUM_VDEVS; ar->num_tids = TARGET_10_4_TGT_NUM_TIDS; - ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC; + ar->htt.max_num_pending_tx = ar->hw_params.num_msdu_desc; ar->fw_stats_req_mask = WMI_STAT_PEER; - ar->max_spatial_stream = WMI_10_4_MAX_SPATIAL_STREAM; + ar->max_spatial_stream = ar->hw_params.max_spatial_stream; break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: @@ -1687,6 +1723,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) goto err_hif_stop; } + ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1; + + INIT_LIST_HEAD(&ar->arvifs); + /* we don't care about HTT in UTF mode */ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) { status = ath10k_htt_setup(&ar->htt); @@ -1700,10 +1740,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) if (status) goto err_hif_stop; - ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1; - - INIT_LIST_HEAD(&ar->arvifs); - return 0; err_hif_stop: @@ -1979,6 +2015,10 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, ar->regs = &qca99x0_regs; ar->hw_values = &qca99x0_values; break; + case ATH10K_HW_QCA4019: + ar->regs = &qca4019_regs; + ar->hw_values = &qca4019_values; + break; default: ath10k_err(ar, "unsupported core hardware revision %d\n", hw_rev); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 7840cf3ef..a62b62a62 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -69,6 +69,7 @@ struct ath10k; enum ath10k_bus { ATH10K_BUS_PCI, + ATH10K_BUS_AHB, }; static inline const char *ath10k_bus_str(enum ath10k_bus bus) @@ -76,6 +77,8 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus) switch (bus) { case ATH10K_BUS_PCI: return "pci"; + case ATH10K_BUS_AHB: + return "ahb"; } return "unknown"; @@ -159,6 +162,7 @@ struct ath10k_fw_stats_peer { u32 peer_rssi; u32 peer_tx_rate; u32 peer_rx_rate; /* 10x only */ + u32 rx_duration; }; struct ath10k_fw_stats_vdev { @@ -315,6 +319,7 @@ struct ath10k_sta { #ifdef CONFIG_MAC80211_DEBUGFS /* protected by conf_mutex */ bool aggr_mode; + u64 rx_duration; #endif }; @@ -510,6 +515,15 @@ enum ath10k_fw_features { /* Firmware supports management frame protection */ ATH10K_FW_FEATURE_MFP_SUPPORT = 12, + /* Firmware supports pull-push model where host shares it's software + * queue state with firmware and firmware generates fetch requests + * telling host which queues to dequeue tx from. + * + * Primary function of this is improved MU-MIMO performance with + * multiple clients. + */ + ATH10K_FW_FEATURE_PEER_FLOW_CONTROL = 13, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; @@ -666,6 +680,12 @@ struct ath10k { /* The padding bytes's location is different on various chips */ enum ath10k_hw_4addr_pad hw_4addr_pad; + u32 num_msdu_desc; + u32 qcache_active_peers; + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 max_spatial_stream; + struct ath10k_hw_params_fw { const char *dir; const char *fw; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 2bdf5408b..0f834646e 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -276,7 +276,7 @@ static const struct file_operations fops_wmi_services = { .llseek = default_llseek, }; -static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head) +static void ath10k_fw_stats_pdevs_free(struct list_head *head) { struct ath10k_fw_stats_pdev *i, *tmp; @@ -286,7 +286,7 @@ static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head) } } -static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head) +static void ath10k_fw_stats_vdevs_free(struct list_head *head) { struct ath10k_fw_stats_vdev *i, *tmp; @@ -296,7 +296,7 @@ static void ath10k_debug_fw_stats_vdevs_free(struct list_head *head) } } -static void ath10k_debug_fw_stats_peers_free(struct list_head *head) +static void ath10k_fw_stats_peers_free(struct list_head *head) { struct ath10k_fw_stats_peer *i, *tmp; @@ -310,16 +310,16 @@ static void ath10k_debug_fw_stats_reset(struct ath10k *ar) { spin_lock_bh(&ar->data_lock); ar->debug.fw_stats_done = false; - ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs); - ath10k_debug_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); - ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers); + ath10k_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs); + ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); + ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers); spin_unlock_bh(&ar->data_lock); } void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_fw_stats stats = {}; - bool is_start, is_started, is_end; + bool is_start, is_started, is_end, peer_stats_svc; size_t num_peers; size_t num_vdevs; int ret; @@ -347,8 +347,14 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb) * delivered which is treated as end-of-data and is itself discarded */ + peer_stats_svc = test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map); + if (peer_stats_svc) + ath10k_sta_update_rx_duration(ar, &stats.peers); + if (ar->debug.fw_stats_done) { - ath10k_warn(ar, "received unsolicited stats update event\n"); + if (!peer_stats_svc) + ath10k_warn(ar, "received unsolicited stats update event\n"); + goto free; } @@ -372,11 +378,13 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb) /* Although this is unlikely impose a sane limit to * prevent firmware from DoS-ing the host. */ + ath10k_fw_stats_peers_free(&ar->debug.fw_stats.peers); ath10k_warn(ar, "dropping fw peer stats\n"); goto free; } if (num_vdevs >= BITS_PER_LONG) { + ath10k_fw_stats_vdevs_free(&ar->debug.fw_stats.vdevs); ath10k_warn(ar, "dropping fw vdev stats\n"); goto free; } @@ -391,9 +399,9 @@ free: /* In some cases lists have been spliced and cleared. Free up * resources if that is not the case. */ - ath10k_debug_fw_stats_pdevs_free(&stats.pdevs); - ath10k_debug_fw_stats_vdevs_free(&stats.vdevs); - ath10k_debug_fw_stats_peers_free(&stats.peers); + ath10k_fw_stats_pdevs_free(&stats.pdevs); + ath10k_fw_stats_vdevs_free(&stats.vdevs); + ath10k_fw_stats_peers_free(&stats.peers); spin_unlock_bh(&ar->data_lock); } @@ -2011,7 +2019,12 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file, goto out; } - if (filter && (filter != ar->debug.pktlog_filter)) { + if (filter == ar->debug.pktlog_filter) { + ret = count; + goto out; + } + + if (filter) { ret = ath10k_wmi_pdev_pktlog_enable(ar, filter); if (ret) { ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n", @@ -2106,6 +2119,7 @@ static ssize_t ath10k_write_btcoex(struct file *file, struct ath10k *ar = file->private_data; char buf[32]; size_t buf_size; + int ret = 0; bool val; buf_size = min(count, (sizeof(buf) - 1)); @@ -2119,6 +2133,12 @@ static ssize_t ath10k_write_btcoex(struct file *file, mutex_lock(&ar->conf_mutex); + if (ar->state != ATH10K_STATE_ON && + ar->state != ATH10K_STATE_RESTARTED) { + ret = -ENETDOWN; + goto exit; + } + if (!(test_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags) ^ val)) goto exit; @@ -2127,17 +2147,15 @@ static ssize_t ath10k_write_btcoex(struct file *file, else clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags); - if (ar->state != ATH10K_STATE_ON) - goto exit; - ath10k_info(ar, "restarting firmware due to btcoex change"); queue_work(ar->workqueue, &ar->restart_work); + ret = count; exit: mutex_unlock(&ar->conf_mutex); - return count; + return ret; } static ssize_t ath10k_read_btcoex(struct file *file, char __user *ubuf, @@ -2176,9 +2194,6 @@ static ssize_t ath10k_debug_fw_checksums_read(struct file *file, mutex_lock(&ar->conf_mutex); - if (len > buf_len) - len = buf_len; - len += scnprintf(buf + len, buf_len - len, "firmware-N.bin\t\t%08x\n", crc32_le(0, ar->firmware->data, ar->firmware->size)); diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 814719cf4..6206edd7c 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -37,6 +37,7 @@ enum ath10k_debug_mask { ATH10K_DBG_TESTMODE = 0x00001000, ATH10K_DBG_WMI_PRINT = 0x00002000, ATH10K_DBG_PCI_PS = 0x00004000, + ATH10K_DBG_AHB = 0x00008000, ATH10K_DBG_ANY = 0xffffffff, }; @@ -153,6 +154,12 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) #ifdef CONFIG_MAC80211_DEBUGFS void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); +void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *peer); +#else +static inline void ath10k_sta_update_rx_duration(struct ath10k *ar, + struct list_head *peer) +{ +} #endif /* CONFIG_MAC80211_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index 95b5c4937..67ef75b60 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -18,6 +18,23 @@ #include "wmi-ops.h" #include "debug.h" +void ath10k_sta_update_rx_duration(struct ath10k *ar, struct list_head *head) +{ struct ieee80211_sta *sta; + struct ath10k_fw_stats_peer *peer; + struct ath10k_sta *arsta; + + rcu_read_lock(); + list_for_each_entry(peer, head, list) { + sta = ieee80211_find_sta_by_ifaddr(ar->hw, peer->peer_macaddr, + NULL); + if (!sta) + continue; + arsta = (struct ath10k_sta *)sta->drv_priv; + arsta->rx_duration += (u64)peer->rx_duration; + } + rcu_read_unlock(); +} + static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -232,6 +249,28 @@ static const struct file_operations fops_delba = { .llseek = default_llseek, }; +static ssize_t ath10k_dbg_sta_read_rx_duration(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + char buf[100]; + int len = 0; + + len = scnprintf(buf, sizeof(buf), + "%llu usecs\n", arsta->rx_duration); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_rx_duration = { + .read = ath10k_dbg_sta_read_rx_duration, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir) { @@ -240,4 +279,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba); debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp); debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba); + debugfs_create_file("rx_duration", S_IRUGO, dir, sta, + &fops_rx_duration); } diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 3e6ba63df..7561f22f1 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -131,12 +131,12 @@ static const enum htt_t2h_msg_type htt_10_4_t2h_msg_types[] = { [HTT_10_4_T2H_MSG_TYPE_AGGR_CONF] = HTT_T2H_MSG_TYPE_AGGR_CONF, [HTT_10_4_T2H_MSG_TYPE_TX_FETCH_IND] = HTT_T2H_MSG_TYPE_TX_FETCH_IND, - [HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONF] = - HTT_T2H_MSG_TYPE_TX_FETCH_CONF, + [HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONFIRM] = + HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM, [HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD] = HTT_T2H_MSG_TYPE_STATS_NOUPLOAD, - [HTT_10_4_T2H_MSG_TYPE_TX_LOW_LATENCY_IND] = - HTT_T2H_MSG_TYPE_TX_LOW_LATENCY_IND, + [HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND] = + HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, }; int ath10k_htt_connect(struct ath10k_htt *htt) diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 47ca048fe..13391ea44 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -52,6 +52,7 @@ enum htt_h2t_msg_type { /* host-to-target */ /* This command is used for sending management frames in HTT < 3.0. * HTT >= 3.0 uses TX_FRM for everything. */ HTT_H2T_MSG_TYPE_MGMT_TX = 7, + HTT_H2T_MSG_TYPE_TX_FETCH_RESP = 11, HTT_H2T_NUM_MSGS /* keep this last */ }; @@ -413,10 +414,10 @@ enum htt_10_4_t2h_msg_type { HTT_10_4_T2H_MSG_TYPE_EN_STATS = 0x14, HTT_10_4_T2H_MSG_TYPE_AGGR_CONF = 0x15, HTT_10_4_T2H_MSG_TYPE_TX_FETCH_IND = 0x16, - HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONF = 0x17, + HTT_10_4_T2H_MSG_TYPE_TX_FETCH_CONFIRM = 0x17, HTT_10_4_T2H_MSG_TYPE_STATS_NOUPLOAD = 0x18, /* 0x19 to 0x2f are reserved */ - HTT_10_4_T2H_MSG_TYPE_TX_LOW_LATENCY_IND = 0x30, + HTT_10_4_T2H_MSG_TYPE_TX_MODE_SWITCH_IND = 0x30, /* keep this last */ HTT_10_4_T2H_NUM_MSGS }; @@ -449,8 +450,8 @@ enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_TEST, HTT_T2H_MSG_TYPE_EN_STATS, HTT_T2H_MSG_TYPE_TX_FETCH_IND, - HTT_T2H_MSG_TYPE_TX_FETCH_CONF, - HTT_T2H_MSG_TYPE_TX_LOW_LATENCY_IND, + HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM, + HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND, /* keep this last */ HTT_T2H_NUM_MSGS }; @@ -1306,9 +1307,43 @@ struct htt_frag_desc_bank_id { * so we use a conservatively safe value for now */ #define HTT_FRAG_DESC_BANK_MAX 4 -#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03 -#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB 0 -#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP (1 << 2) +#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03 +#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB 0 +#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP BIT(2) +#define HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_VALID BIT(3) +#define HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_DEPTH_TYPE_MASK BIT(4) +#define HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_DEPTH_TYPE_LSB 4 + +enum htt_q_depth_type { + HTT_Q_DEPTH_TYPE_BYTES = 0, + HTT_Q_DEPTH_TYPE_MSDUS = 1, +}; + +#define HTT_TX_Q_STATE_NUM_PEERS (TARGET_10_4_NUM_QCACHE_PEERS_MAX + \ + TARGET_10_4_NUM_VDEVS) +#define HTT_TX_Q_STATE_NUM_TIDS 8 +#define HTT_TX_Q_STATE_ENTRY_SIZE 1 +#define HTT_TX_Q_STATE_ENTRY_MULTIPLIER 0 + +/** + * htt_q_state_conf - part of htt_frag_desc_bank_cfg for host q state config + * + * Defines host q state format and behavior. See htt_q_state. + * + * @record_size: Defines the size of each host q entry in bytes. In practice + * however firmware (at least 10.4.3-00191) ignores this host + * configuration value and uses hardcoded value of 1. + * @record_multiplier: This is valid only when q depth type is MSDUs. It + * defines the exponent for the power of 2 multiplication. + */ +struct htt_q_state_conf { + __le32 paddr; + __le16 num_peers; + __le16 num_tids; + u8 record_size; + u8 record_multiplier; + u8 pad[2]; +} __packed; struct htt_frag_desc_bank_cfg { u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */ @@ -1316,6 +1351,114 @@ struct htt_frag_desc_bank_cfg { u8 desc_size; __le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX]; struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX]; + struct htt_q_state_conf q_state; +} __packed; + +#define HTT_TX_Q_STATE_ENTRY_COEFFICIENT 128 +#define HTT_TX_Q_STATE_ENTRY_FACTOR_MASK 0x3f +#define HTT_TX_Q_STATE_ENTRY_FACTOR_LSB 0 +#define HTT_TX_Q_STATE_ENTRY_EXP_MASK 0xc0 +#define HTT_TX_Q_STATE_ENTRY_EXP_LSB 6 + +/** + * htt_q_state - shared between host and firmware via DMA + * + * This structure is used for the host to expose it's software queue state to + * firmware so that its rate control can schedule fetch requests for optimized + * performance. This is most notably used for MU-MIMO aggregation when multiple + * MU clients are connected. + * + * @count: Each element defines the host queue depth. When q depth type was + * configured as HTT_Q_DEPTH_TYPE_BYTES then each entry is defined as: + * FACTOR * 128 * 8^EXP (see HTT_TX_Q_STATE_ENTRY_FACTOR_MASK and + * HTT_TX_Q_STATE_ENTRY_EXP_MASK). When q depth type was configured as + * HTT_Q_DEPTH_TYPE_MSDUS the number of packets is scaled by 2 ** + * record_multiplier (see htt_q_state_conf). + * @map: Used by firmware to quickly check which host queues are not empty. It + * is a bitmap simply saying. + * @seq: Used by firmware to quickly check if the host queues were updated + * since it last checked. + * + * FIXME: Is the q_state map[] size calculation really correct? + */ +struct htt_q_state { + u8 count[HTT_TX_Q_STATE_NUM_TIDS][HTT_TX_Q_STATE_NUM_PEERS]; + u32 map[HTT_TX_Q_STATE_NUM_TIDS][(HTT_TX_Q_STATE_NUM_PEERS + 31) / 32]; + __le32 seq; +} __packed; + +#define HTT_TX_FETCH_RECORD_INFO_PEER_ID_MASK 0x0fff +#define HTT_TX_FETCH_RECORD_INFO_PEER_ID_LSB 0 +#define HTT_TX_FETCH_RECORD_INFO_TID_MASK 0xf000 +#define HTT_TX_FETCH_RECORD_INFO_TID_LSB 12 + +struct htt_tx_fetch_record { + __le16 info; /* HTT_TX_FETCH_IND_RECORD_INFO_ */ + __le16 num_msdus; + __le32 num_bytes; +} __packed; + +struct htt_tx_fetch_ind { + u8 pad0; + __le16 fetch_seq_num; + __le32 token; + __le16 num_resp_ids; + __le16 num_records; + struct htt_tx_fetch_record records[0]; + __le32 resp_ids[0]; /* ath10k_htt_get_tx_fetch_ind_resp_ids() */ +} __packed; + +static inline void * +ath10k_htt_get_tx_fetch_ind_resp_ids(struct htt_tx_fetch_ind *ind) +{ + return (void *)&ind->records[le16_to_cpu(ind->num_records)]; +} + +struct htt_tx_fetch_resp { + u8 pad0; + __le16 resp_id; + __le16 fetch_seq_num; + __le16 num_records; + __le32 token; + struct htt_tx_fetch_record records[0]; +} __packed; + +struct htt_tx_fetch_confirm { + u8 pad0; + __le16 num_resp_ids; + __le32 resp_ids[0]; +} __packed; + +enum htt_tx_mode_switch_mode { + HTT_TX_MODE_SWITCH_PUSH = 0, + HTT_TX_MODE_SWITCH_PUSH_PULL = 1, +}; + +#define HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE BIT(0) +#define HTT_TX_MODE_SWITCH_IND_INFO0_NUM_RECORDS_MASK 0xfffe +#define HTT_TX_MODE_SWITCH_IND_INFO0_NUM_RECORDS_LSB 1 + +#define HTT_TX_MODE_SWITCH_IND_INFO1_MODE_MASK 0x0003 +#define HTT_TX_MODE_SWITCH_IND_INFO1_MODE_LSB 0 +#define HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD_MASK 0xfffc +#define HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD_LSB 2 + +#define HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID_MASK 0x0fff +#define HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID_LSB 0 +#define HTT_TX_MODE_SWITCH_RECORD_INFO0_TID_MASK 0xf000 +#define HTT_TX_MODE_SWITCH_RECORD_INFO0_TID_LSB 12 + +struct htt_tx_mode_switch_record { + __le16 info0; /* HTT_TX_MODE_SWITCH_RECORD_INFO0_ */ + __le16 num_max_msdus; +} __packed; + +struct htt_tx_mode_switch_ind { + u8 pad0; + __le16 info0; /* HTT_TX_MODE_SWITCH_IND_INFO0_ */ + __le16 info1; /* HTT_TX_MODE_SWITCH_IND_INFO1_ */ + u8 pad1[2]; + struct htt_tx_mode_switch_record records[0]; } __packed; union htt_rx_pn_t { @@ -1340,6 +1483,7 @@ struct htt_cmd { struct htt_oob_sync_req oob_sync_req; struct htt_aggr_conf aggr_conf; struct htt_frag_desc_bank_cfg frag_desc_bank_cfg; + struct htt_tx_fetch_resp tx_fetch_resp; }; } __packed; @@ -1364,6 +1508,9 @@ struct htt_resp { struct htt_rx_pn_ind rx_pn_ind; struct htt_rx_offload_ind rx_offload_ind; struct htt_rx_in_ord_ind rx_in_ord_ind; + struct htt_tx_fetch_ind tx_fetch_ind; + struct htt_tx_fetch_confirm tx_fetch_confirm; + struct htt_tx_mode_switch_ind tx_mode_switch_ind; }; } __packed; @@ -1518,6 +1665,14 @@ struct ath10k_htt { dma_addr_t paddr; struct ath10k_htt_txbuf *vaddr; } txbuf; + + struct { + struct htt_q_state *vaddr; + dma_addr_t paddr; + u16 num_peers; + u16 num_tids; + enum htt_q_depth_type type; + } tx_q_state; }; #define RX_HTT_HDR_STATUS_LEN 64 diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index a192d451d..ae9b686a4 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -2011,9 +2011,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_IND: - spin_lock_bh(&htt->rx_ring.lock); - __skb_queue_tail(&htt->rx_compl_q, skb); - spin_unlock_bh(&htt->rx_ring.lock); + skb_queue_tail(&htt->rx_compl_q, skb); tasklet_schedule(&htt->txrx_compl_task); return; case HTT_T2H_MSG_TYPE_PEER_MAP: { @@ -2111,9 +2109,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: { - spin_lock_bh(&htt->rx_ring.lock); - __skb_queue_tail(&htt->rx_in_ord_compl_q, skb); - spin_unlock_bh(&htt->rx_ring.lock); + skb_queue_tail(&htt->rx_in_ord_compl_q, skb); tasklet_schedule(&htt->txrx_compl_task); return; } @@ -2123,10 +2119,12 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; case HTT_T2H_MSG_TYPE_AGGR_CONF: break; - case HTT_T2H_MSG_TYPE_EN_STATS: case HTT_T2H_MSG_TYPE_TX_FETCH_IND: - case HTT_T2H_MSG_TYPE_TX_FETCH_CONF: - case HTT_T2H_MSG_TYPE_TX_LOW_LATENCY_IND: + case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM: + case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND: + /* TODO: Implement pull-push logic */ + break; + case HTT_T2H_MSG_TYPE_EN_STATS: default: ath10k_warn(ar, "htt event (%d) not handled\n", resp->hdr.msg_type); @@ -2152,24 +2150,46 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr) { struct ath10k_htt *htt = (struct ath10k_htt *)ptr; struct ath10k *ar = htt->ar; + struct sk_buff_head tx_q; + struct sk_buff_head rx_q; + struct sk_buff_head rx_ind_q; struct htt_resp *resp; struct sk_buff *skb; + unsigned long flags; + + __skb_queue_head_init(&tx_q); + __skb_queue_head_init(&rx_q); + __skb_queue_head_init(&rx_ind_q); + + spin_lock_irqsave(&htt->tx_compl_q.lock, flags); + skb_queue_splice_init(&htt->tx_compl_q, &tx_q); + spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags); - while ((skb = skb_dequeue(&htt->tx_compl_q))) { + spin_lock_irqsave(&htt->rx_compl_q.lock, flags); + skb_queue_splice_init(&htt->rx_compl_q, &rx_q); + spin_unlock_irqrestore(&htt->rx_compl_q.lock, flags); + + spin_lock_irqsave(&htt->rx_in_ord_compl_q.lock, flags); + skb_queue_splice_init(&htt->rx_in_ord_compl_q, &rx_ind_q); + spin_unlock_irqrestore(&htt->rx_in_ord_compl_q.lock, flags); + + while ((skb = __skb_dequeue(&tx_q))) { ath10k_htt_rx_frm_tx_compl(htt->ar, skb); dev_kfree_skb_any(skb); } - spin_lock_bh(&htt->rx_ring.lock); - while ((skb = __skb_dequeue(&htt->rx_compl_q))) { + while ((skb = __skb_dequeue(&rx_q))) { resp = (struct htt_resp *)skb->data; + spin_lock_bh(&htt->rx_ring.lock); ath10k_htt_rx_handler(htt, &resp->rx_ind); + spin_unlock_bh(&htt->rx_ring.lock); dev_kfree_skb_any(skb); } - while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) { + while ((skb = __skb_dequeue(&rx_ind_q))) { + spin_lock_bh(&htt->rx_ring.lock); ath10k_htt_rx_in_ord_ind(ar, skb); + spin_unlock_bh(&htt->rx_ring.lock); dev_kfree_skb_any(skb); } - spin_unlock_bh(&htt->rx_ring.lock); } diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index b3adadb5f..95acb727c 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -97,6 +97,85 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) idr_remove(&htt->pending_tx, msdu_id); } +static void ath10k_htt_tx_free_cont_frag_desc(struct ath10k_htt *htt) +{ + size_t size; + + if (!htt->frag_desc.vaddr) + return; + + size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc); + + dma_free_coherent(htt->ar->dev, + size, + htt->frag_desc.vaddr, + htt->frag_desc.paddr); +} + +static int ath10k_htt_tx_alloc_cont_frag_desc(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + size_t size; + + if (!ar->hw_params.continuous_frag_desc) + return 0; + + size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc); + htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size, + &htt->frag_desc.paddr, + GFP_KERNEL); + if (!htt->frag_desc.vaddr) { + ath10k_err(ar, "failed to alloc fragment desc memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void ath10k_htt_tx_free_txq(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + size_t size; + + if (!test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, ar->fw_features)) + return; + + size = sizeof(*htt->tx_q_state.vaddr); + + dma_unmap_single(ar->dev, htt->tx_q_state.paddr, size, DMA_TO_DEVICE); + kfree(htt->tx_q_state.vaddr); +} + +static int ath10k_htt_tx_alloc_txq(struct ath10k_htt *htt) +{ + struct ath10k *ar = htt->ar; + size_t size; + int ret; + + if (!test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, ar->fw_features)) + return 0; + + htt->tx_q_state.num_peers = HTT_TX_Q_STATE_NUM_PEERS; + htt->tx_q_state.num_tids = HTT_TX_Q_STATE_NUM_TIDS; + htt->tx_q_state.type = HTT_Q_DEPTH_TYPE_BYTES; + + size = sizeof(*htt->tx_q_state.vaddr); + htt->tx_q_state.vaddr = kzalloc(size, GFP_KERNEL); + if (!htt->tx_q_state.vaddr) + return -ENOMEM; + + htt->tx_q_state.paddr = dma_map_single(ar->dev, htt->tx_q_state.vaddr, + size, DMA_TO_DEVICE); + ret = dma_mapping_error(ar->dev, htt->tx_q_state.paddr); + if (ret) { + ath10k_warn(ar, "failed to dma map tx_q_state: %d\n", ret); + kfree(htt->tx_q_state.vaddr); + return -EIO; + } + + return 0; +} + int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; @@ -118,29 +197,32 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt) goto free_idr_pending_tx; } - if (!ar->hw_params.continuous_frag_desc) - goto skip_frag_desc_alloc; - - size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc); - htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size, - &htt->frag_desc.paddr, - GFP_KERNEL); - if (!htt->frag_desc.vaddr) { - ath10k_warn(ar, "failed to alloc fragment desc memory\n"); - ret = -ENOMEM; + ret = ath10k_htt_tx_alloc_cont_frag_desc(htt); + if (ret) { + ath10k_err(ar, "failed to alloc cont frag desc: %d\n", ret); goto free_txbuf; } -skip_frag_desc_alloc: + ret = ath10k_htt_tx_alloc_txq(htt); + if (ret) { + ath10k_err(ar, "failed to alloc txq: %d\n", ret); + goto free_frag_desc; + } + return 0; +free_frag_desc: + ath10k_htt_tx_free_cont_frag_desc(htt); + free_txbuf: size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf); dma_free_coherent(htt->ar->dev, size, htt->txbuf.vaddr, htt->txbuf.paddr); + free_idr_pending_tx: idr_destroy(&htt->pending_tx); + return ret; } @@ -174,12 +256,8 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt) htt->txbuf.paddr); } - if (htt->frag_desc.vaddr) { - size = htt->max_num_pending_tx * - sizeof(struct htt_msdu_ext_desc); - dma_free_coherent(htt->ar->dev, size, htt->frag_desc.vaddr, - htt->frag_desc.paddr); - } + ath10k_htt_tx_free_txq(htt); + ath10k_htt_tx_free_cont_frag_desc(htt); } void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) @@ -268,7 +346,9 @@ int ath10k_htt_send_frag_desc_bank_cfg(struct ath10k_htt *htt) struct ath10k *ar = htt->ar; struct sk_buff *skb; struct htt_cmd *cmd; + struct htt_frag_desc_bank_cfg *cfg; int ret, size; + u8 info; if (!ar->hw_params.continuous_frag_desc) return 0; @@ -286,14 +366,30 @@ int ath10k_htt_send_frag_desc_bank_cfg(struct ath10k_htt *htt) skb_put(skb, size); cmd = (struct htt_cmd *)skb->data; cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG; - cmd->frag_desc_bank_cfg.info = 0; - cmd->frag_desc_bank_cfg.num_banks = 1; - cmd->frag_desc_bank_cfg.desc_size = sizeof(struct htt_msdu_ext_desc); - cmd->frag_desc_bank_cfg.bank_base_addrs[0] = - __cpu_to_le32(htt->frag_desc.paddr); - cmd->frag_desc_bank_cfg.bank_id[0].bank_min_id = 0; - cmd->frag_desc_bank_cfg.bank_id[0].bank_max_id = - __cpu_to_le16(htt->max_num_pending_tx - 1); + + info = 0; + info |= SM(htt->tx_q_state.type, + HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_DEPTH_TYPE); + + if (test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL, ar->fw_features)) + info |= HTT_FRAG_DESC_BANK_CFG_INFO_Q_STATE_VALID; + + cfg = &cmd->frag_desc_bank_cfg; + cfg->info = info; + cfg->num_banks = 1; + cfg->desc_size = sizeof(struct htt_msdu_ext_desc); + cfg->bank_base_addrs[0] = __cpu_to_le32(htt->frag_desc.paddr); + cfg->bank_id[0].bank_min_id = 0; + cfg->bank_id[0].bank_max_id = __cpu_to_le16(htt->max_num_pending_tx - + 1); + + cfg->q_state.paddr = cpu_to_le32(htt->tx_q_state.paddr); + cfg->q_state.num_peers = cpu_to_le16(htt->tx_q_state.num_peers); + cfg->q_state.num_tids = cpu_to_le16(htt->tx_q_state.num_tids); + cfg->q_state.record_size = HTT_TX_Q_STATE_ENTRY_SIZE; + cfg->q_state.record_multiplier = HTT_TX_Q_STATE_ENTRY_MULTIPLIER; + + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt frag desc bank cmd\n"); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 7b84d08a5..f544d4851 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -109,6 +109,38 @@ const struct ath10k_hw_regs qca99x0_regs = { .pcie_intr_clr_address = 0x00000010, }; +const struct ath10k_hw_regs qca4019_regs = { + .rtc_soc_base_address = 0x00080000, + .soc_core_base_address = 0x00082000, + .ce_wrapper_base_address = 0x0004d000, + .ce0_base_address = 0x0004a000, + .ce1_base_address = 0x0004a400, + .ce2_base_address = 0x0004a800, + .ce3_base_address = 0x0004ac00, + .ce4_base_address = 0x0004b000, + .ce5_base_address = 0x0004b400, + .ce6_base_address = 0x0004b800, + .ce7_base_address = 0x0004bc00, + /* qca4019 supports upto 12 copy engines. Since base address + * of ce8 to ce11 are not directly referred in the code, + * no need have them in separate members in this table. + * Copy Engine Address + * CE8 0x0004c000 + * CE9 0x0004c400 + * CE10 0x0004c800 + * CE11 0x0004cc00 + */ + .soc_reset_control_si0_rst_mask = 0x00000001, + .soc_reset_control_ce_rst_mask = 0x00000100, + .soc_chip_id_address = 0x000000ec, + .fw_indicator_address = 0x0004f00c, + .ce_wrap_intr_sum_host_msi_lsb = 0x0000000c, + .ce_wrap_intr_sum_host_msi_mask = 0x00fff000, + .pcie_intr_fw_mask = 0x00100000, + .pcie_intr_ce_mask_all = 0x000fff00, + .pcie_intr_clr_address = 0x00000010, +}; + const struct ath10k_hw_values qca988x_values = { .rtc_state_val_on = 3, .ce_count = 8, @@ -136,6 +168,13 @@ const struct ath10k_hw_values qca99x0_values = { .ce_desc_meta_data_lsb = 4, }; +const struct ath10k_hw_values qca4019_values = { + .ce_count = 12, + .num_target_ce_config_wlan = 10, + .ce_desc_meta_data_mask = 0xFFF0, + .ce_desc_meta_data_lsb = 4, +}; + void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev) { diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 9e7f21607..3e7d4806a 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -106,6 +106,14 @@ enum qca9377_chip_id_rev { #define QCA9377_HW_1_0_BOARD_DATA_FILE "/*(DEBLOBBED)*/" #define QCA9377_HW_1_0_PATCH_LOAD_ADDR 0x1234 +/* QCA4019 1.0 definitions */ +#define QCA4019_HW_1_0_DEV_VERSION 0x01000000 +#define QCA4019_HW_1_0_FW_DIR ATH10K_FW_DIR "/QCA4019/hw1.0" +#define QCA4019_HW_1_0_FW_FILE "/*(DEBLOBBED)*/" +#define QCA4019_HW_1_0_OTP_FILE "/*(DEBLOBBED)*/" +#define QCA4019_HW_1_0_BOARD_DATA_FILE "/*(DEBLOBBED)*/" +#define QCA4019_HW_1_0_PATCH_LOAD_ADDR 0x1234 + #define ATH10K_FW_API2_FILE "/*(DEBLOBBED)*/" #define ATH10K_FW_API3_FILE "/*(DEBLOBBED)*/" @@ -200,6 +208,7 @@ enum ath10k_hw_rev { ATH10K_HW_QCA6174, ATH10K_HW_QCA99X0, ATH10K_HW_QCA9377, + ATH10K_HW_QCA4019, }; struct ath10k_hw_regs { @@ -232,6 +241,7 @@ struct ath10k_hw_regs { extern const struct ath10k_hw_regs qca988x_regs; extern const struct ath10k_hw_regs qca6174_regs; extern const struct ath10k_hw_regs qca99x0_regs; +extern const struct ath10k_hw_regs qca4019_regs; struct ath10k_hw_values { u32 rtc_state_val_on; @@ -245,6 +255,7 @@ struct ath10k_hw_values { extern const struct ath10k_hw_values qca988x_values; extern const struct ath10k_hw_values qca6174_values; extern const struct ath10k_hw_values qca99x0_values; +extern const struct ath10k_hw_values qca4019_values; void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev); @@ -253,6 +264,7 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, #define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174) #define QCA_REV_99X0(ar) ((ar)->hw_rev == ATH10K_HW_QCA99X0) #define QCA_REV_9377(ar) ((ar)->hw_rev == ATH10K_HW_QCA9377) +#define QCA_REV_40XX(ar) ((ar)->hw_rev == ATH10K_HW_QCA4019) /* Known pecularities: * - raw appears in nwifi decap, raw and nwifi appear in ethernet decap @@ -363,14 +375,19 @@ enum ath10k_hw_4addr_pad { #define TARGET_10X_MAC_AGGR_DELIM 0 #define TARGET_10X_AST_SKID_LIMIT 128 #define TARGET_10X_NUM_STATIONS 128 +#define TARGET_10X_TX_STATS_NUM_STATIONS 118 #define TARGET_10X_NUM_PEERS ((TARGET_10X_NUM_STATIONS) + \ (TARGET_10X_NUM_VDEVS)) +#define TARGET_10X_TX_STATS_NUM_PEERS ((TARGET_10X_TX_STATS_NUM_STATIONS) + \ + (TARGET_10X_NUM_VDEVS)) #define TARGET_10X_NUM_OFFLOAD_PEERS 0 #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0 #define TARGET_10X_NUM_PEER_KEYS 2 #define TARGET_10X_NUM_TIDS_MAX 256 #define TARGET_10X_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \ (TARGET_10X_NUM_PEERS) * 2) +#define TARGET_10X_TX_STATS_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \ + (TARGET_10X_TX_STATS_NUM_PEERS) * 2) #define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) #define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) #define TARGET_10X_RX_TIMEOUT_LO_PRI 100 @@ -414,16 +431,11 @@ enum ath10k_hw_4addr_pad { #define TARGET_10_4_ACTIVE_PEERS 0 #define TARGET_10_4_NUM_QCACHE_PEERS_MAX 512 -#define TARGET_10_4_QCACHE_ACTIVE_PEERS 50 #define TARGET_10_4_NUM_OFFLOAD_PEERS 0 #define TARGET_10_4_NUM_OFFLOAD_REORDER_BUFFS 0 #define TARGET_10_4_NUM_PEER_KEYS 2 #define TARGET_10_4_TGT_NUM_TIDS ((TARGET_10_4_NUM_PEERS) * 2) #define TARGET_10_4_AST_SKID_LIMIT 32 -#define TARGET_10_4_TX_CHAIN_MASK (BIT(0) | BIT(1) | \ - BIT(2) | BIT(3)) -#define TARGET_10_4_RX_CHAIN_MASK (BIT(0) | BIT(1) | \ - BIT(2) | BIT(3)) /* 100 ms for video, best-effort, and background */ #define TARGET_10_4_RX_TIMEOUT_LO_PRI 100 @@ -449,7 +461,6 @@ enum ath10k_hw_4addr_pad { #define TARGET_10_4_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 #define TARGET_10_4_VOW_CONFIG 0 #define TARGET_10_4_GTK_OFFLOAD_MAX_VDEV 3 -#define TARGET_10_4_NUM_MSDU_DESC (1024 + 400) #define TARGET_10_4_11AC_TX_MAX_FRAGS 2 #define TARGET_10_4_MAX_PEER_EXT_STATS 16 #define TARGET_10_4_SMART_ANT_CAP 0 @@ -601,6 +612,7 @@ enum ath10k_hw_4addr_pad { #define FW_INDICATOR_ADDRESS ar->regs->fw_indicator_address #define FW_IND_EVENT_PENDING 1 #define FW_IND_INITIALIZED 2 +#define FW_IND_HOST_READY 0x80000000 /* HOST_REG interrupt from firmware */ #define PCIE_INTR_FIRMWARE_MASK ar->regs->pcie_intr_fw_mask diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 6146a2936..e11160ab4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1358,10 +1358,7 @@ static int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif, const u8 *p2p_ie; int ret; - if (arvif->vdev_type != WMI_VDEV_TYPE_AP) - return 0; - - if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + if (arvif->vif->type != NL80211_IFTYPE_AP || !arvif->vif->p2p) return 0; mgmt = (void *)bcn->data; @@ -3259,8 +3256,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); /* This is case only for P2P_GO */ - if (arvif->vdev_type != WMI_VDEV_TYPE_AP || - arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + if (vif->type != NL80211_IFTYPE_AP || !vif->p2p) return; if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) { @@ -3988,7 +3984,7 @@ static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) static int ath10k_start(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; - u32 burst_enable; + u32 param; int ret = 0; /* @@ -4031,13 +4027,15 @@ static int ath10k_start(struct ieee80211_hw *hw) goto err_power_down; } - ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); + param = ar->wmi.pdev_param->pmf_qos; + ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret) { ath10k_warn(ar, "failed to enable PMF QOS: %d\n", ret); goto err_core_stop; } - ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1); + param = ar->wmi.pdev_param->dynamic_bw; + ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret) { ath10k_warn(ar, "failed to enable dynamic BW: %d\n", ret); goto err_core_stop; @@ -4053,8 +4051,8 @@ static int ath10k_start(struct ieee80211_hw *hw) } if (test_bit(WMI_SERVICE_BURST, ar->wmi.svc_map)) { - burst_enable = ar->wmi.pdev_param->burst_enable; - ret = ath10k_wmi_pdev_set_param(ar, burst_enable, 0); + param = ar->wmi.pdev_param->burst_enable; + ret = ath10k_wmi_pdev_set_param(ar, param, 0); if (ret) { ath10k_warn(ar, "failed to disable burst: %d\n", ret); goto err_core_stop; @@ -4072,8 +4070,8 @@ static int ath10k_start(struct ieee80211_hw *hw) * this problem. */ - ret = ath10k_wmi_pdev_set_param(ar, - ar->wmi.pdev_param->arp_ac_override, 0); + param = ar->wmi.pdev_param->arp_ac_override; + ret = ath10k_wmi_pdev_set_param(ar, param, 0); if (ret) { ath10k_warn(ar, "failed to set arp ac override parameter: %d\n", ret); @@ -4092,8 +4090,8 @@ static int ath10k_start(struct ieee80211_hw *hw) } } - ret = ath10k_wmi_pdev_set_param(ar, - ar->wmi.pdev_param->ani_enable, 1); + param = ar->wmi.pdev_param->ani_enable; + ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret) { ath10k_warn(ar, "failed to enable ani by default: %d\n", ret); @@ -4102,6 +4100,18 @@ static int ath10k_start(struct ieee80211_hw *hw) ar->ani_enabled = true; + if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) { + param = ar->wmi.pdev_param->peer_stats_update_period; + ret = ath10k_wmi_pdev_set_param(ar, param, + PEER_DEFAULT_STATS_UPDATE_PERIOD); + if (ret) { + ath10k_warn(ar, + "failed to set peer stats period : %d\n", + ret); + goto err_core_stop; + } + } + ar->num_started_vdevs = 0; ath10k_regd_update(ar); @@ -4349,25 +4359,29 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, bit, ar->free_vdev_map); arvif->vdev_id = bit; - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + arvif->vdev_subtype = + ath10k_wmi_get_vdev_subtype(ar, WMI_VDEV_SUBTYPE_NONE); switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: arvif->vdev_type = WMI_VDEV_TYPE_STA; - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype + (ar, WMI_VDEV_SUBTYPE_P2P_DEVICE); break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: arvif->vdev_type = WMI_VDEV_TYPE_STA; if (vif->p2p) - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT; + arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype + (ar, WMI_VDEV_SUBTYPE_P2P_CLIENT); break; case NL80211_IFTYPE_ADHOC: arvif->vdev_type = WMI_VDEV_TYPE_IBSS; break; case NL80211_IFTYPE_MESH_POINT: - if (test_bit(WMI_SERVICE_MESH, ar->wmi.svc_map)) { - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH; + if (test_bit(WMI_SERVICE_MESH_11S, ar->wmi.svc_map)) { + arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype + (ar, WMI_VDEV_SUBTYPE_MESH_11S); } else if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) { ret = -EINVAL; ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n"); @@ -4379,7 +4393,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->vdev_type = WMI_VDEV_TYPE_AP; if (vif->p2p) - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; + arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype + (ar, WMI_VDEV_SUBTYPE_P2P_GO); break; case NL80211_IFTYPE_MONITOR: arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; @@ -4474,7 +4489,10 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, goto err_vdev_delete; } - if (ar->cfg_tx_chainmask) { + /* Configuring number of spatial stream for monitor interface is causing + * target assert in qca9888 and qca6174. + */ + if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) { u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask); vdev_param = ar->wmi.vdev_param->nss; @@ -6366,12 +6384,13 @@ static u64 ath10k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static int ath10k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %hu action %d\n", arvif->vdev_id, sta->addr, tid, action); @@ -6434,7 +6453,13 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, def = &vifs[0].new_ctx->def; ar->rx_channel = def->chan; - } else if (ctx && ath10k_mac_num_chanctxs(ar) == 0) { + } else if ((ctx && ath10k_mac_num_chanctxs(ar) == 0) || + (ctx && (ar->state == ATH10K_STATE_RESTARTED))) { + /* During driver restart due to firmware assert, since mac80211 + * already has valid channel context for given radio, channel + * context iteration return num_chanctx > 0. So fix rx_channel + * when restart is in progress. + */ ar->rx_channel = ctx->def.chan; } else { ar->rx_channel = NULL; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 66a481a98..f21f09cce 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -94,7 +94,6 @@ static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = { static void ath10k_pci_buffer_cleanup(struct ath10k *ar); static int ath10k_pci_cold_reset(struct ath10k *ar); static int ath10k_pci_safe_chip_reset(struct ath10k *ar); -static int ath10k_pci_wait_for_target_init(struct ath10k *ar); static int ath10k_pci_init_irq(struct ath10k *ar); static int ath10k_pci_deinit_irq(struct ath10k *ar); static int ath10k_pci_request_irq(struct ath10k *ar); @@ -620,7 +619,7 @@ static void ath10k_pci_sleep_sync(struct ath10k *ar) spin_unlock_irqrestore(&ar_pci->ps_lock, flags); } -void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) +static void ath10k_bus_pci_write32(struct ath10k *ar, u32 offset, u32 value) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; @@ -642,7 +641,7 @@ void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) ath10k_pci_sleep(ar); } -u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) +static u32 ath10k_bus_pci_read32(struct ath10k *ar, u32 offset) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); u32 val; @@ -667,6 +666,20 @@ u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) return val; } +inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ar_pci->bus_ops->write32(ar, offset, value); +} + +inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + return ar_pci->bus_ops->read32(ar, offset); +} + u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr) { return ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + addr); @@ -687,7 +700,7 @@ void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) ath10k_pci_write32(ar, PCIE_LOCAL_BASE_ADDRESS + addr, val); } -static bool ath10k_pci_irq_pending(struct ath10k *ar) +bool ath10k_pci_irq_pending(struct ath10k *ar) { u32 cause; @@ -700,7 +713,7 @@ static bool ath10k_pci_irq_pending(struct ath10k *ar) return false; } -static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) +void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) { /* IMPORTANT: INTR_CLR register has to be set after * INTR_ENABLE is set to 0, otherwise interrupt can not be @@ -716,7 +729,7 @@ static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) PCIE_INTR_ENABLE_ADDRESS); } -static void ath10k_pci_enable_legacy_irq(struct ath10k *ar) +void ath10k_pci_enable_legacy_irq(struct ath10k *ar) { ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, @@ -809,7 +822,7 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe) } } -static void ath10k_pci_rx_post(struct ath10k *ar) +void ath10k_pci_rx_post(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int i; @@ -818,7 +831,7 @@ static void ath10k_pci_rx_post(struct ath10k *ar) ath10k_pci_rx_post_pipe(&ar_pci->pipe_info[i]); } -static void ath10k_pci_rx_replenish_retry(unsigned long ptr) +void ath10k_pci_rx_replenish_retry(unsigned long ptr) { struct ath10k *ar = (void *)ptr; @@ -838,6 +851,7 @@ static u32 ath10k_pci_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr) 0x7ff) << 21; break; case ATH10K_HW_QCA99X0: + case ATH10K_HW_QCA4019: val = ath10k_pci_read32(ar, PCIE_BAR_REG_ADDRESS); break; } @@ -1007,8 +1021,8 @@ static int __ath10k_pci_diag_read_hi(struct ath10k *ar, void *dest, #define ath10k_pci_diag_read_hi(ar, dest, src, len) \ __ath10k_pci_diag_read_hi(ar, dest, HI_ITEM(src), len) -static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, - const void *data, int nbytes) +int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, + const void *data, int nbytes) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret = 0; @@ -1263,8 +1277,8 @@ static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state) ath10k_pci_process_rx_cb(ce_state, ath10k_pci_htt_rx_deliver); } -static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, - struct ath10k_hif_sg_item *items, int n_items) +int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, + struct ath10k_hif_sg_item *items, int n_items) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_pipe *pci_pipe = &ar_pci->pipe_info[pipe_id]; @@ -1332,13 +1346,13 @@ err: return err; } -static int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf, - size_t buf_len) +int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf, + size_t buf_len) { return ath10k_pci_diag_read_mem(ar, address, buf, buf_len); } -static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) +u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -1406,8 +1420,8 @@ static void ath10k_pci_fw_crashed_dump(struct ath10k *ar) queue_work(ar->workqueue, &ar->restart_work); } -static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, - int force) +void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, + int force) { ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif send complete check\n"); @@ -1432,7 +1446,7 @@ static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, ath10k_ce_per_engine_service(ar, pipe); } -static void ath10k_pci_kill_tasklet(struct ath10k *ar) +void ath10k_pci_kill_tasklet(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int i; @@ -1446,8 +1460,8 @@ static void ath10k_pci_kill_tasklet(struct ath10k *ar) del_timer_sync(&ar_pci->rx_post_retry); } -static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, - u8 *ul_pipe, u8 *dl_pipe) +int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) { const struct service_to_pipe *entry; bool ul_set = false, dl_set = false; @@ -1491,8 +1505,8 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, return 0; } -static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, - u8 *ul_pipe, u8 *dl_pipe) +void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, + u8 *ul_pipe, u8 *dl_pipe) { ath10k_dbg(ar, ATH10K_DBG_PCI, "pci hif get default pipe\n"); @@ -1516,6 +1530,7 @@ static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar) CORE_CTRL_ADDRESS, val); break; case ATH10K_HW_QCA99X0: + case ATH10K_HW_QCA4019: /* TODO: Find appropriate register configuration for QCA99X0 * to mask irq/MSI. */ @@ -1538,6 +1553,7 @@ static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar) CORE_CTRL_ADDRESS, val); break; case ATH10K_HW_QCA99X0: + case ATH10K_HW_QCA4019: /* TODO: Find appropriate register configuration for QCA99X0 * to unmask irq/MSI. */ @@ -1668,7 +1684,7 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar) } } -static void ath10k_pci_ce_deinit(struct ath10k *ar) +void ath10k_pci_ce_deinit(struct ath10k *ar) { int i; @@ -1676,7 +1692,7 @@ static void ath10k_pci_ce_deinit(struct ath10k *ar) ath10k_ce_deinit_pipe(ar, i); } -static void ath10k_pci_flush(struct ath10k *ar) +void ath10k_pci_flush(struct ath10k *ar) { ath10k_pci_kill_tasklet(ar); ath10k_pci_buffer_cleanup(ar); @@ -1711,9 +1727,9 @@ static void ath10k_pci_hif_stop(struct ath10k *ar) spin_unlock_irqrestore(&ar_pci->ps_lock, flags); } -static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, - void *req, u32 req_len, - void *resp, u32 *resp_len) +int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, + void *req, u32 req_len, + void *resp, u32 *resp_len) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_pipe *pci_tx = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; @@ -1756,7 +1772,7 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, DMA_FROM_DEVICE); ret = dma_mapping_error(ar->dev, resp_paddr); if (ret) { - ret = EIO; + ret = -EIO; goto err_req; } @@ -1907,7 +1923,14 @@ static int ath10k_pci_get_num_banks(struct ath10k *ar) return 1; } -static int ath10k_pci_init_config(struct ath10k *ar) +static int ath10k_bus_get_num_banks(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + return ar_pci->bus_ops->get_num_banks(ar); +} + +int ath10k_pci_init_config(struct ath10k *ar) { u32 interconnect_targ_addr; u32 pcie_state_targ_addr = 0; @@ -2018,7 +2041,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) /* first bank is switched to IRAM */ ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) & HI_EARLY_ALLOC_MAGIC_MASK); - ealloc_value |= ((ath10k_pci_get_num_banks(ar) << + ealloc_value |= ((ath10k_bus_get_num_banks(ar) << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) & HI_EARLY_ALLOC_IRAM_BANKS_MASK); @@ -2071,7 +2094,7 @@ static void ath10k_pci_override_ce_config(struct ath10k *ar) target_service_to_ce_map_wlan[15].pipenum = __cpu_to_le32(1); } -static int ath10k_pci_alloc_pipes(struct ath10k *ar) +int ath10k_pci_alloc_pipes(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_pipe *pipe; @@ -2102,7 +2125,7 @@ static int ath10k_pci_alloc_pipes(struct ath10k *ar) return 0; } -static void ath10k_pci_free_pipes(struct ath10k *ar) +void ath10k_pci_free_pipes(struct ath10k *ar) { int i; @@ -2110,7 +2133,7 @@ static void ath10k_pci_free_pipes(struct ath10k *ar) ath10k_ce_free_pipe(ar, i); } -static int ath10k_pci_init_pipes(struct ath10k *ar) +int ath10k_pci_init_pipes(struct ath10k *ar) { int i, ret; @@ -2453,7 +2476,7 @@ err_sleep: return ret; } -static void ath10k_pci_hif_power_down(struct ath10k *ar) +void ath10k_pci_hif_power_down(struct ath10k *ar) { ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n"); @@ -2722,7 +2745,7 @@ static void ath10k_pci_free_irq(struct ath10k *ar) free_irq(ar_pci->pdev->irq + i, ar); } -static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) +void ath10k_pci_init_irq_tasklets(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int i; @@ -2808,7 +2831,7 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar) return 0; } -static int ath10k_pci_wait_for_target_init(struct ath10k *ar) +int ath10k_pci_wait_for_target_init(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); unsigned long timeout; @@ -2989,6 +3012,43 @@ static bool ath10k_pci_chip_is_supported(u32 dev_id, u32 chip_id) return false; } +int ath10k_pci_setup_resource(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_init(&ar_pci->ce_lock); + spin_lock_init(&ar_pci->ps_lock); + + setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, + (unsigned long)ar); + + if (QCA_REV_6174(ar)) + ath10k_pci_override_ce_config(ar); + + ret = ath10k_pci_alloc_pipes(ar); + if (ret) { + ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", + ret); + return ret; + } + + return 0; +} + +void ath10k_pci_release_resource(struct ath10k *ar) +{ + ath10k_pci_kill_tasklet(ar); + ath10k_pci_ce_deinit(ar); + ath10k_pci_free_pipes(ar); +} + +static const struct ath10k_bus_ops ath10k_pci_bus_ops = { + .read32 = ath10k_bus_pci_read32, + .write32 = ath10k_bus_pci_write32, + .get_num_banks = ath10k_pci_get_num_banks, +}; + static int ath10k_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_dev) { @@ -3039,40 +3099,32 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar_pci->ar = ar; ar->dev_id = pci_dev->device; ar_pci->pci_ps = pci_ps; + ar_pci->bus_ops = &ath10k_pci_bus_ops; ar->id.vendor = pdev->vendor; ar->id.device = pdev->device; ar->id.subsystem_vendor = pdev->subsystem_vendor; ar->id.subsystem_device = pdev->subsystem_device; - spin_lock_init(&ar_pci->ce_lock); - spin_lock_init(&ar_pci->ps_lock); - - setup_timer(&ar_pci->rx_post_retry, ath10k_pci_rx_replenish_retry, - (unsigned long)ar); setup_timer(&ar_pci->ps_timer, ath10k_pci_ps_timer, (unsigned long)ar); - ret = ath10k_pci_claim(ar); + ret = ath10k_pci_setup_resource(ar); if (ret) { - ath10k_err(ar, "failed to claim device: %d\n", ret); + ath10k_err(ar, "failed to setup resource: %d\n", ret); goto err_core_destroy; } - if (QCA_REV_6174(ar)) - ath10k_pci_override_ce_config(ar); - - ret = ath10k_pci_alloc_pipes(ar); + ret = ath10k_pci_claim(ar); if (ret) { - ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", - ret); - goto err_sleep; + ath10k_err(ar, "failed to claim device: %d\n", ret); + goto err_free_pipes; } ret = ath10k_pci_force_wake(ar); if (ret) { ath10k_warn(ar, "failed to wake up device : %d\n", ret); - goto err_free_pipes; + goto err_sleep; } ath10k_pci_ce_deinit(ar); @@ -3081,7 +3133,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ret = ath10k_pci_init_irq(ar); if (ret) { ath10k_err(ar, "failed to init irqs: %d\n", ret); - goto err_free_pipes; + goto err_sleep; } ath10k_info(ar, "pci irq %s interrupts %d irq_mode %d reset_mode %d\n", @@ -3127,13 +3179,13 @@ err_free_irq: err_deinit_irq: ath10k_pci_deinit_irq(ar); -err_free_pipes: - ath10k_pci_free_pipes(ar); - err_sleep: ath10k_pci_sleep_sync(ar); ath10k_pci_release(ar); +err_free_pipes: + ath10k_pci_free_pipes(ar); + err_core_destroy: ath10k_core_destroy(ar); @@ -3157,10 +3209,8 @@ static void ath10k_pci_remove(struct pci_dev *pdev) ath10k_core_unregister(ar); ath10k_pci_free_irq(ar); - ath10k_pci_kill_tasklet(ar); ath10k_pci_deinit_irq(ar); - ath10k_pci_ce_deinit(ar); - ath10k_pci_free_pipes(ar); + ath10k_pci_release_resource(ar); ath10k_pci_sleep_sync(ar); ath10k_pci_release(ar); ath10k_core_destroy(ar); @@ -3184,6 +3234,10 @@ static int __init ath10k_pci_init(void) printk(KERN_ERR "failed to register ath10k pci driver: %d\n", ret); + ret = ath10k_ahb_init(); + if (ret) + printk(KERN_ERR "ahb init failed: %d\n", ret); + return ret; } module_init(ath10k_pci_init); @@ -3191,6 +3245,7 @@ module_init(ath10k_pci_init); static void __exit ath10k_pci_exit(void) { pci_unregister_driver(&ath10k_pci_driver); + ath10k_ahb_exit(); } module_exit(ath10k_pci_exit); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index f91bf333c..249c73a69 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -22,6 +22,7 @@ #include "hw.h" #include "ce.h" +#include "ahb.h" /* * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite @@ -157,6 +158,12 @@ struct ath10k_pci_supp_chip { u32 rev_id; }; +struct ath10k_bus_ops { + u32 (*read32)(struct ath10k *ar, u32 offset); + void (*write32)(struct ath10k *ar, u32 offset, u32 value); + int (*get_num_banks)(struct ath10k *ar); +}; + struct ath10k_pci { struct pci_dev *pdev; struct device *dev; @@ -225,6 +232,14 @@ struct ath10k_pci { * on MMIO read/write. */ bool pci_ps; + + const struct ath10k_bus_ops *bus_ops; + + /* Keep this entry in the last, memory for struct ath10k_ahb is + * allocated (ahb support enabled case) in the continuation of + * this struct. + */ + struct ath10k_ahb ahb[0]; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) @@ -253,6 +268,40 @@ u32 ath10k_pci_read32(struct ath10k *ar, u32 offset); u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr); u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr); +int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id, + struct ath10k_hif_sg_item *items, int n_items); +int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf, + size_t buf_len); +int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, + const void *data, int nbytes); +int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, void *req, u32 req_len, + void *resp, u32 *resp_len); +int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe); +void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, u8 *ul_pipe, + u8 *dl_pipe); +void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, + int force); +u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe); +void ath10k_pci_hif_power_down(struct ath10k *ar); +int ath10k_pci_alloc_pipes(struct ath10k *ar); +void ath10k_pci_free_pipes(struct ath10k *ar); +void ath10k_pci_free_pipes(struct ath10k *ar); +void ath10k_pci_rx_replenish_retry(unsigned long ptr); +void ath10k_pci_ce_deinit(struct ath10k *ar); +void ath10k_pci_init_irq_tasklets(struct ath10k *ar); +void ath10k_pci_kill_tasklet(struct ath10k *ar); +int ath10k_pci_init_pipes(struct ath10k *ar); +int ath10k_pci_init_config(struct ath10k *ar); +void ath10k_pci_rx_post(struct ath10k *ar); +void ath10k_pci_flush(struct ath10k *ar); +void ath10k_pci_enable_legacy_irq(struct ath10k *ar); +bool ath10k_pci_irq_pending(struct ath10k *ar); +void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar); +int ath10k_pci_wait_for_target_init(struct ath10k *ar); +int ath10k_pci_setup_resource(struct ath10k *ar); +void ath10k_pci_release_resource(struct ath10k *ar); + /* QCA6174 is known to have Tx/Rx issues when SOC_WAKE register is poked too * frequently. To avoid this put SoC to sleep after a very conservative grace * period. Adjust with great care. diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h index 05a421bc3..361f143b0 100644 --- a/drivers/net/wireless/ath/ath10k/targaddrs.h +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -456,4 +456,7 @@ Fw Mode/SubMode Mask #define QCA99X0_BOARD_DATA_SZ 12288 #define QCA99X0_BOARD_EXT_DATA_SZ 0 +#define QCA4019_BOARD_DATA_SZ 12064 +#define QCA4019_BOARD_EXT_DATA_SZ 0 + #endif /* __TARGADDRS_H__ */ diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 71bdb3688..e0d00cef0 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -250,6 +250,7 @@ TRACE_EVENT(ath10k_wmi_dbglog, TP_STRUCT__entry( __string(device, dev_name(ar->dev)) __string(driver, dev_driver_string(ar->dev)) + __field(u8, hw_type); __field(size_t, buf_len) __dynamic_array(u8, buf, buf_len) ), @@ -257,14 +258,16 @@ TRACE_EVENT(ath10k_wmi_dbglog, TP_fast_assign( __assign_str(device, dev_name(ar->dev)); __assign_str(driver, dev_driver_string(ar->dev)); + __entry->hw_type = ar->hw_rev; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(buf), buf, buf_len); ), TP_printk( - "%s %s len %zu", + "%s %s %d len %zu", __get_str(driver), __get_str(device), + __entry->hw_type, __entry->buf_len ) ); @@ -277,6 +280,7 @@ TRACE_EVENT(ath10k_htt_pktlog, TP_STRUCT__entry( __string(device, dev_name(ar->dev)) __string(driver, dev_driver_string(ar->dev)) + __field(u8, hw_type); __field(u16, buf_len) __dynamic_array(u8, pktlog, buf_len) ), @@ -284,14 +288,16 @@ TRACE_EVENT(ath10k_htt_pktlog, TP_fast_assign( __assign_str(device, dev_name(ar->dev)); __assign_str(driver, dev_driver_string(ar->dev)); + __entry->hw_type = ar->hw_rev; __entry->buf_len = buf_len; memcpy(__get_dynamic_array(pktlog), buf, buf_len); ), TP_printk( - "%s %s size %hu", + "%s %s %d size %hu", __get_str(driver), __get_str(device), + __entry->hw_type, __entry->buf_len ) ); @@ -440,6 +446,7 @@ TRACE_EVENT(ath10k_htt_rx_desc, TP_STRUCT__entry( __string(device, dev_name(ar->dev)) __string(driver, dev_driver_string(ar->dev)) + __field(u8, hw_type); __field(u16, len) __dynamic_array(u8, rxdesc, len) ), @@ -447,14 +454,16 @@ TRACE_EVENT(ath10k_htt_rx_desc, TP_fast_assign( __assign_str(device, dev_name(ar->dev)); __assign_str(driver, dev_driver_string(ar->dev)); + __entry->hw_type = ar->hw_rev; __entry->len = len; memcpy(__get_dynamic_array(rxdesc), data, len); ), TP_printk( - "%s %s rxdesc len %d", + "%s %s %d rxdesc len %d", __get_str(driver), __get_str(device), + __entry->hw_type, __entry->len ) ); diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 8f4f6a892..32ab34edc 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -186,6 +186,8 @@ struct wmi_ops { u8 enable, u32 detect_level, u32 detect_margin); + int (*get_vdev_subtype)(struct ath10k *ar, + enum wmi_vdev_subtype subtype); }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -1327,4 +1329,13 @@ ath10k_wmi_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable, ar->wmi.cmd->pdev_enable_adaptive_cca_cmdid); } +static inline int +ath10k_wmi_get_vdev_subtype(struct ath10k *ar, enum wmi_vdev_subtype subtype) +{ + if (!ar->wmi.ops->get_vdev_subtype) + return -EOPNOTSUPP; + + return ar->wmi.ops->get_vdev_subtype(ar, subtype); +} + #endif diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 3b3a27b85..108593202 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3483,6 +3483,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_tdls_peer_update = ath10k_wmi_tlv_op_gen_tdls_peer_update, .gen_adaptive_qcs = ath10k_wmi_tlv_op_gen_adaptive_qcs, .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, + .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, }; static const struct wmi_peer_flags_map wmi_tlv_peer_flags_map = { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index a7c3d2996..70261387d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2862,11 +2862,20 @@ static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar, /* fw doesn't implement vdev stats */ for (i = 0; i < num_peer_stats; i++) { - const struct wmi_10_2_4_peer_stats *src; + const struct wmi_10_2_4_ext_peer_stats *src; struct ath10k_fw_stats_peer *dst; + int stats_len; + bool ext_peer_stats_support; + + ext_peer_stats_support = test_bit(WMI_SERVICE_PEER_STATS, + ar->wmi.svc_map); + if (ext_peer_stats_support) + stats_len = sizeof(struct wmi_10_2_4_ext_peer_stats); + else + stats_len = sizeof(struct wmi_10_2_4_peer_stats); src = (void *)skb->data; - if (!skb_pull(skb, sizeof(*src))) + if (!skb_pull(skb, stats_len)) return -EPROTO; dst = kzalloc(sizeof(*dst), GFP_ATOMIC); @@ -2876,6 +2885,9 @@ static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar, ath10k_wmi_pull_peer_stats(&src->common.old, dst); dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate); + + if (ext_peer_stats_support) + dst->rx_duration = __le32_to_cpu(src->rx_duration); /* FIXME: expose 10.2 specific values */ list_add_tail(&dst->list, &stats->peers); @@ -3184,7 +3196,7 @@ static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, struct sk_buff *bcn, const struct wmi_p2p_noa_info *noa) { - if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + if (!arvif->vif->p2p) return; ath10k_dbg(ar, ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed); @@ -3244,6 +3256,50 @@ static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, return 0; } +static int ath10k_wmi_10_2_4_op_pull_swba_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) +{ + struct wmi_10_2_4_host_swba_event *ev = (void *)skb->data; + u32 map; + size_t i; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->vdev_map = ev->vdev_map; + + for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) { + if (!(map & BIT(0))) + continue; + + /* If this happens there were some changes in firmware and + * ath10k should update the max size of tim_info array. + */ + if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info))) + break; + + if (__le32_to_cpu(ev->bcn_info[i].tim_info.tim_len) > + sizeof(ev->bcn_info[i].tim_info.tim_bitmap)) { + ath10k_warn(ar, "refusing to parse invalid swba structure\n"); + return -EPROTO; + } + + arg->tim_info[i].tim_len = ev->bcn_info[i].tim_info.tim_len; + arg->tim_info[i].tim_mcast = ev->bcn_info[i].tim_info.tim_mcast; + arg->tim_info[i].tim_bitmap = + ev->bcn_info[i].tim_info.tim_bitmap; + arg->tim_info[i].tim_changed = + ev->bcn_info[i].tim_info.tim_changed; + arg->tim_info[i].tim_num_ps_pending = + ev->bcn_info[i].tim_info.tim_num_ps_pending; + i++; + } + + return 0; +} + static int ath10k_wmi_10_4_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_swba_ev_arg *arg) @@ -4562,9 +4618,9 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) if (test_bit(WMI_SERVICE_PEER_CACHING, ar->wmi.svc_map)) { ar->max_num_peers = TARGET_10_4_NUM_QCACHE_PEERS_MAX + - TARGET_10_4_NUM_VDEVS; - ar->num_active_peers = TARGET_10_4_QCACHE_ACTIVE_PEERS + - TARGET_10_4_NUM_VDEVS; + ar->max_num_vdevs; + ar->num_active_peers = ar->hw_params.qcache_active_peers + + ar->max_num_vdevs; ar->num_tids = ar->num_active_peers * 2; ar->max_num_stations = TARGET_10_4_NUM_QCACHE_PEERS_MAX; } @@ -5460,9 +5516,15 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) u32 len, val, features; config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); - config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS); - config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS); + if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) { + config.num_peers = __cpu_to_le32(TARGET_10X_TX_STATS_NUM_PEERS); + config.num_tids = __cpu_to_le32(TARGET_10X_TX_STATS_NUM_TIDS); + } else { + config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); + config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS); + } + config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT); config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK); config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK); @@ -5517,6 +5579,9 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map)) features |= WMI_10_2_COEX_GPIO; + if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) + features |= WMI_10_2_PEER_STATS; + cmd->resource_config.feature_mask = __cpu_to_le32(features); memcpy(&cmd->resource_config.common, &config, sizeof(config)); @@ -5543,8 +5608,8 @@ static struct sk_buff *ath10k_wmi_10_4_op_gen_init(struct ath10k *ar) __cpu_to_le32(TARGET_10_4_NUM_OFFLOAD_REORDER_BUFFS); config.num_peer_keys = __cpu_to_le32(TARGET_10_4_NUM_PEER_KEYS); config.ast_skid_limit = __cpu_to_le32(TARGET_10_4_AST_SKID_LIMIT); - config.tx_chain_mask = __cpu_to_le32(TARGET_10_4_TX_CHAIN_MASK); - config.rx_chain_mask = __cpu_to_le32(TARGET_10_4_RX_CHAIN_MASK); + config.tx_chain_mask = __cpu_to_le32(ar->hw_params.tx_chain_mask); + config.rx_chain_mask = __cpu_to_le32(ar->hw_params.rx_chain_mask); config.rx_timeout_pri[0] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_LO_PRI); config.rx_timeout_pri[1] = __cpu_to_le32(TARGET_10_4_RX_TIMEOUT_LO_PRI); @@ -5575,7 +5640,7 @@ static struct sk_buff *ath10k_wmi_10_4_op_gen_init(struct ath10k *ar) config.vow_config = __cpu_to_le32(TARGET_10_4_VOW_CONFIG); config.gtk_offload_max_vdev = __cpu_to_le32(TARGET_10_4_GTK_OFFLOAD_MAX_VDEV); - config.num_msdu_desc = __cpu_to_le32(TARGET_10_4_NUM_MSDU_DESC); + config.num_msdu_desc = __cpu_to_le32(ar->htt.max_num_pending_tx); config.max_frag_entries = __cpu_to_le32(TARGET_10_4_11AC_TX_MAX_FRAGS); config.max_peer_ext_stats = __cpu_to_le32(TARGET_10_4_MAX_PEER_EXT_STATS); @@ -7126,6 +7191,9 @@ ath10k_wmi_fw_peer_stats_fill(const struct ath10k_fw_stats_peer *peer, "Peer TX rate", peer->peer_tx_rate); len += scnprintf(buf + len, buf_len - len, "%30s %u\n", "Peer RX rate", peer->peer_rx_rate); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer RX duration", peer->rx_duration); + len += scnprintf(buf + len, buf_len - len, "\n"); *length = len; } @@ -7351,6 +7419,71 @@ unlock: buf[len] = 0; } +int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar, + enum wmi_vdev_subtype subtype) +{ + switch (subtype) { + case WMI_VDEV_SUBTYPE_NONE: + return WMI_VDEV_SUBTYPE_LEGACY_NONE; + case WMI_VDEV_SUBTYPE_P2P_DEVICE: + return WMI_VDEV_SUBTYPE_LEGACY_P2P_DEV; + case WMI_VDEV_SUBTYPE_P2P_CLIENT: + return WMI_VDEV_SUBTYPE_LEGACY_P2P_CLI; + case WMI_VDEV_SUBTYPE_P2P_GO: + return WMI_VDEV_SUBTYPE_LEGACY_P2P_GO; + case WMI_VDEV_SUBTYPE_PROXY_STA: + return WMI_VDEV_SUBTYPE_LEGACY_PROXY_STA; + case WMI_VDEV_SUBTYPE_MESH_11S: + case WMI_VDEV_SUBTYPE_MESH_NON_11S: + return -ENOTSUPP; + } + return -ENOTSUPP; +} + +static int ath10k_wmi_10_2_4_op_get_vdev_subtype(struct ath10k *ar, + enum wmi_vdev_subtype subtype) +{ + switch (subtype) { + case WMI_VDEV_SUBTYPE_NONE: + return WMI_VDEV_SUBTYPE_10_2_4_NONE; + case WMI_VDEV_SUBTYPE_P2P_DEVICE: + return WMI_VDEV_SUBTYPE_10_2_4_P2P_DEV; + case WMI_VDEV_SUBTYPE_P2P_CLIENT: + return WMI_VDEV_SUBTYPE_10_2_4_P2P_CLI; + case WMI_VDEV_SUBTYPE_P2P_GO: + return WMI_VDEV_SUBTYPE_10_2_4_P2P_GO; + case WMI_VDEV_SUBTYPE_PROXY_STA: + return WMI_VDEV_SUBTYPE_10_2_4_PROXY_STA; + case WMI_VDEV_SUBTYPE_MESH_11S: + return WMI_VDEV_SUBTYPE_10_2_4_MESH_11S; + case WMI_VDEV_SUBTYPE_MESH_NON_11S: + return -ENOTSUPP; + } + return -ENOTSUPP; +} + +static int ath10k_wmi_10_4_op_get_vdev_subtype(struct ath10k *ar, + enum wmi_vdev_subtype subtype) +{ + switch (subtype) { + case WMI_VDEV_SUBTYPE_NONE: + return WMI_VDEV_SUBTYPE_10_4_NONE; + case WMI_VDEV_SUBTYPE_P2P_DEVICE: + return WMI_VDEV_SUBTYPE_10_4_P2P_DEV; + case WMI_VDEV_SUBTYPE_P2P_CLIENT: + return WMI_VDEV_SUBTYPE_10_4_P2P_CLI; + case WMI_VDEV_SUBTYPE_P2P_GO: + return WMI_VDEV_SUBTYPE_10_4_P2P_GO; + case WMI_VDEV_SUBTYPE_PROXY_STA: + return WMI_VDEV_SUBTYPE_10_4_PROXY_STA; + case WMI_VDEV_SUBTYPE_MESH_11S: + return WMI_VDEV_SUBTYPE_10_4_MESH_11S; + case WMI_VDEV_SUBTYPE_MESH_NON_11S: + return WMI_VDEV_SUBTYPE_10_4_MESH_NON_11S; + } + return -ENOTSUPP; +} + static const struct wmi_ops wmi_ops = { .rx = ath10k_wmi_op_rx, .map_svc = wmi_main_svc_map, @@ -7410,6 +7543,7 @@ static const struct wmi_ops wmi_ops = { .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, .fw_stats_fill = ath10k_wmi_main_op_fw_stats_fill, + .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -7477,6 +7611,7 @@ static const struct wmi_ops wmi_10_1_ops = { .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, + .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -7545,6 +7680,7 @@ static const struct wmi_ops wmi_10_2_ops = { .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, .gen_delba_send = ath10k_wmi_op_gen_delba_send, .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, + .get_vdev_subtype = ath10k_wmi_op_get_vdev_subtype, /* .gen_pdev_enable_adaptive_cca not implemented */ }; @@ -7566,7 +7702,7 @@ static const struct wmi_ops wmi_10_2_4_ops = { .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, - .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_swba = ath10k_wmi_10_2_4_op_pull_swba_ev, .pull_phyerr_hdr = ath10k_wmi_op_pull_phyerr_ev_hdr, .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, .pull_rdy = ath10k_wmi_op_pull_rdy_ev, @@ -7611,6 +7747,7 @@ static const struct wmi_ops wmi_10_2_4_ops = { .fw_stats_fill = ath10k_wmi_10x_op_fw_stats_fill, .gen_pdev_enable_adaptive_cca = ath10k_wmi_op_gen_pdev_enable_adaptive_cca, + .get_vdev_subtype = ath10k_wmi_10_2_4_op_get_vdev_subtype, /* .gen_bcn_tmpl not implemented */ /* .gen_prb_tmpl not implemented */ /* .gen_p2p_go_bcn_ie not implemented */ @@ -7677,6 +7814,7 @@ static const struct wmi_ops wmi_10_4_ops = { /* shared with 10.2 */ .gen_request_stats = ath10k_wmi_op_gen_request_stats, .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature, + .get_vdev_subtype = ath10k_wmi_10_4_op_get_vdev_subtype, }; int ath10k_wmi_attach(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index d85ad7855..4d3cbc44f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -176,7 +176,10 @@ enum wmi_service { WMI_SERVICE_AUX_CHAN_LOAD_INTF, WMI_SERVICE_BSS_CHANNEL_INFO_64, WMI_SERVICE_EXT_RES_CFG_SUPPORT, - WMI_SERVICE_MESH, + WMI_SERVICE_MESH_11S, + WMI_SERVICE_MESH_NON_11S, + WMI_SERVICE_PEER_STATS, + WMI_SERVICE_RESTRT_CHNL_SUPPORT, /* keep last */ WMI_SERVICE_MAX, @@ -213,6 +216,7 @@ enum wmi_10x_service { WMI_10X_SERVICE_BSS_CHANNEL_INFO_64, WMI_10X_SERVICE_MESH, WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT, + WMI_10X_SERVICE_PEER_STATS, }; enum wmi_main_service { @@ -294,7 +298,10 @@ enum wmi_10_4_service { WMI_10_4_SERVICE_AUX_CHAN_LOAD_INTF, WMI_10_4_SERVICE_BSS_CHANNEL_INFO_64, WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT, - WMI_10_4_SERVICE_MESH, + WMI_10_4_SERVICE_MESH_NON_11S, + WMI_10_4_SERVICE_RESTRT_CHNL_SUPPORT, + WMI_10_4_SERVICE_PEER_STATS, + WMI_10_4_SERVICE_MESH_11S, }; static inline char *wmi_service_name(int service_id) @@ -385,7 +392,10 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_AUX_CHAN_LOAD_INTF); SVCSTR(WMI_SERVICE_BSS_CHANNEL_INFO_64); SVCSTR(WMI_SERVICE_EXT_RES_CFG_SUPPORT); - SVCSTR(WMI_SERVICE_MESH); + SVCSTR(WMI_SERVICE_MESH_11S); + SVCSTR(WMI_SERVICE_MESH_NON_11S); + SVCSTR(WMI_SERVICE_PEER_STATS); + SVCSTR(WMI_SERVICE_RESTRT_CHNL_SUPPORT); default: return NULL; } @@ -460,9 +470,11 @@ static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out, SVCMAP(WMI_10X_SERVICE_BSS_CHANNEL_INFO_64, WMI_SERVICE_BSS_CHANNEL_INFO_64, len); SVCMAP(WMI_10X_SERVICE_MESH, - WMI_SERVICE_MESH, len); + WMI_SERVICE_MESH_11S, len); SVCMAP(WMI_10X_SERVICE_EXT_RES_CFG_SUPPORT, WMI_SERVICE_EXT_RES_CFG_SUPPORT, len); + SVCMAP(WMI_10X_SERVICE_PEER_STATS, + WMI_SERVICE_PEER_STATS, len); } static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out, @@ -623,8 +635,14 @@ static inline void wmi_10_4_svc_map(const __le32 *in, unsigned long *out, WMI_SERVICE_BSS_CHANNEL_INFO_64, len); SVCMAP(WMI_10_4_SERVICE_EXT_RES_CFG_SUPPORT, WMI_SERVICE_EXT_RES_CFG_SUPPORT, len); - SVCMAP(WMI_10_4_SERVICE_MESH, - WMI_SERVICE_MESH, len); + SVCMAP(WMI_10_4_SERVICE_MESH_NON_11S, + WMI_SERVICE_MESH_NON_11S, len); + SVCMAP(WMI_10_4_SERVICE_RESTRT_CHNL_SUPPORT, + WMI_SERVICE_RESTRT_CHNL_SUPPORT, len); + SVCMAP(WMI_10_4_SERVICE_PEER_STATS, + WMI_SERVICE_PEER_STATS, len); + SVCMAP(WMI_10_4_SERVICE_MESH_11S, + WMI_SERVICE_MESH_11S, len); } #undef SVCMAP @@ -1800,7 +1818,6 @@ enum wmi_channel_change_cause { #define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13) #define WMI_MAX_SPATIAL_STREAM 3 /* default max ss */ -#define WMI_10_4_MAX_SPATIAL_STREAM 4 /* HT Capabilities*/ #define WMI_HT_CAP_ENABLED 0x0001 /* HT Enabled/ disabled */ @@ -2417,6 +2434,7 @@ enum wmi_10_2_feature_mask { WMI_10_2_RX_BATCH_MODE = BIT(0), WMI_10_2_ATF_CONFIG = BIT(1), WMI_10_2_COEX_GPIO = BIT(3), + WMI_10_2_PEER_STATS = BIT(7), }; struct wmi_resource_config_10_2 { @@ -4227,7 +4245,13 @@ struct wmi_10_2_peer_stats { struct wmi_10_2_4_peer_stats { struct wmi_10_2_peer_stats common; - __le32 unknown_value; /* FIXME: what is this word? */ + __le32 peer_rssi_changed; +} __packed; + +struct wmi_10_2_4_ext_peer_stats { + struct wmi_10_2_peer_stats common; + __le32 peer_rssi_changed; + __le32 rx_duration; } __packed; struct wmi_10_4_peer_stats { @@ -4270,12 +4294,40 @@ enum wmi_vdev_type { }; enum wmi_vdev_subtype { - WMI_VDEV_SUBTYPE_NONE = 0, - WMI_VDEV_SUBTYPE_P2P_DEVICE = 1, - WMI_VDEV_SUBTYPE_P2P_CLIENT = 2, - WMI_VDEV_SUBTYPE_P2P_GO = 3, - WMI_VDEV_SUBTYPE_PROXY_STA = 4, - WMI_VDEV_SUBTYPE_MESH = 5, + WMI_VDEV_SUBTYPE_NONE, + WMI_VDEV_SUBTYPE_P2P_DEVICE, + WMI_VDEV_SUBTYPE_P2P_CLIENT, + WMI_VDEV_SUBTYPE_P2P_GO, + WMI_VDEV_SUBTYPE_PROXY_STA, + WMI_VDEV_SUBTYPE_MESH_11S, + WMI_VDEV_SUBTYPE_MESH_NON_11S, +}; + +enum wmi_vdev_subtype_legacy { + WMI_VDEV_SUBTYPE_LEGACY_NONE = 0, + WMI_VDEV_SUBTYPE_LEGACY_P2P_DEV = 1, + WMI_VDEV_SUBTYPE_LEGACY_P2P_CLI = 2, + WMI_VDEV_SUBTYPE_LEGACY_P2P_GO = 3, + WMI_VDEV_SUBTYPE_LEGACY_PROXY_STA = 4, +}; + +enum wmi_vdev_subtype_10_2_4 { + WMI_VDEV_SUBTYPE_10_2_4_NONE = 0, + WMI_VDEV_SUBTYPE_10_2_4_P2P_DEV = 1, + WMI_VDEV_SUBTYPE_10_2_4_P2P_CLI = 2, + WMI_VDEV_SUBTYPE_10_2_4_P2P_GO = 3, + WMI_VDEV_SUBTYPE_10_2_4_PROXY_STA = 4, + WMI_VDEV_SUBTYPE_10_2_4_MESH_11S = 5, +}; + +enum wmi_vdev_subtype_10_4 { + WMI_VDEV_SUBTYPE_10_4_NONE = 0, + WMI_VDEV_SUBTYPE_10_4_P2P_DEV = 1, + WMI_VDEV_SUBTYPE_10_4_P2P_CLI = 2, + WMI_VDEV_SUBTYPE_10_4_P2P_GO = 3, + WMI_VDEV_SUBTYPE_10_4_PROXY_STA = 4, + WMI_VDEV_SUBTYPE_10_4_MESH_NON_11S = 5, + WMI_VDEV_SUBTYPE_10_4_MESH_11S = 6, }; /* values for vdev_subtype */ @@ -5442,6 +5494,16 @@ struct wmi_host_swba_event { struct wmi_bcn_info bcn_info[0]; } __packed; +struct wmi_10_2_4_bcn_info { + struct wmi_tim_info tim_info; + /* The 10.2.4 FW doesn't have p2p NOA info */ +} __packed; + +struct wmi_10_2_4_host_swba_event { + __le32 vdev_map; + struct wmi_10_2_4_bcn_info bcn_info[0]; +} __packed; + /* 16 words = 512 client + 1 word = for guard */ #define WMI_10_4_TIM_BITMAP_ARRAY_SIZE 17 @@ -6436,5 +6498,7 @@ size_t ath10k_wmi_fw_stats_num_vdevs(struct list_head *head); void ath10k_wmi_10_4_op_fw_stats_fill(struct ath10k *ar, struct ath10k_fw_stats *fw_stats, char *buf); +int ath10k_wmi_op_get_vdev_subtype(struct ath10k *ar, + enum wmi_vdev_subtype subtype); #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index 803030fd1..6a2a16856 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -77,7 +77,7 @@ static const struct pci_device_id ath5k_led_devices[] = { /* HP Compaq CQ60-206US (ddreggors@jumptv.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) }, /* HP Compaq C700 (nitrousnrg@gmail.com) */ - { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, + { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 0) }, /* LiteOn AR5BXB63 (magooz@salug.it) */ { ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) }, /* IBM-specific AR5212 (all others) */ diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 25e45e4d1..815efe9fd 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -126,12 +126,8 @@ static void ath9k_hw_update_mibstats(struct ath_hw *ah, static void ath9k_ani_restart(struct ath_hw *ah) { - struct ar5416AniState *aniState; - - if (!ah->curchan) - return; + struct ar5416AniState *aniState = &ah->ani; - aniState = &ah->ani; aniState->listenTime = 0; ENABLE_REGWRITE_BUFFER(ah); @@ -221,12 +217,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) { - struct ar5416AniState *aniState; - - if (!ah->curchan) - return; - - aniState = &ah->ani; + struct ar5416AniState *aniState = &ah->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false); @@ -281,12 +272,7 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) { - struct ar5416AniState *aniState; - - if (!ah->curchan) - return; - - aniState = &ah->ani; + struct ar5416AniState *aniState = &ah->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1, @@ -299,9 +285,7 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) */ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) { - struct ar5416AniState *aniState; - - aniState = &ah->ani; + struct ar5416AniState *aniState = &ah->ani; /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && @@ -329,7 +313,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) struct ath_common *common = ath9k_hw_common(ah); int ofdm_nil, cck_nil; - if (!ah->curchan) + if (!chan) return; BUG_ON(aniState == NULL); @@ -416,14 +400,10 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) { - struct ar5416AniState *aniState; + struct ar5416AniState *aniState = &ah->ani; struct ath_common *common = ath9k_hw_common(ah); u32 ofdmPhyErrRate, cckPhyErrRate; - if (!ah->curchan) - return; - - aniState = &ah->ani; if (!ath9k_hw_ani_read_counters(ah)) return; @@ -450,7 +430,9 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) } else if (cckPhyErrRate > ah->config.cck_trig_high) { ath9k_hw_ani_cck_err_trigger(ah); aniState->ofdmsTurn = true; - } + } else + return; + ath9k_ani_restart(ah); } } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.c b/drivers/net/wireless/ath/ath9k/ar9003_aic.c index 1db119d77..547cd46da 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_aic.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.c @@ -53,19 +53,19 @@ static bool ar9003_hw_is_aic_enabled(struct ath_hw *ah) return true; } -static int16_t ar9003_aic_find_valid(struct ath_aic_sram_info *cal_sram, +static int16_t ar9003_aic_find_valid(bool *cal_sram_valid, bool dir, u8 index) { int16_t i; if (dir) { for (i = index + 1; i < ATH_AIC_MAX_BT_CHANNEL; i++) { - if (cal_sram[i].valid) + if (cal_sram_valid[i]) break; } } else { for (i = index - 1; i >= 0; i--) { - if (cal_sram[i].valid) + if (cal_sram_valid[i]) break; } } @@ -264,7 +264,7 @@ static u8 ar9003_aic_cal_start(struct ath_hw *ah, u8 min_valid_count) static bool ar9003_aic_cal_post_process(struct ath_hw *ah) { struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic; - struct ath_aic_sram_info cal_sram[ATH_AIC_MAX_BT_CHANNEL]; + bool cal_sram_valid[ATH_AIC_MAX_BT_CHANNEL]; struct ath_aic_out_info aic_sram[ATH_AIC_MAX_BT_CHANNEL]; u32 dir_path_gain_idx, quad_path_gain_idx, value; u32 fixed_com_att_db; @@ -272,33 +272,34 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) int16_t i; bool ret = true; - memset(&cal_sram, 0, sizeof(cal_sram)); + memset(&cal_sram_valid, 0, sizeof(cal_sram_valid)); memset(&aic_sram, 0, sizeof(aic_sram)); for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { + struct ath_aic_sram_info sram; value = aic->aic_sram[i]; - cal_sram[i].valid = + cal_sram_valid[i] = sram.valid = MS(value, AR_PHY_AIC_SRAM_VALID); - cal_sram[i].rot_quad_att_db = + sram.rot_quad_att_db = MS(value, AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB); - cal_sram[i].vga_quad_sign = + sram.vga_quad_sign = MS(value, AR_PHY_AIC_SRAM_VGA_QUAD_SIGN); - cal_sram[i].rot_dir_att_db = + sram.rot_dir_att_db = MS(value, AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB); - cal_sram[i].vga_dir_sign = + sram.vga_dir_sign = MS(value, AR_PHY_AIC_SRAM_VGA_DIR_SIGN); - cal_sram[i].com_att_6db = + sram.com_att_6db = MS(value, AR_PHY_AIC_SRAM_COM_ATT_6DB); - if (cal_sram[i].valid) { - dir_path_gain_idx = cal_sram[i].rot_dir_att_db + - com_att_db_table[cal_sram[i].com_att_6db]; - quad_path_gain_idx = cal_sram[i].rot_quad_att_db + - com_att_db_table[cal_sram[i].com_att_6db]; + if (sram.valid) { + dir_path_gain_idx = sram.rot_dir_att_db + + com_att_db_table[sram.com_att_6db]; + quad_path_gain_idx = sram.rot_quad_att_db + + com_att_db_table[sram.com_att_6db]; - dir_path_sign = (cal_sram[i].vga_dir_sign) ? 1 : -1; - quad_path_sign = (cal_sram[i].vga_quad_sign) ? 1 : -1; + dir_path_sign = (sram.vga_dir_sign) ? 1 : -1; + quad_path_sign = (sram.vga_quad_sign) ? 1 : -1; aic_sram[i].dir_path_gain_lin = dir_path_sign * aic_lin_table[dir_path_gain_idx]; @@ -310,16 +311,16 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { int16_t start_idx, end_idx; - if (cal_sram[i].valid) + if (cal_sram_valid[i]) continue; - start_idx = ar9003_aic_find_valid(cal_sram, 0, i); - end_idx = ar9003_aic_find_valid(cal_sram, 1, i); + start_idx = ar9003_aic_find_valid(cal_sram_valid, 0, i); + end_idx = ar9003_aic_find_valid(cal_sram_valid, 1, i); if (start_idx < 0) { /* extrapolation */ start_idx = end_idx; - end_idx = ar9003_aic_find_valid(cal_sram, 1, start_idx); + end_idx = ar9003_aic_find_valid(cal_sram_valid, 1, start_idx); if (end_idx < 0) { ret = false; @@ -342,7 +343,7 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) if (end_idx < 0) { /* extrapolation */ - end_idx = ar9003_aic_find_valid(cal_sram, 0, start_idx); + end_idx = ar9003_aic_find_valid(cal_sram_valid, 0, start_idx); if (end_idx < 0) { ret = false; @@ -378,19 +379,21 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) } /* From dir/quad_path_gain_lin to sram. */ - i = ar9003_aic_find_valid(cal_sram, 1, 0); + i = ar9003_aic_find_valid(cal_sram_valid, 1, 0); if (i < 0) { i = 0; ret = false; } - fixed_com_att_db = com_att_db_table[cal_sram[i].com_att_6db]; + fixed_com_att_db = com_att_db_table[MS(aic->aic_sram[i], + AR_PHY_AIC_SRAM_COM_ATT_6DB)]; for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { int16_t rot_dir_path_att_db, rot_quad_path_att_db; + struct ath_aic_sram_info sram; - aic_sram[i].sram.vga_dir_sign = + sram.vga_dir_sign = (aic_sram[i].dir_path_gain_lin >= 0) ? 1 : 0; - aic_sram[i].sram.vga_quad_sign= + sram.vga_quad_sign = (aic_sram[i].quad_path_gain_lin >= 0) ? 1 : 0; rot_dir_path_att_db = @@ -400,33 +403,31 @@ static bool ar9003_aic_cal_post_process(struct ath_hw *ah) ar9003_aic_find_index(0, abs(aic_sram[i].quad_path_gain_lin)) - fixed_com_att_db; - aic_sram[i].sram.com_att_6db = + sram.com_att_6db = ar9003_aic_find_index(1, fixed_com_att_db); - aic_sram[i].sram.valid = 1; + sram.valid = 1; - aic_sram[i].sram.rot_dir_att_db = + sram.rot_dir_att_db = min(max(rot_dir_path_att_db, (int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB), ATH_AIC_MAX_ROT_DIR_ATT_DB); - aic_sram[i].sram.rot_quad_att_db = + sram.rot_quad_att_db = min(max(rot_quad_path_att_db, (int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB), ATH_AIC_MAX_ROT_QUAD_ATT_DB); - } - for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) { - aic->aic_sram[i] = (SM(aic_sram[i].sram.vga_dir_sign, + aic->aic_sram[i] = (SM(sram.vga_dir_sign, AR_PHY_AIC_SRAM_VGA_DIR_SIGN) | - SM(aic_sram[i].sram.vga_quad_sign, + SM(sram.vga_quad_sign, AR_PHY_AIC_SRAM_VGA_QUAD_SIGN) | - SM(aic_sram[i].sram.com_att_6db, + SM(sram.com_att_6db, AR_PHY_AIC_SRAM_COM_ATT_6DB) | - SM(aic_sram[i].sram.valid, + SM(sram.valid, AR_PHY_AIC_SRAM_VALID) | - SM(aic_sram[i].sram.rot_dir_att_db, + SM(sram.rot_dir_att_db, AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB) | - SM(aic_sram[i].sram.rot_quad_att_db, + SM(sram.rot_quad_att_db, AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB)); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_aic.h b/drivers/net/wireless/ath/ath9k/ar9003_aic.h index 86f40644b..9512c6379 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_aic.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_aic.h @@ -50,7 +50,6 @@ struct ath_aic_sram_info { struct ath_aic_out_info { int16_t dir_path_gain_lin; int16_t quad_path_gain_lin; - struct ath_aic_sram_info sram; }; u8 ar9003_aic_calibration(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 8b4561e8c..54ed2f72d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -5485,11 +5485,11 @@ unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah, AR9300_PAPRD_SCALE_1); else { if (chan->channel >= 5700) - return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20), - AR9300_PAPRD_SCALE_1); + return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt20), + AR9300_PAPRD_SCALE_1); else if (chan->channel >= 5400) return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40), - AR9300_PAPRD_SCALE_2); + AR9300_PAPRD_SCALE_2); else return MS(le32_to_cpu(eep->modalHeader5G.papdRateMaskHt40), AR9300_PAPRD_SCALE_1); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 8b238c159..2fe12b0de 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -698,6 +698,9 @@ static void ar9003_tx_gain_table_mode2(struct ath_hw *ah) else if (AR_SREV_9340(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9340Modes_low_ob_db_tx_gain_table_1p0); + else if (AR_SREV_9531_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca953x_1p1_modes_no_xpa_low_power_tx_gain_table); else if (AR_SREV_9485_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485Modes_low_ob_db_tx_gain_1_1); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 201425e7f..06c1ca6e8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -976,9 +976,14 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, /* * JAPAN regulatory. */ - if (chan->channel == 2484) + if (chan->channel == 2484) { ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1); + if (AR_SREV_9531(ah)) + REG_RMW_FIELD(ah, AR_PHY_FCAL_2_0, + AR_PHY_FLC_PWR_THRESH, 0); + } + ah->modes_index = modesIndex; ar9003_hw_override_ini(ah); ar9003_hw_set_channel_regs(ah, chan); @@ -2071,7 +2076,8 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) * to be disabled. * * 0x04000409: Packet stuck on receive. - * Full chip reset is required for all chips except AR9340. + * Full chip reset is required for all chips except + * AR9340, AR9531 and AR9561. */ /* @@ -2100,7 +2106,7 @@ bool ar9003_hw_bb_watchdog_check(struct ath_hw *ah) case 0x04000b09: return true; case 0x04000409: - if (AR_SREV_9340(ah) || AR_SREV_9531(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) return false; else return true; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index c5f8bc4b5..566da789f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -487,6 +487,9 @@ #define AR_PHY_ADDAC_PARA_CTL (AR_SM_BASE + 0x150) #define AR_PHY_XPA_CFG (AR_SM_BASE + 0x158) +#define AR_PHY_FLC_PWR_THRESH 7 +#define AR_PHY_FLC_PWR_THRESH_S 0 + #define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW 3 #define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW_S 0 diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h index 6fc0d07e5..c0b90daa3 100644 --- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h @@ -757,6 +757,71 @@ static const u32 qca953x_1p1_modes_xpa_tx_gain_table[][2] = { {0x00016448, 0x6c927a70}, }; +static const u32 qca953x_1p1_modes_no_xpa_low_power_tx_gain_table[][2] = { + /* Addr allmodes */ + {0x0000a2dc, 0xfff55592}, + {0x0000a2e0, 0xfff99924}, + {0x0000a2e4, 0xfffe1e00}, + {0x0000a2e8, 0xffffe000}, + {0x0000a410, 0x000050d6}, + {0x0000a500, 0x00000069}, + {0x0000a504, 0x0400006b}, + {0x0000a508, 0x0800006d}, + {0x0000a50c, 0x0c000269}, + {0x0000a510, 0x1000026b}, + {0x0000a514, 0x1400026d}, + {0x0000a518, 0x18000669}, + {0x0000a51c, 0x1c00066b}, + {0x0000a520, 0x1d000a68}, + {0x0000a524, 0x21000a6a}, + {0x0000a528, 0x25000a6c}, + {0x0000a52c, 0x29000a6e}, + {0x0000a530, 0x2d0012a9}, + {0x0000a534, 0x310012ab}, + {0x0000a538, 0x350012ad}, + {0x0000a53c, 0x39001b0a}, + {0x0000a540, 0x3d001b0c}, + {0x0000a544, 0x41001b0e}, + {0x0000a548, 0x43001bae}, + {0x0000a54c, 0x45001914}, + {0x0000a550, 0x47001916}, + {0x0000a554, 0x49001b96}, + {0x0000a558, 0x49001b96}, + {0x0000a55c, 0x49001b96}, + {0x0000a560, 0x49001b96}, + {0x0000a564, 0x49001b96}, + {0x0000a568, 0x49001b96}, + {0x0000a56c, 0x49001b96}, + {0x0000a570, 0x49001b96}, + {0x0000a574, 0x49001b96}, + {0x0000a578, 0x49001b96}, + {0x0000a57c, 0x49001b96}, + {0x0000a600, 0x00000000}, + {0x0000a604, 0x00000000}, + {0x0000a608, 0x00000000}, + {0x0000a60c, 0x00000000}, + {0x0000a610, 0x00000000}, + {0x0000a614, 0x00000000}, + {0x0000a618, 0x00804201}, + {0x0000a61c, 0x01408201}, + {0x0000a620, 0x01408502}, + {0x0000a624, 0x01408502}, + {0x0000a628, 0x01408502}, + {0x0000a62c, 0x01408502}, + {0x0000a630, 0x01408502}, + {0x0000a634, 0x01408502}, + {0x0000a638, 0x01408502}, + {0x0000a63c, 0x01408502}, + {0x0000b2dc, 0xfff55592}, + {0x0000b2e0, 0xfff99924}, + {0x0000b2e4, 0xfffe1e00}, + {0x0000b2e8, 0xffffe000}, + {0x00016044, 0x044922db}, + {0x00016048, 0x6c927a70}, + {0x00016444, 0x044922db}, + {0x00016448, 0x6c927a70}, +}; + static const u32 qca953x_2p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 3e2e24e48..37f6d66d1 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -241,6 +241,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); s16 default_nf = ath9k_hw_get_default_nf(ah, chan); + u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL); if (ah->caldata) h = ah->caldata->nfCalHist; @@ -264,6 +265,16 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) } /* + * stop NF cal if ongoing to ensure NF load completes immediately + * (or after end rx/tx frame if ongoing) + */ + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + REG_RMW_BUFFER_FLUSH(ah); + ENABLE_REG_RMW_BUFFER(ah); + } + + /* * Load software filtered NF value into baseband internal minCCApwr * variable. */ @@ -276,18 +287,33 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) /* * Wait for load to complete, should be fast, a few 10s of us. - * The max delay was changed from an original 250us to 10000us - * since 250us often results in NF load timeout and causes deaf - * condition during stress testing 12/12/2009 + * The max delay was changed from an original 250us to 22.2 msec. + * This would increase timeout to the longest possible frame + * (11n max length 22.1 msec) */ - for (j = 0; j < 10000; j++) { + for (j = 0; j < 22200; j++) { if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & - AR_PHY_AGC_CONTROL_NF) == 0) + AR_PHY_AGC_CONTROL_NF) == 0) break; udelay(10); } /* + * Restart NF so it can continue. + */ + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) { + ENABLE_REG_RMW_BUFFER(ah); + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF) + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_ENABLE_NF); + if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF) + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + REG_RMW_BUFFER_FLUSH(ah); + } + + /* * We timed out waiting for the noisefloor to load, probably due to an * in-progress rx. Simply return here and allow the load plenty of time * to complete before the next calibration interval. We need to avoid @@ -296,7 +322,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) * here, the baseband nf cal will just be capped by our present * noisefloor until the next calibration timer. */ - if (j == 10000) { + if (j == 22200) { ath_dbg(common, ANY, "Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n", REG_READ(ah, AR_PHY_AGC_CONTROL)); diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 50e614b91..319cb5f25 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -226,7 +226,7 @@ static const char *chanctx_state_string(enum ath_chanctx_state state) } } -static const u32 chanctx_event_delta(struct ath_softc *sc) +static u32 chanctx_event_delta(struct ath_softc *sc) { u64 ms; struct timespec ts, *old; @@ -1454,7 +1454,7 @@ static void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) if (!sc->p2p_ps_timer) return; - if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p) + if (vif->type != NL80211_IFTYPE_STATION) return; sc->p2p_ps_vif = avp; diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 165dd202c..8cbf4904d 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -55,6 +55,8 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { .driver_info = AR9280_USB }, /* Buffalo WLI-UV-AG300P */ { USB_DEVICE(0x04da, 0x3904), .driver_info = AR9280_USB }, + { USB_DEVICE(0x0930, 0x0a08), + .driver_info = AR9280_USB }, /* Toshiba WLM-20U2 and GN-1080 */ { USB_DEVICE(0x0cf3, 0x20ff), .driver_info = STORAGE_DEVICE }, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index fe1fd1a5a..639294a9e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1657,13 +1657,14 @@ static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw, static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_sta *ista; int ret = 0; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 257f46ed4..e7a31016f 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1368,6 +1368,16 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_check_gpm_offset(ah); + /* DMA HALT added to resolve ar9300 and ar9580 bus error during + * RTC_RC reg read + */ + if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) { + REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ); + ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK, + 20 * AH_WAIT_TIMEOUT); + REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ); + } + REG_WRITE(ah, AR_RTC_RC, rst_flags); REGWRITE_BUFFER_FLUSH(ah); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index ab7a1ac37..deded2a9a 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -49,6 +49,10 @@ int ath9k_led_blink; module_param_named(blink, ath9k_led_blink, int, 0444); MODULE_PARM_DESC(blink, "Enable LED blink on activity"); +static int ath9k_led_active_high = -1; +module_param_named(led_active_high, ath9k_led_active_high, int, 0444); +MODULE_PARM_DESC(led_active_high, "Invert LED polarity"); + static int ath9k_btcoex_enable; module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); @@ -600,6 +604,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (ret) return ret; + if (ath9k_led_active_high != -1) + ah->config.led_active_high = ath9k_led_active_high == 1; + /* * Enable WLAN/BT RX Antenna diversity only when: * @@ -751,14 +758,6 @@ static const struct ieee80211_iface_combination if_comb_multi[] = { #endif /* CONFIG_ATH9K_CHANNEL_CONTEXT */ -static const struct ieee80211_iface_limit if_dfs_limits[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_ADHOC) }, -}; - static const struct ieee80211_iface_combination if_comb[] = { { .limits = if_limits, @@ -766,6 +765,11 @@ static const struct ieee80211_iface_combination if_comb[] = { .max_interfaces = 2048, .num_different_channels = 1, .beacon_int_infra_match = true, +#ifdef CONFIG_ATH9K_DFS_CERTIFIED + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40), +#endif }, { .limits = wds_limits, @@ -774,18 +778,6 @@ static const struct ieee80211_iface_combination if_comb[] = { .num_different_channels = 1, .beacon_int_infra_match = true, }, -#ifdef CONFIG_ATH9K_DFS_CERTIFIED - { - .limits = if_dfs_limits, - .n_limits = ARRAY_SIZE(if_dfs_limits), - .max_interfaces = 1, - .num_different_channels = 1, - .beacon_int_infra_match = true, - .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40), - } -#endif }; #ifdef CONFIG_ATH9K_CHANNEL_CONTEXT @@ -863,8 +855,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE); - hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + hw->wiphy->iface_combinations = if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c1b33fdcc..3aed43a63 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -978,7 +978,7 @@ static void ath9k_update_bssid_mask(struct ath_softc *sc, if (ctx->nvifs_assigned != 1) continue; - if (!avp->vif->p2p || !iter_data->has_hw_macaddr) + if (!iter_data->has_hw_macaddr) continue; ether_addr_copy(common->curbssid, avp->bssid); @@ -1255,6 +1255,9 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->cur_chan->nvifs++; + if (vif->type == NL80211_IFTYPE_STATION && ath9k_is_chanctx_enabled()) + vif->driver_flags |= IEEE80211_VIF_GET_NOA_UPDATE; + if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); @@ -1864,14 +1867,16 @@ static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); bool flush = false; int ret = 0; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; mutex_lock(&sc->mutex); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index e6fef1be9..7cdaf40c3 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -28,6 +28,16 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ + +#ifdef CONFIG_ATH9K_PCOEM + /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0029, + PCI_VENDOR_ID_ATHEROS, + 0x2096), + .driver_data = ATH9K_PCI_LED_ACT_HI }, +#endif + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ #ifdef CONFIG_ATH9K_PCOEM diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index caba54dda..c8d35feba 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -34,8 +34,10 @@ #define AR_CFG_SWRG 0x00000010 #define AR_CFG_AP_ADHOC_INDICATION 0x00000020 #define AR_CFG_PHOK 0x00000100 -#define AR_CFG_CLK_GATE_DIS 0x00000400 #define AR_CFG_EEBS 0x00000200 +#define AR_CFG_CLK_GATE_DIS 0x00000400 +#define AR_CFG_HALT_REQ 0x00000800 +#define AR_CFG_HALT_ACK 0x00001000 #define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000 #define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17 diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h index 9111d4ffc..ea1d80f9a 100644 --- a/drivers/net/wireless/ath/carl9170/fwcmd.h +++ b/drivers/net/wireless/ath/carl9170/fwcmd.h @@ -56,6 +56,7 @@ enum carl9170_cmd_oids { CARL9170_CMD_RX_FILTER = 0x07, CARL9170_CMD_WOL = 0x08, CARL9170_CMD_TALLY = 0x09, + CARL9170_CMD_WREGB = 0x0a, /* CAM */ CARL9170_CMD_EKEY = 0x10, @@ -123,6 +124,12 @@ struct carl9170_write_reg { } regs[0] __packed; } __packed; +struct carl9170_write_reg_byte { + __le32 addr; + __le32 count; + u8 val[0]; +} __packed; + #define CARL9170FW_PHY_HT_ENABLE 0x4 #define CARL9170FW_PHY_HT_DYN2040 0x8 #define CARL9170FW_PHY_HT_EXT_CHAN_OFF 0x3 @@ -226,6 +233,7 @@ struct carl9170_cmd { struct carl9170_u32_list echo; struct carl9170_reg_list rreg; struct carl9170_write_reg wreg; + struct carl9170_write_reg_byte wregb; struct carl9170_rf_init rf_init; struct carl9170_psm psm; struct carl9170_wol_cmd wol; diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h index 66848d47c..0533f79cb 100644 --- a/drivers/net/wireless/ath/carl9170/fwdesc.h +++ b/drivers/net/wireless/ath/carl9170/fwdesc.h @@ -81,6 +81,12 @@ enum carl9170fw_feature_list { /* Firmware will pass BA when BARs are queued */ CARL9170FW_RX_BA_FILTER, + /* Firmware has support to write a byte at a time */ + CARL9170FW_HAS_WREGB_CMD, + + /* Pattern generator */ + CARL9170FW_PATTERN_GENERATOR, + /* KEEP LAST */ __CARL9170FW_FEATURE_NUM }; diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h index 0db874abd..08e0ae9c5 100644 --- a/drivers/net/wireless/ath/carl9170/hw.h +++ b/drivers/net/wireless/ath/carl9170/hw.h @@ -453,9 +453,74 @@ #define AR9170_MC_REG_BASE 0x1d1000 #define AR9170_MC_REG_FLASH_WAIT_STATE (AR9170_MC_REG_BASE + 0x000) -#define AR9170_MC_REG_SEEPROM_WP0 (AR9170_MC_REG_BASE + 0x400) -#define AR9170_MC_REG_SEEPROM_WP1 (AR9170_MC_REG_BASE + 0x404) -#define AR9170_MC_REG_SEEPROM_WP2 (AR9170_MC_REG_BASE + 0x408) + +#define AR9170_SPI_REG_BASE (AR9170_MC_REG_BASE + 0x200) +#define AR9170_SPI_REG_CONTROL0 (AR9170_SPI_REG_BASE + 0x000) +#define AR9170_SPI_CONTROL0_BUSY BIT(0) +#define AR9170_SPI_CONTROL0_CMD_GO BIT(1) +#define AR9170_SPI_CONTROL0_PAGE_WR BIT(2) +#define AR9170_SPI_CONTROL0_SEQ_RD BIT(3) +#define AR9170_SPI_CONTROL0_CMD_ABORT BIT(4) +#define AR9170_SPI_CONTROL0_CMD_LEN_S 8 +#define AR9170_SPI_CONTROL0_CMD_LEN 0x00000f00 +#define AR9170_SPI_CONTROL0_RD_LEN_S 12 +#define AR9170_SPI_CONTROL0_RD_LEN 0x00007000 + +#define AR9170_SPI_REG_CONTROL1 (AR9170_SPI_REG_BASE + 0x004) +#define AR9170_SPI_CONTROL1_SCK_RATE BIT(0) +#define AR9170_SPI_CONTROL1_DRIVE_SDO BIT(1) +#define AR9170_SPI_CONTROL1_MODE_SEL_S 2 +#define AR9170_SPI_CONTROL1_MODE_SEL 0x000000c0 +#define AR9170_SPI_CONTROL1_WRITE_PROTECT BIT(4) + +#define AR9170_SPI_REG_COMMAND_PORT0 (AR9170_SPI_REG_BASE + 0x008) +#define AR9170_SPI_COMMAND_PORT0_CMD0_S 0 +#define AR9170_SPI_COMMAND_PORT0_CMD0 0x000000ff +#define AR9170_SPI_COMMAND_PORT0_CMD1_S 8 +#define AR9170_SPI_COMMAND_PORT0_CMD1 0x0000ff00 +#define AR9170_SPI_COMMAND_PORT0_CMD2_S 16 +#define AR9170_SPI_COMMAND_PORT0_CMD2 0x00ff0000 +#define AR9170_SPI_COMMAND_PORT0_CMD3_S 24 +#define AR9170_SPI_COMMAND_PORT0_CMD3 0xff000000 + +#define AR9170_SPI_REG_COMMAND_PORT1 (AR9170_SPI_REG_BASE + 0x00C) +#define AR9170_SPI_COMMAND_PORT1_CMD4_S 0 +#define AR9170_SPI_COMMAND_PORT1_CMD4 0x000000ff +#define AR9170_SPI_COMMAND_PORT1_CMD5_S 8 +#define AR9170_SPI_COMMAND_PORT1_CMD5 0x0000ff00 +#define AR9170_SPI_COMMAND_PORT1_CMD6_S 16 +#define AR9170_SPI_COMMAND_PORT1_CMD6 0x00ff0000 +#define AR9170_SPI_COMMAND_PORT1_CMD7_S 24 +#define AR9170_SPI_COMMAND_PORT1_CMD7 0xff000000 + +#define AR9170_SPI_REG_DATA_PORT (AR9170_SPI_REG_BASE + 0x010) +#define AR9170_SPI_REG_PAGE_WRITE_LEN (AR9170_SPI_REG_BASE + 0x014) + +#define AR9170_EEPROM_REG_BASE (AR9170_MC_REG_BASE + 0x400) +#define AR9170_EEPROM_REG_WP_MAGIC1 (AR9170_EEPROM_REG_BASE + 0x000) +#define AR9170_EEPROM_WP_MAGIC1 0x12345678 + +#define AR9170_EEPROM_REG_WP_MAGIC2 (AR9170_EEPROM_REG_BASE + 0x004) +#define AR9170_EEPROM_WP_MAGIC2 0x55aa00ff + +#define AR9170_EEPROM_REG_WP_MAGIC3 (AR9170_EEPROM_REG_BASE + 0x008) +#define AR9170_EEPROM_WP_MAGIC3 0x13579ace + +#define AR9170_EEPROM_REG_CLOCK_DIV (AR9170_EEPROM_REG_BASE + 0x00C) +#define AR9170_EEPROM_CLOCK_DIV_FAC_S 0 +#define AR9170_EEPROM_CLOCK_DIV_FAC 0x000001ff +#define AR9170_EEPROM_CLOCK_DIV_FAC_39KHZ 0xff +#define AR9170_EEPROM_CLOCK_DIV_FAC_78KHZ 0x7f +#define AR9170_EEPROM_CLOCK_DIV_FAC_312KHZ 0x1f +#define AR9170_EEPROM_CLOCK_DIV_FAC_10MHZ 0x0 +#define AR9170_EEPROM_CLOCK_DIV_SOFT_RST BIT(9) + +#define AR9170_EEPROM_REG_MODE (AR9170_EEPROM_REG_BASE + 0x010) +#define AR9170_EEPROM_MODE_EEPROM_SIZE_16K_PLUS BIT(31) + +#define AR9170_EEPROM_REG_WRITE_PROTECT (AR9170_EEPROM_REG_BASE + 0x014) +#define AR9170_EEPROM_WRITE_PROTECT_WP_STATUS BIT(0) +#define AR9170_EEPROM_WRITE_PROTECT_WP_SET BIT(8) /* Interrupt Controller */ #define AR9170_MAX_INT_SRC 9 @@ -589,11 +654,13 @@ #define AR9170_USB_REG_EP10_MAP (AR9170_USB_REG_BASE + 0x039) #define AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x03f) +#define AR9170_USB_EP_IN_STALL 0x8 #define AR9170_USB_EP_IN_TOGGLE 0x10 #define AR9170_USB_REG_EP_IN_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x03e) #define AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH (AR9170_USB_REG_BASE + 0x05f) +#define AR9170_USB_EP_OUT_STALL 0x8 #define AR9170_USB_EP_OUT_TOGGLE 0x10 #define AR9170_USB_REG_EP_OUT_MAX_SIZE_LOW (AR9170_USB_REG_BASE + 0x05e) diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 19d3d6441..4d1527a2e 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1413,10 +1413,12 @@ static void carl9170_ampdu_work(struct work_struct *work) static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - u16 tid, u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; struct ar9170 *ar = hw->priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; struct carl9170_sta_tid *tid_info; diff --git a/drivers/net/wireless/ath/carl9170/version.h b/drivers/net/wireless/ath/carl9170/version.h index 2282847d4..a0410fe8c 100644 --- a/drivers/net/wireless/ath/carl9170/version.h +++ b/drivers/net/wireless/ath/carl9170/version.h @@ -1,7 +1,7 @@ #ifndef __CARL9170_SHARED_VERSION_H #define __CARL9170_SHARED_VERSION_H -#define CARL9170FW_VERSION_YEAR 12 -#define CARL9170FW_VERSION_MONTH 12 +#define CARL9170FW_VERSION_YEAR 16 +#define CARL9170FW_VERSION_MONTH 2 #define CARL9170FW_VERSION_DAY 15 -#define CARL9170FW_VERSION_GIT "1.9.7" +#define CARL9170FW_VERSION_GIT "1.9.9" #endif /* __CARL9170_SHARED_VERSION_H */ diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 986a45e7e..27f9852e7 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -857,12 +857,14 @@ static int wcn36xx_resume(struct ieee80211_hw *hw) static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct wcn36xx *wcn = hw->priv; struct wcn36xx_sta *sta_priv = NULL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", action, tid); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 20d07ef67..11f1bb8df 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -422,6 +422,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, if (sme->privacy && !rsn_eid) wil_info(wil, "WSC connection\n"); + if (sme->pbss) { + wil_err(wil, "connect - PBSS not yet supported\n"); + return -EOPNOTSUPP; + } + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); @@ -535,7 +540,18 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, wil_dbg_misc(wil, "%s(reason=%d)\n", __func__, reason_code); - rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + if (!(test_bit(wil_status_fwconnecting, wil->status) || + test_bit(wil_status_fwconnected, wil->status))) { + wil_err(wil, "%s: Disconnect was called while disconnected\n", + __func__); + return 0; + } + + rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, + WMI_DISCONNECT_EVENTID, NULL, 0, + WIL6210_DISCONNECT_TO_MS); + if (rc) + wil_err(wil, "%s: disconnect error %d\n", __func__, rc); return rc; } @@ -696,6 +712,79 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy, return rc; } +/** + * find a specific IE in a list of IEs + * return a pointer to the beginning of IE in the list + * or NULL if not found + */ +static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie, + u16 ie_len) +{ + struct ieee80211_vendor_ie *vie; + u32 oui; + + /* IE tag at offset 0, length at offset 1 */ + if (ie_len < 2 || 2 + ie[1] > ie_len) + return NULL; + + if (ie[0] != WLAN_EID_VENDOR_SPECIFIC) + return cfg80211_find_ie(ie[0], ies, ies_len); + + /* make sure there is room for 3 bytes OUI + 1 byte OUI type */ + if (ie[1] < 4) + return NULL; + vie = (struct ieee80211_vendor_ie *)ie; + oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2]; + return cfg80211_find_vendor_ie(oui, vie->oui_type, ies, + ies_len); +} + +/** + * merge the IEs in two lists into a single list. + * do not include IEs from the second list which exist in the first list. + * add only vendor specific IEs from second list to keep + * the merged list sorted (since vendor-specific IE has the + * highest tag number) + * caller must free the allocated memory for merged IEs + */ +static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len, + const u8 *ies2, u16 ies2_len, + u8 **merged_ies, u16 *merged_len) +{ + u8 *buf, *dpos; + const u8 *spos; + + if (ies1_len == 0 && ies2_len == 0) { + *merged_ies = NULL; + *merged_len = 0; + return 0; + } + + buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + memcpy(buf, ies1, ies1_len); + dpos = buf + ies1_len; + spos = ies2; + while (spos + 1 < ies2 + ies2_len) { + /* IE tag at offset 0, length at offset 1 */ + u16 ielen = 2 + spos[1]; + + if (spos + ielen > ies2 + ies2_len) + break; + if (spos[0] == WLAN_EID_VENDOR_SPECIFIC && + !_wil_cfg80211_find_ie(ies1, ies1_len, spos, ielen)) { + memcpy(dpos, spos, ielen); + dpos += ielen; + } + spos += ielen; + } + + *merged_ies = buf; + *merged_len = dpos - buf; + return 0; +} + static void wil_print_bcon_data(struct cfg80211_beacon_data *b) { print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET, @@ -712,49 +801,49 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b) b->assocresp_ies, b->assocresp_ies_len); } -static int wil_fix_bcon(struct wil6210_priv *wil, - struct cfg80211_beacon_data *bcon) -{ - struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp; - size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - - if (bcon->probe_resp_len <= hlen) - return 0; - -/* always use IE's from full probe frame, they has more info - * notable RSN - */ - bcon->proberesp_ies = f->u.probe_resp.variable; - bcon->proberesp_ies_len = bcon->probe_resp_len - hlen; - if (!bcon->assocresp_ies) { - bcon->assocresp_ies = bcon->proberesp_ies; - bcon->assocresp_ies_len = bcon->proberesp_ies_len; - } - - return 1; -} - /* internal functions for device reset and starting AP */ static int _wil_cfg80211_set_ies(struct wiphy *wiphy, struct cfg80211_beacon_data *bcon) { int rc; struct wil6210_priv *wil = wiphy_to_wil(wiphy); + u16 len = 0, proberesp_len = 0; + u8 *ies = NULL, *proberesp = NULL; + + if (bcon->probe_resp) { + struct ieee80211_mgmt *f = + (struct ieee80211_mgmt *)bcon->probe_resp; + size_t hlen = offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + proberesp = f->u.probe_resp.variable; + proberesp_len = bcon->probe_resp_len - hlen; + } + rc = _wil_cfg80211_merge_extra_ies(proberesp, + proberesp_len, + bcon->proberesp_ies, + bcon->proberesp_ies_len, + &ies, &len); - rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, - bcon->proberesp_ies); if (rc) - return rc; + goto out; + + rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, len, ies); + if (rc) + goto out; - rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, - bcon->assocresp_ies); + if (bcon->assocresp_ies) + rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, + bcon->assocresp_ies_len, bcon->assocresp_ies); + else + rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, len, ies); #if 0 /* to use beacon IE's, remove this #if 0 */ if (rc) - return rc; + goto out; rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail); #endif - +out: + kfree(ies); return rc; } @@ -823,14 +912,9 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy, wil_dbg_misc(wil, "%s()\n", __func__); wil_print_bcon_data(bcon); - if (wil_fix_bcon(wil, bcon)) { - wil_dbg_misc(wil, "Fixed bcon\n"); - wil_print_bcon_data(bcon); - } - - if (bcon->proberesp_ies && - cfg80211_find_ie(WLAN_EID_RSN, bcon->proberesp_ies, - bcon->proberesp_ies_len)) + if (bcon->tail && + cfg80211_find_ie(WLAN_EID_RSN, bcon->tail, + bcon->tail_len)) privacy = 1; /* in case privacy has changed, need to restart the AP */ @@ -870,6 +954,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } + if (info->pbss) { + wil_err(wil, "AP: PBSS not yet supported\n"); + return -EOPNOTSUPP; + } + switch (info->hidden_ssid) { case NL80211_HIDDEN_SSID_NOT_IN_USE: hidden_ssid = WMI_HIDDEN_SSID_DISABLED; @@ -900,11 +989,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, wil_print_bcon_data(bcon); wil_print_crypto(wil, crypto); - if (wil_fix_bcon(wil, bcon)) { - wil_dbg_misc(wil, "Fixed bcon\n"); - wil_print_bcon_data(bcon); - } - rc = _wil_cfg80211_start_ap(wiphy, ndev, info->ssid, info->ssid_len, info->privacy, info->beacon_interval, channel->hw_value, diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index a1d10b859..3bbe73b6d 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -68,13 +68,13 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, seq_puts(s, "???\n"); } - if (vring->va && (vring->size < 1025)) { + if (vring->va && (vring->size <= (1 << WIL_RING_SIZE_ORDER_MAX))) { uint i; for (i = 0; i < vring->size; i++) { volatile struct vring_tx_desc *d = &vring->va[i].tx; - if ((i % 64) == 0 && (i != 0)) + if ((i % 128) == 0 && (i != 0)) seq_puts(s, "\n"); seq_printf(s, "%c", (d->dma.status & BIT(0)) ? _s : (vring->ctx[i].skb ? _h : 'h')); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index b39f0bfc5..78ba6e04c 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,9 +23,6 @@ #include "wmi.h" #include "boot_loader.h" -#define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000 -#define WAIT_FOR_DISCONNECT_INTERVAL_MS 10 - bool debug_fw; /* = false; */ module_param(debug_fw, bool, S_IRUGO); MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug"); @@ -155,7 +152,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) if (sta->status != wil_sta_unused) { if (!from_event) - wmi_disconnect_sta(wil, sta->addr, reason_code); + wmi_disconnect_sta(wil, sta->addr, reason_code, true); switch (wdev->iftype) { case NL80211_IFTYPE_AP: @@ -195,8 +192,8 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, struct wireless_dev *wdev = wil->wdev; might_sleep(); - wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid, - reason_code, from_event ? "+" : "-"); + wil_info(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid, + reason_code, from_event ? "+" : "-"); /* Cases are: * - disconnect single STA, still connected @@ -258,13 +255,16 @@ static void wil_disconnect_worker(struct work_struct *work) static void wil_connect_timer_fn(ulong x) { struct wil6210_priv *wil = (void *)x; + bool q; - wil_dbg_misc(wil, "Connect timeout\n"); + wil_err(wil, "Connect timeout detected, disconnect station\n"); /* reschedule to thread context - disconnect won't - * run from atomic context + * run from atomic context. + * queue on wmi_wq to prevent race with connect event. */ - schedule_work(&wil->disconnect_worker); + q = queue_work(wil->wmi_wq, &wil->disconnect_worker); + wil_dbg_wmi(wil, "queue_work of disconnect_worker -> %d\n", q); } static void wil_scan_timer_fn(ulong x) @@ -369,6 +369,32 @@ static int wil_find_free_vring(struct wil6210_priv *wil) return -EINVAL; } +int wil_tx_init(struct wil6210_priv *wil, int cid) +{ + int rc = -EINVAL, ringid; + + if (cid < 0) { + wil_err(wil, "No connection pending\n"); + goto out; + } + ringid = wil_find_free_vring(wil); + if (ringid < 0) { + wil_err(wil, "No free vring found\n"); + goto out; + } + + wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", + cid, ringid); + + rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); + if (rc) + wil_err(wil, "wil_vring_init_tx for CID %d vring %d failed\n", + cid, ringid); + +out: + return rc; +} + int wil_bcast_init(struct wil6210_priv *wil) { int ri = wil->bcast_vring, rc; @@ -399,41 +425,6 @@ void wil_bcast_fini(struct wil6210_priv *wil) wil_vring_fini_tx(wil, ri); } -static void wil_connect_worker(struct work_struct *work) -{ - int rc, cid, ringid; - struct wil6210_priv *wil = container_of(work, struct wil6210_priv, - connect_worker); - struct net_device *ndev = wil_to_ndev(wil); - - mutex_lock(&wil->mutex); - - cid = wil->pending_connect_cid; - if (cid < 0) { - wil_err(wil, "No connection pending\n"); - goto out; - } - ringid = wil_find_free_vring(wil); - if (ringid < 0) { - wil_err(wil, "No free vring found\n"); - goto out; - } - - wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", - cid, ringid); - - rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); - wil->pending_connect_cid = -1; - if (rc == 0) { - wil->sta[cid].status = wil_sta_connected; - netif_tx_wake_all_queues(ndev); - } else { - wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true); - } -out: - mutex_unlock(&wil->mutex); -} - int wil_priv_init(struct wil6210_priv *wil) { uint i; @@ -444,6 +435,9 @@ int wil_priv_init(struct wil6210_priv *wil) for (i = 0; i < WIL6210_MAX_CID; i++) spin_lock_init(&wil->sta[i].tid_rx_lock); + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) + spin_lock_init(&wil->vring_tx_data[i].lock); + mutex_init(&wil->mutex); mutex_init(&wil->wmi_mutex); mutex_init(&wil->back_rx_mutex); @@ -453,12 +447,10 @@ int wil_priv_init(struct wil6210_priv *wil) init_completion(&wil->wmi_ready); init_completion(&wil->wmi_call); - wil->pending_connect_cid = -1; wil->bcast_vring = -1; setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil); - INIT_WORK(&wil->connect_worker, wil_connect_worker); INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); @@ -844,7 +836,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) } /* init after reset */ - wil->pending_connect_cid = -1; wil->ap_isolate = 0; reinit_completion(&wil->wmi_ready); reinit_completion(&wil->wmi_call); @@ -948,8 +939,7 @@ int wil_up(struct wil6210_priv *wil) int __wil_down(struct wil6210_priv *wil) { - int iter = WAIT_FOR_DISCONNECT_TIMEOUT_MS / - WAIT_FOR_DISCONNECT_INTERVAL_MS; + int rc; WARN_ON(!mutex_is_locked(&wil->mutex)); @@ -973,22 +963,16 @@ int __wil_down(struct wil6210_priv *wil) } if (test_bit(wil_status_fwconnected, wil->status) || - test_bit(wil_status_fwconnecting, wil->status)) - wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + test_bit(wil_status_fwconnecting, wil->status)) { - /* make sure wil is idle (not connected) */ - mutex_unlock(&wil->mutex); - while (iter--) { - int idle = !test_bit(wil_status_fwconnected, wil->status) && - !test_bit(wil_status_fwconnecting, wil->status); - if (idle) - break; - msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS); + mutex_unlock(&wil->mutex); + rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, + WMI_DISCONNECT_EVENTID, NULL, 0, + WIL6210_DISCONNECT_TO_MS); + mutex_lock(&wil->mutex); + if (rc) + wil_err(wil, "timeout waiting for disconnect\n"); } - mutex_lock(&wil->mutex); - - if (iter < 0) - wil_err(wil, "timeout waiting for idle FW/HW\n"); wil_reset(wil, false); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 56aaa2d4f..ecc3c1bda 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -108,8 +108,9 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) /* always process ALL Tx complete, regardless budget - it is fast */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { struct vring *vring = &wil->vring_tx[i]; + struct vring_tx_data *txdata = &wil->vring_tx_data[i]; - if (!vring->va) + if (!vring->va || !txdata->enabled) continue; tx_done += wil_tx_complete(wil, i); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 7887e6cfd..6af20903c 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -717,6 +717,21 @@ void wil_rx_fini(struct wil6210_priv *wil) wil_vring_free(wil, vring, 0); } +static inline void wil_tx_data_init(struct vring_tx_data *txdata) +{ + spin_lock_bh(&txdata->lock); + txdata->dot1x_open = 0; + txdata->enabled = 0; + txdata->idle = 0; + txdata->last_idle = 0; + txdata->begin = 0; + txdata->agg_wsize = 0; + txdata->agg_timeout = 0; + txdata->agg_amsdu = 0; + txdata->addba_in_progress = false; + spin_unlock_bh(&txdata->lock); +} + int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, int cid, int tid) { @@ -758,8 +773,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, goto out; } - memset(txdata, 0, sizeof(*txdata)); - spin_lock_init(&txdata->lock); + wil_tx_data_init(txdata); vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -791,9 +805,14 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, return 0; out_free: + spin_lock_bh(&txdata->lock); txdata->dot1x_open = false; txdata->enabled = 0; + spin_unlock_bh(&txdata->lock); wil_vring_free(wil, vring, 1); + wil->vring2cid_tid[id][0] = WIL6210_MAX_CID; + wil->vring2cid_tid[id][1] = 0; + out: return rc; @@ -831,8 +850,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) goto out; } - memset(txdata, 0, sizeof(*txdata)); - spin_lock_init(&txdata->lock); + wil_tx_data_init(txdata); vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -862,8 +880,10 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) return 0; out_free: + spin_lock_bh(&txdata->lock); txdata->enabled = 0; txdata->dot1x_open = false; + spin_unlock_bh(&txdata->lock); wil_vring_free(wil, vring, 1); out: @@ -891,7 +911,6 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) napi_synchronize(&wil->napi_tx); wil_vring_free(wil, vring, 1); - memset(txdata, 0, sizeof(*txdata)); } static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil, @@ -911,10 +930,11 @@ static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil, continue; if (wil->vring2cid_tid[i][0] == cid) { struct vring *v = &wil->vring_tx[i]; + struct vring_tx_data *txdata = &wil->vring_tx_data[i]; wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n", __func__, eth->h_dest, i); - if (v->va) { + if (v->va && txdata->enabled) { return v; } else { wil_dbg_txrx(wil, "vring[%d] not valid\n", i); @@ -935,6 +955,7 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, struct vring *v; int i; u8 cid; + struct vring_tx_data *txdata; /* In the STA mode, it is expected to have only 1 VRING * for the AP we connected to. @@ -942,7 +963,8 @@ static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->vring_tx[i]; - if (!v->va) + txdata = &wil->vring_tx_data[i]; + if (!v->va || !txdata->enabled) continue; cid = wil->vring2cid_tid[i][0]; @@ -978,12 +1000,14 @@ static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil, struct sk_buff *skb) { struct vring *v; + struct vring_tx_data *txdata; int i = wil->bcast_vring; if (i < 0) return NULL; v = &wil->vring_tx[i]; - if (!v->va) + txdata = &wil->vring_tx_data[i]; + if (!v->va || !txdata->enabled) return NULL; if (!wil->vring_tx_data[i].dot1x_open && (skb->protocol != cpu_to_be16(ETH_P_PAE))) @@ -1010,11 +1034,13 @@ static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil, u8 cid; struct ethhdr *eth = (void *)skb->data; char *src = eth->h_source; + struct vring_tx_data *txdata; /* find 1-st vring eligible for data */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->vring_tx[i]; - if (!v->va) + txdata = &wil->vring_tx_data[i]; + if (!v->va || !txdata->enabled) continue; cid = wil->vring2cid_tid[i][0]; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 467bf4b6f..5eb41e1f6 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,7 +51,7 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL_TX_Q_LEN_DEFAULT (4000) #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10) -#define WIL_TX_RING_SIZE_ORDER_DEFAULT (10) +#define WIL_TX_RING_SIZE_ORDER_DEFAULT (12) #define WIL_BCAST_RING_SIZE_ORDER_DEFAULT (7) #define WIL_BCAST_MCS0_LIMIT (1024) /* limit for MCS0 frame size */ /* limit ring size in range [32..32k] */ @@ -92,6 +92,7 @@ static inline u32 wil_mtu2macbuf(u32 mtu) #define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */ #define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000) #define WIL6210_SCAN_TO msecs_to_jiffies(10000) +#define WIL6210_DISCONNECT_TO_MS (2000) #define WIL6210_RX_HIGH_TRSH_INIT (0) #define WIL6210_RX_HIGH_TRSH_DEFAULT \ (1 << (WIL_RX_RING_SIZE_ORDER_DEFAULT - 3)) @@ -581,12 +582,10 @@ struct wil6210_priv { struct workqueue_struct *wmi_wq; /* for deferred calls */ struct work_struct wmi_event_worker; struct workqueue_struct *wq_service; - struct work_struct connect_worker; struct work_struct disconnect_worker; struct work_struct fw_error_worker; /* for FW error recovery */ struct timer_list connect_timer; struct timer_list scan_timer; /* detect scan timeout */ - int pending_connect_cid; struct list_head pending_wmi_ev; /* * protect pending_wmi_ev @@ -756,7 +755,8 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring); int wmi_p2p_cfg(struct wil6210_priv *wil, int channel); int wmi_rxon(struct wil6210_priv *wil, bool on); int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); -int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason); +int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason, + bool full_disconnect); int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout); int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason); int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason); @@ -807,6 +807,7 @@ void wil_rx_fini(struct wil6210_priv *wil); int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, int cid, int tid); void wil_vring_fini_tx(struct wil6210_priv *wil, int id); +int wil_tx_init(struct wil6210_priv *wil, int cid); int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size); int wil_bcast_init(struct wil6210_priv *wil); void wil_bcast_fini(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index e3ea74cdd..493e721c4 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -426,6 +426,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) const size_t assoc_req_ie_offset = sizeof(u16) * 2; /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + int rc; if (len < sizeof(*evt)) { wil_err(wil, "Connect event too short : %d bytes\n", len); @@ -445,8 +446,8 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) } ch = evt->channel + 1; - wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n", - evt->bssid, ch, evt->cid); + wil_info(wil, "Connect %pM channel [%d] cid %d\n", + evt->bssid, ch, evt->cid); wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, evt->assoc_info, len - sizeof(*evt), true); @@ -468,20 +469,67 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) assoc_resp_ielen = 0; } + mutex_lock(&wil->mutex); + if (test_bit(wil_status_resetting, wil->status) || + !test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "status_resetting, cancel connect event, CID %d\n", + evt->cid); + mutex_unlock(&wil->mutex); + /* no need for cleanup, wil_reset will do that */ + return; + } + if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { if (!test_bit(wil_status_fwconnecting, wil->status)) { wil_err(wil, "Not in connecting state\n"); + mutex_unlock(&wil->mutex); return; } del_timer_sync(&wil->connect_timer); - cfg80211_connect_result(ndev, evt->bssid, - assoc_req_ie, assoc_req_ielen, - assoc_resp_ie, assoc_resp_ielen, - WLAN_STATUS_SUCCESS, GFP_KERNEL); + } + + /* FIXME FW can transmit only ucast frames to peer */ + /* FIXME real ring_id instead of hard coded 0 */ + ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid); + wil->sta[evt->cid].status = wil_sta_conn_pending; + rc = wil_tx_init(wil, evt->cid); + if (rc) { + wil_err(wil, "%s: config tx vring failed for CID %d, rc (%d)\n", + __func__, evt->cid, rc); + wmi_disconnect_sta(wil, wil->sta[evt->cid].addr, + WLAN_REASON_UNSPECIFIED, false); + } else { + wil_info(wil, "%s: successful connection to CID %d\n", + __func__, evt->cid); + } + + if ((wdev->iftype == NL80211_IFTYPE_STATION) || + (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (rc) { + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + wil_err(wil, + "%s: cfg80211_connect_result with failure\n", + __func__); + cfg80211_connect_result(ndev, evt->bssid, NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + goto out; + } else { + cfg80211_connect_result(ndev, evt->bssid, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, + GFP_KERNEL); + } } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + if (rc) + goto out; + memset(&sinfo, 0, sizeof(sinfo)); sinfo.generation = wil->sinfo_gen++; @@ -492,17 +540,21 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) } cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); + } else { + wil_err(wil, "%s: unhandled iftype %d for CID %d\n", + __func__, wdev->iftype, evt->cid); + goto out; } - clear_bit(wil_status_fwconnecting, wil->status); - set_bit(wil_status_fwconnected, wil->status); - /* FIXME FW can transmit only ucast frames to peer */ - /* FIXME real ring_id instead of hard coded 0 */ - ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid); - wil->sta[evt->cid].status = wil_sta_conn_pending; + wil->sta[evt->cid].status = wil_sta_connected; + set_bit(wil_status_fwconnected, wil->status); + netif_tx_wake_all_queues(ndev); - wil->pending_connect_cid = evt->cid; - queue_work(wil->wq_service, &wil->connect_worker); +out: + if (rc) + wil->sta[evt->cid].status = wil_sta_unused; + clear_bit(wil_status_fwconnecting, wil->status); + mutex_unlock(&wil->mutex); } static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, @@ -511,8 +563,8 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, struct wmi_disconnect_event *evt = d; u16 reason_code = le16_to_cpu(evt->protocol_reason_status); - wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n", - evt->bssid, reason_code, evt->disconnect_reason); + wil_info(wil, "Disconnect %pM reason [proto %d wmi %d]\n", + evt->bssid, reason_code, evt->disconnect_reason); wil->sinfo_gen++; @@ -727,6 +779,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) void __iomem *src; ulong flags; unsigned n; + unsigned int num_immed_reply = 0; if (!test_bit(wil_status_mbox_ready, wil->status)) { wil_err(wil, "Reset in progress. Cannot handle WMI event\n"); @@ -736,6 +789,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) for (n = 0;; n++) { u16 len; bool q; + bool immed_reply = false; r->head = wil_r(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, rx.head)); @@ -784,6 +838,15 @@ void wmi_recv_cmd(struct wil6210_priv *wil) struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi; u16 id = le16_to_cpu(wmi->id); u32 tstamp = le32_to_cpu(wmi->timestamp); + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + if (wil->reply_id && wil->reply_id == id) { + if (wil->reply_buf) { + memcpy(wil->reply_buf, wmi, + min(len, wil->reply_size)); + immed_reply = true; + } + } + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n", id, wmi->mid, tstamp); @@ -799,15 +862,24 @@ void wmi_recv_cmd(struct wil6210_priv *wil) wil_w(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, rx.tail), r->tail); - /* add to the pending list */ - spin_lock_irqsave(&wil->wmi_ev_lock, flags); - list_add_tail(&evt->list, &wil->pending_wmi_ev); - spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); - q = queue_work(wil->wmi_wq, &wil->wmi_event_worker); - wil_dbg_wmi(wil, "queue_work -> %d\n", q); + if (immed_reply) { + wil_dbg_wmi(wil, "%s: Complete WMI 0x%04x\n", + __func__, wil->reply_id); + kfree(evt); + num_immed_reply++; + complete(&wil->wmi_call); + } else { + /* add to the pending list */ + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_add_tail(&evt->list, &wil->pending_wmi_ev); + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + q = queue_work(wil->wmi_wq, &wil->wmi_event_worker); + wil_dbg_wmi(wil, "queue_work -> %d\n", q); + } } /* normally, 1 event per IRQ should be processed */ - wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n); + wil_dbg_wmi(wil, "%s -> %d events queued, %d completed\n", __func__, + n - num_immed_reply, num_immed_reply); } int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, @@ -818,13 +890,16 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, mutex_lock(&wil->wmi_mutex); + spin_lock(&wil->wmi_ev_lock); + wil->reply_id = reply_id; + wil->reply_buf = reply; + wil->reply_size = reply_size; + spin_unlock(&wil->wmi_ev_lock); + rc = __wmi_send(wil, cmdid, buf, len); if (rc) goto out; - wil->reply_id = reply_id; - wil->reply_buf = reply; - wil->reply_size = reply_size; remain = wait_for_completion_timeout(&wil->wmi_call, msecs_to_jiffies(to_msec)); if (0 == remain) { @@ -837,10 +912,14 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, cmdid, reply_id, to_msec - jiffies_to_msecs(remain)); } + +out: + spin_lock(&wil->wmi_ev_lock); wil->reply_id = 0; wil->reply_buf = NULL; wil->reply_size = 0; - out: + spin_unlock(&wil->wmi_ev_lock); + mutex_unlock(&wil->wmi_mutex); return rc; @@ -1184,7 +1263,8 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) return 0; } -int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason) +int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason, + bool full_disconnect) { int rc; u16 reason_code; @@ -1208,19 +1288,20 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason) return rc; } - /* call event handler manually after processing wmi_call, - * to avoid deadlock - disconnect event handler acquires wil->mutex - * while it is already held here - */ - reason_code = le16_to_cpu(reply.evt.protocol_reason_status); - - wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n", - reply.evt.bssid, reason_code, - reply.evt.disconnect_reason); + if (full_disconnect) { + /* call event handler manually after processing wmi_call, + * to avoid deadlock - disconnect event handler acquires + * wil->mutex while it is already held here + */ + reason_code = le16_to_cpu(reply.evt.protocol_reason_status); - wil->sinfo_gen++; - wil6210_disconnect(wil, reply.evt.bssid, reason_code, true); + wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n", + reply.evt.bssid, reason_code, + reply.evt.disconnect_reason); + wil->sinfo_gen++; + wil6210_disconnect(wil, reply.evt.bssid, reason_code, true); + } return 0; } @@ -1348,14 +1429,11 @@ static void wmi_event_handle(struct wil6210_priv *wil, id, wil->reply_id); /* check if someone waits for this event */ if (wil->reply_id && wil->reply_id == id) { - if (wil->reply_buf) { - memcpy(wil->reply_buf, wmi, - min(len, wil->reply_size)); - } else { - wmi_evt_call_handler(wil, id, evt_data, - len - sizeof(*wmi)); - } - wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id); + WARN_ON(wil->reply_buf); + wmi_evt_call_handler(wil, id, evt_data, + len - sizeof(*wmi)); + wil_dbg_wmi(wil, "%s: Complete WMI 0x%04x\n", + __func__, id); complete(&wil->wmi_call); return; } diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 62a4316c2..bb1ed0ebd 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2474,9 +2474,7 @@ static int at76_probe(struct usb_interface *interface, dev_err(&interface->dev, "error %d downloading internal firmware\n", ret); - goto exit; } - usb_put_dev(udev); goto exit; } diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 3af6826cb..97774b338 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -1209,10 +1209,10 @@ void b43_wireless_core_phy_pll_reset(struct b43_wldev *dev) case B43_BUS_BCMA: bcma_cc = &dev->dev->bdev->bus->drv_cc; - bcma_cc_write32(bcma_cc, BCMA_CC_CHIPCTL_ADDR, 0); - bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); - bcma_cc_set32(bcma_cc, BCMA_CC_CHIPCTL_DATA, 0x4); - bcma_cc_mask32(bcma_cc, BCMA_CC_CHIPCTL_DATA, ~0x4); + bcma_cc_write32(bcma_cc, BCMA_CC_PMU_CHIPCTL_ADDR, 0); + bcma_cc_mask32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, ~0x4); + bcma_cc_set32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, 0x4); + bcma_cc_mask32(bcma_cc, BCMA_CC_PMU_CHIPCTL_DATA, ~0x4); break; #endif #ifdef CONFIG_B43_SSB @@ -4371,12 +4371,10 @@ redo: /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ orig_dev = dev; mutex_unlock(&wl->mutex); - if (b43_bus_host_is_sdio(dev->dev)) { + if (b43_bus_host_is_sdio(dev->dev)) b43_sdio_free_irq(dev); - } else { - synchronize_irq(dev->dev->irq); + else free_irq(dev->dev->irq, dev); - } mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev) @@ -5678,11 +5676,12 @@ static int b43_bcma_probe(struct bcma_device *core) INIT_WORK(&wl->firmware_load, b43_request_firmware); schedule_work(&wl->firmware_load); -bcma_out: return err; bcma_err_wireless_exit: ieee80211_free_hw(wl->hw); +bcma_out: + kfree(dev); return err; } @@ -5710,8 +5709,8 @@ static void b43_bcma_remove(struct bcma_device *core) b43_rng_exit(wl); b43_leds_unregister(wl); - ieee80211_free_hw(wl->hw); + kfree(wldev->dev); } static struct bcma_driver b43_bcma_driver = { @@ -5794,6 +5793,7 @@ static void b43_ssb_remove(struct ssb_device *sdev) b43_leds_unregister(wl); b43_wireless_exit(dev, wl); + kfree(dev); } static struct ssb_driver b43_ssb_driver = { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index b98db8a0a..da0cdd313 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -27,8 +27,6 @@ #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> -#include <linux/platform_device.h> -#include <linux/platform_data/brcmfmac-sdio.h> #include <linux/pm_runtime.h> #include <linux/suspend.h> #include <linux/errno.h> @@ -46,7 +44,6 @@ #include "bus.h" #include "debug.h" #include "sdio.h" -#include "of.h" #include "core.h" #include "common.h" @@ -106,18 +103,18 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { + struct brcmfmac_sdio_pd *pdata; int ret = 0; u8 data; u32 addr, gpiocontrol; unsigned long flags; - if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { + pdata = &sdiodev->settings->bus.sdio; + if (pdata->oob_irq_supported) { brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n", - sdiodev->pdata->oob_irq_nr); - ret = request_irq(sdiodev->pdata->oob_irq_nr, - brcmf_sdiod_oob_irqhandler, - sdiodev->pdata->oob_irq_flags, - "brcmf_oob_intr", + pdata->oob_irq_nr); + ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler, + pdata->oob_irq_flags, "brcmf_oob_intr", &sdiodev->func[1]->dev); if (ret != 0) { brcmf_err("request_irq failed %d\n", ret); @@ -129,7 +126,7 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) sdiodev->irq_en = true; spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); - ret = enable_irq_wake(sdiodev->pdata->oob_irq_nr); + ret = enable_irq_wake(pdata->oob_irq_nr); if (ret != 0) { brcmf_err("enable_irq_wake failed %d\n", ret); return ret; @@ -158,7 +155,7 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) /* redirect, configure and enable io for interrupt signal */ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; - if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) + if (pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) data |= SDIO_SEPINT_ACT_HI; brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); @@ -176,9 +173,12 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) { + struct brcmfmac_sdio_pd *pdata; + brcmf_dbg(SDIO, "Entering\n"); - if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { + pdata = &sdiodev->settings->bus.sdio; + if (pdata->oob_irq_supported) { sdio_claim_host(sdiodev->func[1]); brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); @@ -187,11 +187,10 @@ int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) if (sdiodev->oob_irq_requested) { sdiodev->oob_irq_requested = false; if (sdiodev->irq_wake) { - disable_irq_wake(sdiodev->pdata->oob_irq_nr); + disable_irq_wake(pdata->oob_irq_nr); sdiodev->irq_wake = false; } - free_irq(sdiodev->pdata->oob_irq_nr, - &sdiodev->func[1]->dev); + free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev); sdiodev->irq_en = false; } } else { @@ -523,7 +522,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); - if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + if (!write && sdiodev->settings->bus.sdio.broken_sg_support) { req_sz = 0; skb_queue_walk(pktlist, pkt_next) req_sz += pkt_next->len; @@ -630,7 +629,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, } } - if (sdiodev->pdata && sdiodev->pdata->broken_sg_support && !write) { + if (!write && sdiodev->settings->bus.sdio.broken_sg_support) { local_pkt_next = local_list.next; orig_offset = 0; skb_queue_walk(pktlist, pkt_next) { @@ -901,7 +900,7 @@ void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) return; nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, - sdiodev->bus_if->drvr->settings->sdiod_txglomsz); + sdiodev->settings->bus.sdio.txglomsz); nents += (nents >> 4) + 1; WARN_ON(nents > sdiodev->max_segment_count); @@ -913,7 +912,7 @@ void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) sdiodev->sg_support = false; } - sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz; + sdiodev->txglomsz = sdiodev->settings->bus.sdio.txglomsz; } #ifdef CONFIG_PM_SLEEP @@ -1103,8 +1102,6 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); -static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; - static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev, int val) @@ -1167,20 +1164,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); sdiodev->dev = &sdiodev->func[1]->dev; - sdiodev->pdata = brcmfmac_sdio_pdata; - - if (!sdiodev->pdata) - brcmf_of_probe(sdiodev); - -#ifdef CONFIG_PM_SLEEP - /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ - * is true or when platform data OOB irq is true). - */ - if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && - ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || - (sdiodev->pdata && sdiodev->pdata->oob_irq_supported))) - bus_if->wowl_supported = true; -#endif brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); @@ -1263,8 +1246,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev) sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->wowl_enabled) { - if (sdiodev->pdata->oob_irq_supported) - enable_irq_wake(sdiodev->pdata->oob_irq_nr); + if (sdiodev->settings->bus.sdio.oob_irq_supported) + enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); else sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; } @@ -1296,7 +1279,7 @@ static const struct dev_pm_ops brcmf_sdio_pm_ops = { static struct sdio_driver brcmf_sdmmc_driver = { .probe = brcmf_ops_sdio_probe, .remove = brcmf_ops_sdio_remove, - .name = BRCMFMAC_SDIO_PDATA_NAME, + .name = KBUILD_MODNAME, .id_table = brcmf_sdmmc_ids, .drv = { .owner = THIS_MODULE, @@ -1306,37 +1289,6 @@ static struct sdio_driver brcmf_sdmmc_driver = { }, }; -static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); - - if (brcmfmac_sdio_pdata->power_on) - brcmfmac_sdio_pdata->power_on(); - - return 0; -} - -static int brcmf_sdio_pd_remove(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata->power_off) - brcmfmac_sdio_pdata->power_off(); - - sdio_unregister_driver(&brcmf_sdmmc_driver); - - return 0; -} - -static struct platform_driver brcmf_sdio_pd = { - .remove = brcmf_sdio_pd_remove, - .driver = { - .name = BRCMFMAC_SDIO_PDATA_NAME, - } -}; - void brcmf_sdio_register(void) { int ret; @@ -1350,19 +1302,6 @@ void brcmf_sdio_exit(void) { brcmf_dbg(SDIO, "Enter\n"); - if (brcmfmac_sdio_pdata) - platform_driver_unregister(&brcmf_sdio_pd); - else - sdio_unregister_driver(&brcmf_sdmmc_driver); + sdio_unregister_driver(&brcmf_sdmmc_driver); } -void __init brcmf_sdio_init(void) -{ - int ret; - - brcmf_dbg(SDIO, "Enter\n"); - - ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); - if (ret == -ENODEV) - brcmf_dbg(SDIO, "No platform data available.\n"); -} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 36093f93b..8e02a478e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -43,6 +43,8 @@ enum brcmf_bus_protocol_type { BRCMF_PROTO_MSGBUF }; +struct brcmf_mp_device; + struct brcmf_bus_dcmd { char *name; char *param; @@ -217,7 +219,7 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); /* Indication from bus module regarding presence/insertion of dongle. */ -int brcmf_attach(struct device *dev); +int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings); /* Indication from bus module regarding removal/absence of dongle */ void brcmf_detach(struct device *dev); /* Indication from bus module that dongle should be reset */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 7b01e4ddb..d5c2a2757 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -72,8 +72,13 @@ #define RSN_AKM_NONE 0 /* None (IBSS) */ #define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */ #define RSN_AKM_PSK 2 /* Pre-shared Key */ +#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */ +#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */ #define RSN_CAP_LEN 2 /* Length of RSN capabilities */ -#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C +#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3)) +#define RSN_CAP_MFPR_MASK BIT(6) +#define RSN_CAP_MFPC_MASK BIT(7) +#define RSN_PMKID_COUNT_LEN 2 #define VNDR_IE_CMD_LEN 4 /* length of the set command * string :"add", "del" (+ NUL) @@ -211,12 +216,19 @@ static const struct ieee80211_regdomain brcmf_regdom = { REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } }; -static const u32 __wl_cipher_suites[] = { +/* Note: brcmf_cipher_suites is an array of int defining which cipher suites + * are supported. A pointer to this array and the number of entries is passed + * on to upper layers. AES_CMAC defines whether or not the driver supports MFP. + * So the cipher suite AES_CMAC has to be the last one in the array, and when + * device does not support MFP then the number of suites will be decreased by 1 + */ +static const u32 brcmf_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_AES_CMAC, + /* Keep as last entry: */ + WLAN_CIPHER_SUITE_AES_CMAC }; /* Vendor specific ie. id = 221, oui and type defines exact ie */ @@ -247,7 +259,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", ch->chan->center_freq, ch->center_freq1, ch->width); ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); - primary_offset = ch->center_freq1 - ch->chan->center_freq; + primary_offset = ch->chan->center_freq - ch->center_freq1; switch (ch->width) { case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: @@ -256,24 +268,21 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, break; case NL80211_CHAN_WIDTH_40: ch_inf.bw = BRCMU_CHAN_BW_40; - if (primary_offset < 0) + if (primary_offset > 0) ch_inf.sb = BRCMU_CHAN_SB_U; else ch_inf.sb = BRCMU_CHAN_SB_L; break; case NL80211_CHAN_WIDTH_80: ch_inf.bw = BRCMU_CHAN_BW_80; - if (primary_offset < 0) { - if (primary_offset < -CH_10MHZ_APART) - ch_inf.sb = BRCMU_CHAN_SB_UU; - else - ch_inf.sb = BRCMU_CHAN_SB_UL; - } else { - if (primary_offset > CH_10MHZ_APART) - ch_inf.sb = BRCMU_CHAN_SB_LL; - else - ch_inf.sb = BRCMU_CHAN_SB_LU; - } + if (primary_offset == -30) + ch_inf.sb = BRCMU_CHAN_SB_LL; + else if (primary_offset == -10) + ch_inf.sb = BRCMU_CHAN_SB_LU; + else if (primary_offset == 10) + ch_inf.sb = BRCMU_CHAN_SB_UL; + else + ch_inf.sb = BRCMU_CHAN_SB_UU; break; case NL80211_CHAN_WIDTH_80P80: case NL80211_CHAN_WIDTH_160: @@ -459,7 +468,7 @@ send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) } static s32 -brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) +brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable) { s32 err; u32 mode; @@ -487,6 +496,15 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) enable, mode); } + err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable); + if (err) { + brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n", + enable, err); + err = 0; + } else + brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n", + enable, mode); + return err; } @@ -567,8 +585,8 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, } /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - BRCMF_VIF_EVENT_TIMEOUT); + err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD, + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("timeout occurred\n"); @@ -1128,7 +1146,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, /* Arm scan timeout timer */ mod_timer(&cfg->escan_timeout, jiffies + - WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); + BRCMF_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); return 0; @@ -1527,7 +1545,7 @@ static s32 brcmf_set_auth_type(struct net_device *ndev, static s32 brcmf_set_wsec_mode(struct net_device *ndev, - struct cfg80211_connect_params *sme, bool mfp) + struct cfg80211_connect_params *sme) { struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); struct brcmf_cfg80211_security *sec; @@ -1586,10 +1604,7 @@ brcmf_set_wsec_mode(struct net_device *ndev, sme->privacy) pval = AES_ENABLED; - if (mfp) - wsec = pval | gval | MFP_CAPABLE; - else - wsec = pval | gval; + wsec = pval | gval; err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); if (err) { brcmf_err("error (%d)\n", err); @@ -1606,56 +1621,100 @@ brcmf_set_wsec_mode(struct net_device *ndev, static s32 brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) { - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; + struct brcmf_if *ifp = netdev_priv(ndev); + s32 val; + s32 err; + const struct brcmf_tlv *rsn_ie; + const u8 *ie; + u32 ie_len; + u32 offset; + u16 rsn_cap; + u32 mfp; + u16 count; - if (sme->crypto.n_akm_suites) { - err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), - "wpa_auth", &val); - if (err) { - brcmf_err("could not get wpa_auth (%d)\n", err); - return err; + if (!sme->crypto.n_akm_suites) + return 0; + + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val); + if (err) { + brcmf_err("could not get wpa_auth (%d)\n", err); + return err; + } + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA_AUTH_PSK; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; } - if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { - switch (sme->crypto.akm_suites[0]) { - case WLAN_AKM_SUITE_8021X: - val = WPA_AUTH_UNSPECIFIED; - break; - case WLAN_AKM_SUITE_PSK: - val = WPA_AUTH_PSK; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { - switch (sme->crypto.akm_suites[0]) { - case WLAN_AKM_SUITE_8021X: - val = WPA2_AUTH_UNSPECIFIED; - break; - case WLAN_AKM_SUITE_PSK: - val = WPA2_AUTH_PSK; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA2_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_8021X_SHA256: + val = WPA2_AUTH_1X_SHA256; + break; + case WLAN_AKM_SUITE_PSK_SHA256: + val = WPA2_AUTH_PSK_SHA256; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA2_AUTH_PSK; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; } + } - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), - "wpa_auth", val); - if (err) { - brcmf_err("could not set wpa_auth (%d)\n", err); - return err; - } + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) + goto skip_mfp_config; + /* The MFP mode (1 or 2) needs to be determined, parse IEs. The + * IE will not be verified, just a quick search for MFP config + */ + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len, + WLAN_EID_RSN); + if (!rsn_ie) + goto skip_mfp_config; + ie = (const u8 *)rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + /* Skip unicast suite */ + offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto skip_mfp_config; + /* Skip multicast suite */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto skip_mfp_config; + /* Skip auth key management suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len) + goto skip_mfp_config; + /* Ready to read capabilities */ + mfp = BRCMF_MFP_NONE; + rsn_cap = ie[offset] + (ie[offset + 1] << 8); + if (rsn_cap & RSN_CAP_MFPR_MASK) + mfp = BRCMF_MFP_REQUIRED; + else if (rsn_cap & RSN_CAP_MFPC_MASK) + mfp = BRCMF_MFP_CAPABLE; + brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp); + +skip_mfp_config: + brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); + if (err) { + brcmf_err("could not set wpa_auth (%d)\n", err); + return err; } - sec = &profile->sec; - sec->wpa_auth = sme->crypto.akm_suites[0]; return err; } @@ -1821,7 +1880,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); + err = brcmf_set_wsec_mode(ndev, sme); if (err) { brcmf_err("wl_set_set_cipher failed (%d)\n", err); goto done; @@ -2067,98 +2126,54 @@ done: } static s32 -brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, const u8 *mac_addr, struct key_params *params) +brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr) { struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 err = 0; - u8 keybuf[8]; + struct brcmf_wsec_key *key; + s32 err; - memset(&key, 0, sizeof(key)); - key.index = (u32) key_idx; - /* Instead of bcast for ea address for default wep keys, - driver needs it to be Null */ - if (!is_multicast_ether_addr(mac_addr)) - memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); - key.len = (u32) params->key_len; - /* check for key index change */ - if (key.len == 0) { - /* key delete */ - err = send_key_to_dongle(ifp, &key); - if (err) - brcmf_err("key delete error (%d)\n", err); - } else { - if (key.len > sizeof(key.data)) { - brcmf_err("Invalid key length (%d)\n", key.len); - return -EINVAL; - } + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); - brcmf_dbg(CONN, "Setting the key index %d\n", key.index); - memcpy(key.data, params->key, key.len); + if (!check_vif_up(ifp->vif)) + return -EIO; - 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)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); - } + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + return -EINVAL; + } - /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ - if (params->seq && params->seq_len == 6) { - /* rx iv */ - u8 *ivptr; - ivptr = (u8 *) params->seq; - key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | - (ivptr[3] << 8) | ivptr[2]; - key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; - key.iv_initialized = true; - } + key = &ifp->vif->profile.key[key_idx]; - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - break; - case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - break; - case WLAN_CIPHER_SUITE_TKIP: - key.algo = CRYPTO_ALGO_TKIP; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - break; - case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); - break; - default: - brcmf_err("Invalid cipher (0x%x)\n", params->cipher); - return -EINVAL; - } - err = send_key_to_dongle(ifp, &key); - if (err) - brcmf_err("wsec_key error (%d)\n", err); + if (key->algo == CRYPTO_ALGO_OFF) { + brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n"); + return -EINVAL; } + + memset(key, 0, sizeof(*key)); + key->index = (u32)key_idx; + key->flags = BRCMF_PRIMARY_KEY; + + /* Clear the key/index */ + err = send_key_to_dongle(ifp, key); + + brcmf_dbg(TRACE, "Exit\n"); return err; } static s32 brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr, - struct key_params *params) + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_wsec_key *key; s32 val; s32 wsec; - s32 err = 0; + s32 err; u8 keybuf[8]; + bool ext_key; brcmf_dbg(TRACE, "Enter\n"); brcmf_dbg(CONN, "key index (%d)\n", key_idx); @@ -2171,27 +2186,32 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, return -EINVAL; } - 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); - } - - key = &ifp->vif->profile.key[key_idx]; - memset(key, 0, sizeof(*key)); + if (params->key_len == 0) + return brcmf_cfg80211_del_key(wiphy, ndev, key_idx, pairwise, + mac_addr); if (params->key_len > sizeof(key->data)) { brcmf_err("Too long key length (%u)\n", params->key_len); - err = -EINVAL; - goto done; + return -EINVAL; + } + + ext_key = false; + if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { + brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr); + ext_key = true; } + + key = &ifp->vif->profile.key[key_idx]; + memset(key, 0, sizeof(*key)); + if ((ext_key) && (!is_multicast_ether_addr(mac_addr))) + memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN); key->len = params->key_len; key->index = key_idx; - memcpy(key->data, params->key, key->len); + if (!ext_key) + key->flags = BRCMF_PRIMARY_KEY; - key->flags = BRCMF_PRIMARY_KEY; switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: key->algo = CRYPTO_ALGO_WEP1; @@ -2231,7 +2251,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, } err = send_key_to_dongle(ifp, key); - if (err) + if (ext_key || err) goto done; err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); @@ -2252,41 +2272,10 @@ done: } static s32 -brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { - /* we ignore this key index in this case */ - return -EINVAL; - } - - memset(&key, 0, sizeof(key)); - - key.index = (u32) key_idx; - key.flags = BRCMF_PRIMARY_KEY; - key.algo = CRYPTO_ALGO_OFF; - - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - - /* Set the new key/index */ - err = send_key_to_dongle(ifp, &key); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, - void (*callback) (void *cookie, struct key_params * params)) +brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, + bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, + struct key_params *params)) { struct key_params params; struct brcmf_if *ifp = netdev_priv(ndev); @@ -2338,8 +2327,15 @@ done: static s32 brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, - struct net_device *ndev, u8 key_idx) + struct net_device *ndev, u8 key_idx) { + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx); + + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) + return 0; + brcmf_dbg(INFO, "Not supported\n"); return -EOPNOTSUPP; @@ -3023,7 +3019,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, list = (struct brcmf_scan_results *) cfg->escan_info.escan_buf; - if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { + if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) { brcmf_err("Buffer is too small: ignoring\n"); goto exit; } @@ -3036,8 +3032,8 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, bss_info_le)) goto exit; } - memcpy(&(cfg->escan_info.escan_buf[list->buflen]), - bss_info_le, bi_length); + memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le, + bi_length); list->version = le32_to_cpu(bss_info_le->version); list->buflen += bi_length; list->count++; @@ -3095,6 +3091,11 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, brcmf_dbg(SCAN, "Enter\n"); + if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + if (e->event_code == BRCMF_E_PFN_NET_LOST) { brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); return 0; @@ -3418,6 +3419,11 @@ brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, brcmf_dbg(SCAN, "Enter\n"); + if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + pfn_result = (struct brcmf_pno_scanresults_le *)data; if (e->event_code == BRCMF_E_PFN_NET_LOST) { @@ -3510,6 +3516,10 @@ static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) else wakeup_data.net_detect = cfg->wowl.nd_info; } + if (wakeind & BRCMF_WOWL_GTK_FAILURE) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n"); + wakeup_data.gtk_rekey_failure = true; + } } else { wakeup = NULL; } @@ -3536,7 +3546,8 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_report_wowl_wakeind(wiphy, ifp); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); - brcmf_configure_arp_offload(ifp, true); + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) + brcmf_configure_arp_nd_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, cfg->wowl.pre_pmmode); cfg->wowl.active = false; @@ -3560,7 +3571,8 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, brcmf_dbg(TRACE, "Suspend, wowl config.\n"); - brcmf_configure_arp_offload(ifp, false); + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) + brcmf_configure_arp_nd_offload(ifp, false); brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); @@ -3591,6 +3603,8 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, brcmf_wowl_nd_results); } + if (wowl->gtk_rekey_failure) + wowl_config |= BRCMF_WOWL_GTK_FAILURE; if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) wowl_config |= BRCMF_WOWL_UNASSOC; @@ -3821,7 +3835,7 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, u32 auth = 0; /* d11 open authentication */ u16 count; s32 err = 0; - s32 len = 0; + s32 len; u32 i; u32 wsec; u32 pval = 0; @@ -3831,6 +3845,7 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, u8 *data; u16 rsn_cap; u32 wme_bss_disable; + u32 mfp; brcmf_dbg(TRACE, "Enter\n"); if (wpa_ie == NULL) @@ -3945,19 +3960,53 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : (wpa_auth |= WPA_AUTH_PSK); break; + case RSN_AKM_SHA256_PSK: + brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); + wpa_auth |= WPA2_AUTH_PSK_SHA256; + break; + case RSN_AKM_SHA256_1X: + brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); + wpa_auth |= WPA2_AUTH_1X_SHA256; + break; default: brcmf_err("Ivalid key mgmt info\n"); } offset++; } + mfp = BRCMF_MFP_NONE; if (is_rsn_ie) { wme_bss_disable = 1; if ((offset + RSN_CAP_LEN) <= len) { rsn_cap = data[offset] + (data[offset + 1] << 8); if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK) wme_bss_disable = 0; + if (rsn_cap & RSN_CAP_MFPR_MASK) { + brcmf_dbg(TRACE, "MFP Required\n"); + mfp = BRCMF_MFP_REQUIRED; + /* Firmware only supports mfp required in + * combination with WPA2_AUTH_PSK_SHA256 or + * WPA2_AUTH_1X_SHA256. + */ + if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 | + WPA2_AUTH_1X_SHA256))) { + err = -EINVAL; + goto exit; + } + /* Firmware has requirement that WPA2_AUTH_PSK/ + * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI + * is to be included in the rsn ie. + */ + if (wpa_auth & WPA2_AUTH_PSK_SHA256) + wpa_auth |= WPA2_AUTH_PSK; + else if (wpa_auth & WPA2_AUTH_1X_SHA256) + wpa_auth |= WPA2_AUTH_UNSPECIFIED; + } else if (rsn_cap & RSN_CAP_MFPC_MASK) { + brcmf_dbg(TRACE, "MFP Capable\n"); + mfp = BRCMF_MFP_CAPABLE; + } } + offset += RSN_CAP_LEN; /* set wme_bss_disable to sync RSN Capabilities */ err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable", wme_bss_disable); @@ -3965,6 +4014,21 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, brcmf_err("wme_bss_disable error %d\n", err); goto exit; } + + /* Skip PMKID cnt as it is know to be 0 for AP. */ + offset += RSN_PMKID_COUNT_LEN; + + /* See if there is BIP wpa suite left for MFP */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) && + ((offset + WPA_IE_MIN_OUI_LEN) <= len)) { + err = brcmf_fil_bsscfg_data_set(ifp, "bip", + &data[offset], + WPA_IE_MIN_OUI_LEN); + if (err < 0) { + brcmf_err("bip error %d\n", err); + goto exit; + } + } } /* FOR WPS , set SES_OW_ENABLED */ wsec = (pval | gval | SES_OW_ENABLED); @@ -3981,6 +4045,16 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, brcmf_err("wsec error %d\n", err); goto exit; } + /* Configure MFP, this needs to go after wsec otherwise the wsec command + * will overwrite the values set by MFP + */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) { + err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp); + if (err < 0) { + brcmf_err("mfp error %d\n", err); + goto exit; + } + } /* set upper-layer auth */ err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth); if (err < 0) { @@ -4329,7 +4403,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, if (!mbss) { brcmf_set_mpc(ifp, 0); - brcmf_configure_arp_offload(ifp, false); + brcmf_configure_arp_nd_offload(ifp, false); } /* find the RSN_IE */ @@ -4475,7 +4549,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, exit: if ((err) && (!mbss)) { brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_offload(ifp, true); + brcmf_configure_arp_nd_offload(ifp, true); } return err; } @@ -4533,7 +4607,7 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) brcmf_err("bss_enable config failed %d\n", err); } brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_offload(ifp, true); + brcmf_configure_arp_nd_offload(ifp, true); clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); brcmf_net_setcarrier(ifp, false); @@ -4858,7 +4932,32 @@ static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, return ret; } -static struct cfg80211_ops wl_cfg80211_ops = { +#ifdef CONFIG_PM +static int +brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_gtk_rekey_data *gtk) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_gtk_keyinfo_le gtk_le; + int ret; + + brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx); + + memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck)); + memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek)); + memcpy(gtk_le.replay_counter, gtk->replay_ctr, + sizeof(gtk_le.replay_counter)); + + ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", >k_le, + sizeof(gtk_le)); + if (ret < 0) + brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret); + + return ret; +} +#endif + +static struct cfg80211_ops brcmf_cfg80211_ops = { .add_virtual_intf = brcmf_cfg80211_add_iface, .del_virtual_intf = brcmf_cfg80211_del_iface, .change_virtual_intf = brcmf_cfg80211_change_iface, @@ -5405,14 +5504,14 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) { kfree(cfg->conf); cfg->conf = NULL; - kfree(cfg->escan_ioctl_buf); - cfg->escan_ioctl_buf = NULL; kfree(cfg->extra_buf); cfg->extra_buf = NULL; kfree(cfg->wowl.nd); cfg->wowl.nd = NULL; kfree(cfg->wowl.nd_info); cfg->wowl.nd_info = NULL; + kfree(cfg->escan_info.escan_buf); + cfg->escan_info.escan_buf = NULL; } static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -5420,9 +5519,6 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); if (!cfg->conf) goto init_priv_mem_out; - cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - if (!cfg->escan_ioctl_buf) - goto init_priv_mem_out; cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!cfg->extra_buf) goto init_priv_mem_out; @@ -5434,6 +5530,9 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) GFP_KERNEL); if (!cfg->wowl.nd_info) goto init_priv_mem_out; + cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL); + if (!cfg->escan_info.escan_buf) + goto init_priv_mem_out; return 0; @@ -6123,19 +6222,18 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) { #ifdef CONFIG_PM struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - s32 err; - u32 wowl_cap; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { - err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap); - if (!err) { - if (wowl_cap & BRCMF_WOWL_PFN_FOUND) { - brcmf_wowlan_support.flags |= - WIPHY_WOWLAN_NET_DETECT; - init_waitqueue_head(&cfg->wowl.nd_data_wait); - } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) { + brcmf_wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT; + init_waitqueue_head(&cfg->wowl.nd_data_wait); } } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) { + brcmf_wowlan_support.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY; + brcmf_wowlan_support.flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE; + } + wiphy->wowlan = &brcmf_wowlan_support; #endif } @@ -6177,8 +6275,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->n_addresses = i; wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->cipher_suites = __wl_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wiphy->cipher_suites = brcmf_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites); + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) + wiphy->n_cipher_suites--; wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; @@ -6280,7 +6380,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) if (err) goto default_conf_out; - brcmf_configure_arp_offload(ifp, true); + brcmf_configure_arp_nd_offload(ifp, true); cfg->dongle_up = true; default_conf_out: @@ -6398,8 +6498,9 @@ bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg) return armed; } -int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, - u8 action, ulong timeout) + +int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg, + u8 action, ulong timeout) { struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; @@ -6407,28 +6508,85 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], + struct brcmf_fil_country_le *ccreq) +{ + struct brcmfmac_pd_cc *country_codes; + struct brcmfmac_pd_cc_entry *cc; + s32 found_index; + int i; + + country_codes = drvr->settings->country_codes; + if (!country_codes) { + brcmf_dbg(TRACE, "No country codes configured for device\n"); + return -EINVAL; + } + + if ((alpha2[0] == ccreq->country_abbrev[0]) && + (alpha2[1] == ccreq->country_abbrev[1])) { + brcmf_dbg(TRACE, "Country code already set\n"); + return -EAGAIN; + } + + found_index = -1; + for (i = 0; i < country_codes->table_size; i++) { + cc = &country_codes->table[i]; + if ((cc->iso3166[0] == '\0') && (found_index == -1)) + found_index = i; + if ((cc->iso3166[0] == alpha2[0]) && + (cc->iso3166[1] == alpha2[1])) { + found_index = i; + break; + } + } + if (found_index == -1) { + brcmf_dbg(TRACE, "No country code match found\n"); + return -EINVAL; + } + memset(ccreq, 0, sizeof(*ccreq)); + ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev); + memcpy(ccreq->ccode, country_codes->table[found_index].cc, + BRCMF_COUNTRY_BUF_SZ); + ccreq->country_abbrev[0] = alpha2[0]; + ccreq->country_abbrev[1] = alpha2[1]; + ccreq->country_abbrev[2] = 0; + + return 0; +} + static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *req) { struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct brcmf_fil_country_le ccreq; + s32 err; int i; - brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, - req->alpha2[0], req->alpha2[1]); - /* ignore non-ISO3166 country codes */ for (i = 0; i < sizeof(req->alpha2); i++) if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { - brcmf_err("not a ISO3166 code\n"); + brcmf_err("not a ISO3166 code (0x%02x 0x%02x)\n", + req->alpha2[0], req->alpha2[1]); return; } - memset(&ccreq, 0, sizeof(ccreq)); - ccreq.rev = cpu_to_le32(-1); - memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); - if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { - brcmf_err("firmware rejected country setting\n"); + + brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator, + req->alpha2[0], req->alpha2[1]); + + err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq)); + if (err) { + brcmf_err("Country code iovar returned err = %d\n", err); + return; + } + + err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq); + if (err) + return; + + err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); + if (err) { + brcmf_err("Firmware rejected country setting\n"); return; } brcmf_setup_wiphybands(wiphy); @@ -6464,6 +6622,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; struct brcmf_cfg80211_info *cfg; struct wiphy *wiphy; + struct cfg80211_ops *ops; struct brcmf_cfg80211_vif *vif; struct brcmf_if *ifp; s32 err = 0; @@ -6475,8 +6634,17 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, return NULL; } + ops = kzalloc(sizeof(*ops), GFP_KERNEL); + if (!ops) + return NULL; + + memcpy(ops, &brcmf_cfg80211_ops, sizeof(*ops)); ifp = netdev_priv(ndev); - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); +#ifdef CONFIG_PM + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) + ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; +#endif + wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info)); if (!wiphy) { brcmf_err("Could not allocate wiphy device\n"); return NULL; @@ -6486,6 +6654,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, cfg = wiphy_priv(wiphy); cfg->wiphy = wiphy; + cfg->ops = ops; cfg->pub = drvr; init_vif_event(&cfg->vif_event); INIT_LIST_HEAD(&cfg->vif_list); @@ -6596,7 +6765,8 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) { wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; #ifdef CONFIG_PM - if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT) + if (wiphy->wowlan && + wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT) wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; #endif } @@ -6611,6 +6781,7 @@ priv_out: ifp->vif = NULL; wiphy_out: brcmf_free_wiphy(wiphy); + kfree(ops); return NULL; } @@ -6621,6 +6792,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) brcmf_btcoex_detach(cfg); wiphy_unregister(cfg->wiphy); + kfree(cfg->ops); wl_deinit_priv(cfg); brcmf_free_wiphy(cfg->wiphy); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 40efb539a..95e35bcc1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -28,8 +28,11 @@ #define WL_ROAM_TRIGGER_LEVEL -75 #define WL_ROAM_DELTA 20 -#define WL_ESCAN_BUF_SIZE (1024 * 64) -#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ +/* Keep BRCMF_ESCAN_BUF_SIZE below 64K (65536). Allocing over 64K can be + * problematic on some systems and should be avoided. + */ +#define BRCMF_ESCAN_BUF_SIZE 65000 +#define BRCMF_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ #define WL_ESCAN_ACTION_START 1 #define WL_ESCAN_ACTION_CONTINUE 2 @@ -69,7 +72,7 @@ #define BRCMF_VNDR_IE_P2PAF_SHIFT 12 -#define BRCMF_MAX_DEFAULT_KEYS 4 +#define BRCMF_MAX_DEFAULT_KEYS 6 /* beacon loss timeout defaults */ #define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2 @@ -104,7 +107,6 @@ struct brcmf_cfg80211_security { u32 auth_type; u32 cipher_pairwise; u32 cipher_group; - u32 wpa_auth; }; /** @@ -205,7 +207,7 @@ enum wl_escan_state { struct escan_info { u32 escan_state; - u8 escan_buf[WL_ESCAN_BUF_SIZE]; + u8 *escan_buf; struct wiphy *wiphy; struct brcmf_if *ifp; s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, @@ -253,6 +255,7 @@ struct brcmf_cfg80211_wowl { * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface * * @wiphy: wiphy object for cfg80211 interface. + * @ops: pointer to copy of ops as registered with wiphy object. * @conf: dongle configuration. * @p2p: peer-to-peer specific information. * @btcoex: Bluetooth coexistence information. @@ -278,7 +281,6 @@ struct brcmf_cfg80211_wowl { * @escan_info: escan information. * @escan_timeout: Timer for catch scan timeout. * @escan_timeout_work: scan timeout worker. - * @escan_ioctl_buf: dongle command buffer for escan commands. * @vif_list: linked list of vif instances. * @vif_cnt: number of vif instances. * @vif_event: vif event signalling. @@ -286,6 +288,7 @@ struct brcmf_cfg80211_wowl { */ struct brcmf_cfg80211_info { struct wiphy *wiphy; + struct cfg80211_ops *ops; struct brcmf_cfg80211_conf *conf; struct brcmf_p2p_info p2p; struct brcmf_btcoex_info *btcoex; @@ -309,7 +312,6 @@ struct brcmf_cfg80211_info { struct escan_info escan_info; struct timer_list escan_timeout; struct work_struct escan_timeout_work; - u8 *escan_ioctl_buf; struct list_head vif_list; struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; @@ -402,8 +404,8 @@ bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *vif); bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); -int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, - u8 action, ulong timeout); +int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg, + u8 action, ulong timeout); s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, bool aborted, bool fw_abort); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 82e4382eb..0e8f2a079 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -803,7 +803,14 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, *eromaddr -= 4; return -EFAULT; } - } while (desc != DMP_DESC_ADDRESS); + } while (desc != DMP_DESC_ADDRESS && + desc != DMP_DESC_COMPONENT); + + /* stop if we crossed current component border */ + if (desc == DMP_DESC_COMPONENT) { + *eromaddr -= 4; + return 0; + } /* skip upper 32-bit address descriptor */ if (val & DMP_DESC_ADDRSIZE_GT32) @@ -876,7 +883,8 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; /* need core with ports */ - if (nmw + nsw == 0) + if (nmw + nsw == 0 && + id != BCMA_CORE_PMU) continue; /* try to obtain register address info */ @@ -1006,6 +1014,7 @@ static int brcmf_chip_setup(struct brcmf_chip_priv *chip) { struct brcmf_chip *pub; struct brcmf_core_priv *cc; + struct brcmf_core *pmu; u32 base; u32 val; int ret = 0; @@ -1017,11 +1026,15 @@ static int brcmf_chip_setup(struct brcmf_chip_priv *chip) /* get chipcommon capabilites */ pub->cc_caps = chip->ops->read32(chip->ctx, CORE_CC_REG(base, capabilities)); + pub->cc_caps_ext = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, + capabilities_ext)); /* get pmu caps & rev */ + pmu = brcmf_chip_get_pmu(pub); /* after reading cc_caps_ext */ if (pub->cc_caps & CC_CAP_PMU) { val = chip->ops->read32(chip->ctx, - CORE_CC_REG(base, pmucapabilities)); + CORE_CC_REG(pmu->base, pmucapabilities)); pub->pmurev = val & PCAP_REV_MASK; pub->pmucaps = val; } @@ -1120,6 +1133,23 @@ struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub) return &cc->pub; } +struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub) +{ + struct brcmf_core *cc = brcmf_chip_get_chipcommon(pub); + struct brcmf_core *pmu; + + /* See if there is separated PMU core available */ + if (cc->rev >= 35 && + pub->cc_caps_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) { + pmu = brcmf_chip_get_core(pub, BCMA_CORE_PMU); + if (pmu) + return pmu; + } + + /* Fallback to ChipCommon core for older hardware */ + return cc; +} + bool brcmf_chip_iscoreup(struct brcmf_core *pub) { struct brcmf_core_priv *core; @@ -1290,6 +1320,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) { u32 base, addr, reg, pmu_cc3_mask = ~0; struct brcmf_chip_priv *chip; + struct brcmf_core *pmu = brcmf_chip_get_pmu(pub); brcmf_dbg(TRACE, "Enter\n"); @@ -1309,9 +1340,9 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: /* read PMU chipcontrol register 3 */ - addr = CORE_CC_REG(base, chipcontrol_addr); + addr = CORE_CC_REG(pmu->base, chipcontrol_addr); chip->ops->write32(chip->ctx, addr, 3); - addr = CORE_CC_REG(base, chipcontrol_data); + addr = CORE_CC_REG(pmu->base, chipcontrol_data); reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; case BRCM_CC_43430_CHIP_ID: @@ -1319,12 +1350,12 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) reg = chip->ops->read32(chip->ctx, addr); return reg != 0; default: - addr = CORE_CC_REG(base, pmucapabilities_ext); + addr = CORE_CC_REG(pmu->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); + addr = CORE_CC_REG(pmu->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/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h index f6b5feea2..dd0ec3eba 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h @@ -27,6 +27,7 @@ * @chip: chip identifier. * @chiprev: chip revision. * @cc_caps: chipcommon core capabilities. + * @cc_caps_ext: chipcommon core extended capabilities. * @pmucaps: PMU capabilities. * @pmurev: PMU revision. * @rambase: RAM base address (only applicable for ARM CR4 chips). @@ -38,6 +39,7 @@ struct brcmf_chip { u32 chip; u32 chiprev; u32 cc_caps; + u32 cc_caps_ext; u32 pmucaps; u32 pmurev; u32 rambase; @@ -83,6 +85,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, 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); +struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub); 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, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index cfee477a6..9e909e3c2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -27,6 +27,11 @@ #include "fwil_types.h" #include "tracepoint.h" #include "common.h" +#include "of.h" + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); +MODULE_LICENSE("Dual BSD/GPL"); const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -75,6 +80,7 @@ module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); #endif +static struct brcmfmac_platform_data *brcmfmac_pdata; struct brcmf_mp_global_t brcmf_mp_global; int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) @@ -221,33 +227,147 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) } #endif -void brcmf_mp_attach(void) +static void brcmf_mp_attach(void) { + /* If module param firmware path is set then this will always be used, + * if not set then if available use the platform data version. To make + * sure it gets initialized at all, always copy the module param version + */ strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path, BRCMF_FW_ALTPATH_LEN); + if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) && + (brcmf_mp_global.firmware_path[0] == '\0')) { + strlcpy(brcmf_mp_global.firmware_path, + brcmfmac_pdata->fw_alternative_path, + BRCMF_FW_ALTPATH_LEN); + } } -int brcmf_mp_device_attach(struct brcmf_pub *drvr) +struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, + enum brcmf_bus_type bus_type, + u32 chip, u32 chiprev) { - drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC); - if (!drvr->settings) { - brcmf_err("Failed to alloca storage space for settings\n"); - return -ENOMEM; - } - - drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz; - drvr->settings->p2p_enable = !!brcmf_p2p_enable; - drvr->settings->feature_disable = brcmf_feature_disable; - drvr->settings->fcmode = brcmf_fcmode; - drvr->settings->roamoff = !!brcmf_roamoff; + struct brcmf_mp_device *settings; + struct brcmfmac_pd_device *device_pd; + bool found; + int i; + + brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip, + chiprev); + settings = kzalloc(sizeof(*settings), GFP_ATOMIC); + if (!settings) + return NULL; + + /* start by using the module paramaters */ + settings->p2p_enable = !!brcmf_p2p_enable; + settings->feature_disable = brcmf_feature_disable; + settings->fcmode = brcmf_fcmode; + settings->roamoff = !!brcmf_roamoff; #ifdef DEBUG - drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; + settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; #endif + + if (bus_type == BRCMF_BUSTYPE_SDIO) + settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz; + + /* See if there is any device specific platform data configured */ + found = false; + if (brcmfmac_pdata) { + for (i = 0; i < brcmfmac_pdata->device_count; i++) { + device_pd = &brcmfmac_pdata->devices[i]; + if ((device_pd->bus_type == bus_type) && + (device_pd->id == chip) && + ((device_pd->rev == chiprev) || + (device_pd->rev == -1))) { + brcmf_dbg(INFO, "Platform data for device found\n"); + settings->country_codes = + device_pd->country_codes; + if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO) + memcpy(&settings->bus.sdio, + &device_pd->bus.sdio, + sizeof(settings->bus.sdio)); + found = true; + break; + } + } + } + if ((bus_type == BRCMF_BUSTYPE_SDIO) && (!found)) { + /* No platform data for this device. In case of SDIO try OF + * (Open Firwmare) Device Tree. + */ + brcmf_of_probe(dev, &settings->bus.sdio); + } + return settings; +} + +void brcmf_release_module_param(struct brcmf_mp_device *module_param) +{ + kfree(module_param); +} + +static int __init brcmf_common_pd_probe(struct platform_device *pdev) +{ + brcmf_dbg(INFO, "Enter\n"); + + brcmfmac_pdata = dev_get_platdata(&pdev->dev); + + if (brcmfmac_pdata->power_on) + brcmfmac_pdata->power_on(); + + return 0; +} + +static int brcmf_common_pd_remove(struct platform_device *pdev) +{ + brcmf_dbg(INFO, "Enter\n"); + + if (brcmfmac_pdata->power_off) + brcmfmac_pdata->power_off(); + return 0; } -void brcmf_mp_device_detach(struct brcmf_pub *drvr) +static struct platform_driver brcmf_pd = { + .remove = brcmf_common_pd_remove, + .driver = { + .name = BRCMFMAC_PDATA_NAME, + } +}; + +static int __init brcmfmac_module_init(void) +{ + int err; + + /* Initialize debug system first */ + brcmf_debugfs_init(); + + /* Get the platform data (if available) for our devices */ + err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe); + if (err == -ENODEV) + brcmf_dbg(INFO, "No platform data available.\n"); + + /* Initialize global module paramaters */ + brcmf_mp_attach(); + + /* Continue the initialization by registering the different busses */ + err = brcmf_core_init(); + if (err) { + brcmf_debugfs_exit(); + if (brcmfmac_pdata) + platform_driver_unregister(&brcmf_pd); + } + + return err; +} + +static void __exit brcmfmac_module_exit(void) { - kfree(drvr->settings); + brcmf_core_exit(); + if (brcmfmac_pdata) + platform_driver_unregister(&brcmf_pd); + brcmf_debugfs_exit(); } +module_init(brcmfmac_module_init); +module_exit(brcmfmac_module_exit); + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index 3b0a63b98..bd095abca 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -15,6 +15,10 @@ #ifndef BRCMFMAC_COMMON_H #define BRCMFMAC_COMMON_H +#include <linux/platform_device.h> +#include <linux/platform_data/brcmfmac.h> +#include "fwil_types.h" + extern const u8 ALLFFMAC[ETH_ALEN]; #define BRCMF_FW_ALTPATH_LEN 256 @@ -41,37 +45,30 @@ extern struct brcmf_mp_global_t brcmf_mp_global; /** * struct brcmf_mp_device - Device module paramaters. * - * @sdiod_txglomsz: SDIO txglom size. - * @joinboost_5g_rssi: 5g rssi booost for preferred join selection. * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant). * @feature_disable: Feature_disable bitmask. * @fcmode: FWS flow control. * @roamoff: Firmware roaming off? + * @ignore_probe_fail: Ignore probe failure. + * @country_codes: If available, pointer to struct for translating country codes + * @bus: Bus specific platform data. Only SDIO at the mmoment. */ struct brcmf_mp_device { - int sdiod_txglomsz; - int joinboost_5g_rssi; - bool p2p_enable; - int feature_disable; - int fcmode; - bool roamoff; - bool ignore_probe_fail; + bool p2p_enable; + unsigned int feature_disable; + int fcmode; + bool roamoff; + bool ignore_probe_fail; + struct brcmfmac_pd_cc *country_codes; + union { + struct brcmfmac_sdio_pd sdio; + } bus; }; -void brcmf_mp_attach(void); -int brcmf_mp_device_attach(struct brcmf_pub *drvr); -void brcmf_mp_device_detach(struct brcmf_pub *drvr); -#ifdef DEBUG -static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr) -{ - return drvr->settings->ignore_probe_fail; -} -#else -static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr) -{ - return false; -} -#endif +struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, + enum brcmf_bus_type bus_type, + u32 chip, u32 chiprev); +void brcmf_release_module_param(struct brcmf_mp_device *module_param); /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index ed9998b69..ff825cd77 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -20,6 +20,8 @@ #include <linux/inetdevice.h> #include <net/cfg80211.h> #include <net/rtnetlink.h> +#include <net/addrconf.h> +#include <net/ipv6.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> @@ -36,11 +38,7 @@ #include "pcie.h" #include "common.h" -MODULE_AUTHOR("Broadcom Corporation"); -MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); -MODULE_LICENSE("Dual BSD/GPL"); - -#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(50) +#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(950) /* AMPDU rx reordering definitions */ #define BRCMF_RXREORDER_FLOWID_OFFSET 0 @@ -172,6 +170,35 @@ _brcmf_set_mac_address(struct work_struct *work) } } +#if IS_ENABLED(CONFIG_IPV6) +static void _brcmf_update_ndtable(struct work_struct *work) +{ + struct brcmf_if *ifp; + int i, ret; + + ifp = container_of(work, struct brcmf_if, ndoffload_work); + + /* clear the table in firmware */ + ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip_clear", NULL, 0); + if (ret) { + brcmf_dbg(TRACE, "fail to clear nd ip table err:%d\n", ret); + return; + } + + for (i = 0; i < ifp->ipv6addr_idx; i++) { + ret = brcmf_fil_iovar_data_set(ifp, "nd_hostip", + &ifp->ipv6_addr_tbl[i], + sizeof(struct in6_addr)); + if (ret) + brcmf_err("add nd ip err %d\n", ret); + } +} +#else +static void _brcmf_update_ndtable(struct work_struct *work) +{ +} +#endif + static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) { struct brcmf_if *ifp = netdev_priv(ndev); @@ -685,6 +712,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); + INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable); if (rtnl_locked) err = register_netdevice(ndev); @@ -884,6 +912,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx) if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { cancel_work_sync(&ifp->setmacaddr_work); cancel_work_sync(&ifp->multicast_work); + cancel_work_sync(&ifp->ndoffload_work); } brcmf_net_detach(ifp->ndev); } else { @@ -1006,14 +1035,14 @@ static int brcmf_inetaddr_changed(struct notifier_block *nb, return NOTIFY_OK; } for (i = 0; i < ARPOL_MAX_ENTRIES; i++) { - if (addr_table[i] != 0) { - brcmf_fil_iovar_data_set(ifp, - "arp_hostip", &addr_table[i], - sizeof(addr_table[i])); - if (ret) - brcmf_err("add arp ip err %d\n", - ret); - } + if (addr_table[i] == 0) + continue; + ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip", + &addr_table[i], + sizeof(addr_table[i])); + if (ret) + brcmf_err("add arp ip err %d\n", + ret); } } break; @@ -1025,7 +1054,57 @@ static int brcmf_inetaddr_changed(struct notifier_block *nb, } #endif -int brcmf_attach(struct device *dev) +#if IS_ENABLED(CONFIG_IPV6) +static int brcmf_inet6addr_changed(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub, + inet6addr_notifier); + struct inet6_ifaddr *ifa = data; + struct brcmf_if *ifp; + int i; + struct in6_addr *table; + + /* Only handle primary interface */ + ifp = drvr->iflist[0]; + if (!ifp) + return NOTIFY_DONE; + if (ifp->ndev != ifa->idev->dev) + return NOTIFY_DONE; + + table = ifp->ipv6_addr_tbl; + for (i = 0; i < NDOL_MAX_ENTRIES; i++) + if (ipv6_addr_equal(&ifa->addr, &table[i])) + break; + + switch (action) { + case NETDEV_UP: + if (i == NDOL_MAX_ENTRIES) { + if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) { + table[ifp->ipv6addr_idx++] = ifa->addr; + } else { + for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++) + table[i] = table[i + 1]; + table[NDOL_MAX_ENTRIES - 1] = ifa->addr; + } + } + break; + case NETDEV_DOWN: + if (i < NDOL_MAX_ENTRIES) + for (; i < ifp->ipv6addr_idx; i++) + table[i] = table[i + 1]; + break; + default: + break; + } + + schedule_work(&ifp->ndoffload_work); + + return NOTIFY_OK; +} +#endif + +int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings) { struct brcmf_pub *drvr = NULL; int ret = 0; @@ -1047,10 +1126,7 @@ int brcmf_attach(struct device *dev) drvr->hdrlen = 0; drvr->bus_if = dev_get_drvdata(dev); drvr->bus_if->drvr = drvr; - - /* Initialize device specific settings */ - if (brcmf_mp_device_attach(drvr)) - goto fail; + drvr->settings = settings; /* attach debug facilities */ brcmf_debug_attach(drvr); @@ -1164,30 +1240,41 @@ int brcmf_bus_start(struct device *dev) #ifdef CONFIG_INET drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed; ret = register_inetaddr_notifier(&drvr->inetaddr_notifier); + if (ret) + goto fail; + +#if IS_ENABLED(CONFIG_IPV6) + drvr->inet6addr_notifier.notifier_call = brcmf_inet6addr_changed; + ret = register_inet6addr_notifier(&drvr->inet6addr_notifier); + if (ret) { + unregister_inetaddr_notifier(&drvr->inetaddr_notifier); + goto fail; + } #endif +#endif /* CONFIG_INET */ + + return 0; fail: - if (ret < 0) { - brcmf_err("failed: %d\n", ret); - if (drvr->config) { - brcmf_cfg80211_detach(drvr->config); - drvr->config = NULL; - } - if (drvr->fws) { - brcmf_fws_del_interface(ifp); - brcmf_fws_deinit(drvr); - } - if (ifp) - brcmf_net_detach(ifp->ndev); - if (p2p_ifp) - brcmf_net_detach(p2p_ifp->ndev); - drvr->iflist[0] = NULL; - drvr->iflist[1] = NULL; - if (brcmf_ignoring_probe_fail(drvr)) - ret = 0; - return ret; + brcmf_err("failed: %d\n", ret); + if (drvr->config) { + brcmf_cfg80211_detach(drvr->config); + drvr->config = NULL; } - return 0; + if (drvr->fws) { + brcmf_fws_del_interface(ifp); + brcmf_fws_deinit(drvr); + } + if (ifp) + brcmf_net_detach(ifp->ndev); + if (p2p_ifp) + brcmf_net_detach(p2p_ifp->ndev); + drvr->iflist[0] = NULL; + drvr->iflist[1] = NULL; + if (drvr->settings->ignore_probe_fail) + ret = 0; + + return ret; } void brcmf_bus_add_txhdrlen(struct device *dev, uint len) @@ -1237,6 +1324,10 @@ void brcmf_detach(struct device *dev) unregister_inetaddr_notifier(&drvr->inetaddr_notifier); #endif +#if IS_ENABLED(CONFIG_IPV6) + unregister_inet6addr_notifier(&drvr->inet6addr_notifier); +#endif + /* stop firmware event handling */ brcmf_fweh_detach(drvr); if (drvr->config) @@ -1256,8 +1347,6 @@ void brcmf_detach(struct device *dev) brcmf_proto_detach(drvr); - brcmf_mp_device_detach(drvr); - brcmf_debug_detach(drvr); bus_if->drvr = NULL; kfree(drvr); @@ -1324,19 +1413,15 @@ static void brcmf_driver_register(struct work_struct *work) } static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); -static int __init brcmfmac_module_init(void) +int __init brcmf_core_init(void) { - brcmf_debugfs_init(); -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_init(); -#endif if (!schedule_work(&brcmf_driver_work)) return -EBUSY; return 0; } -static void __exit brcmfmac_module_exit(void) +void __exit brcmf_core_exit(void) { cancel_work_sync(&brcmf_driver_work); @@ -1349,8 +1434,5 @@ static void __exit brcmfmac_module_exit(void) #ifdef CONFIG_BRCMFMAC_PCIE brcmf_pcie_exit(); #endif - brcmf_debugfs_exit(); } -module_init(brcmfmac_module_init); -module_exit(brcmfmac_module_exit); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 8f39435f9..7bdb6fef9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -48,6 +48,8 @@ */ #define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 +#define NDOL_MAX_ENTRIES 8 + /** * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info * @@ -143,6 +145,7 @@ struct brcmf_pub { #endif struct notifier_block inetaddr_notifier; + struct notifier_block inet6addr_notifier; struct brcmf_mp_device *settings; }; @@ -175,6 +178,7 @@ enum brcmf_netif_stop_reason { * @stats: interface specific network statistics. * @setmacaddr_work: worker object for setting mac address. * @multicast_work: worker object for multicast provisioning. + * @ndoffload_work: worker object for neighbor discovery offload configuration. * @fws_desc: interface specific firmware-signalling descriptor. * @ifidx: interface index in device firmware. * @bsscfgidx: index of bss associated with this interface. @@ -191,6 +195,7 @@ struct brcmf_if { struct net_device_stats stats; struct work_struct setmacaddr_work; struct work_struct multicast_work; + struct work_struct ndoffload_work; struct brcmf_fws_mac_descriptor *fws_desc; int ifidx; s32 bsscfgidx; @@ -199,6 +204,8 @@ struct brcmf_if { spinlock_t netif_stop_lock; atomic_t pend_8021x_cnt; wait_queue_head_t pend_8021x_wait; + struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES]; + u8 ipv6addr_idx; }; struct brcmf_skb_reorder_data { @@ -220,5 +227,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); +int __init brcmf_core_init(void); +void __exit brcmf_core_exit(void); #endif /* BRCMFMAC_CORE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 1ffa95f1b..62985f2c0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -136,6 +136,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) { struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); struct brcmf_pno_macaddr_le pfn_mac; + u32 wowl_cap; s32 err; brcmf_feat_firmware_capabilities(ifp); @@ -143,11 +144,24 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) { + err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap); + if (!err) { + ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_WOWL_ARP_ND); + if (wowl_cap & BRCMF_WOWL_PFN_FOUND) + ifp->drvr->feat_flags |= + BIT(BRCMF_FEAT_WOWL_ND); + if (wowl_cap & BRCMF_WOWL_GTK_FAILURE) + ifp->drvr->feat_flags |= + BIT(BRCMF_FEAT_WOWL_GTK); + } + } /* MBSS does not work for 43362 */ if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID) ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp"); pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 2e2479d41..db4733a95 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -27,6 +27,10 @@ * RSDB: Real Simultaneous Dual Band * TDLS: Tunneled Direct Link Setup * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan. + * WOWL_ND: WOWL net detect (PNO) + * WOWL_GTK: (WOWL) GTK rekeying offload + * WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL. + * MFP: 802.11w Management Frame Protection. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -36,7 +40,11 @@ BRCMF_FEAT_DEF(P2P) \ BRCMF_FEAT_DEF(RSDB) \ BRCMF_FEAT_DEF(TDLS) \ - BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) + BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \ + BRCMF_FEAT_DEF(WOWL_ND) \ + BRCMF_FEAT_DEF(WOWL_GTK) \ + BRCMF_FEAT_DEF(WOWL_ARP_ND) \ + BRCMF_FEAT_DEF(MFP) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 67443144c..d278c1117 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -93,7 +93,7 @@ static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) c = nvp->data[nvp->pos]; if (c == '\n') return COMMENT; - if (is_whitespace(c)) + if (is_whitespace(c) || c == '\0') goto proceed; if (c == '#') return COMMENT; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 7b26fb1b4..d414fbbcc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -26,50 +26,6 @@ #include "fwil.h" /** - * struct brcm_ethhdr - broadcom specific ether header. - * - * @subtype: subtype for this packet. - * @length: TODO: length of appended data. - * @version: version indication. - * @oui: OUI of this packet. - * @usr_subtype: subtype for this OUI. - */ -struct brcm_ethhdr { - __be16 subtype; - __be16 length; - u8 version; - u8 oui[3]; - __be16 usr_subtype; -} __packed; - -struct brcmf_event_msg_be { - __be16 version; - __be16 flags; - __be32 event_type; - __be32 status; - __be32 reason; - __be32 auth_type; - __be32 datalen; - u8 addr[ETH_ALEN]; - char ifname[IFNAMSIZ]; - u8 ifidx; - u8 bsscfgidx; -} __packed; - -/** - * struct brcmf_event - contents of broadcom event packet. - * - * @eth: standard ether header. - * @hdr: broadcom specific ether header. - * @msg: common part of the actual event message. - */ -struct brcmf_event { - struct ethhdr eth; - struct brcm_ethhdr hdr; - struct brcmf_event_msg_be msg; -} __packed; - -/** * struct brcmf_fweh_queue_item - event item on event queue. * * @q: list element for queuing. @@ -85,6 +41,7 @@ struct brcmf_fweh_queue_item { u8 ifidx; u8 ifaddr[ETH_ALEN]; struct brcmf_event_msg_be emsg; + u32 datalen; u8 data[0]; }; @@ -294,6 +251,11 @@ static void brcmf_fweh_event_worker(struct work_struct *work) brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data, min_t(u32, emsg.datalen, 64), "event payload, len=%d\n", emsg.datalen); + if (emsg.datalen > event->datalen) { + brcmf_err("event invalid length header=%d, msg=%d\n", + event->datalen, emsg.datalen); + goto event_free; + } /* special handling of interface event */ if (event->code == BRCMF_E_IF) { @@ -439,7 +401,8 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp) * dispatch the event to a registered handler (using worker). */ void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet) + struct brcmf_event *event_packet, + u32 packet_len) { enum brcmf_fweh_event_code code; struct brcmf_fweh_info *fweh = &drvr->fweh; @@ -459,6 +422,9 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, if (code != BRCMF_E_IF && !fweh->evt_handler[code]) return; + if (datalen > BRCMF_DCMD_MAXLEN) + return; + if (in_interrupt()) alloc_flag = GFP_ATOMIC; @@ -472,6 +438,7 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, /* use memcpy to get aligned event message */ memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); memcpy(event->data, data, datalen); + event->datalen = datalen; memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN); brcmf_fweh_queue_event(fweh, event); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h index 5e39e2a9e..26ff5a964 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -27,7 +27,6 @@ struct brcmf_pub; struct brcmf_if; struct brcmf_cfg80211_info; -struct brcmf_event; /* list of firmware events */ #define BRCMF_FWEH_EVENT_ENUM_DEFLIST \ @@ -180,13 +179,55 @@ enum brcmf_fweh_event_code { /** * definitions for event packet validation. */ -#define BRCMF_EVENT_OUI_OFFSET 19 -#define BRCM_OUI "\x00\x10\x18" -#define DOT11_OUI_LEN 3 -#define BCMILCP_BCM_SUBTYPE_EVENT 1 +#define BRCM_OUI "\x00\x10\x18" +#define BCMILCP_BCM_SUBTYPE_EVENT 1 /** + * struct brcm_ethhdr - broadcom specific ether header. + * + * @subtype: subtype for this packet. + * @length: TODO: length of appended data. + * @version: version indication. + * @oui: OUI of this packet. + * @usr_subtype: subtype for this OUI. + */ +struct brcm_ethhdr { + __be16 subtype; + __be16 length; + u8 version; + u8 oui[3]; + __be16 usr_subtype; +} __packed; + +struct brcmf_event_msg_be { + __be16 version; + __be16 flags; + __be32 event_type; + __be32 status; + __be32 reason; + __be32 auth_type; + __be32 datalen; + u8 addr[ETH_ALEN]; + char ifname[IFNAMSIZ]; + u8 ifidx; + u8 bsscfgidx; +} __packed; + +/** + * struct brcmf_event - contents of broadcom event packet. + * + * @eth: standard ether header. + * @hdr: broadcom specific ether header. + * @msg: common part of the actual event message. + */ +struct brcmf_event { + struct ethhdr eth; + struct brcm_ethhdr hdr; + struct brcmf_event_msg_be msg; +} __packed; + +/** * struct brcmf_event_msg - firmware event message. * * @version: version information. @@ -256,34 +297,35 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code); int brcmf_fweh_activate_events(struct brcmf_if *ifp); void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet); + struct brcmf_event *event_packet, + u32 packet_len); void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, struct sk_buff *skb) { struct brcmf_event *event_packet; - u8 *data; u16 usr_stype; /* only process events when protocol matches */ if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) return; + if ((skb->len + ETH_HLEN) < sizeof(*event_packet)) + return; + /* check for BRCM oui match */ event_packet = (struct brcmf_event *)skb_mac_header(skb); - data = (u8 *)event_packet; - data += BRCMF_EVENT_OUI_OFFSET; - if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN)) + if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0], + sizeof(event_packet->hdr.oui))) return; /* final match on usr_subtype */ - data += DOT11_OUI_LEN; - usr_stype = get_unaligned_be16(data); + usr_stype = get_unaligned_be16(&event_packet->hdr.usr_subtype); if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) return; - brcmf_fweh_process_event(drvr, event_packet); + brcmf_fweh_process_event(drvr, event_packet, skb->len + ETH_HLEN); } #endif /* FWEH_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 1afc2ad83..a4118c0ef 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -111,7 +111,9 @@ /* Wakeup if received matched secured pattern: */ #define BRCMF_WOWL_SECURE (1 << 25) /* Wakeup on finding preferred network */ -#define BRCMF_WOWL_PFN_FOUND (1 << 26) +#define BRCMF_WOWL_PFN_FOUND (1 << 27) +/* Wakeup on receiving pairwise key EAP packets: */ +#define WIPHY_WOWL_EAP_PK (1 << 28) /* Link Down indication in WoWL mode: */ #define BRCMF_WOWL_LINKDOWN (1 << 31) @@ -134,6 +136,16 @@ #define BRCMF_PFN_MAC_OUI_ONLY BIT(0) #define BRCMF_PFN_SET_MAC_UNASSOC BIT(1) +#define BRCMF_MCSSET_LEN 16 + +#define BRCMF_RSN_KCK_LENGTH 16 +#define BRCMF_RSN_KEK_LENGTH 16 +#define BRCMF_RSN_REPLAY_LEN 8 + +#define BRCMF_MFP_NONE 0 +#define BRCMF_MFP_CAPABLE 1 +#define BRCMF_MFP_REQUIRED 2 + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, @@ -279,7 +291,7 @@ struct brcmf_bss_info_le { __le32 reserved32[1]; /* Reserved for expansion of BSS properties */ u8 flags; /* flags */ u8 reserved[3]; /* Reserved for expansion of BSS properties */ - u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ + u8 basic_mcs[BRCMF_MCSSET_LEN]; /* 802.11N BSS required MCS set */ __le16 ie_offset; /* offset at which IEs start, from beginning */ __le32 ie_length; /* byte length of Information Elements */ @@ -787,4 +799,17 @@ struct brcmf_pktcnt_le { __le32 rx_ocast_good_pkt; }; +/** + * struct brcmf_gtk_keyinfo_le - GTP rekey data + * + * @kck: key confirmation key. + * @kek: key encryption key. + * @replay_counter: replay counter. + */ +struct brcmf_gtk_keyinfo_le { + u8 kck[BRCMF_RSN_KCK_LENGTH]; + u8 kek[BRCMF_RSN_KEK_LENGTH]; + u8 replay_counter[BRCMF_RSN_REPLAY_LEN]; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index 03f35e0c5..425c41dc0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -16,17 +16,15 @@ #include <linux/init.h> #include <linux/of.h> #include <linux/of_irq.h> -#include <linux/mmc/card.h> -#include <linux/platform_data/brcmfmac-sdio.h> -#include <linux/mmc/sdio_func.h> #include <defs.h> #include "debug.h" -#include "sdio.h" +#include "core.h" +#include "common.h" +#include "of.h" -void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio) { - struct device *dev = sdiodev->dev; struct device_node *np = dev->of_node; int irq; u32 irqf; @@ -35,12 +33,8 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) return; - sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL); - if (!sdiodev->pdata) - return; - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdiodev->pdata->drive_strength = val; + sdio->drive_strength = val; /* make sure there are interrupts defined in the node */ if (!of_find_property(np, "interrupts", NULL)) @@ -53,7 +47,7 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) } irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); - sdiodev->pdata->oob_irq_supported = true; - sdiodev->pdata->oob_irq_nr = irq; - sdiodev->pdata->oob_irq_flags = irqf; + sdio->oob_irq_supported = true; + sdio->oob_irq_nr = irq; + sdio->oob_irq_flags = irqf; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h index 5f7c3550d..a9d94c15d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.h @@ -14,9 +14,9 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef CONFIG_OF -void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev); +void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio); #else -static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +static void brcmf_of_probe(struct device *dev, struct brcmfmac_sdio_pd *sdio) { } #endif /* CONFIG_OF */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index 821b6494f..b5a49e564 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -1361,6 +1361,11 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, u16 mgmt_type; u8 action; + if (e->datalen < sizeof(*rxframe)) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + ch.chspec = be16_to_cpu(rxframe->chanspec); cfg->d11inf.decchspec(&ch); /* Check if wpa_supplicant has registered for this frame */ @@ -1858,6 +1863,11 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code, e->reason); + if (e->datalen < sizeof(*rxframe)) { + brcmf_dbg(SCAN, "Event data to small. Ignore\n"); + return 0; + } + ch.chspec = be16_to_cpu(rxframe->chanspec); cfg->d11inf.decchspec(&ch); @@ -1988,8 +1998,8 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, brcmf_cfg80211_arm_vif_event(cfg, NULL); return err; } - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE, - BRCMF_VIF_EVENT_TIMEOUT); + err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_CHANGE, + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("No BRCMF_E_IF_CHANGE event received\n"); @@ -2090,8 +2100,8 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, } /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, - BRCMF_VIF_EVENT_TIMEOUT); + err = brcmf_cfg80211_wait_vif_event(p2p->cfg, BRCMF_E_IF_ADD, + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); brcmf_fweh_p2pdev_setup(pri_ifp, false); if (!err) { @@ -2180,8 +2190,8 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, } /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - BRCMF_VIF_EVENT_TIMEOUT); + err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD, + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("timeout occurred\n"); @@ -2272,8 +2282,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) } if (!err) { /* wait for firmware event */ - err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, - BRCMF_VIF_EVENT_TIMEOUT); + err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL, + BRCMF_VIF_EVENT_TIMEOUT); if (!err) err = -EIO; else diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 3d2be4e9c..099879ce8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -37,6 +37,8 @@ #include "pcie.h" #include "firmware.h" #include "chip.h" +#include "core.h" +#include "common.h" enum brcmf_pcie_state { @@ -53,6 +55,7 @@ BRCMF_FW_NVRAM_DEF(4358, "/*(DEBLOBBED)*/", "brcmfmac4358-pcie.txt"); BRCMF_FW_NVRAM_DEF(4359, "/*(DEBLOBBED)*/", "brcmfmac4359-pcie.txt"); BRCMF_FW_NVRAM_DEF(4365B, "/*(DEBLOBBED)*/", "brcmfmac4365b-pcie.txt"); BRCMF_FW_NVRAM_DEF(4366B, "/*(DEBLOBBED)*/", "brcmfmac4366b-pcie.txt"); +BRCMF_FW_NVRAM_DEF(4366C, "/*(DEBLOBBED)*/", "brcmfmac4366c-pcie.txt"); BRCMF_FW_NVRAM_DEF(4371, "/*(DEBLOBBED)*/", "brcmfmac4371-pcie.txt"); static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { @@ -66,13 +69,13 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4358_CHIP_ID, 0xFFFFFFFF, 4358), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4365_CHIP_ID, 0xFFFFFFFF, 4365B), - BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFFF, 4366B), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0x0000000F, 4366B), + BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), }; #define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ -#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024) #define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) /* backplane addres space accessed by BAR0 */ @@ -99,9 +102,6 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 #define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140 -#define BRCMF_PCIE_GENREV1 1 -#define BRCMF_PCIE_GENREV2 2 - #define BRCMF_PCIE2_INTA 0x01 #define BRCMF_PCIE2_INTB 0x02 @@ -207,6 +207,10 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 #define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 +/* Magic number at a magic location to find RAM size */ +#define BRCMF_RAMSIZE_MAGIC 0x534d4152 /* SMAR */ +#define BRCMF_RAMSIZE_OFFSET 0x6c + struct brcmf_pcie_console { u32 base_addr; @@ -248,14 +252,11 @@ struct brcmf_pciedev_info { char nvram_name[BRCMF_FW_NAME_LEN]; void __iomem *regs; void __iomem *tcm; - u32 tcm_size; u32 ram_base; u32 ram_size; struct brcmf_chip *ci; u32 coreid; - u32 generic_corerev; struct brcmf_pcie_shared_info shared; - void (*ringbell)(struct brcmf_pciedev_info *devinfo); wait_queue_head_t mbdata_resp_wait; bool mbdata_completed; bool irq_allocated; @@ -267,6 +268,7 @@ struct brcmf_pciedev_info { u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u16 value); + struct brcmf_mp_device *settings; }; struct brcmf_pcie_ringbuf { @@ -675,10 +677,8 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); - if (waitqueue_active(&devinfo->mbdata_resp_wait)) { - devinfo->mbdata_completed = true; - wake_up(&devinfo->mbdata_resp_wait); - } + devinfo->mbdata_completed = true; + wake_up(&devinfo->mbdata_resp_wait); } } @@ -742,68 +742,22 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) } -static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo) -{ - u32 reg_value; - - brcmf_dbg(PCIE, "RING !\n"); - reg_value = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_MAILBOXINT); - reg_value |= BRCMF_PCIE2_INTB; - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, - reg_value); -} - - -static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo) -{ - brcmf_dbg(PCIE, "RING !\n"); - /* Any arbitrary value will do, lets use 1 */ - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1); -} - - static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) { - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, - 0); - else - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, - 0); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, 0); } static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) { - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, - BRCMF_PCIE_INT_DEF); - else - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, - BRCMF_PCIE_MB_INT_D2H_DB | - BRCMF_PCIE_MB_INT_FN0_0 | - BRCMF_PCIE_MB_INT_FN0_1); -} - - -static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - u32 status; - - status = 0; - pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status); - if (status) { - brcmf_pcie_intr_disable(devinfo); - brcmf_dbg(PCIE, "Enter\n"); - return IRQ_WAKE_THREAD; - } - return IRQ_NONE; + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + BRCMF_PCIE_MB_INT_D2H_DB | + BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1); } -static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg) +static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; @@ -816,29 +770,7 @@ static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg) } -static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg) -{ - struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - const struct pci_dev *pdev = devinfo->pdev; - u32 status; - - devinfo->in_irq = true; - status = 0; - pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); - brcmf_dbg(PCIE, "Enter %x\n", status); - if (status) { - pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); - if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) - brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev); - } - if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) - brcmf_pcie_intr_enable(devinfo); - devinfo->in_irq = false; - return IRQ_HANDLED; -} - - -static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg) +static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; u32 status; @@ -875,28 +807,14 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) brcmf_pcie_intr_disable(devinfo); brcmf_dbg(PCIE, "Enter\n"); - /* is it a v1 or v2 implementation */ + pci_enable_msi(pdev); - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { - if (request_threaded_irq(pdev->irq, - brcmf_pcie_quick_check_isr_v1, - brcmf_pcie_isr_thread_v1, - IRQF_SHARED, "brcmf_pcie_intr", - devinfo)) { - pci_disable_msi(pdev); - brcmf_err("Failed to request IRQ %d\n", pdev->irq); - return -EIO; - } - } else { - if (request_threaded_irq(pdev->irq, - brcmf_pcie_quick_check_isr_v2, - brcmf_pcie_isr_thread_v2, - IRQF_SHARED, "brcmf_pcie_intr", - devinfo)) { - pci_disable_msi(pdev); - brcmf_err("Failed to request IRQ %d\n", pdev->irq); - return -EIO; - } + if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr, + brcmf_pcie_isr_thread, IRQF_SHARED, + "brcmf_pcie_intr", devinfo)) { + pci_disable_msi(pdev); + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; } devinfo->irq_allocated = true; return 0; @@ -927,16 +845,9 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) if (devinfo->in_irq) brcmf_err("Still in IRQ (processing) !!!\n"); - if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { - status = 0; - pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); - pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); - } else { - status = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_MAILBOXINT); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, - status); - } + status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status); + devinfo->irq_allocated = false; } @@ -985,7 +896,9 @@ static int brcmf_pcie_ring_mb_ring_bell(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - devinfo->ringbell(devinfo); + brcmf_dbg(PCIE, "RING !\n"); + /* Any arbitrary value will do, lets use 1 */ + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1); return 0; } @@ -1412,6 +1325,28 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { }; +static void +brcmf_pcie_adjust_ramsize(struct brcmf_pciedev_info *devinfo, u8 *data, + u32 data_len) +{ + __le32 *field; + u32 newsize; + + if (data_len < BRCMF_RAMSIZE_OFFSET + 8) + return; + + field = (__le32 *)&data[BRCMF_RAMSIZE_OFFSET]; + if (le32_to_cpup(field) != BRCMF_RAMSIZE_MAGIC) + return; + field++; + newsize = le32_to_cpup(field); + + brcmf_dbg(PCIE, "Found ramsize info in FW, adjusting to 0x%x\n", + newsize); + devinfo->ci->ramsize = newsize; +} + + static int brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, u32 sharedram_addr) @@ -1477,9 +1412,6 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, u32 address; u32 resetintr; - devinfo->ringbell = brcmf_pcie_ringbell_v2; - devinfo->generic_corerev = BRCMF_PCIE_GENREV2; - brcmf_dbg(PCIE, "Halt ARM.\n"); err = brcmf_pcie_enter_download_state(devinfo); if (err) @@ -1566,8 +1498,7 @@ static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) } devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); - devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE); - devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE; + devinfo->tcm = ioremap_nocache(bar1_addr, bar1_size); if (!devinfo->regs || !devinfo->tcm) { brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs, @@ -1576,8 +1507,9 @@ static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) } brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n", devinfo->regs, (unsigned long long)bar0_addr); - brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n", - devinfo->tcm, (unsigned long long)bar1_addr); + brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx size 0x%x\n", + devinfo->tcm, (unsigned long long)bar1_addr, + (unsigned int)bar1_size); return 0; } @@ -1594,16 +1526,16 @@ static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) } -static int brcmf_pcie_attach_bus(struct device *dev) +static int brcmf_pcie_attach_bus(struct brcmf_pciedev_info *devinfo) { int ret; /* Attach to the common driver interface */ - ret = brcmf_attach(dev); + ret = brcmf_attach(&devinfo->pdev->dev, devinfo->settings); if (ret) { brcmf_err("brcmf_attach failed\n"); } else { - ret = brcmf_bus_start(dev); + ret = brcmf_bus_start(&devinfo->pdev->dev); if (ret) brcmf_err("dongle is not responding\n"); } @@ -1694,6 +1626,13 @@ static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, brcmf_pcie_attach(devinfo); + /* Some of the firmwares have the size of the memory of the device + * defined inside the firmware. This is because part of the memory in + * the device is shared and the devision is determined by FW. Parse + * the firmware and adjust the chip memory size now. + */ + brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size); + ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); if (ret) goto fail; @@ -1734,7 +1673,7 @@ static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, init_waitqueue_head(&devinfo->mbdata_resp_wait); brcmf_pcie_intr_enable(devinfo); - if (brcmf_pcie_attach_bus(bus->dev) == 0) + if (brcmf_pcie_attach_bus(devinfo) == 0) return; brcmf_pcie_bus_console_read(devinfo); @@ -1778,6 +1717,15 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto fail; } + devinfo->settings = brcmf_get_module_param(&devinfo->pdev->dev, + BRCMF_BUSTYPE_PCIE, + devinfo->ci->chip, + devinfo->ci->chiprev); + if (!devinfo->settings) { + ret = -ENOMEM; + goto fail; + } + bus = kzalloc(sizeof(*bus), GFP_KERNEL); if (!bus) { ret = -ENOMEM; @@ -1822,6 +1770,8 @@ fail: brcmf_pcie_release_resource(devinfo); if (devinfo->ci) brcmf_chip_detach(devinfo->ci); + if (devinfo->settings) + brcmf_release_module_param(devinfo->settings); kfree(pcie_bus_dev); kfree(devinfo); return ret; @@ -1861,6 +1811,8 @@ brcmf_pcie_remove(struct pci_dev *pdev) if (devinfo->ci) brcmf_chip_detach(devinfo->ci); + if (devinfo->settings) + brcmf_release_module_param(devinfo->settings); kfree(devinfo); dev_set_drvdata(&pdev->dev, NULL); @@ -1951,6 +1903,9 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { #define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } +#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev) { \ + BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ + subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), @@ -1966,6 +1921,7 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365), BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 88ddcdd1b..c5be36079 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -33,8 +33,6 @@ #include <linux/bcma/bcma.h> #include <linux/debugfs.h> #include <linux/vmalloc.h> -#include <linux/platform_data/brcmfmac-sdio.h> -#include <linux/moduleparam.h> #include <asm/unaligned.h> #include <defs.h> #include <brcmu_wifi.h> @@ -44,9 +42,11 @@ #include "sdio.h" #include "chip.h" #include "firmware.h" +#include "core.h" +#include "common.h" -#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2000) -#define CTL_DONE_TIMEOUT msecs_to_jiffies(2000) +#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) +#define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) #ifdef DEBUG @@ -2442,15 +2442,17 @@ static void brcmf_sdio_bus_stop(struct device *dev) static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) { + struct brcmf_sdio_dev *sdiodev; unsigned long flags; - if (bus->sdiodev->oob_irq_requested) { - spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); - if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { - enable_irq(bus->sdiodev->pdata->oob_irq_nr); - bus->sdiodev->irq_en = true; + sdiodev = bus->sdiodev; + if (sdiodev->oob_irq_requested) { + spin_lock_irqsave(&sdiodev->irq_en_lock, flags); + if (!sdiodev->irq_en && !atomic_read(&bus->ipend)) { + enable_irq(sdiodev->settings->bus.sdio.oob_irq_nr); + sdiodev->irq_en = true; } - spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); + spin_unlock_irqrestore(&sdiodev->irq_en_lock, flags); } } @@ -3394,9 +3396,7 @@ static int brcmf_sdio_bus_preinit(struct device *dev) sizeof(u32)); } else { /* otherwise, set txglomalign */ - value = 4; - if (sdiodev->pdata) - value = sdiodev->pdata->sd_sgentry_align; + value = sdiodev->settings->bus.sdio.sd_sgentry_align; /* SDIO ADMA requires at least 32 bit alignment */ value = max_t(u32, value, 4); err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, @@ -3615,7 +3615,6 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, 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; @@ -3658,14 +3657,15 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } if (str_tab != NULL) { + struct brcmf_core *pmu = brcmf_chip_get_pmu(ci); + 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); + addr = CORE_CC_REG(pmu->base, chipcontrol_addr); brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); cc_data_temp &= ~str_mask; @@ -3775,26 +3775,28 @@ static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { static bool brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { + struct brcmf_sdio_dev *sdiodev; u8 clkctl = 0; int err = 0; int reg_addr; u32 reg_val; u32 drivestrength; - sdio_claim_host(bus->sdiodev->func[1]); + sdiodev = bus->sdiodev; + sdio_claim_host(sdiodev->func[1]); pr_debug("F1 signature read @0x18000000=0x%4x\n", - brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); + brcmf_sdiod_regrl(sdiodev, SI_ENUM_BASE, NULL)); /* * Force PLL off until brcmf_chip_attach() * programs PLL control regs */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, BRCMF_INIT_CLKCTL1, &err); if (!err) - clkctl = brcmf_sdiod_regrb(bus->sdiodev, + clkctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { @@ -3803,51 +3805,81 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; } - bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + bus->ci = brcmf_chip_attach(sdiodev, &brcmf_sdio_buscore_ops); if (IS_ERR(bus->ci)) { brcmf_err("brcmf_chip_attach failed!\n"); bus->ci = NULL; goto fail; } + sdiodev->settings = brcmf_get_module_param(sdiodev->dev, + BRCMF_BUSTYPE_SDIO, + bus->ci->chip, + bus->ci->chiprev); + if (!sdiodev->settings) { + brcmf_err("Failed to get device parameters\n"); + goto fail; + } + /* platform specific configuration: + * alignments must be at least 4 bytes for ADMA + */ + bus->head_align = ALIGNMENT; + bus->sgentry_align = ALIGNMENT; + if (sdiodev->settings->bus.sdio.sd_head_align > ALIGNMENT) + bus->head_align = sdiodev->settings->bus.sdio.sd_head_align; + if (sdiodev->settings->bus.sdio.sd_sgentry_align > ALIGNMENT) + bus->sgentry_align = + sdiodev->settings->bus.sdio.sd_sgentry_align; + + /* allocate scatter-gather table. sg support + * will be disabled upon allocation failure. + */ + brcmf_sdiod_sgtable_alloc(sdiodev); + +#ifdef CONFIG_PM_SLEEP + /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ + * is true or when platform data OOB irq is true). + */ + if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && + ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || + (sdiodev->settings->bus.sdio.oob_irq_supported))) + sdiodev->bus_if->wowl_supported = true; +#endif if (brcmf_sdio_kso_init(bus)) { brcmf_err("error enabling KSO\n"); goto fail; } - if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength)) - drivestrength = bus->sdiodev->pdata->drive_strength; + if (sdiodev->settings->bus.sdio.drive_strength) + drivestrength = sdiodev->settings->bus.sdio.drive_strength; else drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + brcmf_sdio_drivestrengthinit(sdiodev, bus->ci, drivestrength); /* Set card control so an SDIO card reset does a WLAN backplane reset */ - reg_val = brcmf_sdiod_regrb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, &err); + reg_val = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err); if (err) goto fail; reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; - brcmf_sdiod_regwb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); if (err) goto fail; /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, - pmucontrol); - reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); + reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol); + reg_val = brcmf_sdiod_regrl(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(sdiodev, reg_addr, reg_val, &err); if (err) goto fail; - sdio_release_host(bus->sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); @@ -3868,7 +3900,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) return true; fail: - sdio_release_host(bus->sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); return false; } @@ -4046,18 +4078,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->txminmax = BRCMF_TXMINMAX; bus->tx_seq = SDPCM_SEQ_WRAP - 1; - /* platform specific configuration: - * alignments must be at least 4 bytes for ADMA - */ - bus->head_align = ALIGNMENT; - bus->sgentry_align = ALIGNMENT; - if (sdiodev->pdata) { - if (sdiodev->pdata->sd_head_align > ALIGNMENT) - bus->head_align = sdiodev->pdata->sd_head_align; - if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) - bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; - } - /* single-threaded workqueue */ wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, dev_name(&sdiodev->func[1]->dev)); @@ -4108,7 +4128,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; /* Attach to the common layer, reserve hdr space */ - ret = brcmf_attach(bus->sdiodev->dev); + ret = brcmf_attach(bus->sdiodev->dev, bus->sdiodev->settings); if (ret != 0) { brcmf_err("brcmf_attach failed\n"); goto fail; @@ -4212,6 +4232,8 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) } brcmf_chip_detach(bus->ci); } + if (bus->sdiodev->settings) + brcmf_release_module_param(bus->sdiodev->settings); kfree(bus->rxbuf); kfree(bus->hdrbuf); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 23f223150..dcf0ce8cd 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -184,7 +184,7 @@ struct brcmf_sdio_dev { struct brcmf_sdio *bus; struct device *dev; struct brcmf_bus *bus_if; - struct brcmfmac_sdio_platform_data *pdata; + struct brcmf_mp_device *settings; bool oob_irq_requested; bool irq_en; /* irq enable flags */ spinlock_t irq_en_lock; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index cbd14fe9f..7fb546547 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -27,6 +27,8 @@ #include "debug.h" #include "firmware.h" #include "usb.h" +#include "core.h" +#include "common.h" #define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -171,6 +173,7 @@ struct brcmf_usbdev_info { struct urb *bulk_urb; /* used for FW download */ bool wowl_enabled; + struct brcmf_mp_device *settings; }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, @@ -1027,6 +1030,9 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) kfree(devinfo->tx_reqs); kfree(devinfo->rx_reqs); + + if (devinfo->settings) + brcmf_release_module_param(devinfo->settings); } @@ -1136,7 +1142,7 @@ static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) int ret; /* Attach to the common driver interface */ - ret = brcmf_attach(devinfo->dev); + ret = brcmf_attach(devinfo->dev, devinfo->settings); if (ret) { brcmf_err("brcmf_attach failed\n"); return ret; @@ -1223,6 +1229,14 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->wowl_supported = true; #endif + devinfo->settings = brcmf_get_module_param(bus->dev, BRCMF_BUSTYPE_USB, + bus_pub->devid, + bus_pub->chiprev); + if (!devinfo->settings) { + ret = -ENOMEM; + goto fail; + } + if (!brcmf_usb_dlneeded(devinfo)) { ret = brcmf_usb_bus_setup(devinfo); if (ret) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index 2ce12ed2a..4ab06e1e6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -817,13 +817,15 @@ brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, static int brcms_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct brcms_info *wl = hw->priv; struct scb *scb = &wl->wlc->pri_scb; int status; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u8 buf_size = params->buf_size; if (WARN_ON(scb->magic != SCB_MAGIC)) return -EIDRM; diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h index 3f68dd5ec..7b9a77981 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h @@ -236,6 +236,8 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WPA2_AUTH_RESERVED3 0x0200 #define WPA2_AUTH_RESERVED4 0x0400 #define WPA2_AUTH_RESERVED5 0x0800 +#define WPA2_AUTH_1X_SHA256 0x1000 /* 1X with SHA256 key derivation */ +#define WPA2_AUTH_PSK_SHA256 0x8000 /* PSK with SHA256 key derivation */ #define DOT11_DEFAULT_RTS_LEN 2347 #define DOT11_DEFAULT_FRAG_LEN 2346 diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 1f0312dbf..baee8fe03 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -5982,12 +5982,14 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct il_priv *il = hw->priv; int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); diff --git a/drivers/net/wireless/intel/iwlegacy/4965.h b/drivers/net/wireless/intel/iwlegacy/4965.h index 8ab8706f9..e432715e0 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965.h +++ b/drivers/net/wireless/intel/iwlegacy/4965.h @@ -182,9 +182,7 @@ void il4965_mac_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); void diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 7438fbeef..16c4f3834 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -98,6 +98,18 @@ config IWLWIFI_UAPSD If unsure, say N. +config IWLWIFI_PCIE_RTPM + bool "Enable runtime power management mode for PCIe devices" + depends on IWLMVM && PM + default false + help + Say Y here to enable runtime power management for PCIe + devices. If enabled, the device will go into low power mode + when idle for a short period of time, allowing for improved + power saving during runtime. + + If unsure, say N. + menu "Debugging Options" config IWLWIFI_DEBUG diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c index 1aabb5ec0..1bbd17ada 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c @@ -152,11 +152,14 @@ static void iwl_led_brightness_set(struct led_classdev *led_cdev, { struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); unsigned long on = 0; + unsigned long off = 0; if (brightness > 0) on = IWL_LED_SOLID; + else + off = IWL_LED_SOLID; - iwl_led_cmd(priv, on, 0); + iwl_led_cmd(priv, on, off); } static int iwl_led_blink_set(struct led_classdev *led_cdev, diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index 4841be2aa..179946926 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -943,14 +943,16 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: if (sta) { + u64 pn64; + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; rx_p1ks = data->tkip->rx_uni; - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + pn64 = atomic64_read(&key->tx_pn); + tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64)); + tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64)); ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); @@ -996,19 +998,13 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, break; case WLAN_CIPHER_SUITE_CCMP: if (sta) { - u8 *pn = seq.ccmp.pn; + u64 pn64; aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - ieee80211_get_key_tx_seq(key, &seq); - aes_tx_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); + pn64 = atomic64_read(&key->tx_pn); + aes_tx_sc->pn = cpu_to_le64(pn64); } else aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 29ea1c670..c63ea7957 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -396,7 +396,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - iwl_trans_d3_suspend(priv->trans, false); + iwl_trans_d3_suspend(priv->trans, false, true); goto out; @@ -469,7 +469,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) /* we'll clear ctx->vif during iwlagn_prepare_restart() */ vif = ctx->vif; - ret = iwl_trans_d3_resume(priv->trans, &d3_status, false); + ret = iwl_trans_d3_resume(priv->trans, &d3_status, false, true); if (ret) goto out_unlock; @@ -732,12 +732,15 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index f62c2d727..856281279 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1652,10 +1652,10 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv) trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, table.data1, table.data2, table.line, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.ucode_ver, - table.hw_ver, 0, table.brd_ver); + table.blink2, table.ilink1, table.ilink2, + table.bcon_time, table.gp1, table.gp2, + table.gp3, table.ucode_ver, table.hw_ver, + 0, table.brd_ver); IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); IWL_ERR(priv, "0x%08X | uPc\n", table.pc); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index 978e53da1..786a919fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -73,8 +73,8 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 #define IWL7265_UCODE_API_MAX 17 -#define IWL7265D_UCODE_API_MAX 20 -#define IWL3168_UCODE_API_MAX 20 +#define IWL7265D_UCODE_API_MAX 21 +#define IWL3168_UCODE_API_MAX 21 /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 13 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index 5c7ba6fc0..16846cae3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -70,8 +70,8 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 20 -#define IWL8265_UCODE_API_MAX 20 +#define IWL8000_UCODE_API_MAX 21 +#define IWL8265_UCODE_API_MAX 21 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 13 @@ -217,6 +217,7 @@ const struct iwl_cfg iwl8265_2ac_cfg = { .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .vht_mu_mimo_supported = true, }; const struct iwl_cfg iwl4165_2ac_cfg = { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index c2261ffbf..7b34387d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL9000_UCODE_API_MAX 20 +#define IWL9000_UCODE_API_MAX 21 /* Oldest version we won't warn about */ #define IWL9000_UCODE_API_OK 13 @@ -138,7 +138,10 @@ static const struct iwl_tt_params iwl9000_tt_params = { .smem_offset = IWL9000_SMEM_OFFSET, \ .smem_len = IWL9000_SMEM_LEN, \ .thermal_params = &iwl9000_tt_params, \ - .apmg_not_supported = true + .apmg_not_supported = true, \ + .mq_rx_supported = true, \ + .vht_mu_mimo_supported = true, \ + .mac_addr_from_csr = true const struct iwl_cfg iwl9260_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9260", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index f99048135..3e4d346be 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -297,6 +297,7 @@ struct iwl_pwr_tx_backoff { * @host_interrupt_operation_mode: device needs host interrupt operation * mode set * @nvm_hw_section_num: the ID of the HW NVM section + * @mac_addr_from_csr: read HW address from CSR registers * @features: hw features, any combination of feature_whitelist * @pwr_tx_backoffs: translation table between power limits and backoffs * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response @@ -311,6 +312,8 @@ struct iwl_pwr_tx_backoff { * @dccm2_len: length of the second DCCM * @smem_offset: offset from which the SMEM begins * @smem_len: the length of SMEM + * @mq_rx_supported: multi-queue rx support + * @vht_mu_mimo_supported: VHT MU-MIMO support * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -343,6 +346,7 @@ struct iwl_cfg { const bool host_interrupt_operation_mode; bool high_temp; u8 nvm_hw_section_num; + bool mac_addr_from_csr; bool lp_xtal_workaround; const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; bool no_power_up_nic_in_init; @@ -362,6 +366,8 @@ struct iwl_cfg { const u32 smem_len; const struct iwl_tt_params *thermal_params; bool apmg_not_supported; + bool mq_rx_supported; + bool vht_mu_mimo_supported; }; /* diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 163b21bc2..b978f6cae 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -7,6 +7,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -549,4 +550,62 @@ enum dtd_diode_reg { DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ }; +/***************************************************************************** + * MSIX related registers * + *****************************************************************************/ + +#define CSR_MSIX_BASE (0x2000) +#define CSR_MSIX_FH_INT_CAUSES_AD (CSR_MSIX_BASE + 0x800) +#define CSR_MSIX_FH_INT_MASK_AD (CSR_MSIX_BASE + 0x804) +#define CSR_MSIX_HW_INT_CAUSES_AD (CSR_MSIX_BASE + 0x808) +#define CSR_MSIX_HW_INT_MASK_AD (CSR_MSIX_BASE + 0x80C) +#define CSR_MSIX_AUTOMASK_ST_AD (CSR_MSIX_BASE + 0x810) +#define CSR_MSIX_RX_IVAR_AD_REG (CSR_MSIX_BASE + 0x880) +#define CSR_MSIX_IVAR_AD_REG (CSR_MSIX_BASE + 0x890) +#define CSR_MSIX_PENDING_PBA_AD (CSR_MSIX_BASE + 0x1000) +#define CSR_MSIX_RX_IVAR(cause) (CSR_MSIX_RX_IVAR_AD_REG + (cause)) +#define CSR_MSIX_IVAR(cause) (CSR_MSIX_IVAR_AD_REG + (cause)) + +#define MSIX_FH_INT_CAUSES_Q(q) (q) + +/* + * Causes for the FH register interrupts + */ +enum msix_fh_int_causes { + MSIX_FH_INT_CAUSES_D2S_CH0_NUM = BIT(16), + MSIX_FH_INT_CAUSES_D2S_CH1_NUM = BIT(17), + MSIX_FH_INT_CAUSES_S2D = BIT(19), + MSIX_FH_INT_CAUSES_FH_ERR = BIT(21), +}; + +/* + * Causes for the HW register interrupts + */ +enum msix_hw_int_causes { + MSIX_HW_INT_CAUSES_REG_ALIVE = BIT(0), + MSIX_HW_INT_CAUSES_REG_WAKEUP = BIT(1), + MSIX_HW_INT_CAUSES_REG_CT_KILL = BIT(6), + MSIX_HW_INT_CAUSES_REG_RF_KILL = BIT(7), + MSIX_HW_INT_CAUSES_REG_PERIODIC = BIT(8), + MSIX_HW_INT_CAUSES_REG_SW_ERR = BIT(25), + MSIX_HW_INT_CAUSES_REG_SCD = BIT(26), + MSIX_HW_INT_CAUSES_REG_FH_TX = BIT(27), + MSIX_HW_INT_CAUSES_REG_HW_ERR = BIT(29), + MSIX_HW_INT_CAUSES_REG_HAP = BIT(30), +}; + +#define MSIX_MIN_INTERRUPT_VECTORS 2 +#define MSIX_AUTO_CLEAR_CAUSE 0 +#define MSIX_NON_AUTO_CLEAR_CAUSE BIT(7) + +/***************************************************************************** + * HW address related registers * + *****************************************************************************/ + +#define CSR_ADDR_BASE (0x380) +#define CSR_MAC_ADDR0_OTP (CSR_ADDR_BASE) +#define CSR_MAC_ADDR1_OTP (CSR_ADDR_BASE + 4) +#define CSR_MAC_ADDR0_STRAP (CSR_ADDR_BASE + 8) +#define CSR_MAC_ADDR1_STRAP (CSR_ADDR_BASE + 0xC) + #endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h index 22786d7dc..f02e2c89a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace-iwlwifi.h @@ -2,6 +2,7 @@ * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -73,12 +74,12 @@ TRACE_EVENT(iwlwifi_dev_rx, TP_ARGS(dev, trans, pkt, len), TP_STRUCT__entry( DEV_ENTRY - __field(u8, cmd) + __field(u16, cmd) __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, pkt, len)) ), TP_fast_assign( DEV_ASSIGN; - __entry->cmd = pkt->hdr.cmd; + __entry->cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); memcpy(__get_dynamic_array(rxbuf), pkt, iwl_rx_trace_len(trans, pkt, len)); ), @@ -121,13 +122,12 @@ TRACE_EVENT(iwlwifi_dev_tx, TRACE_EVENT(iwlwifi_dev_ucode_error, TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, - u32 data1, u32 data2, u32 line, u32 blink1, - u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, - u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver, - u32 brd_ver), + u32 data1, u32 data2, u32 line, u32 blink2, u32 ilink1, + u32 ilink2, u32 bcon_time, u32 gp1, u32 gp2, u32 rev_type, + u32 major, u32 minor, u32 hw_ver, u32 brd_ver), TP_ARGS(dev, desc, tsf_low, data1, data2, line, - blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, - gp3, major, minor, hw_ver, brd_ver), + blink2, ilink1, ilink2, bcon_time, gp1, gp2, + rev_type, major, minor, hw_ver, brd_ver), TP_STRUCT__entry( DEV_ENTRY __field(u32, desc) @@ -135,14 +135,13 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, __field(u32, data1) __field(u32, data2) __field(u32, line) - __field(u32, blink1) __field(u32, blink2) __field(u32, ilink1) __field(u32, ilink2) __field(u32, bcon_time) __field(u32, gp1) __field(u32, gp2) - __field(u32, gp3) + __field(u32, rev_type) __field(u32, major) __field(u32, minor) __field(u32, hw_ver) @@ -155,29 +154,27 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, __entry->data1 = data1; __entry->data2 = data2; __entry->line = line; - __entry->blink1 = blink1; __entry->blink2 = blink2; __entry->ilink1 = ilink1; __entry->ilink2 = ilink2; __entry->bcon_time = bcon_time; __entry->gp1 = gp1; __entry->gp2 = gp2; - __entry->gp3 = gp3; + __entry->rev_type = rev_type; __entry->major = major; __entry->minor = minor; __entry->hw_ver = hw_ver; __entry->brd_ver = brd_ver; ), TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " - "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " - "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X " + "blink2 0x%05X ilink 0x%05X 0x%05X " + "bcon_tm %010u gp 0x%08X 0x%08X rev_type 0x%08X major 0x%08X " "minor 0x%08X hw 0x%08X brd 0x%08X", __get_str(dev), __entry->desc, __entry->tsf_low, - __entry->data1, - __entry->data2, __entry->line, __entry->blink1, + __entry->data1, __entry->data2, __entry->line, __entry->blink2, __entry->ilink1, __entry->ilink2, __entry->bcon_time, __entry->gp1, __entry->gp2, - __entry->gp3, __entry->major, __entry->minor, + __entry->rev_type, __entry->major, __entry->minor, __entry->hw_ver, __entry->brd_ver) ); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 9527ae08c..7ce381ba3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -7,6 +7,7 @@ * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -236,19 +238,6 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) snprintf(drv->firmware_name, sizeof(drv->firmware_name), "/*(DEBLOBBED)*/", name_pre, tag); - /* - * Starting 8000B - FW name format has changed. This overwrites the - * previous name and uses the new format. - */ - if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { - char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev); - - if (rev_step != 'A') - snprintf(drv->firmware_name, - sizeof(drv->firmware_name), "/*(DEBLOBBED)*/", - name_pre, rev_step, tag); - } - IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) ? "EXPERIMENTAL " : "", @@ -374,15 +363,12 @@ static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) return 0; } -static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data, - const u32 len) +static void iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data, + const u32 len) { struct iwl_fw_gscan_capabilities *fw_capa = (void *)data; struct iwl_gscan_capabilities *capa = &fw->gscan_capa; - if (len < sizeof(*fw_capa)) - return -EINVAL; - capa->max_scan_cache_size = le32_to_cpu(fw_capa->max_scan_cache_size); capa->max_scan_buckets = le32_to_cpu(fw_capa->max_scan_buckets); capa->max_ap_cache_per_scan = @@ -395,7 +381,15 @@ static int iwl_store_gscan_capa(struct iwl_fw *fw, const u8 *data, le32_to_cpu(fw_capa->max_significant_change_aps); capa->max_bssid_history_entries = le32_to_cpu(fw_capa->max_bssid_history_entries); - return 0; + capa->max_hotlist_ssids = le32_to_cpu(fw_capa->max_hotlist_ssids); + capa->max_number_epno_networks = + le32_to_cpu(fw_capa->max_number_epno_networks); + capa->max_number_epno_networks_by_ssid = + le32_to_cpu(fw_capa->max_number_epno_networks_by_ssid); + capa->max_number_of_white_listed_ssid = + le32_to_cpu(fw_capa->max_number_of_white_listed_ssid); + capa->max_number_of_black_listed_ssid = + le32_to_cpu(fw_capa->max_number_of_black_listed_ssid); } /* @@ -1023,8 +1017,15 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, le32_to_cpup((__le32 *)tlv_data); break; case IWL_UCODE_TLV_FW_GSCAN_CAPA: - if (iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len)) - goto invalid_tlv_len; + /* + * Don't return an error in case of a shorter tlv_len + * to enable loading of FW that has an old format + * of GSCAN capabilities TLV. + */ + if (tlv_len < sizeof(struct iwl_fw_gscan_capabilities)) + break; + + iwl_store_gscan_capa(&drv->fw, tlv_data, tlv_len); gscan_capa = true; break; default: @@ -1033,7 +1034,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, } } - if (usniffer_req && !*usniffer_images) { + if (!fw_has_capa(capa, IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED) && + usniffer_req && !*usniffer_images) { IWL_ERR(drv, "user selected to work with usniffer but usniffer image isn't available in ucode package\n"); return -EINVAL; @@ -1047,13 +1049,16 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, /* * If ucode advertises that it supports GSCAN but GSCAN - * capabilities TLV is not present, warn and continue without GSCAN. + * capabilities TLV is not present, or if it has an old format, + * warn and continue without GSCAN. */ if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT) && - WARN(!gscan_capa, - "GSCAN is supported but capabilities TLV is unavailable\n")) + !gscan_capa) { + IWL_DEBUG_INFO(drv, + "GSCAN is supported but capabilities TLV is unavailable\n"); __clear_bit((__force long)IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT, capa->_capa); + } return 0; @@ -1718,3 +1723,7 @@ MODULE_PARM_DESC(fw_monitor, module_param_named(d0i3_timeout, iwlwifi_mod_params.d0i3_entry_delay, uint, S_IRUGO); MODULE_PARM_DESC(d0i3_timeout, "Timeout to D0i3 entry when idle (ms)"); + +module_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool, + S_IRUGO); +MODULE_PARM_DESC(disable_11ac, "Disable VHT capabilities"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 5cc6be927..582008a66 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -312,6 +314,81 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) #define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 #define FH_MEM_TB_MAX_LENGTH (0x00020000) +/* 9000 rx series registers */ + +#define RFH_Q0_FRBDCB_BA_LSB 0xA08000 /* 64 bit address */ +#define RFH_Q_FRBDCB_BA_LSB(q) (RFH_Q0_FRBDCB_BA_LSB + (q) * 8) +/* Write index table */ +#define RFH_Q0_FRBDCB_WIDX 0xA08080 +#define RFH_Q_FRBDCB_WIDX(q) (RFH_Q0_FRBDCB_WIDX + (q) * 4) +/* Read index table */ +#define RFH_Q0_FRBDCB_RIDX 0xA080C0 +#define RFH_Q_FRBDCB_RIDX(q) (RFH_Q0_FRBDCB_RIDX + (q) * 4) +/* Used list table */ +#define RFH_Q0_URBDCB_BA_LSB 0xA08100 /* 64 bit address */ +#define RFH_Q_URBDCB_BA_LSB(q) (RFH_Q0_URBDCB_BA_LSB + (q) * 8) +/* Write index table */ +#define RFH_Q0_URBDCB_WIDX 0xA08180 +#define RFH_Q_URBDCB_WIDX(q) (RFH_Q0_URBDCB_WIDX + (q) * 4) +#define RFH_Q0_URBDCB_VAID 0xA081C0 +#define RFH_Q_URBDCB_VAID(q) (RFH_Q0_URBDCB_VAID + (q) * 4) +/* stts */ +#define RFH_Q0_URBD_STTS_WPTR_LSB 0xA08200 /*64 bits address */ +#define RFH_Q_URBD_STTS_WPTR_LSB(q) (RFH_Q0_URBD_STTS_WPTR_LSB + (q) * 8) + +#define RFH_Q0_ORB_WPTR_LSB 0xA08280 +#define RFH_Q_ORB_WPTR_LSB(q) (RFH_Q0_ORB_WPTR_LSB + (q) * 8) +#define RFH_RBDBUF_RBD0_LSB 0xA08300 +#define RFH_RBDBUF_RBD_LSB(q) (RFH_RBDBUF_RBD0_LSB + (q) * 8) + +/* DMA configuration */ +#define RFH_RXF_DMA_CFG 0xA09820 +/* RB size */ +#define RFH_RXF_DMA_RB_SIZE_MASK (0x000F0000) /* bits 16-19 */ +#define RFH_RXF_DMA_RB_SIZE_POS 16 +#define RFH_RXF_DMA_RB_SIZE_1K (0x1 << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_2K (0x2 << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_4K (0x4 << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_8K (0x8 << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_12K (0x9 << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_16K (0xA << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_20K (0xB << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_24K (0xC << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_28K (0xD << RFH_RXF_DMA_RB_SIZE_POS) +#define RFH_RXF_DMA_RB_SIZE_32K (0xE << RFH_RXF_DMA_RB_SIZE_POS) +/* RB Circular Buffer size:defines the table sizes in RBD units */ +#define RFH_RXF_DMA_RBDCB_SIZE_MASK (0x00F00000) /* bits 20-23 */ +#define RFH_RXF_DMA_RBDCB_SIZE_POS 20 +#define RFH_RXF_DMA_RBDCB_SIZE_8 (0x3 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_16 (0x4 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_32 (0x5 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_64 (0x7 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_128 (0x7 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_256 (0x8 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_512 (0x9 << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_1024 (0xA << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_RBDCB_SIZE_2048 (0xB << RFH_RXF_DMA_RBDCB_SIZE_POS) +#define RFH_RXF_DMA_MIN_RB_SIZE_MASK (0x03000000) /* bit 24-25 */ +#define RFH_RXF_DMA_MIN_RB_SIZE_POS 24 +#define RFH_RXF_DMA_MIN_RB_4_8 (3 << RFH_RXF_DMA_MIN_RB_SIZE_POS) +#define RFH_RXF_DMA_DROP_TOO_LARGE_MASK (0x04000000) /* bit 26 */ +#define RFH_RXF_DMA_SINGLE_FRAME_MASK (0x20000000) /* bit 29 */ +#define RFH_DMA_EN_MASK (0xC0000000) /* bits 30-31*/ +#define RFH_DMA_EN_ENABLE_VAL BIT(31) + +#define RFH_RXF_RXQ_ACTIVE 0xA0980C + +#define RFH_GEN_CFG 0xA09800 +#define RFH_GEN_CFG_SERVICE_DMA_SNOOP BIT(0) +#define RFH_GEN_CFG_RFH_DMA_SNOOP BIT(1) +#define RFH_GEN_CFG_RB_CHUNK_SIZE BIT(4) /* 0 - 64B, 1- 128B */ +#define RFH_GEN_CFG_DEFAULT_RXQ_NUM_MASK 0xF00 +#define RFH_GEN_CFG_DEFAULT_RXQ_NUM_POS 8 + +#define DEFAULT_RXQ_NUM 0 + +/* end of 9000 rx series registers */ + /* TFDB Area - TFDs buffer table */ #define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) #define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900) @@ -434,6 +511,13 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) */ #define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) +#define MQ_RX_TABLE_SIZE 512 +#define MQ_RX_TABLE_MASK (MQ_RX_TABLE_SIZE - 1) +#define MQ_RX_NUM_RBDS (MQ_RX_TABLE_SIZE - 1) +#define RX_POOL_SIZE (MQ_RX_NUM_RBDS + \ + IWL_MAX_RX_HW_QUEUES * \ + (RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC)) + #define RX_QUEUE_SIZE 256 #define RX_QUEUE_MASK 255 #define RX_QUEUE_SIZE_LOG 8 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h index a5aaf6853..8425e1a58 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -293,6 +293,8 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) * @FW_DBG_TX_LATENCY: trigger log collection when the tx latency goes above a * threshold. * @FW_DBG_TDLS: trigger log collection upon TDLS related events. + * @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when + * the firmware sends a tx reply. */ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_INVALID = 0, @@ -309,6 +311,7 @@ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_BA, FW_DBG_TRIGGER_TX_LATENCY, FW_DBG_TRIGGER_TDLS, + FW_DBG_TRIGGER_TX_STATUS, /* must be last */ FW_DBG_TRIGGER_MAX, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 84f8aeb92..15ec4e290 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -297,10 +297,12 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), * which also implies support for the scheduler configuration command * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching + * @IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG: Consolidated D3-D0 image * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics + * @IWL_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD: support p2p standalone U-APSD * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different * sources for the MCC. This TLV bit is a future replacement to @@ -313,7 +315,15 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what * antenna the beacon should be transmitted + * @IWL_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon + * from AP and will send it upon d0i3 exit. * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2 + * @IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW: firmware responsible for CT-kill + * @IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT: supports temperature + * thresholds reporting + * @IWL_UCODE_TLV_CAPA_CTDP_SUPPORT: supports cTDP command + * @IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED: supports usniffer enabled in + * regular image. * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -330,10 +340,12 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = (__force iwl_ucode_tlv_capa_t)11, IWL_UCODE_TLV_CAPA_DQA_SUPPORT = (__force iwl_ucode_tlv_capa_t)12, IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = (__force iwl_ucode_tlv_capa_t)13, + IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG = (__force iwl_ucode_tlv_capa_t)17, IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = (__force iwl_ucode_tlv_capa_t)18, IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19, IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21, IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22, + IWL_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD = (__force iwl_ucode_tlv_capa_t)26, IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = (__force iwl_ucode_tlv_capa_t)28, IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29, IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30, @@ -341,8 +353,14 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, + IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT = (__force iwl_ucode_tlv_capa_t)68, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, + IWL_UCODE_TLV_CAPA_BEACON_STORING = (__force iwl_ucode_tlv_capa_t)72, IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73, + IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW = (__force iwl_ucode_tlv_capa_t)74, + IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT = (__force iwl_ucode_tlv_capa_t)75, + IWL_UCODE_TLV_CAPA_CTDP_SUPPORT = (__force iwl_ucode_tlv_capa_t)76, + IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED = (__force iwl_ucode_tlv_capa_t)77, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ @@ -748,6 +766,19 @@ struct iwl_fw_dbg_trigger_tdls { } __packed; /** + * struct iwl_fw_dbg_trigger_tx_status - configures trigger for tx response + * status. + * @statuses: the list of statuses to trigger the collection on + */ +struct iwl_fw_dbg_trigger_tx_status { + struct tx_status { + u8 status; + u8 reserved[3]; + } __packed statuses[16]; + __le32 reserved[2]; +} __packed; + +/** * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration. * @id: conf id * @usniffer: should the uSniffer image be used @@ -778,6 +809,12 @@ struct iwl_fw_dbg_conf_tlv { * change APs. * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can * hold. + * @max_hotlist_ssids: maximum number of entries for hotlist SSIDs. + * @max_number_epno_networks: max number of epno entries. + * @max_number_epno_networks_by_ssid: max number of epno entries if ssid is + * specified. + * @max_number_of_white_listed_ssid: max number of white listed SSIDs. + * @max_number_of_black_listed_ssid: max number of black listed SSIDs. */ struct iwl_fw_gscan_capabilities { __le32 max_scan_cache_size; @@ -788,6 +825,11 @@ struct iwl_fw_gscan_capabilities { __le32 max_hotlist_aps; __le32 max_significant_change_aps; __le32 max_bssid_history_entries; + __le32 max_hotlist_ssids; + __le32 max_number_epno_networks; + __le32 max_number_epno_networks_by_ssid; + __le32 max_number_of_white_listed_ssid; + __le32 max_number_of_black_listed_ssid; } __packed; #endif /* __iwl_fw_file_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index 85d6d6d55..2942571c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h @@ -7,6 +7,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -205,6 +207,12 @@ struct iwl_fw_cscheme_list { * change APs. * @max_bssid_history_entries: number of BSSID/RSSI entries that the device can * hold. + * @max_hotlist_ssids: maximum number of entries for hotlist SSIDs. + * @max_number_epno_networks: max number of epno entries. + * @max_number_epno_networks_by_ssid: max number of epno entries if ssid is + * specified. + * @max_number_of_white_listed_ssid: max number of white listed SSIDs. + * @max_number_of_black_listed_ssid: max number of black listed SSIDs. */ struct iwl_gscan_capabilities { u32 max_scan_cache_size; @@ -215,6 +223,11 @@ struct iwl_gscan_capabilities { u32 max_hotlist_aps; u32 max_significant_change_aps; u32 max_bssid_history_entries; + u32 max_hotlist_ssids; + u32 max_number_epno_networks; + u32 max_number_epno_networks_by_ssid; + u32 max_number_of_white_listed_ssid; + u32 max_number_of_black_listed_ssid; }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index fd42f63f5..d1a5dd160 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -108,11 +108,14 @@ enum iwl_amsdu_size { * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 + * @nvm_file: specifies a external NVM file + * @uapsd_disable: disable U-APSD, default = 1 * @d0i3_disable: disable d0i3, default = 1, * @d0i3_entry_delay: time to wait after no refs are taken before * entering D0i3 (in msecs) * @lar_disable: disable LAR (regulatory), default = 0 * @fw_monitor: allow to use firmware monitor + * @disable_11ac: disable VHT capabilities, default = false. */ struct iwl_mod_params { int sw_crypto; @@ -133,6 +136,7 @@ struct iwl_mod_params { unsigned int d0i3_entry_delay; bool lar_disable; bool fw_monitor; + bool disable_11ac; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 7b89bfc8c..93a689583 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -7,6 +7,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -69,6 +70,9 @@ #include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-nvm-parse.h" +#include "iwl-prph.h" +#include "iwl-io.h" +#include "iwl-csr.h" /* NVM offsets (in words) definitions */ enum wkp_nvm_offsets { @@ -366,6 +370,9 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, max_ampdu_exponent << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + if (cfg->vht_mu_mimo_supported) + vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + if (cfg->ht_params->ldpc) vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; @@ -449,7 +456,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, IEEE80211_BAND_5GHZ); iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, tx_chains, rx_chains); - if (data->sku_cap_11ac_enable) + if (data->sku_cap_11ac_enable && !iwlwifi_mod_params.disable_11ac) iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap, tx_chains, rx_chains); @@ -519,27 +526,41 @@ static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg); } -static void iwl_set_hw_address(const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const __le16 *nvm_sec) +static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest) { - const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR); - - /* The byte order is little endian 16 bit, meaning 214365 */ - data->hw_addr[0] = hw_addr[1]; - data->hw_addr[1] = hw_addr[0]; - data->hw_addr[2] = hw_addr[3]; - data->hw_addr[3] = hw_addr[2]; - data->hw_addr[4] = hw_addr[5]; - data->hw_addr[5] = hw_addr[4]; + const u8 *hw_addr; + + hw_addr = (const u8 *)&mac_addr0; + dest[0] = hw_addr[3]; + dest[1] = hw_addr[2]; + dest[2] = hw_addr[1]; + dest[3] = hw_addr[0]; + + hw_addr = (const u8 *)&mac_addr1; + dest[4] = hw_addr[1]; + dest[5] = hw_addr[0]; } -static void iwl_set_hw_address_family_8000(struct device *dev, +static void iwl_set_hw_address_from_csr(struct iwl_trans *trans, + struct iwl_nvm_data *data) +{ + __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP)); + __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP)); + + /* If OEM did not fuse address - get it from OTP */ + if (!mac_addr0 && !mac_addr1) { + mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_OTP)); + mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_OTP)); + } + + iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); +} + +static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *mac_override, - const __le16 *nvm_hw, - u32 mac_addr0, u32 mac_addr1) + const __le16 *nvm_hw) { const u8 *hw_addr; @@ -565,44 +586,68 @@ static void iwl_set_hw_address_family_8000(struct device *dev, memcmp(reserved_mac, hw_addr, ETH_ALEN) != 0) return; - IWL_ERR_DEV(dev, - "mac address from nvm override section is not valid\n"); + IWL_ERR(trans, + "mac address from nvm override section is not valid\n"); } if (nvm_hw) { - /* read the MAC address from HW resisters */ - hw_addr = (const u8 *)&mac_addr0; - data->hw_addr[0] = hw_addr[3]; - data->hw_addr[1] = hw_addr[2]; - data->hw_addr[2] = hw_addr[1]; - data->hw_addr[3] = hw_addr[0]; + /* read the mac address from WFMP registers */ + __le32 mac_addr0 = cpu_to_le32(iwl_trans_read_prph(trans, + WFMP_MAC_ADDR_0)); + __le32 mac_addr1 = cpu_to_le32(iwl_trans_read_prph(trans, + WFMP_MAC_ADDR_1)); - hw_addr = (const u8 *)&mac_addr1; - data->hw_addr[4] = hw_addr[1]; - data->hw_addr[5] = hw_addr[0]; - - if (!is_valid_ether_addr(data->hw_addr)) - IWL_ERR_DEV(dev, - "mac address from hw section is not valid\n"); + iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); return; } - IWL_ERR_DEV(dev, "mac address is not found\n"); + IWL_ERR(trans, "mac address is not found\n"); +} + +static int iwl_set_hw_address(struct iwl_trans *trans, + const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, const __le16 *nvm_hw, + const __le16 *mac_override) +{ + if (cfg->mac_addr_from_csr) { + iwl_set_hw_address_from_csr(trans, data); + } else if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR); + + /* The byte order is little endian 16 bit, meaning 214365 */ + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; + } else { + iwl_set_hw_address_family_8000(trans, cfg, data, + mac_override, nvm_hw); + } + + if (!is_valid_ether_addr(data->hw_addr)) { + IWL_ERR(trans, "no valid mac address was found\n"); + return -EINVAL; + } + + return 0; } struct iwl_nvm_data * -iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, +iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, - u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1) + u8 tx_chains, u8 rx_chains, bool lar_fw_supported) { + struct device *dev = trans->dev; struct iwl_nvm_data *data; - u32 sku; - u32 radio_cfg; + bool lar_enabled; + u32 sku, radio_cfg; u16 lar_config; + const __le16 *ch_section; if (cfg->device_family != IWL_DEVICE_FAMILY_8000) data = kzalloc(sizeof(*data) + @@ -641,21 +686,16 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { /* Checking for required sections */ if (!nvm_calib) { - IWL_ERR_DEV(dev, - "Can't parse empty Calib NVM sections\n"); + IWL_ERR(trans, + "Can't parse empty Calib NVM sections\n"); kfree(data); return NULL; } /* in family 8000 Xtal calibration values moved to OTP */ data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); - } - - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { - iwl_set_hw_address(cfg, data, nvm_hw); - - iwl_init_sbands(dev, cfg, data, nvm_sw, - tx_chains, rx_chains, lar_fw_supported); + lar_enabled = true; + ch_section = nvm_sw; } else { u16 lar_offset = data->nvm_version < 0xE39 ? NVM_LAR_OFFSET_FAMILY_8000_OLD : @@ -664,16 +704,18 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, lar_config = le16_to_cpup(regulatory + lar_offset); data->lar_enabled = !!(lar_config & NVM_LAR_ENABLED_FAMILY_8000); + lar_enabled = data->lar_enabled; + ch_section = regulatory; + } - /* MAC address in family 8000 */ - iwl_set_hw_address_family_8000(dev, cfg, data, mac_override, - nvm_hw, mac_addr0, mac_addr1); - - iwl_init_sbands(dev, cfg, data, regulatory, - tx_chains, rx_chains, - lar_fw_supported && data->lar_enabled); + /* If no valid mac address was found - bail out */ + if (iwl_set_hw_address(trans, cfg, data, nvm_hw, mac_override)) { + kfree(data); + return NULL; } + iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains, + lar_fw_supported && lar_enabled); data->calib_version = 255; return data; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 92466ee72..d704d52aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -74,12 +74,11 @@ * later with iwl_free_nvm_data(). */ struct iwl_nvm_data * -iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, +iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, const __le16 *mac_override, const __le16 *phy_sku, - u8 tx_chains, u8 rx_chains, bool lar_fw_supported, - u32 mac_addr0, u32 mac_addr1); + u8 tx_chains, u8 rx_chains, bool lar_fw_supported); /** * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 5bde23a47..c46e596e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -404,4 +404,6 @@ enum { LMPM_PAGE_PASS_NOTIF_POS = BIT(20), }; +#define UREG_CHICK (0xA05C00) +#define UREG_CHICK_MSIX_ENABLE BIT(25) #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 82fb3a97a..91d74b3f6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -506,7 +506,7 @@ struct iwl_trans_config { bool sw_csum_tx; const struct iwl_hcmd_arr *command_groups; int command_groups_size; - + u32 sdio_adma_addr; }; @@ -618,9 +618,9 @@ struct iwl_trans_ops { void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans, bool low_power); - void (*d3_suspend)(struct iwl_trans *trans, bool test); + void (*d3_suspend)(struct iwl_trans *trans, bool test, bool reset); int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status, - bool test); + bool test, bool reset); int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); @@ -736,6 +736,11 @@ enum iwl_plat_pm_mode { IWL_PLAT_PM_MODE_D0I3, }; +/* Max time to wait for trans to become idle/non-idle on d0i3 + * enter/exit (in msecs). + */ +#define IWL_TRANS_IDLE_TIMEOUT 2000 + /** * struct iwl_trans - transport common data * @@ -831,6 +836,7 @@ struct iwl_trans { enum iwl_plat_pm_mode system_pm_mode; enum iwl_plat_pm_mode runtime_pm_mode; + bool suspending; /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ @@ -920,22 +926,23 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans) _iwl_trans_stop_device(trans, true); } -static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test) +static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test, + bool reset) { might_sleep(); if (trans->ops->d3_suspend) - trans->ops->d3_suspend(trans, test); + trans->ops->d3_suspend(trans, test, reset); } static inline int iwl_trans_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status, - bool test) + bool test, bool reset) { might_sleep(); if (!trans->ops->d3_resume) return 0; - return trans->ops->d3_resume(trans, status, test); + return trans->ops->d3_resume(trans, status, test, reset); } static inline void iwl_trans_ref(struct iwl_trans *trans) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h index b00c03fcd..4b560e441 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/constants.h @@ -6,7 +6,8 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -32,7 +33,8 @@ * BSD LICENSE * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,6 +75,7 @@ #define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) #define IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */ #define IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */ +#define IWL_MVM_P2P_UAPSD_STANDALONE 0 #define IWL_MVM_P2P_LOWLATENCY_PS_ENABLE 0 #define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) #define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) @@ -107,6 +110,7 @@ #define IWL_MVM_RS_80_20_FAR_RANGE_TWEAK 1 #define IWL_MVM_TOF_IS_RESPONDER 0 #define IWL_MVM_SW_TX_CSUM_OFFLOAD 0 +#define IWL_MVM_COLLECT_FW_ERR_DUMP 1 #define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 #define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index d3e21d95c..c1a313149 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -249,16 +251,19 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, return; case WLAN_CIPHER_SUITE_TKIP: if (sta) { + u64 pn64; + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; rx_p1ks = data->tkip->rx_uni; - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + pn64 = atomic64_read(&key->tx_pn); + tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64)); + tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64)); - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); + ieee80211_get_tkip_p1k_iv(key, TKIP_PN_TO_IV32(pn64), + p1k); iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k); memcpy(data->tkip->mic_keys.tx, @@ -811,8 +816,7 @@ static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm) { iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true); - iwl_trans_stop_device(mvm->trans); - + iwl_mvm_stop_device(mvm); /* * Set the HW restart bit -- this is mostly true as we're * going to load new firmware and reprogram that, though @@ -1023,14 +1027,18 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm, struct ieee80211_sta *ap_sta) { int ret; + bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); - ret = iwl_mvm_switch_to_d3(mvm); - if (ret) - return ret; + if (!unified_image) { + ret = iwl_mvm_switch_to_d3(mvm); + if (ret) + return ret; - ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta); - if (ret) - return ret; + ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta); + if (ret) + return ret; + } if (!iwlwifi_mod_params.sw_crypto) { /* @@ -1072,10 +1080,14 @@ iwl_mvm_netdetect_config(struct iwl_mvm *mvm, { struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; int ret; + bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); - ret = iwl_mvm_switch_to_d3(mvm); - if (ret) - return ret; + if (!unified_image) { + ret = iwl_mvm_switch_to_d3(mvm); + if (ret) + return ret; + } /* rfkill release can be either for wowlan or netdetect */ if (wowlan->rfkill_release) @@ -1151,6 +1163,8 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, }; int ret; int len __maybe_unused; + bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); if (!wowlan) { /* @@ -1236,7 +1250,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_trans_d3_suspend(mvm->trans, test); + iwl_trans_d3_suspend(mvm->trans, test, !unified_image); out: if (ret < 0) { iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); @@ -1299,7 +1313,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); mutex_unlock(&mvm->d0i3_suspend_mutex); - iwl_trans_d3_suspend(trans, false); + iwl_trans_d3_suspend(trans, false, false); return 0; } @@ -1601,7 +1615,9 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, case WLAN_CIPHER_SUITE_TKIP: iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); - ieee80211_set_key_tx_seq(key, &seq); + atomic64_set(&key->tx_pn, + (u64)seq.tkip.iv16 | + ((u64)seq.tkip.iv32 << 16)); break; } @@ -2041,9 +2057,14 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) { struct ieee80211_vif *vif = NULL; - int ret; + int ret = 1; enum iwl_d3_status d3_status; bool keep = false; + bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); + + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | + CMD_WAKE_UP_TRANS; mutex_lock(&mvm->mutex); @@ -2052,7 +2073,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) if (IS_ERR_OR_NULL(vif)) goto err; - ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test); + ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test, !unified_image); if (ret) goto err; @@ -2095,17 +2116,28 @@ out_iterate: iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); out: - /* return 1 to reconfigure the device */ + if (unified_image && !ret) { + ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); + if (!ret) /* D3 ended successfully - no need to reset device */ + return 0; + } + + /* + * Reconfigure the device in one of the following cases: + * 1. We are not using a unified image + * 2. We are using a unified image but had an error while exiting D3 + */ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status); - - /* We always return 1, which causes mac80211 to do a reconfig - * with IEEE80211_RECONFIG_TYPE_RESTART. This type of - * reconfig calls iwl_mvm_restart_complete(), where we unref - * the IWL_MVM_REF_UCODE_DOWN, so we need to take the - * reference here. + /* + * When switching images we return 1, which causes mac80211 + * to do a reconfig with IEEE80211_RECONFIG_TYPE_RESTART. + * This type of reconfig calls iwl_mvm_restart_complete(), + * where we unref the IWL_MVM_REF_UCODE_DOWN, so we need + * to take the reference here. */ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + return 1; } @@ -2122,7 +2154,7 @@ static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm) enum iwl_d3_status d3_status; struct iwl_trans *trans = mvm->trans; - iwl_trans_d3_resume(trans, &d3_status, false); + iwl_trans_d3_resume(trans, &d3_status, false, false); /* * make sure to clear D0I3_DEFER_WAKEUP before diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index 9e0d46368..14004456b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1255,6 +1257,7 @@ static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; + bool prev; u8 value; int ret; @@ -1265,7 +1268,9 @@ static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, return -EINVAL; mutex_lock(&mvm->mutex); - iwl_mvm_update_low_latency(mvm, vif, value); + prev = iwl_mvm_vif_low_latency(mvmvif); + mvmvif->low_latency_dbgfs = value; + iwl_mvm_update_low_latency(mvm, vif, prev); mutex_unlock(&mvm->mutex); return count; @@ -1277,11 +1282,15 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file, { struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[2]; + char buf[30] = {}; + int len; - buf[0] = mvmvif->low_latency ? '1' : '0'; - buf[1] = '\n'; - return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); + len = snprintf(buf, sizeof(buf) - 1, + "traffic=%d\ndbgfs=%d\nvcmd=%d\n", + mvmvif->low_latency_traffic, + mvmvif->low_latency_dbgfs, + mvmvif->low_latency_vcmd); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file, @@ -1363,6 +1372,59 @@ static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); } +static void iwl_dbgfs_quota_check(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int *ret = data; + + if (mvmvif->dbgfs_quota_min) + *ret = -EINVAL; +} + +static ssize_t iwl_dbgfs_quota_min_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u16 value; + int ret; + + ret = kstrtou16(buf, 0, &value); + if (ret) + return ret; + + if (value > 95) + return -EINVAL; + + mutex_lock(&mvm->mutex); + + mvmvif->dbgfs_quota_min = 0; + ieee80211_iterate_interfaces(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_dbgfs_quota_check, &ret); + if (ret == 0) { + mvmvif->dbgfs_quota_min = value; + iwl_mvm_update_quotas(mvm, false, NULL); + } + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_quota_min_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[10]; + int len; + + len = snprintf(buf, sizeof(buf), "%d\n", mvmvif->dbgfs_quota_min); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ @@ -1386,6 +1448,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_req_ext, 32); MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_range_abort, 32); MVM_DEBUGFS_READ_FILE_OPS(tof_range_response); MVM_DEBUGFS_READ_WRITE_FILE_OPS(tof_responder_params, 32); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(quota_min, 32); void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -1423,6 +1486,8 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(quota_min, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && mvmvif == mvm->bf_allowed_vif) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 90500e2d1..a43b3921c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -63,6 +64,7 @@ * *****************************************************************************/ #include <linux/vmalloc.h> +#include <linux/ieee80211.h> #include "mvm.h" #include "fw-dbg.h" @@ -71,6 +73,44 @@ #include "debugfs.h" #include "iwl-fw-error-dump.h" +static ssize_t iwl_dbgfs_ctdp_budget_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[16]; + int pos, budget; + + if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) + return -EIO; + + mutex_lock(&mvm->mutex); + budget = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_REPORT, 0); + mutex_unlock(&mvm->mutex); + + if (budget < 0) + return budget; + + pos = scnprintf(buf, sizeof(buf), "%d\n", budget); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + int ret; + + if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) + return -EIO; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_STOP, 0); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { @@ -261,17 +301,18 @@ static ssize_t iwl_dbgfs_nic_temp_read(struct file *file, { struct iwl_mvm *mvm = file->private_data; char buf[16]; - int pos, temp; + int pos, ret; + s32 temp; if (!mvm->ucode_loaded) return -EIO; mutex_lock(&mvm->mutex); - temp = iwl_mvm_get_temp(mvm); + ret = iwl_mvm_get_temp(mvm, &temp); mutex_unlock(&mvm->mutex); - if (temp < 0) - return temp; + if (ret) + return -EIO; pos = scnprintf(buf , sizeof(buf), "%d\n", temp); @@ -942,6 +983,47 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, return count; } +static ssize_t iwl_dbgfs_indirection_tbl_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_rss_config_cmd cmd = { + .flags = cpu_to_le32(IWL_RSS_ENABLE), + .hash_mask = IWL_RSS_HASH_TYPE_IPV4_TCP | + IWL_RSS_HASH_TYPE_IPV4_PAYLOAD | + IWL_RSS_HASH_TYPE_IPV6_TCP | + IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, + }; + int ret, i, num_repeats, nbytes = count / 2; + + ret = hex2bin(cmd.indirection_table, buf, nbytes); + if (ret) + return ret; + + /* + * The input is the redirection table, partial or full. + * Repeat the pattern if needed. + * For example, input of 01020F will be repeated 42 times, + * indirecting RSS hash results to queues 1, 2, 15 (skipping + * queues 3 - 14). + */ + num_repeats = ARRAY_SIZE(cmd.indirection_table) / nbytes; + for (i = 1; i < num_repeats; i++) + memcpy(&cmd.indirection_table[i * nbytes], + cmd.indirection_table, nbytes); + /* handle cut in the middle pattern for the last places */ + memcpy(&cmd.indirection_table[i * nbytes], cmd.indirection_table, + ARRAY_SIZE(cmd.indirection_table) % nbytes); + + memcpy(cmd.secret_key, mvm->secret_key, sizeof(cmd.secret_key)); + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -983,7 +1065,7 @@ static ssize_t iwl_dbgfs_cont_recording_write(struct iwl_mvm *mvm, trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) return -EOPNOTSUPP; - ret = kstrtouint(buf, 0, &rec_mode); + ret = kstrtoint(buf, 0, &rec_mode); if (ret) return ret; @@ -1037,6 +1119,22 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, return count; } +static ssize_t iwl_dbgfs_max_amsdu_len_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + unsigned int max_amsdu_len; + int ret; + + ret = kstrtouint(buf, 0, &max_amsdu_len); + + if (max_amsdu_len > IEEE80211_MAX_MPDU_LEN_VHT_11454) + return -EINVAL; + mvm->max_amsdu_len = max_amsdu_len; + + return count; +} + #define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) #ifdef CONFIG_IWLWIFI_BCAST_FILTERING static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, @@ -1433,6 +1531,8 @@ iwl_dbgfs_send_echo_cmd_write(struct iwl_mvm *mvm, char *buf, MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ +MVM_DEBUGFS_READ_FILE_OPS(ctdp_budget); +MVM_DEBUGFS_WRITE_FILE_OPS(stop_ctdp, 8); MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); MVM_DEBUGFS_WRITE_FILE_OPS(send_echo_cmd, 8); @@ -1454,6 +1554,9 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 64); MVM_DEBUGFS_WRITE_FILE_OPS(cont_recording, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(max_amsdu_len, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(indirection_tbl, + (IWL_RSS_INDIRECTION_TABLE_SIZE * 2)); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); @@ -1479,6 +1582,8 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(set_nic_temperature, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(ctdp_budget, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(stop_ctdp, dbgfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); @@ -1496,13 +1601,18 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(max_amsdu_len, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(send_echo_cmd, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(cont_recording, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(indirection_tbl, mvm->debugfs_dir, S_IWUSR); if (!debugfs_create_bool("enable_scan_iteration_notif", S_IRUSR | S_IWUSR, mvm->debugfs_dir, &mvm->scan_iter_notif_enabled)) goto err; + if (!debugfs_create_bool("drop_bcn_ap_mode", S_IRUSR | S_IWUSR, + mvm->debugfs_dir, &mvm->drop_bcn_ap_mode)) + goto err; #ifdef CONFIG_IWLWIFI_BCAST_FILTERING if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h index 62b9a0a96..eec52c57f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h @@ -251,6 +251,7 @@ enum iwl_wowlan_flags { ENABLE_L3_FILTERING = BIT(1), ENABLE_NBNS_FILTERING = BIT(2), ENABLE_DHCP_FILTERING = BIT(3), + ENABLE_STORE_BEACON = BIT(4), }; struct iwl_wowlan_config_cmd { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index fb6d341d6..7a16e55df 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -264,9 +264,8 @@ enum iwl_rx_mpdu_mac_flags2 { }; enum iwl_rx_mpdu_amsdu_info { - IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x3f, - IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x40, - /* 0x80 bit reserved for now */ + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK = 0x7f, + IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x80, }; enum iwl_rx_l3l4_flags { @@ -287,16 +286,13 @@ enum iwl_rx_mpdu_status { IWL_RX_MPDU_STATUS_KEY_ERROR = BIT(4), IWL_RX_MPDU_STATUS_ICV_OK = BIT(5), IWL_RX_MPDU_STATUS_MIC_OK = BIT(6), - /* TODO - verify this is the correct value */ IWL_RX_MPDU_RES_STATUS_TTAK_OK = BIT(7), IWL_RX_MPDU_STATUS_SEC_MASK = 0x7 << 8, IWL_RX_MPDU_STATUS_SEC_NONE = 0x0 << 8, IWL_RX_MPDU_STATUS_SEC_WEP = 0x1 << 8, IWL_RX_MPDU_STATUS_SEC_CCM = 0x2 << 8, IWL_RX_MPDU_STATUS_SEC_TKIP = 0x3 << 8, - /* TODO - define IWL_RX_MPDU_STATUS_SEC_EXT_ENC - this is a stub */ IWL_RX_MPDU_STATUS_SEC_EXT_ENC = 0x4 << 8, - /* TODO - define IWL_RX_MPDU_STATUS_SEC_GCM - this is a stub */ IWL_RX_MPDU_STATUS_SEC_GCM = 0x5 << 8, IWL_RX_MPDU_STATUS_DECRYPTED = BIT(11), IWL_RX_MPDU_STATUS_WEP_MATCH = BIT(12), @@ -350,11 +346,11 @@ struct iwl_rx_mpdu_desc { /* DW8 */ __le32 filter_match; /* DW9 */ - __le32 gp2_on_air_rise; - /* DW10 */ __le32 rate_n_flags; + /* DW10 */ + u8 energy_a, energy_b, channel, reserved; /* DW11 */ - u8 energy_a, energy_b, energy_c, channel; + __le32 gp2_on_air_rise; /* DW12 & DW13 */ __le64 tsf_on_air_rise; } __packed; @@ -365,4 +361,85 @@ struct iwl_frame_release { __le16 nssn; }; +enum iwl_rss_hash_func_en { + IWL_RSS_HASH_TYPE_IPV4_TCP, + IWL_RSS_HASH_TYPE_IPV4_UDP, + IWL_RSS_HASH_TYPE_IPV4_PAYLOAD, + IWL_RSS_HASH_TYPE_IPV6_TCP, + IWL_RSS_HASH_TYPE_IPV6_UDP, + IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, +}; + +#define IWL_RSS_HASH_KEY_CNT 10 +#define IWL_RSS_INDIRECTION_TABLE_SIZE 128 +#define IWL_RSS_ENABLE 1 + +/** + * struct iwl_rss_config_cmd - RSS (Receive Side Scaling) configuration + * + * @flags: 1 - enable, 0 - disable + * @hash_mask: Type of RSS to use. Values are from %iwl_rss_hash_func_en + * @secret_key: 320 bit input of random key configuration from driver + * @indirection_table: indirection table + */ +struct iwl_rss_config_cmd { + __le32 flags; + u8 hash_mask; + u8 reserved[3]; + __le32 secret_key[IWL_RSS_HASH_KEY_CNT]; + u8 indirection_table[IWL_RSS_INDIRECTION_TABLE_SIZE]; +} __packed; /* RSS_CONFIG_CMD_API_S_VER_1 */ + +#define IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE 128 +#define IWL_MULTI_QUEUE_SYNC_SENDER_POS 0 +#define IWL_MULTI_QUEUE_SYNC_SENDER_MSK 0xf + +/** + * struct iwl_rxq_sync_cmd - RXQ notification trigger + * + * @flags: flags of the notification. bit 0:3 are the sender queue + * @rxq_mask: rx queues to send the notification on + * @count: number of bytes in payload, should be DWORD aligned + * @payload: data to send to rx queues + */ +struct iwl_rxq_sync_cmd { + __le32 flags; + __le32 rxq_mask; + __le32 count; + u8 payload[]; +} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ + +/** + * struct iwl_rxq_sync_notification - Notification triggered by RXQ + * sync command + * + * @count: number of bytes in payload + * @payload: data to send to rx queues + */ +struct iwl_rxq_sync_notification { + __le32 count; + u8 payload[]; +} __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ + +/** +* Internal message identifier +* +* @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA +*/ +enum iwl_mvm_rxq_notif_type { + IWL_MVM_RXQ_NOTIF_DEL_BA, +}; + +/** +* struct iwl_mvm_internal_rxq_notif - Internal representation of the data sent +* in &iwl_rxq_sync_cmd. Should be DWORD aligned. +* +* @type: value from &iwl_mvm_rxq_notif_type +* @data: payload +*/ +struct iwl_mvm_internal_rxq_notif { + u32 type; + u8 data[]; +} __packed; + #endif /* __fw_api_rx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index 6fca4fb1d..90d911394 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -253,6 +255,68 @@ struct iwl_mvm_keyinfo { __le64 hw_tkip_mic_tx_key; } __packed; +#define IWL_ADD_STA_STATUS_MASK 0xFF +#define IWL_ADD_STA_BAID_MASK 0xFF00 + +/** + * struct iwl_mvm_add_sta_cmd_v7 - Add/modify a station in the fw's sta table. + * ( REPLY_ADD_STA = 0x18 ) + * @add_modify: 1: modify existing, 0: add new station + * @awake_acs: + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. + * @mac_id_n_color: the Mac context this station belongs to + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave + * alone. 1 - modify, 0 - don't change. + * @station_flags: look at %iwl_sta_flags + * @station_flags_msk: what of %station_flags have changed + * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) + * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set + * add_immediate_ba_ssn. + * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) + * Set %STA_MODIFY_REMOVE_BA_TID to use this field + * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with + * add_immediate_ba_tid. + * @sleep_tx_count: number of packets to transmit to station even though it is + * asleep. Used to synchronise PS-poll and u-APSD responses while ucode + * keeps track of STA sleep state. + * @sleep_state_flags: Look at %iwl_sta_sleep_flag. + * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP + * mac-addr. + * @beamform_flags: beam forming controls + * @tfd_queue_msk: tfd queues used by this station + * + * The device contains an internal table of per-station information, with info + * on security keys, aggregation parameters, and Tx rates for initial Tx + * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD). + * + * ADD_STA sets up the table entry for one station, either creating a new + * entry, or modifying a pre-existing one. + */ +struct iwl_mvm_add_sta_cmd_v7 { + u8 add_modify; + u8 awake_acs; + __le16 tid_disable_tx; + __le32 mac_id_n_color; + u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ + __le16 reserved2; + u8 sta_id; + u8 modify_mask; + __le16 reserved3; + __le32 station_flags; + __le32 station_flags_msk; + u8 add_immediate_ba_tid; + u8 remove_immediate_ba_tid; + __le16 add_immediate_ba_ssn; + __le16 sleep_tx_count; + __le16 sleep_state_flags; + __le16 assoc_id; + __le16 beamform_flags; + __le32 tfd_queue_msk; +} __packed; /* ADD_STA_CMD_API_S_VER_7 */ + /** * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. * ( REPLY_ADD_STA = 0x18 ) @@ -282,6 +346,7 @@ struct iwl_mvm_keyinfo { * mac-addr. * @beamform_flags: beam forming controls * @tfd_queue_msk: tfd queues used by this station + * @rx_ba_window: aggregation window size * * The device contains an internal table of per-station information, with info * on security keys, aggregation parameters, and Tx rates for initial Tx @@ -310,7 +375,9 @@ struct iwl_mvm_add_sta_cmd { __le16 assoc_id; __le16 beamform_flags; __le32 tfd_queue_msk; -} __packed; /* ADD_STA_CMD_API_S_VER_7 */ + __le16 rx_ba_window; + __le16 reserved; +} __packed; /* ADD_STA_CMD_API_S_VER_8 */ /** * struct iwl_mvm_add_sta_key_cmd - add/modify sta key diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 82049bb13..4a0fc47c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -119,6 +119,8 @@ enum { SCAN_ABORT_UMAC = 0xe, SCAN_COMPLETE_UMAC = 0xf, + BA_WINDOW_STATUS_NOTIFICATION_ID = 0x13, + /* station table */ ADD_STA_KEY = 0x17, ADD_STA = 0x18, @@ -213,6 +215,8 @@ enum { MFUART_LOAD_NOTIFICATION = 0xb1, + RSS_CONFIG_CMD = 0xb3, + REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, FRAME_RELEASE = 0xc3, @@ -277,14 +281,30 @@ enum { */ enum iwl_phy_ops_subcmd_ids { CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, + CTDP_CONFIG_CMD = 0x03, + TEMP_REPORTING_THRESHOLDS_CMD = 0x04, + CT_KILL_NOTIFICATION = 0xFE, DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, }; +enum iwl_data_path_subcmd_ids { + UPDATE_MU_GROUPS_CMD = 0x1, + TRIGGER_RX_QUEUES_NOTIF_CMD = 0x2, + MU_GROUP_MGMT_NOTIF = 0xFE, + RX_QUEUES_NOTIFICATION = 0xFF, +}; + +enum iwl_prot_offload_subcmd_ids { + STORED_BEACON_NTF = 0xFF, +}; + /* command groups */ enum { LEGACY_GROUP = 0x0, LONG_GROUP = 0x1, PHY_OPS_GROUP = 0x4, + DATA_PATH_GROUP = 0x5, + PROT_OFFLOAD_GROUP = 0xb, }; /** @@ -1271,6 +1291,26 @@ struct iwl_fw_bcast_filter { struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; } __packed; /* BCAST_FILTER_S_VER_1 */ +#define BA_WINDOW_STREAMS_MAX 16 +#define BA_WINDOW_STATUS_TID_MSK 0x000F +#define BA_WINDOW_STATUS_STA_ID_POS 4 +#define BA_WINDOW_STATUS_STA_ID_MSK 0x01F0 +#define BA_WINDOW_STATUS_VALID_MSK BIT(9) + +/** + * struct iwl_ba_window_status_notif - reordering window's status notification + * @bitmap: bitmap of received frames [start_seq_num + 0]..[start_seq_num + 63] + * @ra_tid: bit 3:0 - TID, bit 8:4 - STA_ID, bit 9 - valid + * @start_seq_num: the start sequence number of the bitmap + * @mpdu_rx_count: the number of received MPDUs since entering D0i3 + */ +struct iwl_ba_window_status_notif { + __le64 bitmap[BA_WINDOW_STREAMS_MAX]; + __le16 ra_tid[BA_WINDOW_STREAMS_MAX]; + __le32 start_seq_num[BA_WINDOW_STREAMS_MAX]; + __le16 mpdu_rx_count[BA_WINDOW_STREAMS_MAX]; +} __packed; /* BA_WINDOW_STATUS_NTFY_API_S_VER_1 */ + /** * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. * @default_discard: default action for this mac (discard (1) / pass (0)). @@ -1668,15 +1708,77 @@ struct iwl_ext_dts_measurement_cmd { } __packed; /* XVT_FW_DTS_CONTROL_MEASUREMENT_REQUEST_API_S */ /** - * iwl_dts_measurement_notif - notification received with the measurements + * struct iwl_dts_measurement_notif_v1 - measurements notification * * @temp: the measured temperature * @voltage: the measured voltage */ -struct iwl_dts_measurement_notif { +struct iwl_dts_measurement_notif_v1 { __le32 temp; __le32 voltage; -} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */ +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/ + +/** + * struct iwl_dts_measurement_notif_v2 - measurements notification + * + * @temp: the measured temperature + * @voltage: the measured voltage + * @threshold_idx: the trip index that was crossed + */ +struct iwl_dts_measurement_notif_v2 { + __le32 temp; + __le32 voltage; + __le32 threshold_idx; +} __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_2 */ + +/** + * struct ct_kill_notif - CT-kill entry notification + * + * @temperature: the current temperature in celsius + * @reserved: reserved + */ +struct ct_kill_notif { + __le16 temperature; + __le16 reserved; +} __packed; /* GRP_PHY_CT_KILL_NTF */ + +/** +* enum ctdp_cmd_operation - CTDP command operations +* @CTDP_CMD_OPERATION_START: update the current budget +* @CTDP_CMD_OPERATION_STOP: stop ctdp +* @CTDP_CMD_OPERATION_REPORT: get the avgerage budget +*/ +enum iwl_mvm_ctdp_cmd_operation { + CTDP_CMD_OPERATION_START = 0x1, + CTDP_CMD_OPERATION_STOP = 0x2, + CTDP_CMD_OPERATION_REPORT = 0x4, +};/* CTDP_CMD_OPERATION_TYPE_E */ + +/** + * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget + * + * @operation: see &enum iwl_mvm_ctdp_cmd_operation + * @budget: the budget in milliwatt + * @window_size: defined in API but not used + */ +struct iwl_mvm_ctdp_cmd { + __le32 operation; + __le32 budget; + __le32 window_size; +} __packed; + +#define IWL_MAX_DTS_TRIPS 8 + +/** + * struct iwl_temp_report_ths_cmd - set temperature thresholds + * + * @num_temps: number of temperature thresholds passed + * @thresholds: array with the thresholds to be configured + */ +struct temp_report_ths_cmd { + __le32 num_temps; + __le16 thresholds[IWL_MAX_DTS_TRIPS]; +} __packed; /* GRP_PHY_TEMP_REPORTING_THRESHOLDS_CMD */ /*********************************** * TDLS API @@ -1851,4 +1953,53 @@ struct iwl_shared_mem_cfg { __le32 page_buff_size; } __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */ +/** + * VHT MU-MIMO group configuration + * + * @membership_status: a bitmap of MU groups + * @user_position:the position of station in a group. If the station is in the + * group then bits (group * 2) is the position -1 + */ +struct iwl_mu_group_mgmt_cmd { + __le32 reserved; + __le32 membership_status[2]; + __le32 user_position[4]; +} __packed; /* MU_GROUP_ID_MNG_TABLE_API_S_VER_1 */ + +/** + * struct iwl_mu_group_mgmt_notif - VHT MU-MIMO group id notification + * + * @membership_status: a bitmap of MU groups + * @user_position: the position of station in a group. If the station is in the + * group then bits (group * 2) is the position -1 + */ +struct iwl_mu_group_mgmt_notif { + __le32 membership_status[2]; + __le32 user_position[4]; +} __packed; /* MU_GROUP_MNG_NTFY_API_S_VER_1 */ + +#define MAX_STORED_BEACON_SIZE 600 + +/** + * Stored beacon notification + * + * @system_time: system time on air rise + * @tsf: TSF on air rise + * @beacon_timestamp: beacon on air rise + * @phy_flags: general phy flags: band, modulation, etc. + * @channel: channel this beacon was received on + * @rates: rate in ucode internal format + * @byte_count: frame's byte count + */ +struct iwl_stored_beacon_notif { + __le32 system_time; + __le64 tsf; + __le32 beacon_timestamp; + __le16 phy_flags; + __le16 channel; + __le32 rates; + __le32 byte_count; + u8 data[MAX_STORED_BEACON_SIZE]; +} __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_1 */ + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 0813f8184..6938cd37b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -435,6 +435,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) bool monitor_dump_only = false; int i; + if (!IWL_MVM_COLLECT_FW_ERR_DUMP && + !mvm->trans->dbg_dest_tlv) + return; + lockdep_assert_held(&mvm->mutex); /* there's no point in fw dump if the bus is dead */ @@ -522,7 +526,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; /* Make room for fw's virtual image pages, if it exists */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size && + mvm->fw_paging_db[0].fw_paging_block) file_len += mvm->num_of_paging_blk * (sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_paging) + @@ -639,9 +644,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) } /* Dump fw's virtual image */ - if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { - u32 i; - + if (mvm->fw->img[mvm->cur_ucode].paging_mem_size && + mvm->fw_paging_db[0].fw_paging_block) { for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { struct iwl_fw_error_dump_paging *paging; struct page *pages = diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 0ccc697fe..09d895faf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -107,6 +108,24 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) sizeof(tx_ant_cmd), &tx_ant_cmd); } +static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) +{ + int i; + struct iwl_rss_config_cmd cmd = { + .flags = cpu_to_le32(IWL_RSS_ENABLE), + .hash_mask = IWL_RSS_HASH_TYPE_IPV4_TCP | + IWL_RSS_HASH_TYPE_IPV4_PAYLOAD | + IWL_RSS_HASH_TYPE_IPV6_TCP | + IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, + }; + + for (i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) + cmd.indirection_table[i] = i % mvm->trans->num_rx_queues; + memcpy(cmd.secret_key, mvm->secret_key, sizeof(cmd.secret_key)); + + return iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd); +} + void iwl_free_fw_paging(struct iwl_mvm *mvm) { int i; @@ -125,9 +144,11 @@ void iwl_free_fw_paging(struct iwl_mvm *mvm) __free_pages(mvm->fw_paging_db[i].fw_paging_block, get_order(mvm->fw_paging_db[i].fw_paging_size)); + mvm->fw_paging_db[i].fw_paging_block = NULL; } kfree(mvm->trans->paging_download_buf); mvm->trans->paging_download_buf = NULL; + mvm->trans->paging_db = NULL; memset(mvm->fw_paging_db, 0, sizeof(mvm->fw_paging_db)); } @@ -520,7 +541,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, struct iwl_sf_region st_fwrd_space; if (ucode_type == IWL_UCODE_REGULAR && - iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE)) + iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) && + !(fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED))) fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER); else fw = iwl_get_ucode_image(mvm, ucode_type); @@ -896,6 +919,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; + /* Init RSS configuration */ + if (iwl_mvm_has_new_rx_api(mvm)) { + ret = iwl_send_rss_cfg_cmd(mvm); + if (ret) { + IWL_ERR(mvm, "Failed to configure RSS queues: %d\n", + ret); + goto error; + } + } + /* init the fw <-> mac80211 STA mapping */ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); @@ -925,8 +958,26 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } +#ifdef CONFIG_THERMAL + if (iwl_mvm_is_tt_in_fw(mvm)) { + /* in order to give the responsibility of ct-kill and + * TX backoff to FW we need to send empty temperature reporting + * cmd during init time + */ + iwl_mvm_send_temp_report_ths_cmd(mvm); + } else { + /* Initialize tx backoffs to the minimal possible */ + iwl_mvm_tt_tx_backoff(mvm, 0); + } + + /* TODO: read the budget from BIOS / Platform NVM */ + if (iwl_mvm_is_ctdp_supported(mvm) && mvm->cooling_dev.cur_state > 0) + ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, + mvm->cooling_dev.cur_state); +#else /* Initialize tx backoffs to the minimal possible */ iwl_mvm_tt_tx_backoff(mvm, 0); +#endif WARN_ON(iwl_mvm_config_ltr(mvm)); @@ -962,7 +1013,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: - iwl_trans_stop_device(mvm->trans); + iwl_mvm_stop_device(mvm); return ret; } @@ -1006,7 +1057,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) return 0; error: - iwl_trans_stop_device(mvm->trans); + iwl_mvm_stop_device(mvm); return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index bf1e5eb5d..e885db346 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -744,7 +744,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, * wake-ups. */ cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (mvmvif->ap_assoc_sta_count) { + if (mvmvif->ap_assoc_sta_count || !mvm->drop_bcn_ap_mode) { cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); } else { @@ -1462,3 +1462,42 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, iwl_mvm_beacon_loss_iterator, mb); } + +void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_stored_beacon_notif *sb = (void *)pkt->data; + struct ieee80211_rx_status rx_status; + struct sk_buff *skb; + u32 size = le32_to_cpu(sb->byte_count); + + if (size == 0) + return; + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mvm, "alloc_skb failed\n"); + return; + } + + /* update rx_status according to the notification's metadata */ + memset(&rx_status, 0, sizeof(rx_status)); + rx_status.mactime = le64_to_cpu(sb->tsf); + /* TSF as indicated by the firmware is at INA time */ + rx_status.flag |= RX_FLAG_MACTIME_PLCP_START; + rx_status.device_timestamp = le32_to_cpu(sb->system_time); + rx_status.band = + (sb->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(sb->channel), + rx_status.band); + + /* copy the data */ + memcpy(skb_put(skb, size), sb->data, size); + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + + /* pass it as regular rx to mac80211 */ + ieee80211_rx_napi(mvm->hw, skb, NULL); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 1486f33a7..a50f4df7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -69,7 +70,6 @@ #include <linux/etherdevice.h> #include <linux/ip.h> #include <linux/if_arp.h> -#include <linux/devcoredump.h> #include <linux/time.h> #include <net/mac80211.h> #include <net/ieee80211_radiotap.h> @@ -85,7 +85,6 @@ #include "testmode.h" #include "iwl-fw-error-dump.h" #include "iwl-prph.h" -#include "iwl-csr.h" #include "iwl-nvm-parse.h" #include "fw-dbg.h" @@ -837,13 +836,17 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; bool tx_agg_ref = false; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; + bool amsdu = params->amsdu; IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", sta->addr, tid, action); @@ -884,10 +887,10 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ret = -EINVAL; break; } - ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true); + ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true, buf_size); break; case IEEE80211_AMPDU_RX_STOP: - ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false); + ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false, buf_size); break; case IEEE80211_AMPDU_TX_START: if (!iwl_enable_tx_ampdu(mvm->cfg)) { @@ -904,7 +907,8 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ret = iwl_mvm_sta_tx_agg_flush(mvm, vif, sta, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: - ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size); + ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, + buf_size, amsdu); break; default: WARN_ON_ONCE(1); @@ -966,7 +970,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) */ iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); - iwl_trans_stop_device(mvm->trans); + iwl_mvm_stop_device(mvm); mvm->scan_status = 0; mvm->ps_disabled = false; @@ -1135,7 +1139,7 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) */ flush_work(&mvm->roc_done_wk); - iwl_trans_stop_device(mvm->trans); + iwl_mvm_stop_device(mvm); iwl_mvm_async_handlers_purge(mvm); /* async_handlers_list is empty and will stay empty: HW is stopped */ @@ -1168,8 +1172,6 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) mvm->scan_uid_status[i] = 0; } } - - mvm->ucode_loaded = false; } static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) @@ -1761,6 +1763,50 @@ static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm) } #endif +static int iwl_mvm_update_mu_groups(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mu_group_mgmt_cmd cmd = {}; + + memcpy(cmd.membership_status, vif->bss_conf.mu_group.membership, + WLAN_MEMBERSHIP_LEN); + memcpy(cmd.user_position, vif->bss_conf.mu_group.position, + WLAN_USER_POSITION_LEN); + + return iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(DATA_PATH_GROUP, + UPDATE_MU_GROUPS_CMD), + 0, sizeof(cmd), &cmd); +} + +static void iwl_mvm_mu_mimo_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (vif->mu_mimo_owner) { + struct iwl_mu_group_mgmt_notif *notif = _data; + + /* + * MU-MIMO Group Id action frame is little endian. We treat + * the data received from firmware as if it came from the + * action frame, so no conversion is needed. + */ + ieee80211_update_mu_groups(vif, + (u8 *)¬if->membership_status, + (u8 *)¬if->user_position); + } +} + +void iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_mu_mimo_iface_iterator, notif); +} + static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -1869,6 +1915,18 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, vif->addr); } + /* + * The firmware tracks the MU-MIMO group on its own. + * However, on HW restart we should restore this data. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + (changes & BSS_CHANGED_MU_GROUPS) && vif->mu_mimo_owner) { + ret = iwl_mvm_update_mu_groups(mvm, vif); + if (ret) + IWL_ERR(mvm, + "failed to update VHT MU_MIMO groups\n"); + } + iwl_mvm_recalc_multicast(mvm); iwl_mvm_configure_bcast_filter(mvm); @@ -1895,7 +1953,12 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); } - if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS)) { + if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | BSS_CHANGED_QOS | + /* + * Send power command on every beacon change, + * because we may have not enabled beacon abort yet. + */ + BSS_CHANGED_BEACON_INFO)) { ret = iwl_mvm_power_update_mac(mvm); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); @@ -2082,7 +2145,6 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, bss_conf->txpower); iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower); } - } static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, @@ -2275,6 +2337,11 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) return; + if (vif->p2p && !iwl_mvm_is_p2p_standalone_uapsd_supported(mvm)) { + vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + return; + } + if (iwlwifi_mod_params.uapsd_disable) { vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; return; @@ -2489,10 +2556,8 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS, - 200 + vif->bss_conf.beacon_int); - u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS, - 100 + vif->bss_conf.beacon_int); + u32 duration = IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; + u32 min_duration = IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS; if (WARN_ON_ONCE(vif->bss_conf.assoc)) return; @@ -2584,7 +2649,7 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; break; case WLAN_CIPHER_SUITE_CCMP: key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; @@ -2623,8 +2688,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, * GTK on AP interface is a TX-only key, return 0; * on IBSS they're per-station and because we're lazy * we don't support them for RX, so do the same. + * CMAC in AP/IBSS modes must be done in software. */ - ret = 0; + if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) + ret = -EOPNOTSUPP; + else + ret = 0; key->hw_key_idx = STA_KEY_IDX_INVALID; break; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ff7c6df9f..9abbc93e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -71,6 +73,10 @@ #include <linux/leds.h> #include <linux/in6.h> +#ifdef CONFIG_THERMAL +#include <linux/thermal.h> +#endif + #include "iwl-op-mode.h" #include "iwl-trans.h" #include "iwl-notif-wait.h" @@ -346,8 +352,9 @@ struct iwl_mvm_vif_bf_data { * @pm_enabled - Indicate if MAC power management is allowed * @monitor_active: indicates that monitor context is configured, and that the * interface should get quota etc. - * @low_latency: indicates that this interface is in low-latency mode - * (VMACLowLatencyMode) + * @low_latency_traffic: indicates low latency traffic was detected + * @low_latency_dbgfs: low latency mode set from debugfs + * @low_latency_vcmd: low latency mode set from vendor command * @ps_disabled: indicates that this interface requires PS to be disabled * @queue_params: QoS params for this MAC * @bcast_sta: station used for broadcast packets. Used by the following @@ -375,7 +382,7 @@ struct iwl_mvm_vif { bool ap_ibss_active; bool pm_enabled; bool monitor_active; - bool low_latency; + bool low_latency_traffic, low_latency_dbgfs, low_latency_vcmd; bool ps_disabled; struct iwl_mvm_vif_bf_data bf_data; @@ -432,6 +439,7 @@ struct iwl_mvm_vif { struct iwl_dbgfs_pm dbgfs_pm; struct iwl_dbgfs_bf dbgfs_bf; struct iwl_mac_power_cmd mac_pwr_cmd; + int dbgfs_quota_min; #endif enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; @@ -485,6 +493,12 @@ enum iwl_mvm_scan_type { IWL_SCAN_TYPE_FRAGMENTED, }; +enum iwl_mvm_sched_scan_pass_all_states { + SCHED_SCAN_PASS_ALL_DISABLED, + SCHED_SCAN_PASS_ALL_ENABLED, + SCHED_SCAN_PASS_ALL_FOUND, +}; + /** * struct iwl_nvm_section - describes an NVM section in memory. * @@ -515,6 +529,30 @@ struct iwl_mvm_tt_mgmt { bool throttle; }; +#ifdef CONFIG_THERMAL +/** + *struct iwl_mvm_thermal_device - thermal zone related data + * @temp_trips: temperature thresholds for report + * @fw_trips_index: keep indexes to original array - temp_trips + * @tzone: thermal zone device data +*/ +struct iwl_mvm_thermal_device { + s16 temp_trips[IWL_MAX_DTS_TRIPS]; + u8 fw_trips_index[IWL_MAX_DTS_TRIPS]; + struct thermal_zone_device *tzone; +}; + +/* + * struct iwl_mvm_cooling_device + * @cur_state: current state + * @cdev: struct thermal cooling device + */ +struct iwl_mvm_cooling_device { + u32 cur_state; + struct thermal_cooling_device *cdev; +}; +#endif + #define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8 struct iwl_mvm_frame_stats { @@ -645,6 +683,7 @@ struct iwl_mvm { atomic_t pending_frames[IWL_MVM_STATION_COUNT]; u32 tfd_drained[IWL_MVM_STATION_COUNT]; u8 rx_ba_sessions; + u32 secret_key[IWL_RSS_HASH_KEY_CNT]; /* configured by mac80211 */ u32 rts_threshold; @@ -654,6 +693,7 @@ struct iwl_mvm { void *scan_cmd; struct iwl_mcast_filter_cmd *mcast_filter_cmd; enum iwl_mvm_scan_type scan_type; + enum iwl_mvm_sched_scan_pass_all_states sched_scan_pass_all; /* max number of simultaneous scans the FW supports */ unsigned int max_scans; @@ -794,6 +834,11 @@ struct iwl_mvm { /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; +#ifdef CONFIG_THERMAL + struct iwl_mvm_thermal_device tz_device; + struct iwl_mvm_cooling_device cooling_dev; +#endif + s32 temperature; /* Celsius */ /* * Debug option to set the NIC temperature. This option makes the @@ -816,6 +861,7 @@ struct iwl_mvm { /* Indicate if device power save is allowed */ u8 ps_disabled; /* u8 instead of bool to ease debugfs_create_* usage */ + unsigned int max_amsdu_len; /* used for debugfs only */ struct ieee80211_vif __rcu *csa_vif; struct ieee80211_vif __rcu *csa_tx_blocked_vif; @@ -856,6 +902,12 @@ struct iwl_mvm { u32 ciphers[6]; struct iwl_mvm_tof_data tof_data; + + /* + * Drop beacons from other APs in AP mode when there are no connected + * clients. + */ + bool drop_bcn_ap_mode; }; /* Extract MVM priv from op_mode and _hw */ @@ -934,8 +986,9 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) { - return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_DQA_SUPPORT); + /* Make sure DQA isn't allowed in driver until feature is complete */ + return false && fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_DQA_SUPPORT); } static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) @@ -1005,10 +1058,40 @@ static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm) IWL_MVM_BT_COEX_MPLUT; } +static inline +bool iwl_mvm_is_p2p_standalone_uapsd_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD) && + IWL_MVM_P2P_UAPSD_STANDALONE; +} + static inline bool iwl_mvm_has_new_rx_api(struct iwl_mvm *mvm) { - /* firmware flag isn't defined yet */ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT); +} + +static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm) +{ +#ifdef CONFIG_THERMAL + /* these two TLV are redundant since the responsibility to CT-kill by + * FW happens only after we send at least one command of + * temperature THs report. + */ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CT_KILL_BY_FW) && + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT); +#else /* CONFIG_THERMAL */ return false; +#endif /* CONFIG_THERMAL */ +} + +static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm) +{ + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CTDP_SUPPORT); } extern const u8 iwl_mvm_ac_to_tx_fifo[]; @@ -1143,6 +1226,10 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue); void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, int queue); +int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, + const u8 *data, u32 count); +void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, + int queue); void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, @@ -1184,6 +1271,12 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_mu_mimo_grp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_window_status_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, @@ -1420,8 +1513,9 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) * binding, so this has no real impact. For now, just return * the current desired low-latency state. */ - - return mvmvif->low_latency; + return mvmvif->low_latency_dbgfs || + mvmvif->low_latency_traffic || + mvmvif->low_latency_vcmd; } /* hw scheduler queue config */ @@ -1459,32 +1553,29 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); } -static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, - int mac80211_queue, int fifo, - int sta_id, int tid, int frame_limit, - u16 ssn, unsigned int wdg_timeout) +static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm) { - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = fifo, - .sta_id = sta_id, - .tid = tid, - .frame_limit = frame_limit, - .aggregate = true, - }; - - iwl_mvm_enable_txq(mvm, queue, mac80211_queue, ssn, &cfg, wdg_timeout); + mvm->ucode_loaded = false; + iwl_trans_stop_device(mvm->trans); } +/* Stop/start all mac queues in a given bitmap */ +void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq); +void iwl_mvm_stop_mac_queues(struct iwl_mvm *mvm, unsigned long mq); + /* Thermal management and CT-kill */ void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp); void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_tt_handler(struct iwl_mvm *mvm); -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff); -void iwl_mvm_tt_exit(struct iwl_mvm *mvm); +void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff); +void iwl_mvm_thermal_exit(struct iwl_mvm *mvm); void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); -int iwl_mvm_get_temp(struct iwl_mvm *mvm); +int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp); +void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm); +int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 budget); /* Location Aware Regulatory */ struct iwl_mcc_update_resp * diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index a891598c8..9a8cf2f41 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -300,7 +300,6 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) struct iwl_nvm_section *sections = mvm->nvm_sections; const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; bool lar_enabled; - u32 mac_addr0, mac_addr1; /* Checking for required sections */ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { @@ -336,10 +335,6 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) if (WARN_ON(!mvm->cfg)) return NULL; - /* read the mac address from WFMP registers */ - mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0); - mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1); - hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; @@ -352,10 +347,10 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT); - return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, + return iwl_parse_nvm_data(mvm->trans, mvm->cfg, hw, sw, calib, regulatory, mac_override, phy_sku, mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, - lar_enabled, mac_addr0, mac_addr1); + lar_enabled); } #define MAX_NVM_FILE_LEN 16384 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 89ea70dee..d27839909 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -33,6 +33,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -204,70 +205,107 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); } +/** + * enum iwl_rx_handler_context context for Rx handler + * @RX_HANDLER_SYNC : this means that it will be called in the Rx path + * which can't acquire mvm->mutex. + * @RX_HANDLER_ASYNC_LOCKED : If the handler needs to hold mvm->mutex + * (and only in this case!), it should be set as ASYNC. In that case, + * it will be called from a worker with mvm->mutex held. + * @RX_HANDLER_ASYNC_UNLOCKED : in case the handler needs to lock the + * mutex itself, it will be called from a worker without mvm->mutex held. + */ +enum iwl_rx_handler_context { + RX_HANDLER_SYNC, + RX_HANDLER_ASYNC_LOCKED, + RX_HANDLER_ASYNC_UNLOCKED, +}; + +/** + * struct iwl_rx_handlers handler for FW notification + * @cmd_id: command id + * @context: see &iwl_rx_handler_context + * @fn: the function is called when notification is received + */ struct iwl_rx_handlers { u16 cmd_id; - bool async; + enum iwl_rx_handler_context context; void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); }; -#define RX_HANDLER(_cmd_id, _fn, _async) \ - { .cmd_id = _cmd_id , .fn = _fn , .async = _async } -#define RX_HANDLER_GRP(_grp, _cmd, _fn, _async) \ - { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .async = _async } +#define RX_HANDLER(_cmd_id, _fn, _context) \ + { .cmd_id = _cmd_id, .fn = _fn, .context = _context } +#define RX_HANDLER_GRP(_grp, _cmd, _fn, _context) \ + { .cmd_id = WIDE_ID(_grp, _cmd), .fn = _fn, .context = _context } /* * Handlers for fw notifications * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME * This list should be in order of frequency for performance purposes. * - * The handler can be SYNC - this means that it will be called in the Rx path - * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and - * only in this case!), it should be set as ASYNC. In that case, it will be - * called from a worker with mvm->mutex held. + * The handler can be one from three contexts, see &iwl_rx_handler_context */ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { - RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false), - RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), - - RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), - RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true), - RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), + RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, RX_HANDLER_SYNC), + RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, RX_HANDLER_SYNC), + + RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, + RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, + RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, + RX_HANDLER_ASYNC_LOCKED), RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, - iwl_mvm_rx_ant_coupling_notif, true), + iwl_mvm_rx_ant_coupling_notif, RX_HANDLER_ASYNC_LOCKED), + + RX_HANDLER(BA_WINDOW_STATUS_NOTIFICATION_ID, + iwl_mvm_window_status_notif, RX_HANDLER_SYNC), - RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), - RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true), + RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, + RX_HANDLER_SYNC), + RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, + RX_HANDLER_ASYNC_LOCKED), - RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), + RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, RX_HANDLER_SYNC), RX_HANDLER(SCAN_ITERATION_COMPLETE, - iwl_mvm_rx_lmac_scan_iter_complete_notif, false), + iwl_mvm_rx_lmac_scan_iter_complete_notif, RX_HANDLER_SYNC), RX_HANDLER(SCAN_OFFLOAD_COMPLETE, - iwl_mvm_rx_lmac_scan_complete_notif, true), + iwl_mvm_rx_lmac_scan_complete_notif, + RX_HANDLER_ASYNC_LOCKED), RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_match_found, - false), + RX_HANDLER_SYNC), RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif, - true), + RX_HANDLER_ASYNC_LOCKED), RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC, - iwl_mvm_rx_umac_scan_iter_complete_notif, false), + iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC), - RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), + RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, + RX_HANDLER_SYNC), RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, - false), + RX_HANDLER_SYNC), - RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), + RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, RX_HANDLER_SYNC), RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, - iwl_mvm_power_uapsd_misbehaving_ap_notif, false), - RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true), + iwl_mvm_power_uapsd_misbehaving_ap_notif, RX_HANDLER_SYNC), + RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, + RX_HANDLER_ASYNC_LOCKED), RX_HANDLER_GRP(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, - iwl_mvm_temp_notif, true), + iwl_mvm_temp_notif, RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER_GRP(PHY_OPS_GROUP, CT_KILL_NOTIFICATION, + iwl_mvm_ct_kill_notif, RX_HANDLER_SYNC), RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif, - true), - RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false), - RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true), - + RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, + RX_HANDLER_SYNC), + RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, + RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER_GRP(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF, + iwl_mvm_rx_stored_beacon_notif, RX_HANDLER_SYNC), + RX_HANDLER_GRP(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, + iwl_mvm_mu_mimo_grp_notif, RX_HANDLER_SYNC), }; #undef RX_HANDLER #undef RX_HANDLER_GRP @@ -289,6 +327,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(SCAN_COMPLETE_UMAC), HCMD_NAME(TOF_CMD), HCMD_NAME(TOF_NOTIFICATION), + HCMD_NAME(BA_WINDOW_STATUS_NOTIFICATION_ID), HCMD_NAME(ADD_STA_KEY), HCMD_NAME(ADD_STA), HCMD_NAME(REMOVE_STA), @@ -344,6 +383,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { HCMD_NAME(MAC_PM_POWER_TABLE), HCMD_NAME(TDLS_CHANNEL_SWITCH_NOTIFICATION), HCMD_NAME(MFUART_LOAD_NOTIFICATION), + HCMD_NAME(RSS_CONFIG_CMD), HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), HCMD_NAME(REPLY_RX_PHY_CMD), HCMD_NAME(REPLY_RX_MPDU_CMD), @@ -383,16 +423,37 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { */ static const struct iwl_hcmd_names iwl_mvm_phy_names[] = { HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), + HCMD_NAME(CTDP_CONFIG_CMD), + HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD), + HCMD_NAME(CT_KILL_NOTIFICATION), HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), }; +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { + HCMD_NAME(UPDATE_MU_GROUPS_CMD), + HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), + HCMD_NAME(MU_GROUP_MGMT_NOTIF), + HCMD_NAME(RX_QUEUES_NOTIFICATION), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mvm_prot_offload_names[] = { + HCMD_NAME(STORED_BEACON_NTF), +}; + static const struct iwl_hcmd_arr iwl_mvm_groups[] = { [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names), + [DATA_PATH_GROUP] = HCMD_ARR(iwl_mvm_data_path_names), + [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names), }; - /* this forward declaration can avoid to export the function */ static void iwl_mvm_async_handlers_wk(struct work_struct *wk); static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); @@ -463,8 +524,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (iwl_mvm_has_new_rx_api(mvm)) { op_mode->ops = &iwl_mvm_ops_mq; + trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_desc); } else { op_mode->ops = &iwl_mvm_ops; + trans->rx_mpdu_cmd_hdr_size = + sizeof(struct iwl_rx_mpdu_res_start); if (WARN_ON(trans->num_rx_queues > 1)) goto out_free; @@ -481,6 +545,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, } mvm->sf_state = SF_UNINIT; mvm->cur_ucode = IWL_UCODE_INIT; + mvm->drop_bcn_ap_mode = true; mutex_init(&mvm->mutex); mutex_init(&mvm->d0i3_suspend_mutex); @@ -555,7 +620,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_trans_configure(mvm->trans, &trans_cfg); trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv; trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, @@ -575,9 +639,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, IWL_INFO(mvm, "Detected %s, REV=0x%X\n", mvm->cfg->name, mvm->trans->hw_rev); - min_backoff = calc_min_backoff(trans, cfg); - iwl_mvm_tt_initialize(mvm, min_backoff); - if (iwlwifi_mod_params.nvm_file) mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; else @@ -607,7 +668,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE); err = iwl_run_init_mvm_ucode(mvm, true); if (!err || !iwlmvm_mod_params.init_dbg) - iwl_trans_stop_device(trans); + iwl_mvm_stop_device(mvm); iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE); mutex_unlock(&mvm->mutex); /* returns 0 if successful, 1 if success but in rfkill */ @@ -630,22 +691,31 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_free; + min_backoff = calc_min_backoff(trans, cfg); + iwl_mvm_thermal_initialize(mvm, min_backoff); + err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); if (err) goto out_unregister; memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); - /* rpm starts with a taken reference, we can release it now */ - iwl_trans_unref(mvm->trans); + /* The transport always starts with a taken reference, we can + * release it now if d0i3 is supported */ + if (iwl_mvm_is_d0i3_supported(mvm)) + iwl_trans_unref(mvm->trans); iwl_mvm_tof_init(mvm); + /* init RSS hash key */ + get_random_bytes(mvm->secret_key, sizeof(mvm->secret_key)); + return op_mode; out_unregister: ieee80211_unregister_hw(mvm->hw); iwl_mvm_leds_exit(mvm); + iwl_mvm_thermal_exit(mvm); out_free: flush_delayed_work(&mvm->fw_dump_wk); iwl_phy_db_free(mvm->phy_db); @@ -661,9 +731,16 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); int i; + /* If d0i3 is supported, we have released the reference that + * the transport started with, so we should take it back now + * that we are leaving. + */ + if (iwl_mvm_is_d0i3_supported(mvm)) + iwl_trans_ref(mvm->trans); + iwl_mvm_leds_exit(mvm); - iwl_mvm_tt_exit(mvm); + iwl_mvm_thermal_exit(mvm); ieee80211_unregister_hw(mvm->hw); @@ -692,6 +769,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) struct iwl_async_handler_entry { struct list_head list; struct iwl_rx_cmd_buffer rxb; + enum iwl_rx_handler_context context; void (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); }; @@ -718,7 +796,6 @@ static void iwl_mvm_async_handlers_wk(struct work_struct *wk) INIT_LIST_HEAD(&local_list); /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */ - mutex_lock(&mvm->mutex); /* * Sync with Rx path with a lock. Remove all the entries from this list, @@ -729,12 +806,15 @@ static void iwl_mvm_async_handlers_wk(struct work_struct *wk) spin_unlock_bh(&mvm->async_handlers_lock); list_for_each_entry_safe(entry, tmp, &local_list, list) { + if (entry->context == RX_HANDLER_ASYNC_LOCKED) + mutex_lock(&mvm->mutex); entry->fn(mvm, &entry->rxb); iwl_free_rxb(&entry->rxb); list_del(&entry->list); + if (entry->context == RX_HANDLER_ASYNC_LOCKED) + mutex_unlock(&mvm->mutex); kfree(entry); } - mutex_unlock(&mvm->mutex); } static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, @@ -791,7 +871,7 @@ static void iwl_mvm_rx_common(struct iwl_mvm *mvm, if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) continue; - if (!rx_h->async) { + if (rx_h->context == RX_HANDLER_SYNC) { rx_h->fn(mvm, rxb); return; } @@ -805,6 +885,7 @@ static void iwl_mvm_rx_common(struct iwl_mvm *mvm, entry->rxb._offset = rxb->_offset; entry->rxb._rx_page_order = rxb->_rx_page_order; entry->fn = rx_h->fn; + entry->context = rx_h->context; spin_lock(&mvm->async_handlers_lock); list_add_tail(&entry->list, &mvm->async_handlers_list); spin_unlock(&mvm->async_handlers_lock); @@ -841,28 +922,24 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0); else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) iwl_mvm_rx_phy_cmd_mq(mvm, rxb); + else if (unlikely(pkt->hdr.group_id == DATA_PATH_GROUP && + pkt->hdr.cmd == RX_QUEUES_NOTIFICATION)) + iwl_mvm_rx_queue_notif(mvm, rxb, 0); else iwl_mvm_rx_common(mvm, rxb, pkt); } -static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) +void iwl_mvm_stop_mac_queues(struct iwl_mvm *mvm, unsigned long mq) { - struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); - unsigned long mq; int q; - spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[queue].hw_queue_to_mac80211; - spin_unlock_bh(&mvm->queue_info_lock); - if (WARN_ON_ONCE(!mq)) return; for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { if (atomic_inc_return(&mvm->mac80211_queue_stop_count[q]) > 1) { IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) already stopped\n", - queue, q); + "mac80211 %d already stopped\n", q); continue; } @@ -882,24 +959,29 @@ static void iwl_mvm_async_cb(struct iwl_op_mode *op_mode, iwl_trans_block_txq_ptrs(mvm->trans, false); } -static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) +static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int hw_queue) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); unsigned long mq; - int q; spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[queue].hw_queue_to_mac80211; + mq = mvm->queue_info[hw_queue].hw_queue_to_mac80211; spin_unlock_bh(&mvm->queue_info_lock); + iwl_mvm_stop_mac_queues(mvm, mq); +} + +void iwl_mvm_start_mac_queues(struct iwl_mvm *mvm, unsigned long mq) +{ + int q; + if (WARN_ON_ONCE(!mq)) return; for_each_set_bit(q, &mq, IEEE80211_MAX_QUEUES) { if (atomic_dec_return(&mvm->mac80211_queue_stop_count[q]) > 0) { IWL_DEBUG_TX_QUEUES(mvm, - "queue %d (mac80211 %d) still stopped\n", - queue, q); + "mac80211 %d still stopped\n", q); continue; } @@ -907,6 +989,18 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) } } +static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + unsigned long mq; + + spin_lock_bh(&mvm->queue_info_lock); + mq = mvm->queue_info[hw_queue].hw_queue_to_mac80211; + spin_unlock_bh(&mvm->queue_info_lock); + + iwl_mvm_start_mac_queues(mvm, mq); +} + void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) { if (state) @@ -1196,7 +1290,7 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, cmd->is_11n_connection = ap_sta->ht_cap.ht_supported; cmd->offloading_tid = iter_data->offloading_tid; cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING | - ENABLE_DHCP_FILTERING; + ENABLE_DHCP_FILTERING | ENABLE_STORE_BEACON; /* * The d0i3 uCode takes care of the nonqos counters, * so configure only the qos seq ones. @@ -1217,8 +1311,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) struct iwl_wowlan_config_cmd wowlan_config_cmd = { .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | IWL_WOWLAN_WAKEUP_BEACON_MISS | - IWL_WOWLAN_WAKEUP_LINK_CHANGE | - IWL_WOWLAN_WAKEUP_BCN_FILTERING), + IWL_WOWLAN_WAKEUP_LINK_CHANGE), }; struct iwl_d3_manager_config d3_cfg_cmd = { .min_sleep_time = cpu_to_le32(1000), @@ -1268,6 +1361,12 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) /* configure wowlan configuration only if needed */ if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { + /* wake on beacons only if beacon storing isn't supported */ + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BEACON_STORING)) + wowlan_config_cmd.wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BCN_FILTERING); + iwl_mvm_wowlan_config_key_params(mvm, d0i3_iter_data.connected_vif, true, flags); @@ -1508,6 +1607,9 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, if (unlikely(pkt->hdr.cmd == FRAME_RELEASE)) iwl_mvm_rx_frame_release(mvm, rxb, queue); + else if (unlikely(pkt->hdr.cmd == RX_QUEUES_NOTIFICATION && + pkt->hdr.group_id == DATA_PATH_GROUP)) + iwl_mvm_rx_queue_notif(mvm, rxb, queue); else iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, queue); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 9de159f1e..f313910cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -259,6 +259,26 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; } +static void iwl_mvm_p2p_standalone_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *is_p2p_standalone = _data; + + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP: + *is_p2p_standalone = false; + break; + case NL80211_IFTYPE_STATION: + if (vif->bss_conf.assoc) + *is_p2p_standalone = false; + break; + + default: + break; + } +} + static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -268,9 +288,6 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, ETH_ALEN)) return false; - if (vif->p2p && - !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) - return false; /* * Avoid using uAPSD if P2P client is associated to GO that uses * opportunistic power save. This is due to current FW limitation. @@ -287,6 +304,22 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, if (iwl_mvm_phy_ctx_count(mvm) >= 2) return false; + if (vif->p2p) { + /* Allow U-APSD only if p2p is stand alone */ + bool is_p2p_standalone = true; + + if (!iwl_mvm_is_p2p_standalone_uapsd_supported(mvm)) + return false; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_p2p_standalone_iterator, + &is_p2p_standalone); + + if (!is_p2p_standalone) + return false; + } + return true; } @@ -544,7 +577,6 @@ void iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, struct iwl_power_vifs { struct iwl_mvm *mvm; - struct ieee80211_vif *bf_vif; struct ieee80211_vif *bss_vif; struct ieee80211_vif *p2p_vif; struct ieee80211_vif *ap_vif; @@ -617,11 +649,6 @@ static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->bss_active = true; - - if (mvmvif->bf_data.bf_enabled && - !WARN_ON(power_iterator->bf_vif)) - power_iterator->bf_vif = vif; - break; default: @@ -850,29 +877,9 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false); } -static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - bool enable) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; - - if (!mvmvif->bf_data.bf_enabled) - return 0; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN) - cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); - - mvmvif->bf_data.ba_enabled = enable; - return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); -} - -int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 flags) +static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags, bool d0i3) { struct iwl_beacon_filter_cmd cmd = {}; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -883,12 +890,20 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); - if (!ret) + /* don't change bf_enabled in case of temporary d0i3 configuration */ + if (!ret && !d0i3) mvmvif->bf_data.bf_enabled = false; return ret; } +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags) +{ + return _iwl_mvm_disable_beacon_filter(mvm, vif, flags, false); +} + static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) { bool disable_ps; @@ -918,21 +933,26 @@ static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm) } static int iwl_mvm_power_set_ba(struct iwl_mvm *mvm, - struct iwl_power_vifs *vifs) + struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif; - bool ba_enable; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; - if (!vifs->bf_vif) + if (!mvmvif->bf_data.bf_enabled) return 0; - mvmvif = iwl_mvm_vif_from_mac80211(vifs->bf_vif); + if (mvm->cur_ucode == IWL_UCODE_WOWLAN) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); - ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || - !vifs->bf_vif->bss_conf.ps || - iwl_mvm_vif_low_latency(mvmvif)); + mvmvif->bf_data.ba_enabled = !(!mvmvif->pm_enabled || + mvm->ps_disabled || + !vif->bss_conf.ps || + iwl_mvm_vif_low_latency(mvmvif)); - return iwl_mvm_update_beacon_abort(mvm, vifs->bf_vif, ba_enable); + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); } int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) @@ -953,7 +973,10 @@ int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) if (ret) return ret; - return iwl_mvm_power_set_ba(mvm, &vifs); + if (vifs.bss_vif) + return iwl_mvm_power_set_ba(mvm, vifs.bss_vif); + + return 0; } int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) @@ -988,7 +1011,10 @@ int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) return ret; } - return iwl_mvm_power_set_ba(mvm, &vifs); + if (vifs.bss_vif) + return iwl_mvm_power_set_ba(mvm, vifs.bss_vif); + + return 0; } int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, @@ -1025,8 +1051,17 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, IWL_BF_CMD_CONFIG_D0I3, .bf_enable_beacon_filter = cpu_to_le32(1), }; - ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, - flags, true); + /* + * When beacon storing is supported - disable beacon filtering + * altogether - the latest beacon will be sent when exiting d0i3 + */ + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BEACON_STORING)) + ret = _iwl_mvm_disable_beacon_filter(mvm, vif, flags, + true); + else + ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, + flags, true); } else { if (mvmvif->bf_data.bf_enabled) ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c index 0b762b4f8..2141db5bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -74,6 +76,9 @@ struct iwl_mvm_quota_iterator_data { int n_interfaces[MAX_BINDINGS]; int colors[MAX_BINDINGS]; int low_latency[MAX_BINDINGS]; +#ifdef CONFIG_IWLWIFI_DEBUGFS + int dbgfs_min[MAX_BINDINGS]; +#endif int n_low_latency_bindings; struct ieee80211_vif *disabled_vif; }; @@ -129,6 +134,12 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, data->n_interfaces[id]++; +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_quota_min) + data->dbgfs_min[id] = max(data->dbgfs_min[id], + mvmvif->dbgfs_quota_min); +#endif + if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { data->n_low_latency_bindings++; data->low_latency[id] = true; @@ -259,6 +270,11 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, if (data.n_interfaces[i] <= 0) cmd.quotas[idx].quota = cpu_to_le32(0); +#ifdef CONFIG_IWLWIFI_DEBUGFS + else if (data.dbgfs_min[i]) + cmd.quotas[idx].quota = + cpu_to_le32(data.dbgfs_min[i] * QUOTA_100 / 100); +#endif else if (data.n_low_latency_bindings == 1 && n_non_lowlat && data.low_latency[i]) /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 94caa88df..61d0a8cd1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -556,6 +556,7 @@ static char *rs_pretty_rate(const struct rs_rate *rate) if (is_type_legacy(rate->type) && (rate->index <= IWL_RATE_54M_INDEX)) rate_str = legacy_rates[rate->index]; else if ((is_type_ht(rate->type) || is_type_vht(rate->type)) && + (rate->index >= IWL_RATE_MCS_0_INDEX) && (rate->index <= IWL_RATE_MCS_9_INDEX)) rate_str = ht_vht_rates[rate->index]; else @@ -1672,6 +1673,20 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) } } +static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, + enum rs_action scale_action) +{ + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + + if ((!is_vht(&tbl->rate) && !is_ht(&tbl->rate)) || + tbl->rate.index < IWL_RATE_MCS_5_INDEX || + scale_action == RS_ACTION_DOWNSCALE) + sta_priv->tlc_amsdu = false; + else + sta_priv->tlc_amsdu = true; +} + /* * setup rate table in uCode */ @@ -2062,7 +2077,8 @@ static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, } /* try decreasing first if applicable */ - if (weak != TPC_INVALID) { + if (sr >= RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && + weak != TPC_INVALID) { if (weak_tpt == IWL_INVALID_VALUE && (strong_tpt == IWL_INVALID_VALUE || current_tpt >= strong_tpt)) { @@ -2414,6 +2430,7 @@ lq_update: tbl->rate.index = index; if (IWL_MVM_RS_80_20_FAR_RANGE_TWEAK) rs_tweak_rate_tbl(mvm, sta, lq_sta, tbl, scale_action); + rs_set_amsdu_len(mvm, sta, tbl, scale_action); rs_update_rate_tbl(mvm, sta, lq_sta, tbl); } @@ -3097,6 +3114,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, sband = hw->wiphy->bands[band]; lq_sta->lq.sta_id = sta_priv->sta_id; + sta_priv->tlc_amsdu = false; for (j = 0; j < LQ_SIZE; j++) rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]); @@ -3656,10 +3674,13 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, ssize_t ret; struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_mvm_sta *mvmsta = + container_of(lq_sta, struct iwl_mvm_sta, lq_sta); struct iwl_mvm *mvm; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct rs_rate *rate = &tbl->rate; u32 ss_params; + mvm = lq_sta->pers.drv; buff = kmalloc(2048, GFP_KERNEL); if (!buff) @@ -3685,10 +3706,11 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (is_ht20(rate)) ? "20MHz" : (is_ht40(rate)) ? "40MHz" : (is_ht80(rate)) ? "80Mhz" : "BAD BW"); - desc += sprintf(buff + desc, " %s %s %s\n", + desc += sprintf(buff + desc, " %s %s %s %s\n", (rate->sgi) ? "SGI" : "NGI", (rate->ldpc) ? "LDPC" : "BCC", - (lq_sta->is_agg) ? "AGG on" : ""); + (lq_sta->is_agg) ? "AGG on" : "", + (mvmsta->tlc_amsdu) ? "AMSDU on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 145ec68ce..485cfc1a4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -322,11 +323,9 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, rx_status->freq = ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel), rx_status->band); - /* - * TSF as indicated by the fw is at INA time, but mac80211 expects the - * TSF at the beginning of the MPDU. - */ - /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/ + + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; iwl_mvm_get_signal_strength(mvm, phy_info, rx_status); @@ -448,6 +447,12 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_update_frame_stats(mvm, rate_n_flags, rx_status->flag & RX_FLAG_AMPDU_DETAILS); #endif + + if (unlikely((ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) && + mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED)) + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND; + iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, crypt_len, rxb); } @@ -622,3 +627,51 @@ void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb)); } + +void iwl_mvm_window_status_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_ba_window_status_notif *notif = (void *)pkt->data; + int i; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + + if (WARN_ONCE(pkt_len != sizeof(*notif), + "Received window status notification of wrong size (%u)\n", + pkt_len)) + return; + + rcu_read_lock(); + for (i = 0; i < BA_WINDOW_STREAMS_MAX; i++) { + struct ieee80211_sta *sta; + u8 sta_id, tid; + u64 bitmap; + u32 ssn; + u16 ratid; + u16 received_mpdu; + + ratid = le16_to_cpu(notif->ra_tid[i]); + /* check that this TID is valid */ + if (!(ratid & BA_WINDOW_STATUS_VALID_MSK)) + continue; + + received_mpdu = le16_to_cpu(notif->mpdu_rx_count[i]); + if (received_mpdu == 0) + continue; + + tid = ratid & BA_WINDOW_STATUS_TID_MSK; + /* get the station */ + sta_id = (ratid & BA_WINDOW_STATUS_STA_ID_MSK) + >> BA_WINDOW_STATUS_STA_ID_POS; + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (IS_ERR_OR_NULL(sta)) + continue; + bitmap = le64_to_cpu(notif->bitmap[i]); + ssn = le32_to_cpu(notif->start_seq_num[i]); + + /* update mac80211 with the bitmap for the reordering buffer */ + ieee80211_mark_rx_ba_filtered_frames(sta, tid, ssn, bitmap, + received_mpdu); + } + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 0c073e02f..9a54f2d2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -29,7 +29,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -156,7 +156,14 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, u16 len, u8 crypt_len, struct iwl_rx_cmd_buffer *rxb) { - unsigned int hdrlen, fraglen; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; + unsigned int headlen, fraglen, pad_len = 0; + unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); + + if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) + pad_len = 2; + len -= pad_len; /* If frame is small enough to fit in skb->head, pull it completely. * If not, only pull ieee80211_hdr (including crypto if present, and @@ -170,14 +177,23 @@ static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, * If the latter changes (there are efforts in the standards group * to do so) we should revisit this and ieee80211_data_to_8023(). */ - hdrlen = (len <= skb_tailroom(skb)) ? len : - sizeof(*hdr) + crypt_len + 8; + headlen = (len <= skb_tailroom(skb)) ? len : + hdrlen + crypt_len + 8; + /* The firmware may align the packet to DWORD. + * The padding is inserted after the IV. + * After copying the header + IV skip the padding if + * present before copying packet data. + */ + hdrlen += crypt_len; memcpy(skb_put(skb, hdrlen), hdr, hdrlen); - fraglen = len - hdrlen; + memcpy(skb_put(skb, headlen - hdrlen), (u8 *)hdr + hdrlen + pad_len, + headlen - hdrlen); + + fraglen = len - headlen; if (fraglen) { - int offset = (void *)hdr + hdrlen - + int offset = (void *)hdr + headlen + pad_len - rxb_addr(rxb) + rxb_offset(rxb); skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, @@ -201,25 +217,22 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, struct iwl_rx_mpdu_desc *desc, struct ieee80211_rx_status *rx_status) { - int energy_a, energy_b, energy_c, max_energy; + int energy_a, energy_b, max_energy; energy_a = desc->energy_a; energy_a = energy_a ? -energy_a : S8_MIN; energy_b = desc->energy_b; energy_b = energy_b ? -energy_b : S8_MIN; - energy_c = desc->energy_c; - energy_c = energy_c ? -energy_c : S8_MIN; max_energy = max(energy_a, energy_b); - max_energy = max(max_energy, energy_c); - IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n", - energy_a, energy_b, energy_c, max_energy); + IWL_DEBUG_STATS(mvm, "energy In A %d B %d, and max %d\n", + energy_a, energy_b, max_energy); rx_status->signal = max_energy; rx_status->chains = 0; /* TODO: phy info */ rx_status->chain_signal[0] = energy_a; rx_status->chain_signal[1] = energy_b; - rx_status->chain_signal[2] = energy_c; + rx_status->chain_signal[2] = S8_MIN; } static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, @@ -288,13 +301,121 @@ static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, skb->ip_summed = CHECKSUM_UNNECESSARY; } +/* + * returns true if a packet outside BA session is a duplicate and + * should be dropped + */ +static bool iwl_mvm_is_nonagg_dup(struct ieee80211_sta *sta, int queue, + struct ieee80211_rx_status *rx_status, + struct ieee80211_hdr *hdr, + struct iwl_rx_mpdu_desc *desc) +{ + struct iwl_mvm_sta *mvm_sta; + struct iwl_mvm_rxq_dup_data *dup_data; + u8 baid, tid, sub_frame_idx; + + if (WARN_ON(IS_ERR_OR_NULL(sta))) + return false; + + baid = (le32_to_cpu(desc->reorder_data) & + IWL_RX_MPDU_REORDER_BAID_MASK) >> + IWL_RX_MPDU_REORDER_BAID_SHIFT; + + if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) + return false; + + mvm_sta = iwl_mvm_sta_from_mac80211(sta); + dup_data = &mvm_sta->dup_data[queue]; + + /* + * Drop duplicate 802.11 retransmissions + * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") + */ + if (ieee80211_is_ctl(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) { + rx_status->flag |= RX_FLAG_DUP_VALIDATED; + return false; + } + + if (ieee80211_is_data_qos(hdr->frame_control)) + /* frame has qos control */ + tid = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_TID_MASK; + else + tid = IWL_MAX_TID_COUNT; + + /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */ + sub_frame_idx = desc->amsdu_info & IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + + if (unlikely(ieee80211_has_retry(hdr->frame_control) && + dup_data->last_seq[tid] == hdr->seq_ctrl && + dup_data->last_sub_frame[tid] >= sub_frame_idx)) + return true; + + dup_data->last_seq[tid] = hdr->seq_ctrl; + dup_data->last_sub_frame[tid] = sub_frame_idx; + + rx_status->flag |= RX_FLAG_DUP_VALIDATED; + + return false; +} + +int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, + const u8 *data, u32 count) +{ + struct iwl_rxq_sync_cmd *cmd; + u32 data_size = sizeof(*cmd) + count; + int ret; + + /* should be DWORD aligned */ + if (WARN_ON(count & 3 || count > IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE)) + return -EINVAL; + + cmd = kzalloc(data_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->rxq_mask = cpu_to_le32(rxq_mask); + cmd->count = cpu_to_le32(count); + cmd->flags = 0; + memcpy(cmd->payload, data, count); + + ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(DATA_PATH_GROUP, + TRIGGER_RX_QUEUES_NOTIF_CMD), + 0, data_size, cmd); + + kfree(cmd); + return ret; +} + +void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, + int queue) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rxq_sync_notification *notif; + struct iwl_mvm_internal_rxq_notif *internal_notif; + + notif = (void *)pkt->data; + internal_notif = (void *)notif->payload; + + switch (internal_notif->type) { + case IWL_MVM_RXQ_NOTIF_DEL_BA: + /* TODO */ + break; + default: + WARN_ONCE(1, "Invalid identifier %d", internal_notif->type); + } +} + void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { struct ieee80211_rx_status *rx_status; struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; - struct ieee80211_hdr *hdr = (void *)(desc + 1); + struct ieee80211_hdr *hdr = (void *)(pkt->data + sizeof(*desc)); u32 len = le16_to_cpu(desc->mpdu_len); u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); struct ieee80211_sta *sta = NULL; @@ -335,6 +456,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rx_status->freq = ieee80211_channel_to_frequency(desc->channel, rx_status->band); iwl_mvm_get_signal_strength(mvm, desc, rx_status); + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; rcu_read_lock(); @@ -390,6 +513,24 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (ieee80211_is_data(hdr->frame_control)) iwl_mvm_rx_csum(sta, skb, desc); + + if (iwl_mvm_is_nonagg_dup(sta, queue, rx_status, hdr, desc)) { + kfree_skb(skb); + rcu_read_unlock(); + return; + } + + /* + * Our hardware de-aggregates AMSDUs but copies the mac header + * as it to the de-aggregated MPDUs. We need to turn off the + * AMSDU bit in the QoS control ourselves. + */ + if ((desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) && + !WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + + *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } } /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index ea1e177c2..09eb72c4a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -297,6 +299,12 @@ void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, iwl_mvm_dump_channel_list(notif->results, notif->scanned_channels, buf, sizeof(buf))); + + if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_FOUND) { + IWL_DEBUG_SCAN(mvm, "Pass all scheduled scan results found\n"); + ieee80211_sched_scan_results(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_ENABLED; + } } void iwl_mvm_rx_scan_match_found(struct iwl_mvm *mvm, @@ -380,6 +388,7 @@ void iwl_mvm_rx_lmac_scan_complete_notif(struct iwl_mvm *mvm, mvm->scan_status &= ~IWL_MVM_SCAN_SCHED; ieee80211_sched_scan_stopped(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; } else if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) { IWL_DEBUG_SCAN(mvm, "Regular scan %s, EBS status %s (FW)\n", aborted ? "aborted" : "completed", @@ -533,10 +542,13 @@ static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm, IWL_DEBUG_SCAN(mvm, "Sending scheduled scan with filtering, n_match_sets %d\n", req->n_match_sets); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; return false; } IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n"); + + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_ENABLED; return true; } @@ -788,6 +800,9 @@ static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; #endif + if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED) + flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; + if (iwl_mvm_is_regular_scan(params) && vif->type != NL80211_IFTYPE_P2P_DEVICE && params->type != IWL_SCAN_TYPE_FRAGMENTED) @@ -930,8 +945,11 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) return -ENOBUFS; - if (type == mvm->scan_type) + if (type == mvm->scan_type) { + IWL_DEBUG_SCAN(mvm, + "Ignoring UMAC scan config of the same type\n"); return 0; + } cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels; @@ -1071,6 +1089,9 @@ static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; #endif + if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; + if (iwl_mvm_is_regular_scan(params) && vif->type != NL80211_IFTYPE_P2P_DEVICE && params->type != IWL_SCAN_TYPE_FRAGMENTED) @@ -1109,7 +1130,7 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->general_flags = cpu_to_le32(iwl_mvm_scan_umac_flags(mvm, params, vif)); - if (type == IWL_MVM_SCAN_SCHED) + if (type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT) cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); if (iwl_mvm_scan_use_ebs(mvm, vif)) @@ -1298,10 +1319,6 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, return -EBUSY; } - /* we don't support "match all" in the firmware */ - if (!req->n_match_sets) - return -EOPNOTSUPP; - ret = iwl_mvm_check_running_scans(mvm, type); if (ret) return ret; @@ -1355,7 +1372,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, IWL_MVM_SCAN_SCHED); + ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, type); } else { hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); @@ -1397,6 +1414,7 @@ void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { ieee80211_sched_scan_stopped(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; } mvm->scan_status &= ~mvm->scan_uid_status[uid]; @@ -1431,6 +1449,12 @@ void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, iwl_mvm_dump_channel_list(notif->results, notif->scanned_channels, buf, sizeof(buf))); + + if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_FOUND) { + IWL_DEBUG_SCAN(mvm, "Pass all scheduled scan results found\n"); + ieee80211_sched_scan_results(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_ENABLED; + } } static int iwl_mvm_umac_scan_abort(struct iwl_mvm *mvm, int type) @@ -1525,6 +1549,7 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) uid = iwl_mvm_scan_uid_by_status(mvm, IWL_MVM_SCAN_SCHED); if (uid >= 0 && !mvm->restart_fw) { ieee80211_sched_scan_stopped(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; mvm->scan_uid_status[uid] = 0; } @@ -1546,8 +1571,11 @@ void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) * restart_hw, so do not report if FW is about to be * restarted. */ - if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && !mvm->restart_fw) + if ((mvm->scan_status & IWL_MVM_SCAN_SCHED) && + !mvm->restart_fw) { ieee80211_sched_scan_stopped(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; + } } } @@ -1583,6 +1611,7 @@ out: ieee80211_scan_completed(mvm->hw, true); } else if (notify) { ieee80211_sched_scan_stopped(mvm->hw); + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; } return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index b556e3365..ef99942d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +70,18 @@ #include "sta.h" #include "rs.h" +/* + * New version of ADD_STA_sta command added new fields at the end of the + * structure, so sending the size of the relevant API's structure is enough to + * support both API versions. + */ +static inline int iwl_mvm_add_sta_cmd_size(struct iwl_mvm *mvm) +{ + return iwl_mvm_has_new_rx_api(mvm) ? + sizeof(struct iwl_mvm_add_sta_cmd) : + sizeof(struct iwl_mvm_add_sta_cmd_v7); +} + static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, enum nl80211_iftype iftype) { @@ -187,12 +201,13 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd), + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), &add_sta_cmd, &status); if (ret) return ret; - switch (status) { + switch (status & IWL_ADD_STA_STATUS_MASK) { case ADD_STA_SUCCESS: IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n"); break; @@ -265,6 +280,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_mvm_rxq_dup_data *dup_data; int i, ret, sta_id; lockdep_assert_held(&mvm->mutex); @@ -312,6 +328,16 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, } mvm_sta->agg_tids = 0; + if (iwl_mvm_has_new_rx_api(mvm) && + !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + dup_data = kcalloc(mvm->trans->num_rx_queues, + sizeof(*dup_data), + GFP_KERNEL); + if (!dup_data) + return -ENOMEM; + mvm_sta->dup_data = dup_data; + } + ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); if (ret) goto err; @@ -357,12 +383,13 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), &cmd, &status); if (ret) return ret; - switch (status) { + switch (status & IWL_ADD_STA_STATUS_MASK) { case ADD_STA_SUCCESS: IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n", mvmsta->sta_id); @@ -492,6 +519,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); + if (iwl_mvm_has_new_rx_api(mvm)) + kfree(mvm_sta->dup_data); + if (vif->type == NL80211_IFTYPE_STATION && mvmvif->ap_sta_id == mvm_sta->sta_id) { ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); @@ -623,12 +653,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, if (addr) memcpy(cmd.addr, addr, ETH_ALEN); - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), &cmd, &status); if (ret) return ret; - switch (status) { + switch (status & IWL_ADD_STA_STATUS_MASK) { case ADD_STA_SUCCESS: IWL_DEBUG_INFO(mvm, "Internal station added.\n"); return 0; @@ -819,7 +850,7 @@ int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) #define IWL_MAX_RX_BA_SESSIONS 16 int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u16 ssn, bool start) + int tid, u16 ssn, bool start, u8 buf_size) { struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = {}; @@ -839,6 +870,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (start) { cmd.add_immediate_ba_tid = (u8) tid; cmd.add_immediate_ba_ssn = cpu_to_le16(ssn); + cmd.rx_ba_window = cpu_to_le16((u16)buf_size); } else { cmd.remove_immediate_ba_tid = (u8) tid; } @@ -846,12 +878,13 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, STA_MODIFY_REMOVE_BA_TID; status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), &cmd, &status); if (ret) return ret; - switch (status) { + switch (status & IWL_ADD_STA_STATUS_MASK) { case ADD_STA_SUCCESS: IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n", start ? "start" : "stopp"); @@ -904,12 +937,13 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), &cmd, &status); if (ret) return ret; - switch (status) { + switch (status & IWL_ADD_STA_STATUS_MASK) { case ADD_STA_SUCCESS: break; default: @@ -1011,15 +1045,23 @@ release_locks: } int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size) + struct ieee80211_sta *sta, u16 tid, u8 buf_size, + bool amsdu) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; unsigned int wdg_timeout = iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); - int queue, fifo, ret; + int queue, ret; u16 ssn; + struct iwl_trans_txq_scd_cfg cfg = { + .sta_id = mvmsta->sta_id, + .tid = tid, + .frame_limit = buf_size, + .aggregate = true, + }; + BUILD_BUG_ON((sizeof(mvmsta->agg_tids) * BITS_PER_BYTE) != IWL_MAX_TID_COUNT); @@ -1031,13 +1073,13 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, tid_data->state = IWL_AGG_ON; mvmsta->agg_tids |= BIT(tid); tid_data->ssn = 0xffff; + tid_data->amsdu_in_ampdu_allowed = amsdu; spin_unlock_bh(&mvmsta->lock); - fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; + cfg.fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; - iwl_mvm_enable_agg_txq(mvm, queue, - vif->hw_queue[tid_to_mac80211_ac[tid]], fifo, - mvmsta->sta_id, tid, buf_size, ssn, wdg_timeout); + iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[tid_to_mac80211_ac[tid]], + ssn, &cfg, wdg_timeout); ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); if (ret) @@ -1640,7 +1682,8 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, }; int ret; - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, + iwl_mvm_add_sta_cmd_size(mvm), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } @@ -1731,7 +1774,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC | CMD_WANT_ASYNC_CALLBACK, - sizeof(cmd), &cmd); + iwl_mvm_add_sta_cmd_size(mvm), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } @@ -1766,7 +1809,8 @@ void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, }; int ret; - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, + iwl_mvm_add_sta_cmd_size(mvm), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 39fdf5224..1a8f69a41 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -258,8 +258,7 @@ enum iwl_mvm_agg_state { * This is basically (last acked packet++). * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). - * @reduced_tpc: Reduced tx power. Holds the data between the - * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). + * @amsdu_in_ampdu_allowed: true if A-MSDU in A-MPDU is allowed. * @state: state of the BA agreement establishment / tear down. * @txq_id: Tx queue used by the BA session * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or @@ -273,7 +272,7 @@ struct iwl_mvm_tid_data { u16 next_reclaimed; /* The rest is Tx AGG related */ u32 rate_n_flags; - u8 reduced_tpc; + bool amsdu_in_ampdu_allowed; enum iwl_mvm_agg_state state; u16 txq_id; u16 ssn; @@ -294,6 +293,16 @@ struct iwl_mvm_key_pn { }; /** + * struct iwl_mvm_rxq_dup_data - per station per rx queue data + * @last_seq: last sequence per tid for duplicate packet detection + * @last_sub_frame: last subframe packet + */ +struct iwl_mvm_rxq_dup_data { + __le16 last_seq[IWL_MAX_TID_COUNT + 1]; + u8 last_sub_frame[IWL_MAX_TID_COUNT + 1]; +} ____cacheline_aligned_in_smp; + +/** * struct iwl_mvm_sta - representation of a station in the driver * @sta_id: the index of the station in the fw (will be replaced by id_n_color) * @tfd_queue_msk: the tfd queues used by the station @@ -311,6 +320,7 @@ struct iwl_mvm_key_pn { * @tx_protection: reference counter for controlling the Tx protection. * @tt_tx_protection: is thermal throttling enable Tx protection? * @disable_tx: is tx to this STA disabled? + * @tlc_amsdu: true if A-MSDU is allowed * @agg_tids: bitmap of tids whose status is operational aggregated (IWL_AGG_ON) * @sleep_tx_count: the number of frames that we told the firmware to let out * even when that station is asleep. This is useful in case the queue @@ -318,6 +328,7 @@ struct iwl_mvm_key_pn { * we are sending frames from an AMPDU queue and there was a hole in * the BA window. To be used for UAPSD only. * @ptk_pn: per-queue PTK PN data structures + * @dup_data: per queue duplicate packet detection data * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -337,14 +348,15 @@ struct iwl_mvm_sta { struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; struct iwl_lq_sta lq_sta; struct ieee80211_vif *vif; - struct iwl_mvm_key_pn __rcu *ptk_pn[4]; + struct iwl_mvm_rxq_dup_data *dup_data; /* Temporary, until the new TLC will control the Tx protection */ s8 tx_protection; bool tt_tx_protection; bool disable_tx; + bool tlc_amsdu; u8 agg_tids; u8 sleep_tx_count; }; @@ -401,11 +413,12 @@ void iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, /* AMPDU */ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - int tid, u16 ssn, bool start); + int tid, u16 ssn, bool start, u8 buf_size); int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size); + struct ieee80211_sta *sta, u16 tid, u8 buf_size, + bool amsdu); int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 924dd6a41..2c12789e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -371,20 +371,13 @@ static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, iwl_mvm_te_check_trigger(mvm, notif, te_data); - if (!le32_to_cpu(notif->status)) { - IWL_DEBUG_TE(mvm, - "ERROR: Aux ROC Time Event %s notification failure\n", - (le32_to_cpu(notif->action) & - TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end"); - return -EINVAL; - } - IWL_DEBUG_TE(mvm, - "Aux ROC time event notification - UID = 0x%x action %d\n", + "Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n", le32_to_cpu(notif->unique_id), - le32_to_cpu(notif->action)); + le32_to_cpu(notif->action), le32_to_cpu(notif->status)); - if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { + if (!le32_to_cpu(notif->status) || + le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) { /* End TE, notify mac80211 */ ieee80211_remain_on_channel_expired(mvm->hw); iwl_mvm_roc_finished(mvm); /* flush aux queue */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h index 99d9a35ad..3d2e8b615 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.h @@ -115,7 +115,7 @@ * needed by the driver. */ -#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500 +#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 600 #define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index fb76004ee..f1f28255a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -7,6 +7,7 @@ * * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,6 +65,8 @@ * *****************************************************************************/ +#include <linux/sort.h> + #include "mvm.h" #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ @@ -79,8 +82,10 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) IWL_ERR(mvm, "Enter CT Kill\n"); iwl_mvm_set_hw_ctkill_state(mvm, true); - tt->throttle = false; - tt->dynamic_smps = false; + if (!iwl_mvm_is_tt_in_fw(mvm)) { + tt->throttle = false; + tt->dynamic_smps = false; + } /* Don't schedule an exit work if we're in test mode, since * the temperature will not change unless we manually set it @@ -116,18 +121,21 @@ void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp) static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { - struct iwl_dts_measurement_notif *notif; + struct iwl_dts_measurement_notif_v1 *notif_v1; int len = iwl_rx_packet_payload_len(pkt); int temp; - if (WARN_ON_ONCE(len < sizeof(*notif))) { + /* we can use notif_v1 only, because v2 only adds an additional + * parameter, which is not used in this function. + */ + if (WARN_ON_ONCE(len < sizeof(*notif_v1))) { IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); return -EINVAL; } - notif = (void *)pkt->data; + notif_v1 = (void *)pkt->data; - temp = le32_to_cpu(notif->temp); + temp = le32_to_cpu(notif_v1->temp); /* shouldn't be negative, but since it's s32, make sure it isn't */ if (WARN_ON_ONCE(temp < 0)) @@ -158,17 +166,78 @@ static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_dts_measurement_notif_v2 *notif_v2; + int len = iwl_rx_packet_payload_len(pkt); int temp; + u32 ths_crossed; /* the notification is handled synchronously in ctkill, so skip here */ if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) return; temp = iwl_mvm_temp_notif_parse(mvm, pkt); - if (temp < 0) + + if (!iwl_mvm_is_tt_in_fw(mvm)) { + if (temp >= 0) + iwl_mvm_tt_temp_changed(mvm, temp); return; + } + + if (WARN_ON_ONCE(len < sizeof(*notif_v2))) { + IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); + return; + } + + notif_v2 = (void *)pkt->data; + ths_crossed = le32_to_cpu(notif_v2->threshold_idx); - iwl_mvm_tt_temp_changed(mvm, temp); + /* 0xFF in ths_crossed means the notification is not related + * to a trip, so we can ignore it here. + */ + if (ths_crossed == 0xFF) + return; + + IWL_DEBUG_TEMP(mvm, "Temp = %d Threshold crossed = %d\n", + temp, ths_crossed); + +#ifdef CONFIG_THERMAL + if (WARN_ON(ths_crossed >= IWL_MAX_DTS_TRIPS)) + return; + + /* + * We are now handling a temperature notification from the firmware + * in ASYNC and hold the mutex. thermal_notify_framework will call + * us back through get_temp() which ought to send a SYNC command to + * the firmware and hence to take the mutex. + * Avoid the deadlock by unlocking the mutex here. + */ + if (mvm->tz_device.tzone) { + struct iwl_mvm_thermal_device *tz_dev = &mvm->tz_device; + + mutex_unlock(&mvm->mutex); + thermal_notify_framework(tz_dev->tzone, + tz_dev->fw_trips_index[ths_crossed]); + mutex_lock(&mvm->mutex); + } +#endif /* CONFIG_THERMAL */ +} + +void iwl_mvm_ct_kill_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct ct_kill_notif *notif; + int len = iwl_rx_packet_payload_len(pkt); + + if (WARN_ON_ONCE(len != sizeof(*notif))) { + IWL_ERR(mvm, "Invalid CT_KILL_NOTIFICATION\n"); + return; + } + + notif = (struct ct_kill_notif *)pkt->data; + IWL_DEBUG_TEMP(mvm, "CT Kill notification temperature = %d\n", + notif->temperature); + + iwl_mvm_enter_ctkill(mvm); } static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) @@ -194,12 +263,12 @@ static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm) return iwl_mvm_send_cmd_pdu(mvm, cmdid, 0, sizeof(extcmd), &extcmd); } -int iwl_mvm_get_temp(struct iwl_mvm *mvm) +int iwl_mvm_get_temp(struct iwl_mvm *mvm, s32 *temp) { struct iwl_notification_wait wait_temp_notif; static u16 temp_notif[] = { WIDE_ID(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE) }; - int ret, temp; + int ret; if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR)) temp_notif[0] = DTS_MEASUREMENT_NOTIFICATION; @@ -208,7 +277,7 @@ int iwl_mvm_get_temp(struct iwl_mvm *mvm) iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif, temp_notif, ARRAY_SIZE(temp_notif), - iwl_mvm_temp_notif_wait, &temp); + iwl_mvm_temp_notif_wait, temp); ret = iwl_mvm_get_temp_cmd(mvm); if (ret) { @@ -219,12 +288,10 @@ int iwl_mvm_get_temp(struct iwl_mvm *mvm) ret = iwl_wait_notification(&mvm->notif_wait, &wait_temp_notif, IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT); - if (ret) { + if (ret) IWL_ERR(mvm, "Getting the temperature timed out\n"); - return ret; - } - return temp; + return ret; } static void check_exit_ctkill(struct work_struct *work) @@ -233,10 +300,17 @@ static void check_exit_ctkill(struct work_struct *work) struct iwl_mvm *mvm; u32 duration; s32 temp; + int ret; tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); mvm = container_of(tt, struct iwl_mvm, thermal_throttle); + if (iwl_mvm_is_tt_in_fw(mvm)) { + iwl_mvm_exit_ctkill(mvm); + + return; + } + duration = tt->params.ct_kill_duration; mutex_lock(&mvm->mutex); @@ -250,13 +324,13 @@ static void check_exit_ctkill(struct work_struct *work) goto reschedule; } - temp = iwl_mvm_get_temp(mvm); + ret = iwl_mvm_get_temp(mvm, &temp); iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL); __iwl_mvm_mac_stop(mvm); - if (temp < 0) + if (ret) goto reschedule; IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); @@ -436,7 +510,378 @@ static const struct iwl_tt_params iwl_mvm_default_tt_params = { .support_tx_backoff = true, }; -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) +/* budget in mWatt */ +static const u32 iwl_mvm_cdev_budgets[] = { + 2000, /* cooling state 0 */ + 1800, /* cooling state 1 */ + 1600, /* cooling state 2 */ + 1400, /* cooling state 3 */ + 1200, /* cooling state 4 */ + 1000, /* cooling state 5 */ + 900, /* cooling state 6 */ + 800, /* cooling state 7 */ + 700, /* cooling state 8 */ + 650, /* cooling state 9 */ + 600, /* cooling state 10 */ + 550, /* cooling state 11 */ + 500, /* cooling state 12 */ + 450, /* cooling state 13 */ + 400, /* cooling state 14 */ + 350, /* cooling state 15 */ + 300, /* cooling state 16 */ + 250, /* cooling state 17 */ + 200, /* cooling state 18 */ + 150, /* cooling state 19 */ +}; + +int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state) +{ + struct iwl_mvm_ctdp_cmd cmd = { + .operation = cpu_to_le32(op), + .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]), + .window_size = 0, + }; + int ret; + u32 status; + + lockdep_assert_held(&mvm->mutex); + + ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP, + CTDP_CONFIG_CMD), + sizeof(cmd), &cmd, &status); + + if (ret) { + IWL_ERR(mvm, "cTDP command failed (err=%d)\n", ret); + return ret; + } + + switch (op) { + case CTDP_CMD_OPERATION_START: +#ifdef CONFIG_THERMAL + mvm->cooling_dev.cur_state = state; +#endif /* CONFIG_THERMAL */ + break; + case CTDP_CMD_OPERATION_REPORT: + IWL_DEBUG_TEMP(mvm, "cTDP avg energy in mWatt = %d\n", status); + /* when the function is called with CTDP_CMD_OPERATION_REPORT + * option the function should return the average budget value + * that is received from the FW. + * The budget can't be less or equal to 0, so it's possible + * to distinguish between error values and budgets. + */ + return status; + case CTDP_CMD_OPERATION_STOP: + IWL_DEBUG_TEMP(mvm, "cTDP stopped successfully\n"); + break; + } + + return 0; +} + +#ifdef CONFIG_THERMAL +static int compare_temps(const void *a, const void *b) +{ + return ((s16)le16_to_cpu(*(__le16 *)a) - + (s16)le16_to_cpu(*(__le16 *)b)); +} + +int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm) +{ + struct temp_report_ths_cmd cmd = {0}; + int ret, i, j, idx = 0; + + lockdep_assert_held(&mvm->mutex); + + if (!mvm->tz_device.tzone) + return -EINVAL; + + /* The driver holds array of temperature trips that are unsorted + * and uncompressed, the FW should get it compressed and sorted + */ + + /* compress temp_trips to cmd array, remove uninitialized values*/ + for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) { + if (mvm->tz_device.temp_trips[i] != S16_MIN) { + cmd.thresholds[idx++] = + cpu_to_le16(mvm->tz_device.temp_trips[i]); + } + } + cmd.num_temps = cpu_to_le32(idx); + + if (!idx) + goto send; + + /*sort cmd array*/ + sort(cmd.thresholds, idx, sizeof(s16), compare_temps, NULL); + + /* we should save the indexes of trips because we sort + * and compress the orginal array + */ + for (i = 0; i < idx; i++) { + for (j = 0; j < IWL_MAX_DTS_TRIPS; j++) { + if (le16_to_cpu(cmd.thresholds[i]) == + mvm->tz_device.temp_trips[j]) + mvm->tz_device.fw_trips_index[i] = j; + } + } + +send: + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP, + TEMP_REPORTING_THRESHOLDS_CMD), + 0, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "TEMP_REPORT_THS_CMD command failed (err=%d)\n", + ret); + + return ret; +} + +static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device, + int *temperature) +{ + struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata; + int ret; + int temp; + + mutex_lock(&mvm->mutex); + + if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) { + ret = -EIO; + goto out; + } + + ret = iwl_mvm_get_temp(mvm, &temp); + if (ret) + goto out; + + *temperature = temp * 1000; + +out: + mutex_unlock(&mvm->mutex); + return ret; +} + +static int iwl_mvm_tzone_get_trip_temp(struct thermal_zone_device *device, + int trip, int *temp) +{ + struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata; + + if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) + return -EINVAL; + + *temp = mvm->tz_device.temp_trips[trip] * 1000; + + return 0; +} + +static int iwl_mvm_tzone_get_trip_type(struct thermal_zone_device *device, + int trip, enum thermal_trip_type *type) +{ + if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) + return -EINVAL; + + *type = THERMAL_TRIP_PASSIVE; + + return 0; +} + +static int iwl_mvm_tzone_set_trip_temp(struct thermal_zone_device *device, + int trip, int temp) +{ + struct iwl_mvm *mvm = (struct iwl_mvm *)device->devdata; + struct iwl_mvm_thermal_device *tzone; + int i, ret; + s16 temperature; + + mutex_lock(&mvm->mutex); + + if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) { + ret = -EIO; + goto out; + } + + if (trip < 0 || trip >= IWL_MAX_DTS_TRIPS) { + ret = -EINVAL; + goto out; + } + + if ((temp / 1000) > S16_MAX) { + ret = -EINVAL; + goto out; + } + + temperature = (s16)(temp / 1000); + tzone = &mvm->tz_device; + + if (!tzone) { + ret = -EIO; + goto out; + } + + /* no updates*/ + if (tzone->temp_trips[trip] == temperature) { + ret = 0; + goto out; + } + + /* already existing temperature */ + for (i = 0; i < IWL_MAX_DTS_TRIPS; i++) { + if (tzone->temp_trips[i] == temperature) { + ret = -EINVAL; + goto out; + } + } + + tzone->temp_trips[trip] = temperature; + + ret = iwl_mvm_send_temp_report_ths_cmd(mvm); +out: + mutex_unlock(&mvm->mutex); + return ret; +} + +static struct thermal_zone_device_ops tzone_ops = { + .get_temp = iwl_mvm_tzone_get_temp, + .get_trip_temp = iwl_mvm_tzone_get_trip_temp, + .get_trip_type = iwl_mvm_tzone_get_trip_type, + .set_trip_temp = iwl_mvm_tzone_set_trip_temp, +}; + +/* make all trips writable */ +#define IWL_WRITABLE_TRIPS_MSK (BIT(IWL_MAX_DTS_TRIPS) - 1) + +static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm) +{ + int i; + char name[] = "iwlwifi"; + + if (!iwl_mvm_is_tt_in_fw(mvm)) { + mvm->tz_device.tzone = NULL; + + return; + } + + BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + + mvm->tz_device.tzone = thermal_zone_device_register(name, + IWL_MAX_DTS_TRIPS, + IWL_WRITABLE_TRIPS_MSK, + mvm, &tzone_ops, + NULL, 0, 0); + if (IS_ERR(mvm->tz_device.tzone)) { + IWL_DEBUG_TEMP(mvm, + "Failed to register to thermal zone (err = %ld)\n", + PTR_ERR(mvm->tz_device.tzone)); + mvm->tz_device.tzone = NULL; + return; + } + + /* 0 is a valid temperature, + * so initialize the array with S16_MIN which invalid temperature + */ + for (i = 0 ; i < IWL_MAX_DTS_TRIPS; i++) + mvm->tz_device.temp_trips[i] = S16_MIN; +} + +static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1; + + return 0; +} + +static int iwl_mvm_tcool_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata); + + if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) + return -EBUSY; + + *state = mvm->cooling_dev.cur_state; + + return 0; +} + +static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long new_state) +{ + struct iwl_mvm *mvm = (struct iwl_mvm *)(cdev->devdata); + int ret; + + if (!mvm->ucode_loaded || !(mvm->cur_ucode == IWL_UCODE_REGULAR)) + return -EIO; + + if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) + return -EBUSY; + + mutex_lock(&mvm->mutex); + + if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) { + ret = -EINVAL; + goto unlock; + } + + ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, + new_state); + +unlock: + mutex_unlock(&mvm->mutex); + return ret; +} + +static struct thermal_cooling_device_ops tcooling_ops = { + .get_max_state = iwl_mvm_tcool_get_max_state, + .get_cur_state = iwl_mvm_tcool_get_cur_state, + .set_cur_state = iwl_mvm_tcool_set_cur_state, +}; + +static void iwl_mvm_cooling_device_register(struct iwl_mvm *mvm) +{ + char name[] = "iwlwifi"; + + if (!iwl_mvm_is_ctdp_supported(mvm)) + return; + + BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + + mvm->cooling_dev.cdev = + thermal_cooling_device_register(name, + mvm, + &tcooling_ops); + + if (IS_ERR(mvm->cooling_dev.cdev)) { + IWL_DEBUG_TEMP(mvm, + "Failed to register to cooling device (err = %ld)\n", + PTR_ERR(mvm->cooling_dev.cdev)); + mvm->cooling_dev.cdev = NULL; + return; + } +} + +static void iwl_mvm_thermal_zone_unregister(struct iwl_mvm *mvm) +{ + if (!iwl_mvm_is_tt_in_fw(mvm) || !mvm->tz_device.tzone) + return; + + IWL_DEBUG_TEMP(mvm, "Thermal zone device unregister\n"); + thermal_zone_device_unregister(mvm->tz_device.tzone); + mvm->tz_device.tzone = NULL; +} + +static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm) +{ + if (!iwl_mvm_is_ctdp_supported(mvm) || !mvm->cooling_dev.cdev) + return; + + IWL_DEBUG_TEMP(mvm, "Cooling device unregister\n"); + thermal_cooling_device_unregister(mvm->cooling_dev.cdev); + mvm->cooling_dev.cdev = NULL; +} +#endif /* CONFIG_THERMAL */ + +void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff) { struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; @@ -451,10 +896,20 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) tt->dynamic_smps = false; tt->min_backoff = min_backoff; INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); + +#ifdef CONFIG_THERMAL + iwl_mvm_cooling_device_register(mvm); + iwl_mvm_thermal_zone_register(mvm); +#endif } -void iwl_mvm_tt_exit(struct iwl_mvm *mvm) +void iwl_mvm_thermal_exit(struct iwl_mvm *mvm) { cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); + +#ifdef CONFIG_THERMAL + iwl_mvm_cooling_device_unregister(mvm); + iwl_mvm_thermal_zone_unregister(mvm); +#endif } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index a040edc55..34731e29c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -65,6 +66,7 @@ #include <linux/ieee80211.h> #include <linux/etherdevice.h> #include <linux/tcp.h> +#include <net/ip.h> #include "iwl-trans.h" #include "iwl-eeprom-parse.h" @@ -103,6 +105,7 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, u8 sta_id) { + struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (void *)skb->data; __le16 fc = hdr->frame_control; u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags); @@ -182,7 +185,8 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_cmd->tx_flags = cpu_to_le32(tx_flags); /* Total # bytes to be transmitted */ - tx_cmd->len = cpu_to_le16((u16)skb->len); + tx_cmd->len = cpu_to_le16((u16)skb->len + + (uintptr_t)skb_info->driver_data[0]); tx_cmd->next_frame_len = 0; tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); tx_cmd->sta_id = sta_id; @@ -299,6 +303,8 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, case WLAN_CIPHER_SUITE_TKIP: tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + pn = atomic64_inc_return(&keyconf->tx_pn); + ieee80211_tkip_add_iv(crypto_hdr, keyconf, pn); ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); break; @@ -322,10 +328,11 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, */ static struct iwl_device_cmd * iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, - int hdrlen, struct ieee80211_sta *sta, u8 sta_id) + struct ieee80211_tx_info *info, int hdrlen, + struct ieee80211_sta *sta, u8 sta_id) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; @@ -345,10 +352,10 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); - memset(&info->status, 0, sizeof(info->status)); - memset(info->driver_data, 0, sizeof(info->driver_data)); + memset(&skb_info->status, 0, sizeof(skb_info->status)); + memset(skb_info->driver_data, 0, sizeof(skb_info->driver_data)); - info->driver_data[1] = dev_cmd; + skb_info->driver_data[1] = dev_cmd; return dev_cmd; } @@ -356,20 +363,26 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info info; struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; u8 sta_id; int hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) + memcpy(&info, skb->cb, sizeof(info)); + + if (WARN_ON_ONCE(info.flags & IEEE80211_TX_CTL_AMPDU)) return -1; - if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && - (!info->control.vif || - info->hw_queue != info->control.vif->cab_queue))) + if (WARN_ON_ONCE(info.flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && + (!info.control.vif || + info.hw_queue != info.control.vif->cab_queue))) return -1; + /* This holds the amsdu headers length */ + skb_info->driver_data[0] = (void *)(uintptr_t)0; + /* * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel @@ -377,7 +390,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) * and hence needs to be sent on the aux queue */ if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && - info->control.vif->type == NL80211_IFTYPE_STATION) + info.control.vif->type == NL80211_IFTYPE_STATION) IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue; /* @@ -390,14 +403,14 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) * AUX station. */ sta_id = mvm->aux_sta.sta_id; - if (info->control.vif) { + if (info.control.vif) { struct iwl_mvm_vif *mvmvif = - iwl_mvm_vif_from_mac80211(info->control.vif); + iwl_mvm_vif_from_mac80211(info.control.vif); - if (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || - info->control.vif->type == NL80211_IFTYPE_AP) + if (info.control.vif->type == NL80211_IFTYPE_P2P_DEVICE || + info.control.vif->type == NL80211_IFTYPE_AP) sta_id = mvmvif->bcast_sta.sta_id; - else if (info->control.vif->type == NL80211_IFTYPE_STATION && + else if (info.control.vif->type == NL80211_IFTYPE_STATION && is_multicast_ether_addr(hdr->addr1)) { u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id); @@ -406,19 +419,18 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) } } - IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); + IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info.hw_queue); - dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, NULL, sta_id); + dev_cmd = iwl_mvm_set_tx_params(mvm, skb, &info, hdrlen, NULL, sta_id); if (!dev_cmd) return -1; - /* From now on, we cannot access info->control */ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdrlen); - if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { + if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info.hw_queue)) { iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); return -1; } @@ -435,42 +447,208 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) return 0; } -static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb_gso, +#ifdef CONFIG_INET +static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff_head *mpdus_skb) { + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int mss = skb_shinfo(skb)->gso_size; struct sk_buff *tmp, *next; - char cb[sizeof(skb_gso->cb)]; + char cb[sizeof(skb->cb)]; + unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len; + bool ipv4 = (skb->protocol == htons(ETH_P_IP)); + u16 ip_base_id = ipv4 ? ntohs(ip_hdr(skb)->id) : 0; + u16 amsdu_add, snap_ip_tcp, pad, i = 0; + unsigned int dbg_max_amsdu_len; + u8 *qc, tid, txf; + + snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) + + tcp_hdrlen(skb); + + qc = ieee80211_get_qos_ctl(hdr); + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + if (!sta->max_amsdu_len || + !ieee80211_is_data_qos(hdr->frame_control) || + !mvmsta->tlc_amsdu) { + num_subframes = 1; + pad = 0; + goto segment; + } + + /* + * No need to lock amsdu_in_ampdu_allowed since it can't be modified + * during an BA session. + */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed) { + num_subframes = 1; + pad = 0; + goto segment; + } + + max_amsdu_len = sta->max_amsdu_len; + dbg_max_amsdu_len = ACCESS_ONCE(mvm->max_amsdu_len); + + /* the Tx FIFO to which this A-MSDU will be routed */ + txf = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; + + /* + * Don't send an AMSDU that will be longer than the TXF. + * Add a security margin of 256 for the TX command + headers. + * We also want to have the start of the next packet inside the + * fifo to be able to send bursts. + */ + max_amsdu_len = min_t(unsigned int, max_amsdu_len, + mvm->shared_mem_cfg.txfifo_size[txf] - 256); + + if (dbg_max_amsdu_len) + max_amsdu_len = min_t(unsigned int, max_amsdu_len, + dbg_max_amsdu_len); - memcpy(cb, skb_gso->cb, sizeof(cb)); - next = skb_gso_segment(skb_gso, 0); - if (IS_ERR(next)) + /* + * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not + * supported. This is a spec requirement (IEEE 802.11-2015 + * section 8.7.3 NOTE 3). + */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + !sta->vht_cap.vht_supported) + max_amsdu_len = min_t(unsigned int, max_amsdu_len, 4095); + + /* Sub frame header + SNAP + IP header + TCP header + MSS */ + subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; + pad = (4 - subf_len) & 0x3; + + /* + * If we have N subframes in the A-MSDU, then the A-MSDU's size is + * N * subf_len + (N - 1) * pad. + */ + num_subframes = (max_amsdu_len + pad) / (subf_len + pad); + if (num_subframes > 1) + *qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; + + tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + /* + * Make sure we have enough TBs for the A-MSDU: + * 2 for each subframe + * 1 more for each fragment + * 1 more for the potential data in the header + */ + num_subframes = + min_t(unsigned int, num_subframes, + (mvm->trans->max_skb_frags - 1 - + skb_shinfo(skb)->nr_frags) / 2); + + /* This skb fits in one single A-MSDU */ + if (num_subframes * mss >= tcp_payload_len) { + struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); + + /* + * Compute the length of all the data added for the A-MSDU. + * This will be used to compute the length to write in the TX + * command. We have: SNAP + IP + TCP for n -1 subframes and + * ETH header for n subframes. Note that the original skb + * already had one set of SNAP / IP / TCP headers. + */ + num_subframes = DIV_ROUND_UP(tcp_payload_len, mss); + amsdu_add = num_subframes * sizeof(struct ethhdr) + + (num_subframes - 1) * (snap_ip_tcp + pad); + /* This holds the amsdu headers length */ + skb_info->driver_data[0] = (void *)(uintptr_t)amsdu_add; + + __skb_queue_tail(mpdus_skb, skb); + return 0; + } + + /* + * Trick the segmentation function to make it + * create SKBs that can fit into one A-MSDU. + */ +segment: + skb_shinfo(skb)->gso_size = num_subframes * mss; + memcpy(cb, skb->cb, sizeof(cb)); + + next = skb_gso_segment(skb, NETIF_F_CSUM_MASK | NETIF_F_SG); + skb_shinfo(skb)->gso_size = mss; + if (WARN_ON_ONCE(IS_ERR(next))) return -EINVAL; else if (next) - consume_skb(skb_gso); + consume_skb(skb); while (next) { tmp = next; next = tmp->next; + memcpy(tmp->cb, cb, sizeof(tmp->cb)); + /* + * Compute the length of all the data added for the A-MSDU. + * This will be used to compute the length to write in the TX + * command. We have: SNAP + IP + TCP for n -1 subframes and + * ETH header for n subframes. + */ + tcp_payload_len = skb_tail_pointer(tmp) - + skb_transport_header(tmp) - + tcp_hdrlen(tmp) + tmp->data_len; + + if (ipv4) + ip_hdr(tmp)->id = htons(ip_base_id + i * num_subframes); + + if (tcp_payload_len > mss) { + struct ieee80211_tx_info *skb_info = + IEEE80211_SKB_CB(tmp); + + num_subframes = DIV_ROUND_UP(tcp_payload_len, mss); + amsdu_add = num_subframes * sizeof(struct ethhdr) + + (num_subframes - 1) * (snap_ip_tcp + pad); + skb_info->driver_data[0] = + (void *)(uintptr_t)amsdu_add; + skb_shinfo(tmp)->gso_size = mss; + } else { + qc = ieee80211_get_qos_ctl((void *)tmp->data); + + if (ipv4) + ip_send_check(ip_hdr(tmp)); + *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + skb_shinfo(tmp)->gso_size = 0; + } tmp->prev = NULL; tmp->next = NULL; __skb_queue_tail(mpdus_skb, tmp); + i++; } return 0; } +#else /* CONFIG_INET */ +static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff_head *mpdus_skb) +{ + /* Impossible to get TSO with CONFIG_INET */ + WARN_ON(1); + + return -1; +} +#endif /* * Sets the fields in the Tx cmd that are crypto related */ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, + struct ieee80211_tx_info *info, struct ieee80211_sta *sta) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_mvm_sta *mvmsta; struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; @@ -491,7 +669,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) return -1; - dev_cmd = iwl_mvm_set_tx_params(mvm, skb, hdrlen, sta, mvmsta->sta_id); + dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen, + sta, mvmsta->sta_id); if (!dev_cmd) goto drop; @@ -567,6 +746,8 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info info; struct sk_buff_head mpdus_skbs; unsigned int payload_len; int ret; @@ -577,18 +758,23 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) return -1; + memcpy(&info, skb->cb, sizeof(info)); + + /* This holds the amsdu headers length */ + skb_info->driver_data[0] = (void *)(uintptr_t)0; + if (!skb_is_gso(skb)) - return iwl_mvm_tx_mpdu(mvm, skb, sta); + return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - tcp_hdrlen(skb) + skb->data_len; if (payload_len <= skb_shinfo(skb)->gso_size) - return iwl_mvm_tx_mpdu(mvm, skb, sta); + return iwl_mvm_tx_mpdu(mvm, skb, &info, sta); __skb_queue_head_init(&mpdus_skbs); - ret = iwl_mvm_tx_tso(mvm, skb, sta, &mpdus_skbs); + ret = iwl_mvm_tx_tso(mvm, skb, &info, sta, &mpdus_skbs); if (ret) return ret; @@ -596,9 +782,9 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, return ret; while (!skb_queue_empty(&mpdus_skbs)) { - struct sk_buff *skb = __skb_dequeue(&mpdus_skbs); + skb = __skb_dequeue(&mpdus_skbs); - ret = iwl_mvm_tx_mpdu(mvm, skb, sta); + ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta); if (ret) { __skb_queue_purge(&mpdus_skbs); return ret; @@ -745,6 +931,37 @@ static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); } +static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, + u32 status) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_tx_status *status_trig; + int i; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TX_STATUS)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TX_STATUS); + status_trig = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig)) + return; + + for (i = 0; i < ARRAY_SIZE(status_trig->statuses); i++) { + /* don't collect on status 0 */ + if (!status_trig->statuses[i].status) + break; + + if (status_trig->statuses[i].status != (status & TX_STATUS_MSK)) + continue; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "Tx status %d was received", + status & TX_STATUS_MSK); + break; + } +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -760,6 +977,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct sk_buff_head skbs; u8 skb_freed = 0; u16 next_reclaimed, seq_ctl; + bool is_ndp = false; __skb_queue_head_init(&skbs); @@ -793,6 +1011,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, break; } + iwl_mvm_tx_status_check_trigger(mvm, status); + info->status.rates[0].count = tx_resp->failure_frame + 1; iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), info); @@ -811,6 +1031,20 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, seq_ctl = le16_to_cpu(hdr->seq_ctrl); } + if (unlikely(!seq_ctl)) { + struct ieee80211_hdr *hdr = (void *)skb->data; + + /* + * If it is an NDP, we can't update next_reclaim since + * its sequence control is 0. Note that for that same + * reason, NDPs are never sent to A-MPDU'able queues + * so that we can never have more than one freed frame + * for a single Tx resonse (see WARN_ON below). + */ + if (ieee80211_is_qos_nullfunc(hdr->frame_control)) + is_ndp = true; + } + /* * TODO: this is not accurate if we are freeing more than one * packet. @@ -874,9 +1108,16 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, bool send_eosp_ndp = false; spin_lock_bh(&mvmsta->lock); - tid_data->next_reclaimed = next_reclaimed; - IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n", - next_reclaimed); + if (!is_ndp) { + tid_data->next_reclaimed = next_reclaimed; + IWL_DEBUG_TX_REPLY(mvm, + "Next reclaimed packet:%d\n", + next_reclaimed); + } else { + IWL_DEBUG_TX_REPLY(mvm, + "NDP - don't update next_reclaimed\n"); + } + iwl_mvm_check_ratid_empty(mvm, sta, tid); if (mvmsta->sleep_tx_count) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 3a989f5c2..53cdc5760 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -376,8 +376,8 @@ struct iwl_error_event_table_v1 { struct iwl_error_event_table { u32 valid; /* (nonzero) valid, (0) log is empty */ u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ + u32 trm_hw_status0; /* TRM HW status */ + u32 trm_hw_status1; /* TRM HW status */ u32 blink2; /* branch link */ u32 ilink1; /* interrupt link */ u32 ilink2; /* interrupt link */ @@ -389,7 +389,7 @@ struct iwl_error_event_table { u32 tsf_hi; /* network timestamp function timer */ u32 gp1; /* GP1 timer register */ u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ + u32 fw_rev_type; /* firmware revision type */ u32 major; /* uCode version major */ u32 minor; /* uCode version minor */ u32 hw_ver; /* HW Silicon version */ @@ -408,7 +408,7 @@ struct iwl_error_event_table { * time_flag */ u32 isr4; /* isr status register LMPM_NIC_ISR4: * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + u32 last_cmd_id; /* last HCMD id handled by the firmware */ u32 wait_event; /* wait event() caller address */ u32 l2p_control; /* L2pControlField */ u32 l2p_duration; /* L2pDurationField */ @@ -419,7 +419,7 @@ struct iwl_error_event_table { u32 u_timestamp; /* indicate when the date and time of the * compilation */ u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */; +} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; /* * UMAC error struct - relevant starting from family 8000 chip. @@ -529,9 +529,9 @@ static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm) trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, table.data1, table.data2, table.data3, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.ucode_ver, 0, + table.blink2, table.ilink1, table.ilink2, + table.bcon_time, table.gp1, table.gp2, + table.gp3, table.ucode_ver, 0, table.hw_ver, table.brd_ver); IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); @@ -615,14 +615,14 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, table.data1, table.data2, table.data3, - table.blink1, table.blink2, table.ilink1, + table.blink2, table.ilink1, table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.major, + table.gp2, table.fw_rev_type, table.major, table.minor, table.hw_ver, table.brd_ver); IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, desc_lookup(table.error_id)); - IWL_ERR(mvm, "0x%08X | uPc\n", table.pc); - IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1); + IWL_ERR(mvm, "0x%08X | trm_hw_status0\n", table.trm_hw_status0); + IWL_ERR(mvm, "0x%08X | trm_hw_status1\n", table.trm_hw_status1); IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2); IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1); IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2); @@ -634,7 +634,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi); IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1); IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3); + IWL_ERR(mvm, "0x%08X | uCode revision type\n", table.fw_rev_type); IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major); IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor); IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver); @@ -645,7 +645,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2); IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3); IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4); - IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref); + IWL_ERR(mvm, "0x%08X | last cmd Id\n", table.last_cmd_id); IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event); IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control); IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration); @@ -937,18 +937,16 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) } int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - bool value) + bool prev) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int res; lockdep_assert_held(&mvm->mutex); - if (mvmvif->low_latency == value) + if (iwl_mvm_vif_low_latency(mvmvif) == prev) return 0; - mvmvif->low_latency = value; - res = iwl_mvm_update_quotas(mvm, false, NULL); if (res) return res; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 00335ea6b..79d7cd7d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -7,6 +7,7 @@ * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -66,6 +67,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/pci.h> #include <linux/pci-aspm.h> #include <linux/acpi.h> @@ -477,8 +479,18 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9010, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8050, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8265_2ac_cfg)}, /* 9000 Series */ {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl5165_2ac_cfg)}, @@ -627,6 +639,33 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto out_free_drv; + /* if RTPM is in use, enable it in our device */ + if (iwl_trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) { + /* We explicitly set the device to active here to + * clear contingent errors. + */ + pm_runtime_set_active(&pdev->dev); + + pm_runtime_set_autosuspend_delay(&pdev->dev, + iwlwifi_mod_params.d0i3_entry_delay); + pm_runtime_use_autosuspend(&pdev->dev); + + /* We are not supposed to call pm_runtime_allow() by + * ourselves, but let userspace enable runtime PM via + * sysfs. However, since we don't enable this from + * userspace yet, we need to allow/forbid() ourselves. + */ + pm_runtime_allow(&pdev->dev); + } + + /* The PCI device starts with a reference taken and we are + * supposed to release it here. But to simplify the + * interaction with the opmode, we don't do it now, but let + * the opmode release it when it's ready. To account for this + * reference, we start with ref_count set to 1. + */ + trans_pcie->ref_count = 1; + return 0; out_free_drv: @@ -641,7 +680,17 @@ static void iwl_pci_remove(struct pci_dev *pdev) struct iwl_trans *trans = pci_get_drvdata(pdev); struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + /* if RTPM was in use, restore it to the state before probe */ + if (trans->runtime_pm_mode != IWL_PLAT_PM_MODE_DISABLED) { + /* We should not call forbid here, but we do for now. + * Check the comment to pm_runtime_allow() in + * iwl_pci_probe(). + */ + pm_runtime_forbid(trans->dev); + } + iwl_drv_stop(trans_pcie->drv); + iwl_trans_pcie_free(trans); } @@ -693,15 +742,173 @@ static int iwl_pci_resume(struct device *device) return 0; } -static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); +int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + if (test_bit(STATUS_FW_ERROR, &trans->status)) + return 0; + + set_bit(STATUS_TRANS_GOING_IDLE, &trans->status); + + /* config the fw */ + ret = iwl_op_mode_enter_d0i3(trans->op_mode); + if (ret == 1) { + IWL_DEBUG_RPM(trans, "aborting d0i3 entrance\n"); + clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status); + return -EBUSY; + } + if (ret) + goto err; + + ret = wait_event_timeout(trans_pcie->d0i3_waitq, + test_bit(STATUS_TRANS_IDLE, &trans->status), + msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT)); + if (!ret) { + IWL_ERR(trans, "Timeout entering D0i3\n"); + ret = -ETIMEDOUT; + goto err; + } + + clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status); + + return 0; +err: + clear_bit(STATUS_TRANS_GOING_IDLE, &trans->status); + iwl_trans_fw_error(trans); + return ret; +} + +int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + /* sometimes a D0i3 entry is not followed through */ + if (!test_bit(STATUS_TRANS_IDLE, &trans->status)) + return 0; + + /* config the fw */ + ret = iwl_op_mode_exit_d0i3(trans->op_mode); + if (ret) + goto err; + + /* we clear STATUS_TRANS_IDLE only when D0I3_END command is completed */ + + ret = wait_event_timeout(trans_pcie->d0i3_waitq, + !test_bit(STATUS_TRANS_IDLE, &trans->status), + msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT)); + if (!ret) { + IWL_ERR(trans, "Timeout exiting D0i3\n"); + ret = -ETIMEDOUT; + goto err; + } + + return 0; +err: + clear_bit(STATUS_TRANS_IDLE, &trans->status); + iwl_trans_fw_error(trans); + return ret; +} + +#ifdef CONFIG_IWLWIFI_PCIE_RTPM +static int iwl_pci_runtime_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *trans = pci_get_drvdata(pdev); + int ret; + + IWL_DEBUG_RPM(trans, "entering runtime suspend\n"); + + if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) { + ret = iwl_pci_fw_enter_d0i3(trans); + if (ret < 0) + return ret; + } + + trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3; + + iwl_trans_d3_suspend(trans, false, false); + + return 0; +} + +static int iwl_pci_runtime_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *trans = pci_get_drvdata(pdev); + enum iwl_d3_status d3_status; + + IWL_DEBUG_RPM(trans, "exiting runtime suspend (resume)\n"); + + iwl_trans_d3_resume(trans, &d3_status, false, false); + + if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + return iwl_pci_fw_exit_d0i3(trans); + + return 0; +} + +static int iwl_pci_system_prepare(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *trans = pci_get_drvdata(pdev); + + IWL_DEBUG_RPM(trans, "preparing for system suspend\n"); + + /* This is called before entering system suspend and before + * the runtime resume is called. Set the suspending flag to + * prevent the wakelock from being taken. + */ + trans->suspending = true; + + /* Wake the device up from runtime suspend before going to + * platform suspend. This is needed because we don't know + * whether wowlan any is set and, if it's not, mac80211 will + * disconnect (in which case, we can't be in D0i3). + */ + pm_runtime_resume(device); + + return 0; +} + +static void iwl_pci_system_complete(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *trans = pci_get_drvdata(pdev); + + IWL_DEBUG_RPM(trans, "completing system suspend\n"); + + /* This is called as a counterpart to the prepare op. It is + * called either when suspending fails or when suspend + * completed successfully. Now there's no risk of grabbing + * the wakelock anymore, so we can release the suspending + * flag. + */ + trans->suspending = false; +} +#endif /* CONFIG_IWLWIFI_PCIE_RTPM */ + +static const struct dev_pm_ops iwl_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(iwl_pci_suspend, + iwl_pci_resume) +#ifdef CONFIG_IWLWIFI_PCIE_RTPM + SET_RUNTIME_PM_OPS(iwl_pci_runtime_suspend, + iwl_pci_runtime_resume, + NULL) + .prepare = iwl_pci_system_prepare, + .complete = iwl_pci_system_complete, +#endif /* CONFIG_IWLWIFI_PCIE_RTPM */ +}; #define IWL_PM_OPS (&iwl_dev_pm_ops) -#else +#else /* CONFIG_PM_SLEEP */ #define IWL_PM_OPS NULL -#endif +#endif /* CONFIG_PM_SLEEP */ static struct pci_driver iwl_pci_driver = { .name = DRV_NAME, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 73c95594e..dadafbdef 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -2,6 +2,7 @@ * * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -56,17 +57,23 @@ #define RX_NUM_QUEUES 1 #define RX_POST_REQ_ALLOC 2 #define RX_CLAIM_REQ_ALLOC 8 -#define RX_POOL_SIZE ((RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC) * RX_NUM_QUEUES) -#define RX_LOW_WATERMARK 8 +#define RX_PENDING_WATERMARK 16 struct iwl_host_cmd; /*This file includes the declaration that are internal to the * trans_pcie layer */ +/** + * struct iwl_rx_mem_buffer + * @page_dma: bus address of rxb page + * @page: driver's pointer to the rxb page + * @vid: index of this rxb in the global table + */ struct iwl_rx_mem_buffer { dma_addr_t page_dma; struct page *page; + u16 vid; struct list_head list; }; @@ -90,8 +97,12 @@ struct isr_statistics { /** * struct iwl_rxq - Rx queue - * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) + * @id: queue index + * @bd: driver's pointer to buffer of receive buffer descriptors (rbd). + * Address size is 32 bit in pre-9000 devices and 64 bit in 9000 devices. * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) + * @ubd: driver's pointer to buffer of used receive buffer descriptors (rbd) + * @ubd_dma: physical address of buffer of used receive buffer descriptors (rbd) * @read: Shared index to newest available Rx buffer * @write: Shared index to oldest written Rx packet * @free_count: Number of pre-allocated buffers in rx_free @@ -103,32 +114,34 @@ struct isr_statistics { * @rb_stts: driver's pointer to receive buffer status * @rb_stts_dma: bus address of receive buffer status * @lock: - * @pool: initial pool of iwl_rx_mem_buffer for the queue - * @queue: actual rx queue + * @queue: actual rx queue. Not used for multi-rx queue. * * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers */ struct iwl_rxq { - __le32 *bd; + int id; + void *bd; dma_addr_t bd_dma; + __le32 *used_bd; + dma_addr_t used_bd_dma; u32 read; u32 write; u32 free_count; u32 used_count; u32 write_actual; + u32 queue_size; struct list_head rx_free; struct list_head rx_used; bool need_update; struct iwl_rb_status *rb_stts; dma_addr_t rb_stts_dma; spinlock_t lock; - struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE]; + struct napi_struct napi; struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; }; /** * struct iwl_rb_allocator - Rx allocator - * @pool: initial pool of allocator * @req_pending: number of requests the allcator had not processed yet * @req_ready: number of requests honored and ready for claiming * @rbd_allocated: RBDs with pages allocated and ready to be handled to @@ -140,7 +153,6 @@ struct iwl_rxq { * @rx_alloc: work struct for background calls */ struct iwl_rb_allocator { - struct iwl_rx_mem_buffer pool[RX_POOL_SIZE]; atomic_t req_pending; atomic_t req_ready; struct list_head rbd_allocated; @@ -280,6 +292,7 @@ struct iwl_txq { bool ampdu; bool block; unsigned long wd_timeout; + struct sk_buff_head overflow_q; }; static inline dma_addr_t @@ -297,6 +310,8 @@ struct iwl_tso_hdr_page { /** * struct iwl_trans_pcie - PCIe transport specific data * @rxq: all the RX queue data + * @rx_pool: initial pool of iwl_rx_mem_buffer for all the queues + * @global_table: table mapping received VID from hw to rxb * @rba: allocator for RX replenishing * @drv - pointer to iwl_drv * @trans: pointer to the generic transport area @@ -321,15 +336,24 @@ struct iwl_tso_hdr_page { * @fw_mon_phys: physical address of the buffer for the firmware monitor * @fw_mon_page: points to the first page of the buffer for the firmware monitor * @fw_mon_size: size of the buffer for the firmware monitor + * @msix_entries: array of MSI-X entries + * @msix_enabled: true if managed to enable MSI-X + * @allocated_vector: the number of interrupt vector allocated by the OS + * @default_irq_num: default irq for non rx interrupt + * @fh_init_mask: initial unmasked fh causes + * @hw_init_mask: initial unmasked hw causes + * @fh_mask: current unmasked fh causes + * @hw_mask: current unmasked hw causes */ struct iwl_trans_pcie { - struct iwl_rxq rxq; + struct iwl_rxq *rxq; + struct iwl_rx_mem_buffer rx_pool[RX_POOL_SIZE]; + struct iwl_rx_mem_buffer *global_table[MQ_RX_TABLE_SIZE]; struct iwl_rb_allocator rba; struct iwl_trans *trans; struct iwl_drv *drv; struct net_device napi_dev; - struct napi_struct napi; struct __percpu iwl_tso_hdr_page *tso_hdr_page; @@ -359,6 +383,7 @@ struct iwl_trans_pcie { bool ucode_write_complete; wait_queue_head_t ucode_write_waitq; wait_queue_head_t wait_command_queue; + wait_queue_head_t d0i3_waitq; u8 cmd_queue; u8 cmd_fifo; @@ -385,6 +410,15 @@ struct iwl_trans_pcie { dma_addr_t fw_mon_phys; struct page *fw_mon_page; u32 fw_mon_size; + + struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES]; + bool msix_enabled; + u32 allocated_vector; + u32 default_irq_num; + u32 fh_init_mask; + u32 hw_init_mask; + u32 fh_mask; + u32 hw_mask; }; static inline struct iwl_trans_pcie * @@ -413,7 +447,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); * RX ******************************************************/ int iwl_pcie_rx_init(struct iwl_trans *trans); +irqreturn_t iwl_pcie_msix_isr(int irq, void *data); irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id); +irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id); +irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id); int iwl_pcie_rx_stop(struct iwl_trans *trans); void iwl_pcie_rx_free(struct iwl_trans *trans); @@ -468,15 +505,24 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans); ******************************************************/ static inline void iwl_disable_interrupts(struct iwl_trans *trans) { - clear_bit(STATUS_INT_ENABLED, &trans->status); - - /* disable interrupts from uCode/NIC to host */ - iwl_write32(trans, CSR_INT_MASK, 0x00000000); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - /* acknowledge/clear/reset any interrupts still pending - * from uCode or flow handler (Rx/Tx DMA) */ - iwl_write32(trans, CSR_INT, 0xffffffff); - iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); + clear_bit(STATUS_INT_ENABLED, &trans->status); + if (!trans_pcie->msix_enabled) { + /* disable interrupts from uCode/NIC to host */ + iwl_write32(trans, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl_write32(trans, CSR_INT, 0xffffffff); + iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); + } else { + /* disable all the interrupt we might use */ + iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, + trans_pcie->fh_init_mask); + iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD, + trans_pcie->hw_init_mask); + } IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); } @@ -486,8 +532,37 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans) IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); set_bit(STATUS_INT_ENABLED, &trans->status); - trans_pcie->inta_mask = CSR_INI_SET_MASK; - iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + if (!trans_pcie->msix_enabled) { + trans_pcie->inta_mask = CSR_INI_SET_MASK; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + } else { + /* + * fh/hw_mask keeps all the unmasked causes. + * Unlike msi, in msix cause is enabled when it is unset. + */ + trans_pcie->hw_mask = trans_pcie->hw_init_mask; + trans_pcie->fh_mask = trans_pcie->fh_init_mask; + iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, + ~trans_pcie->fh_mask); + iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD, + ~trans_pcie->hw_mask); + } +} + +static inline void iwl_enable_hw_int_msk_msix(struct iwl_trans *trans, u32 msk) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD, ~msk); + trans_pcie->hw_mask = msk; +} + +static inline void iwl_enable_fh_int_msk_msix(struct iwl_trans *trans, u32 msk) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, ~msk); + trans_pcie->fh_mask = msk; } static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) @@ -495,8 +570,15 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_ISR(trans, "Enabling FW load interrupt\n"); - trans_pcie->inta_mask = CSR_INT_BIT_FH_TX; - iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + if (!trans_pcie->msix_enabled) { + trans_pcie->inta_mask = CSR_INT_BIT_FH_TX; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + } else { + iwl_write32(trans, CSR_MSIX_HW_INT_MASK_AD, + trans_pcie->hw_init_mask); + iwl_enable_fh_int_msk_msix(trans, + MSIX_FH_INT_CAUSES_D2S_CH0_NUM); + } } static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) @@ -504,8 +586,15 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); - trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL; - iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + if (!trans_pcie->msix_enabled) { + trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); + } else { + iwl_write32(trans, CSR_MSIX_FH_INT_MASK_AD, + trans_pcie->fh_init_mask); + iwl_enable_hw_int_msk_msix(trans, + MSIX_HW_INT_CAUSES_REG_RF_KILL); + } } static inline void iwl_wake_queue(struct iwl_trans *trans, @@ -588,4 +677,7 @@ static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) } #endif +int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans); +int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans); + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 152cf9ad9..4be3c35af 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -2,6 +2,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -140,8 +141,8 @@ */ static int iwl_rxq_space(const struct iwl_rxq *rxq) { - /* Make sure RX_QUEUE_SIZE is a power of 2 */ - BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1)); + /* Make sure rx queue size is a power of 2 */ + WARN_ON(rxq->queue_size & (rxq->queue_size - 1)); /* * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity @@ -149,7 +150,7 @@ static int iwl_rxq_space(const struct iwl_rxq *rxq) * The following is equivalent to modulo by RX_QUEUE_SIZE and is well * defined for negative dividends. */ - return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1); + return (rxq->read - rxq->write - 1) & (rxq->queue_size - 1); } /* @@ -160,6 +161,12 @@ static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr) return cpu_to_le32((u32)(dma_addr >> 8)); } +static void iwl_pcie_write_prph_64(struct iwl_trans *trans, u64 ofs, u64 val) +{ + iwl_write_prph(trans, ofs, val & 0xffffffff); + iwl_write_prph(trans, ofs + 4, val >> 32); +} + /* * iwl_pcie_rx_stop - stops the Rx DMA */ @@ -173,10 +180,9 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans) /* * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue */ -static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans) +static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_rxq *rxq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; u32 reg; lockdep_assert_held(&rxq->lock); @@ -201,41 +207,84 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans) } rxq->write_actual = round_down(rxq->write, 8); - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); + if (trans->cfg->mq_rx_supported) + iwl_write_prph(trans, RFH_Q_FRBDCB_WIDX(rxq->id), + rxq->write_actual); + else + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); } static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; + int i; - spin_lock(&rxq->lock); + for (i = 0; i < trans->num_rx_queues; i++) { + struct iwl_rxq *rxq = &trans_pcie->rxq[i]; + + if (!rxq->need_update) + continue; + spin_lock(&rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans, rxq); + rxq->need_update = false; + spin_unlock(&rxq->lock); + } +} + +/* + * iwl_pcie_rxq_mq_restock - restock implementation for multi-queue rx + */ +static void iwl_pcie_rxq_mq_restock(struct iwl_trans *trans, + struct iwl_rxq *rxq) +{ + struct iwl_rx_mem_buffer *rxb; + + /* + * If the device isn't enabled - no need to try to add buffers... + * This can happen when we stop the device and still have an interrupt + * pending. We stop the APM before we sync the interrupts because we + * have to (see comment there). On the other hand, since the APM is + * stopped, we cannot access the HW (in particular not prph). + * So don't try to restock if the APM has been already stopped. + */ + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + return; - if (!rxq->need_update) - goto exit_unlock; + spin_lock(&rxq->lock); + while (rxq->free_count) { + __le64 *bd = (__le64 *)rxq->bd; - iwl_pcie_rxq_inc_wr_ptr(trans); - rxq->need_update = false; + /* Get next free Rx buffer, remove from free list */ + rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); - exit_unlock: + /* 12 first bits are expected to be empty */ + WARN_ON(rxb->page_dma & DMA_BIT_MASK(12)); + /* Point to Rx buffer via next RBD in circular buffer */ + bd[rxq->write] = cpu_to_le64(rxb->page_dma | rxb->vid); + rxq->write = (rxq->write + 1) & MQ_RX_TABLE_MASK; + rxq->free_count--; + } spin_unlock(&rxq->lock); + + /* + * If we've added more space for the firmware to place data, tell it. + * Increment device's write pointer in multiples of 8. + */ + if (rxq->write_actual != (rxq->write & ~0x7)) { + spin_lock(&rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans, rxq); + spin_unlock(&rxq->lock); + } } /* - * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool - * - * If there are slots in the RX queue that need to be restocked, - * and we have free pre-allocated buffers, fill the ranks as much - * as we can, pulling from rx_free. - * - * This moves the 'write' index forward to catch up with 'processed', and - * also updates the memory address in the firmware to reference the new - * target buffer. + * iwl_pcie_rxq_sq_restock - restock implementation for single queue rx */ -static void iwl_pcie_rxq_restock(struct iwl_trans *trans) +static void iwl_pcie_rxq_sq_restock(struct iwl_trans *trans, + struct iwl_rxq *rxq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rx_mem_buffer *rxb; /* @@ -251,6 +300,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) spin_lock(&rxq->lock); while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) { + __le32 *bd = (__le32 *)rxq->bd; /* The overwritten rxb must be a used one */ rxb = rxq->queue[rxq->write]; BUG_ON(rxb && rxb->page); @@ -261,7 +311,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) list_del(&rxb->list); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma); + bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -272,12 +322,32 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) { spin_lock(&rxq->lock); - iwl_pcie_rxq_inc_wr_ptr(trans); + iwl_pcie_rxq_inc_wr_ptr(trans, rxq); spin_unlock(&rxq->lock); } } /* + * iwl_pcie_rxq_restock - refill RX queue from pre-allocated pool + * + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can, pulling from rx_free. + * + * This moves the 'write' index forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +static +void iwl_pcie_rxq_restock(struct iwl_trans *trans, struct iwl_rxq *rxq) +{ + if (trans->cfg->mq_rx_supported) + iwl_pcie_rxq_mq_restock(trans, rxq); + else + iwl_pcie_rxq_sq_restock(trans, rxq); +} + +/* * iwl_pcie_rx_alloc_page - allocates and returns a page. * */ @@ -285,13 +355,9 @@ static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; struct page *page; gfp_t gfp_mask = priority; - if (rxq->free_count > RX_LOW_WATERMARK) - gfp_mask |= __GFP_NOWARN; - if (trans_pcie->rx_page_order > 0) gfp_mask |= __GFP_COMP; @@ -301,16 +367,13 @@ static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans, if (net_ratelimit()) IWL_DEBUG_INFO(trans, "alloc_pages failed, order: %d\n", trans_pcie->rx_page_order); - /* Issue an error if the hardware has consumed more than half - * of its free buffer list and we don't have enough - * pre-allocated buffers. + /* + * Issue an error if we don't have enough pre-allocated + * buffers. ` */ - if (rxq->free_count <= RX_LOW_WATERMARK && - iwl_rxq_space(rxq) > (RX_QUEUE_SIZE / 2) && - net_ratelimit()) + if (!(gfp_mask & __GFP_NOWARN) && net_ratelimit()) IWL_CRIT(trans, - "Failed to alloc_pages with GFP_KERNEL. Only %u free buffers remaining.\n", - rxq->free_count); + "Failed to alloc_pages\n"); return NULL; } return page; @@ -325,10 +388,10 @@ static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans, * iwl_pcie_rxq_restock. The latter function will update the HW to use the newly * allocated buffers. */ -static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) +static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority, + struct iwl_rxq *rxq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rx_mem_buffer *rxb; struct page *page; @@ -372,10 +435,6 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) __free_pages(page, trans_pcie->rx_page_order); return; } - /* dma address must be no more than 36 bits */ - BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); - /* and also 256 byte aligned! */ - BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); spin_lock(&rxq->lock); @@ -386,41 +445,24 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) } } -static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans) +static void iwl_pcie_free_rbs_pool(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; int i; - lockdep_assert_held(&rxq->lock); - - for (i = 0; i < RX_QUEUE_SIZE; i++) { - if (!rxq->pool[i].page) + for (i = 0; i < RX_POOL_SIZE; i++) { + if (!trans_pcie->rx_pool[i].page) continue; - dma_unmap_page(trans->dev, rxq->pool[i].page_dma, + dma_unmap_page(trans->dev, trans_pcie->rx_pool[i].page_dma, PAGE_SIZE << trans_pcie->rx_page_order, DMA_FROM_DEVICE); - __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order); - rxq->pool[i].page = NULL; + __free_pages(trans_pcie->rx_pool[i].page, + trans_pcie->rx_page_order); + trans_pcie->rx_pool[i].page = NULL; } } /* - * iwl_pcie_rx_replenish - Move all used buffers from rx_used to rx_free - * - * When moving to rx_free an page is allocated for the slot. - * - * Also restock the Rx queue via iwl_pcie_rxq_restock. - * This is called only during initialization - */ -static void iwl_pcie_rx_replenish(struct iwl_trans *trans) -{ - iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL); - - iwl_pcie_rxq_restock(trans); -} - -/* * iwl_pcie_rx_allocator - Allocates pages in the background for RX queues * * Allocates for each received request 8 pages @@ -444,6 +486,11 @@ static void iwl_pcie_rx_allocator(struct iwl_trans *trans) while (pending) { int i; struct list_head local_allocated; + gfp_t gfp_mask = GFP_KERNEL; + + /* Do not post a warning if there are only a few requests */ + if (pending < RX_PENDING_WATERMARK) + gfp_mask |= __GFP_NOWARN; INIT_LIST_HEAD(&local_allocated); @@ -463,7 +510,7 @@ static void iwl_pcie_rx_allocator(struct iwl_trans *trans) BUG_ON(rxb->page); /* Alloc a new receive buffer */ - page = iwl_pcie_rx_alloc_page(trans, GFP_KERNEL); + page = iwl_pcie_rx_alloc_page(trans, gfp_mask); if (!page) continue; rxb->page = page; @@ -477,10 +524,6 @@ static void iwl_pcie_rx_allocator(struct iwl_trans *trans) __free_pages(page, trans_pcie->rx_page_order); continue; } - /* dma address must be no more than 36 bits */ - BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); - /* and also 256 byte aligned! */ - BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); /* move the allocated entry to the out list */ list_move(&rxb->list, &local_allocated); @@ -512,40 +555,46 @@ static void iwl_pcie_rx_allocator(struct iwl_trans *trans) } /* - * iwl_pcie_rx_allocator_get - Returns the pre-allocated pages + * iwl_pcie_rx_allocator_get - returns the pre-allocated pages .* .* Called by queue when the queue posted allocation request and * has freed 8 RBDs in order to restock itself. + * This function directly moves the allocated RBs to the queue's ownership + * and updates the relevant counters. */ -static int iwl_pcie_rx_allocator_get(struct iwl_trans *trans, - struct iwl_rx_mem_buffer - *out[RX_CLAIM_REQ_ALLOC]) +static void iwl_pcie_rx_allocator_get(struct iwl_trans *trans, + struct iwl_rxq *rxq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rb_allocator *rba = &trans_pcie->rba; int i; + lockdep_assert_held(&rxq->lock); + /* * atomic_dec_if_positive returns req_ready - 1 for any scenario. * If req_ready is 0 atomic_dec_if_positive will return -1 and this - * function will return -ENOMEM, as there are no ready requests. + * function will return early, as there are no ready requests. * atomic_dec_if_positive will perofrm the *actual* decrement only if * req_ready > 0, i.e. - there are ready requests and the function * hands one request to the caller. */ if (atomic_dec_if_positive(&rba->req_ready) < 0) - return -ENOMEM; + return; spin_lock(&rba->lock); for (i = 0; i < RX_CLAIM_REQ_ALLOC; i++) { /* Get next free Rx buffer, remove it from free list */ - out[i] = list_first_entry(&rba->rbd_allocated, - struct iwl_rx_mem_buffer, list); - list_del(&out[i]->list); + struct iwl_rx_mem_buffer *rxb = + list_first_entry(&rba->rbd_allocated, + struct iwl_rx_mem_buffer, list); + + list_move(&rxb->list, &rxq->rx_free); } spin_unlock(&rba->lock); - return 0; + rxq->used_count -= RX_CLAIM_REQ_ALLOC; + rxq->free_count += RX_CLAIM_REQ_ALLOC; } static void iwl_pcie_rx_allocator_work(struct work_struct *data) @@ -561,38 +610,83 @@ static void iwl_pcie_rx_allocator_work(struct work_struct *data) static int iwl_pcie_rx_alloc(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rb_allocator *rba = &trans_pcie->rba; struct device *dev = trans->dev; + int i; + int free_size = trans->cfg->mq_rx_supported ? sizeof(__le64) : + sizeof(__le32); - memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); + if (WARN_ON(trans_pcie->rxq)) + return -EINVAL; + + trans_pcie->rxq = kcalloc(trans->num_rx_queues, sizeof(struct iwl_rxq), + GFP_KERNEL); + if (!trans_pcie->rxq) + return -EINVAL; - spin_lock_init(&rxq->lock); spin_lock_init(&rba->lock); - if (WARN_ON(rxq->bd || rxq->rb_stts)) - return -EINVAL; + for (i = 0; i < trans->num_rx_queues; i++) { + struct iwl_rxq *rxq = &trans_pcie->rxq[i]; - /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - &rxq->bd_dma, GFP_KERNEL); - if (!rxq->bd) - goto err_bd; + spin_lock_init(&rxq->lock); + if (trans->cfg->mq_rx_supported) + rxq->queue_size = MQ_RX_TABLE_SIZE; + else + rxq->queue_size = RX_QUEUE_SIZE; - /*Allocate the driver's pointer to receive buffer status */ - rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), - &rxq->rb_stts_dma, GFP_KERNEL); - if (!rxq->rb_stts) - goto err_rb_stts; + /* + * Allocate the circular buffer of Read Buffer Descriptors + * (RBDs) + */ + rxq->bd = dma_zalloc_coherent(dev, + free_size * rxq->queue_size, + &rxq->bd_dma, GFP_KERNEL); + if (!rxq->bd) + goto err; + + if (trans->cfg->mq_rx_supported) { + rxq->used_bd = dma_zalloc_coherent(dev, + sizeof(__le32) * + rxq->queue_size, + &rxq->used_bd_dma, + GFP_KERNEL); + if (!rxq->used_bd) + goto err; + } + /*Allocate the driver's pointer to receive buffer status */ + rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), + &rxq->rb_stts_dma, + GFP_KERNEL); + if (!rxq->rb_stts) + goto err; + } return 0; -err_rb_stts: - dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - rxq->bd_dma = 0; - rxq->bd = NULL; -err_bd: +err: + for (i = 0; i < trans->num_rx_queues; i++) { + struct iwl_rxq *rxq = &trans_pcie->rxq[i]; + + if (rxq->bd) + dma_free_coherent(dev, free_size * rxq->queue_size, + rxq->bd, rxq->bd_dma); + rxq->bd_dma = 0; + rxq->bd = NULL; + + if (rxq->rb_stts) + dma_free_coherent(trans->dev, + sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + + if (rxq->used_bd) + dma_free_coherent(dev, sizeof(__le32) * rxq->queue_size, + rxq->used_bd, rxq->used_bd_dma); + rxq->used_bd_dma = 0; + rxq->used_bd = NULL; + } + kfree(trans_pcie->rxq); + return -ENOMEM; } @@ -659,65 +753,112 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); } -static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) +static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 rb_size, enabled = 0; int i; - lockdep_assert_held(&rxq->lock); - - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - rxq->free_count = 0; - rxq->used_count = 0; + switch (trans_pcie->rx_buf_size) { + case IWL_AMSDU_4K: + rb_size = RFH_RXF_DMA_RB_SIZE_4K; + break; + case IWL_AMSDU_8K: + rb_size = RFH_RXF_DMA_RB_SIZE_8K; + break; + case IWL_AMSDU_12K: + rb_size = RFH_RXF_DMA_RB_SIZE_12K; + break; + default: + WARN_ON(1); + rb_size = RFH_RXF_DMA_RB_SIZE_4K; + } - for (i = 0; i < RX_QUEUE_SIZE; i++) - list_add(&rxq->pool[i].list, &rxq->rx_used); -} + /* Stop Rx DMA */ + iwl_write_prph(trans, RFH_RXF_DMA_CFG, 0); + /* disable free amd used rx queue operation */ + iwl_write_prph(trans, RFH_RXF_RXQ_ACTIVE, 0); + + for (i = 0; i < trans->num_rx_queues; i++) { + /* Tell device where to find RBD free table in DRAM */ + iwl_pcie_write_prph_64(trans, RFH_Q_FRBDCB_BA_LSB(i), + (u64)(trans_pcie->rxq[i].bd_dma)); + /* Tell device where to find RBD used table in DRAM */ + iwl_pcie_write_prph_64(trans, RFH_Q_URBDCB_BA_LSB(i), + (u64)(trans_pcie->rxq[i].used_bd_dma)); + /* Tell device where in DRAM to update its Rx status */ + iwl_pcie_write_prph_64(trans, RFH_Q_URBD_STTS_WPTR_LSB(i), + trans_pcie->rxq[i].rb_stts_dma); + /* Reset device indice tables */ + iwl_write_prph(trans, RFH_Q_FRBDCB_WIDX(i), 0); + iwl_write_prph(trans, RFH_Q_FRBDCB_RIDX(i), 0); + iwl_write_prph(trans, RFH_Q_URBDCB_WIDX(i), 0); + + enabled |= BIT(i) | BIT(i + 16); + } -static void iwl_pcie_rx_init_rba(struct iwl_rb_allocator *rba) -{ - int i; + /* restock default queue */ + iwl_pcie_rxq_mq_restock(trans, &trans_pcie->rxq[0]); - lockdep_assert_held(&rba->lock); + /* + * Enable Rx DMA + * Single frame mode + * Rx buffer size 4 or 8k or 12k + * Min RB size 4 or 8 + * Drop frames that exceed RB size + * 512 RBDs + */ + iwl_write_prph(trans, RFH_RXF_DMA_CFG, + RFH_DMA_EN_ENABLE_VAL | + rb_size | RFH_RXF_DMA_SINGLE_FRAME_MASK | + RFH_RXF_DMA_MIN_RB_4_8 | + RFH_RXF_DMA_DROP_TOO_LARGE_MASK | + RFH_RXF_DMA_RBDCB_SIZE_512); - INIT_LIST_HEAD(&rba->rbd_allocated); - INIT_LIST_HEAD(&rba->rbd_empty); + /* + * Activate DMA snooping. + * Set RX DMA chunk size to 64B + * Default queue is 0 + */ + iwl_write_prph(trans, RFH_GEN_CFG, RFH_GEN_CFG_RFH_DMA_SNOOP | + (DEFAULT_RXQ_NUM << RFH_GEN_CFG_DEFAULT_RXQ_NUM_POS) | + RFH_GEN_CFG_SERVICE_DMA_SNOOP); + /* Enable the relevant rx queues */ + iwl_write_prph(trans, RFH_RXF_RXQ_ACTIVE, enabled); - for (i = 0; i < RX_POOL_SIZE; i++) - list_add(&rba->pool[i].list, &rba->rbd_empty); + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); } -static void iwl_pcie_rx_free_rba(struct iwl_trans *trans) +static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rb_allocator *rba = &trans_pcie->rba; - int i; + lockdep_assert_held(&rxq->lock); - lockdep_assert_held(&rba->lock); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + rxq->free_count = 0; + rxq->used_count = 0; +} - for (i = 0; i < RX_POOL_SIZE; i++) { - if (!rba->pool[i].page) - continue; - dma_unmap_page(trans->dev, rba->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rba->pool[i].page, trans_pcie->rx_page_order); - rba->pool[i].page = NULL; - } +static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) +{ + WARN_ON(1); + return 0; } int iwl_pcie_rx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_rxq *def_rxq; struct iwl_rb_allocator *rba = &trans_pcie->rba; - int i, err; + int i, err, queue_size, allocator_pool_size, num_alloc; - if (!rxq->bd) { + if (!trans_pcie->rxq) { err = iwl_pcie_rx_alloc(trans); if (err) return err; } + def_rxq = trans_pcie->rxq; if (!rba->alloc_wq) rba->alloc_wq = alloc_workqueue("rb_allocator", WQ_HIGHPRI | WQ_UNBOUND, 1); @@ -726,34 +867,69 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) spin_lock(&rba->lock); atomic_set(&rba->req_pending, 0); atomic_set(&rba->req_ready, 0); - /* free all first - we might be reconfigured for a different size */ - iwl_pcie_rx_free_rba(trans); - iwl_pcie_rx_init_rba(rba); + INIT_LIST_HEAD(&rba->rbd_allocated); + INIT_LIST_HEAD(&rba->rbd_empty); spin_unlock(&rba->lock); - spin_lock(&rxq->lock); - /* free all first - we might be reconfigured for a different size */ - iwl_pcie_rxq_free_rbs(trans); - iwl_pcie_rx_init_rxb_lists(rxq); + iwl_pcie_free_rbs_pool(trans); for (i = 0; i < RX_QUEUE_SIZE; i++) - rxq->queue[i] = NULL; + def_rxq->queue[i] = NULL; - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); - spin_unlock(&rxq->lock); + for (i = 0; i < trans->num_rx_queues; i++) { + struct iwl_rxq *rxq = &trans_pcie->rxq[i]; - iwl_pcie_rx_replenish(trans); + rxq->id = i; - iwl_pcie_rx_hw_init(trans, rxq); + spin_lock(&rxq->lock); + /* + * Set read write pointer to reflect that we have processed + * and used all buffers, but have not restocked the Rx queue + * with fresh buffers + */ + rxq->read = 0; + rxq->write = 0; + rxq->write_actual = 0; + memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); - spin_lock(&rxq->lock); - iwl_pcie_rxq_inc_wr_ptr(trans); - spin_unlock(&rxq->lock); + iwl_pcie_rx_init_rxb_lists(rxq); + + if (!rxq->napi.poll) + netif_napi_add(&trans_pcie->napi_dev, &rxq->napi, + iwl_pcie_dummy_napi_poll, 64); + + spin_unlock(&rxq->lock); + } + + /* move the pool to the default queue and allocator ownerships */ + queue_size = trans->cfg->mq_rx_supported ? + MQ_RX_NUM_RBDS : RX_QUEUE_SIZE; + allocator_pool_size = trans->num_rx_queues * + (RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC); + num_alloc = queue_size + allocator_pool_size; + for (i = 0; i < num_alloc; i++) { + struct iwl_rx_mem_buffer *rxb = &trans_pcie->rx_pool[i]; + + if (i < allocator_pool_size) + list_add(&rxb->list, &rba->rbd_empty); + else + list_add(&rxb->list, &def_rxq->rx_used); + trans_pcie->global_table[i] = rxb; + rxb->vid = (u16)i; + } + + iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL, def_rxq); + if (trans->cfg->mq_rx_supported) { + iwl_pcie_rx_mq_hw_init(trans); + } else { + iwl_pcie_rxq_sq_restock(trans, def_rxq); + iwl_pcie_rx_hw_init(trans, def_rxq); + } + + spin_lock(&def_rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans, def_rxq); + spin_unlock(&def_rxq->lock); return 0; } @@ -761,12 +937,16 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) void iwl_pcie_rx_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rb_allocator *rba = &trans_pcie->rba; + int free_size = trans->cfg->mq_rx_supported ? sizeof(__le64) : + sizeof(__le32); + int i; - /*if rxq->bd is NULL, it means that nothing has been allocated, - * exit now */ - if (!rxq->bd) { + /* + * if rxq is NULL, it means that nothing has been allocated, + * exit now + */ + if (!trans_pcie->rxq) { IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); return; } @@ -777,27 +957,37 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) rba->alloc_wq = NULL; } - spin_lock(&rba->lock); - iwl_pcie_rx_free_rba(trans); - spin_unlock(&rba->lock); - - spin_lock(&rxq->lock); - iwl_pcie_rxq_free_rbs(trans); - spin_unlock(&rxq->lock); - - dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - rxq->bd_dma = 0; - rxq->bd = NULL; - - if (rxq->rb_stts) - dma_free_coherent(trans->dev, - sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - else - IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); - rxq->rb_stts_dma = 0; - rxq->rb_stts = NULL; + iwl_pcie_free_rbs_pool(trans); + + for (i = 0; i < trans->num_rx_queues; i++) { + struct iwl_rxq *rxq = &trans_pcie->rxq[i]; + + if (rxq->bd) + dma_free_coherent(trans->dev, + free_size * rxq->queue_size, + rxq->bd, rxq->bd_dma); + rxq->bd_dma = 0; + rxq->bd = NULL; + + if (rxq->rb_stts) + dma_free_coherent(trans->dev, + sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + else + IWL_DEBUG_INFO(trans, + "Free rxq->rb_stts which is NULL\n"); + + if (rxq->used_bd) + dma_free_coherent(trans->dev, + sizeof(__le32) * rxq->queue_size, + rxq->used_bd, rxq->used_bd_dma); + rxq->used_bd_dma = 0; + rxq->used_bd = NULL; + + if (rxq->napi.poll) + netif_napi_del(&rxq->napi); + } + kfree(trans_pcie->rxq); } /* @@ -841,11 +1031,11 @@ static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans, } static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, + struct iwl_rxq *rxq, struct iwl_rx_mem_buffer *rxb, bool emergency) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; bool page_stolen = false; int max_len = PAGE_SIZE << trans_pcie->rx_page_order; @@ -911,7 +1101,12 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, index = SEQ_TO_INDEX(sequence); cmd_index = get_cmd_index(&txq->q, index); - iwl_op_mode_rx(trans->op_mode, &trans_pcie->napi, &rxcb); + if (rxq->id == 0) + iwl_op_mode_rx(trans->op_mode, &rxq->napi, + &rxcb); + else + iwl_op_mode_rx_rss(trans->op_mode, &rxq->napi, + &rxcb, rxq->id); if (reclaim) { kzfree(txq->entries[cmd_index].free_buf); @@ -972,11 +1167,11 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, /* * iwl_pcie_rx_handle - Main entry function for receiving responses from fw */ -static void iwl_pcie_rx_handle(struct iwl_trans *trans) +static void iwl_pcie_rx_handle(struct iwl_trans *trans, int queue) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - u32 r, i, j, count = 0; + struct iwl_rxq *rxq = &trans_pcie->rxq[queue]; + u32 r, i, count = 0; bool emergency = false; restart: @@ -986,80 +1181,73 @@ restart: r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; i = rxq->read; + /* W/A 9000 device step A0 wrap-around bug */ + r &= (rxq->queue_size - 1); + /* Rx interrupt, but nothing sent from uCode */ if (i == r) - IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); + IWL_DEBUG_RX(trans, "Q %d: HW = SW = %d\n", rxq->id, r); while (i != r) { struct iwl_rx_mem_buffer *rxb; - if (unlikely(rxq->used_count == RX_QUEUE_SIZE / 2)) + if (unlikely(rxq->used_count == rxq->queue_size / 2)) emergency = true; - rxb = rxq->queue[i]; - rxq->queue[i] = NULL; + if (trans->cfg->mq_rx_supported) { + /* + * used_bd is a 32 bit but only 12 are used to retrieve + * the vid + */ + u16 vid = le32_to_cpu(rxq->used_bd[i]) & 0x0FFF; + + if (WARN(vid >= ARRAY_SIZE(trans_pcie->global_table), + "Invalid rxb index from HW %u\n", (u32)vid)) + goto out; + rxb = trans_pcie->global_table[vid]; + } else { + rxb = rxq->queue[i]; + rxq->queue[i] = NULL; + } - IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d\n", r, i); - iwl_pcie_rx_handle_rb(trans, rxb, emergency); + IWL_DEBUG_RX(trans, "Q %d: HW = %d, SW = %d\n", rxq->id, r, i); + iwl_pcie_rx_handle_rb(trans, rxq, rxb, emergency); - i = (i + 1) & RX_QUEUE_MASK; + i = (i + 1) & (rxq->queue_size - 1); - /* If we have RX_CLAIM_REQ_ALLOC released rx buffers - - * try to claim the pre-allocated buffers from the allocator */ - if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) { + /* + * If we have RX_CLAIM_REQ_ALLOC released rx buffers - + * try to claim the pre-allocated buffers from the allocator. + * If not ready - will try to reclaim next time. + * There is no need to reschedule work - allocator exits only + * on success + */ + if (rxq->used_count >= RX_CLAIM_REQ_ALLOC) + iwl_pcie_rx_allocator_get(trans, rxq); + + if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 && !emergency) { struct iwl_rb_allocator *rba = &trans_pcie->rba; - struct iwl_rx_mem_buffer *out[RX_CLAIM_REQ_ALLOC]; - - if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 && - !emergency) { - /* Add the remaining 6 empty RBDs - * for allocator use - */ - spin_lock(&rba->lock); - list_splice_tail_init(&rxq->rx_used, - &rba->rbd_empty); - spin_unlock(&rba->lock); - } - /* If not ready - continue, will try to reclaim later. - * No need to reschedule work - allocator exits only on - * success */ - if (!iwl_pcie_rx_allocator_get(trans, out)) { - /* If success - then RX_CLAIM_REQ_ALLOC - * buffers were retrieved and should be added - * to free list */ - rxq->used_count -= RX_CLAIM_REQ_ALLOC; - for (j = 0; j < RX_CLAIM_REQ_ALLOC; j++) { - list_add_tail(&out[j]->list, - &rxq->rx_free); - rxq->free_count++; - } - } - } - if (emergency) { + /* Add the remaining empty RBDs for allocator use */ + spin_lock(&rba->lock); + list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty); + spin_unlock(&rba->lock); + } else if (emergency) { count++; if (count == 8) { count = 0; - if (rxq->used_count < RX_QUEUE_SIZE / 3) + if (rxq->used_count < rxq->queue_size / 3) emergency = false; + + rxq->read = i; spin_unlock(&rxq->lock); - iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); - spin_lock(&rxq->lock); + iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC, rxq); + iwl_pcie_rxq_restock(trans, rxq); + goto restart; } } - /* handle restock for three cases, can be all of them at once: - * - we just pulled buffers from the allocator - * - we have 8+ unstolen pages accumulated - * - we are in emergency and allocated buffers - */ - if (rxq->free_count >= RX_CLAIM_REQ_ALLOC) { - rxq->read = i; - spin_unlock(&rxq->lock); - iwl_pcie_rxq_restock(trans); - goto restart; - } } - +out: /* Backtrack one entry */ rxq->read = i; spin_unlock(&rxq->lock); @@ -1077,10 +1265,60 @@ restart: * will be restocked by the next call of iwl_pcie_rxq_restock. */ if (unlikely(emergency && count)) - iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); + iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC, rxq); + + if (rxq->napi.poll) + napi_gro_flush(&rxq->napi, false); + + iwl_pcie_rxq_restock(trans, rxq); +} + +static struct iwl_trans_pcie *iwl_pcie_get_trans_pcie(struct msix_entry *entry) +{ + u8 queue = entry->entry; + struct msix_entry *entries = entry - queue; + + return container_of(entries, struct iwl_trans_pcie, msix_entries[0]); +} - if (trans_pcie->napi.poll) - napi_gro_flush(&trans_pcie->napi, false); +static inline void iwl_pcie_clear_irq(struct iwl_trans *trans, + struct msix_entry *entry) +{ + /* + * Before sending the interrupt the HW disables it to prevent + * a nested interrupt. This is done by writing 1 to the corresponding + * bit in the mask register. After handling the interrupt, it should be + * re-enabled by clearing this bit. This register is defined as + * write 1 clear (W1C) register, meaning that it's being clear + * by writing 1 to the bit. + */ + iwl_write_direct32(trans, CSR_MSIX_AUTOMASK_ST_AD, BIT(entry->entry)); +} + +/* + * iwl_pcie_rx_msix_handle - Main entry function for receiving responses from fw + * This interrupt handler should be used with RSS queue only. + */ +irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id) +{ + struct msix_entry *entry = dev_id; + struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry); + struct iwl_trans *trans = trans_pcie->trans; + + if (WARN_ON(entry->entry >= trans->num_rx_queues)) + return IRQ_NONE; + + lock_map_acquire(&trans->sync_cmd_lockdep_map); + + local_bh_disable(); + iwl_pcie_rx_handle(trans, entry->entry); + local_bh_enable(); + + iwl_pcie_clear_irq(trans, entry); + + lock_map_release(&trans->sync_cmd_lockdep_map); + + return IRQ_HANDLED; } /* @@ -1413,7 +1651,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) isr_stats->rx++; local_bh_disable(); - iwl_pcie_rx_handle(trans); + iwl_pcie_rx_handle(trans, 0); local_bh_enable(); } @@ -1556,3 +1794,129 @@ irqreturn_t iwl_pcie_isr(int irq, void *data) return IRQ_WAKE_THREAD; } + +irqreturn_t iwl_pcie_msix_isr(int irq, void *data) +{ + return IRQ_WAKE_THREAD; +} + +irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) +{ + struct msix_entry *entry = dev_id; + struct iwl_trans_pcie *trans_pcie = iwl_pcie_get_trans_pcie(entry); + struct iwl_trans *trans = trans_pcie->trans; + struct isr_statistics *isr_stats = isr_stats = &trans_pcie->isr_stats; + u32 inta_fh, inta_hw; + + lock_map_acquire(&trans->sync_cmd_lockdep_map); + + spin_lock(&trans_pcie->irq_lock); + inta_fh = iwl_read_direct32(trans, CSR_MSIX_FH_INT_CAUSES_AD); + inta_hw = iwl_read_direct32(trans, CSR_MSIX_HW_INT_CAUSES_AD); + /* + * Clear causes registers to avoid being handling the same cause. + */ + iwl_write_direct32(trans, CSR_MSIX_FH_INT_CAUSES_AD, inta_fh); + iwl_write_direct32(trans, CSR_MSIX_HW_INT_CAUSES_AD, inta_hw); + spin_unlock(&trans_pcie->irq_lock); + + if (unlikely(!(inta_fh | inta_hw))) { + IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); + lock_map_release(&trans->sync_cmd_lockdep_map); + return IRQ_NONE; + } + + if (iwl_have_debug_level(IWL_DL_ISR)) + IWL_DEBUG_ISR(trans, "ISR inta_fh 0x%08x, enabled 0x%08x\n", + inta_fh, + iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD)); + + /* This "Tx" DMA channel is used only for loading uCode */ + if (inta_fh & MSIX_FH_INT_CAUSES_D2S_CH0_NUM) { + IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); + isr_stats->tx++; + /* + * Wake up uCode load routine, + * now that load is complete + */ + trans_pcie->ucode_write_complete = true; + wake_up(&trans_pcie->ucode_write_waitq); + } + + /* Error detected by uCode */ + if ((inta_fh & MSIX_FH_INT_CAUSES_FH_ERR) || + (inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR)) { + IWL_ERR(trans, + "Microcode SW error detected. Restarting 0x%X.\n", + inta_fh); + isr_stats->sw++; + iwl_pcie_irq_handle_error(trans); + } + + /* After checking FH register check HW register */ + if (iwl_have_debug_level(IWL_DL_ISR)) + IWL_DEBUG_ISR(trans, + "ISR inta_hw 0x%08x, enabled 0x%08x\n", + inta_hw, + iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD)); + + /* Alive notification via Rx interrupt will do the real work */ + if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) { + IWL_DEBUG_ISR(trans, "Alive interrupt\n"); + isr_stats->alive++; + } + + /* uCode wakes up after power-down sleep */ + if (inta_hw & MSIX_HW_INT_CAUSES_REG_WAKEUP) { + IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); + iwl_pcie_rxq_check_wrptr(trans); + iwl_pcie_txq_check_wrptrs(trans); + + isr_stats->wakeup++; + } + + /* Chip got too hot and stopped itself */ + if (inta_hw & MSIX_HW_INT_CAUSES_REG_CT_KILL) { + IWL_ERR(trans, "Microcode CT kill error detected.\n"); + isr_stats->ctkill++; + } + + /* HW RF KILL switch toggled */ + if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL) { + bool hw_rfkill; + + hw_rfkill = iwl_is_rfkill_set(trans); + IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", + hw_rfkill ? "disable radio" : "enable radio"); + + isr_stats->rfkill++; + + mutex_lock(&trans_pcie->mutex); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + mutex_unlock(&trans_pcie->mutex); + if (hw_rfkill) { + set_bit(STATUS_RFKILL, &trans->status); + if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status)) + IWL_DEBUG_RF_KILL(trans, + "Rfkill while SYNC HCMD in flight\n"); + wake_up(&trans_pcie->wait_command_queue); + } else { + clear_bit(STATUS_RFKILL, &trans->status); + } + } + + if (inta_hw & MSIX_HW_INT_CAUSES_REG_HW_ERR) { + IWL_ERR(trans, + "Hardware error detected. Restarting.\n"); + + isr_stats->hw++; + iwl_pcie_irq_handle_error(trans); + } + + iwl_pcie_clear_irq(trans, entry); + + lock_map_release(&trans->sync_cmd_lockdep_map); + + return IRQ_HANDLED; +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 1198caac3..b2b79354d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -72,6 +72,7 @@ #include <linux/bitops.h> #include <linux/gfp.h> #include <linux/vmalloc.h> +#include <linux/pm_runtime.h> #include "iwl-drv.h" #include "iwl-trans.h" @@ -615,38 +616,38 @@ static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, dma_addr_t phy_addr, u32 byte_cnt) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; int ret; trans_pcie->ucode_write_complete = false; - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); + if (!iwl_trans_grab_nic_access(trans, &flags)) + return -EIO; + + iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); + + iwl_write32(trans, FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), + dst_addr); - iwl_write_direct32(trans, - FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), - dst_addr); + iwl_write32(trans, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), + phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); - iwl_write_direct32(trans, - FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), - phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + iwl_write32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), + (iwl_get_dma_hi_addr(phy_addr) + << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); - iwl_write_direct32(trans, - FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), - (iwl_get_dma_hi_addr(phy_addr) - << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + iwl_write32(trans, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), + BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM) | + BIT(FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX) | + FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | - FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); + iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | + FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | - FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); + iwl_trans_release_nic_access(trans, &flags); ret = wait_event_timeout(trans_pcie->ucode_write_waitq, trans_pcie->ucode_write_complete, 5 * HZ); @@ -1122,6 +1123,20 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) iwl_pcie_prepare_card_hw(trans); } +static void iwl_pcie_synchronize_irqs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (trans_pcie->msix_enabled) { + int i; + + for (i = 0; i < trans_pcie->allocated_vector; i++) + synchronize_irq(trans_pcie->msix_entries[i].vector); + } else { + synchronize_irq(trans_pcie->pci_dev->irq); + } +} + static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill) { @@ -1148,7 +1163,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, iwl_disable_interrupts(trans); /* Make sure it finished running */ - synchronize_irq(trans_pcie->pci_dev->irq); + iwl_pcie_synchronize_irqs(trans); mutex_lock(&trans_pcie->mutex); @@ -1248,11 +1263,10 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) _iwl_trans_pcie_stop_device(trans, true); } -static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) +static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, + bool reset) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) { + if (!reset) { /* Enable persistence mode to avoid reset */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PERSIST_MODE); @@ -1269,14 +1283,14 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) iwl_pcie_disable_ict(trans); - synchronize_irq(trans_pcie->pci_dev->irq); + iwl_pcie_synchronize_irqs(trans); iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D3) { + if (reset) { /* * reset TX queues -- some of their registers reset during S3 * so if we don't reset everything here the D3 image would try @@ -1290,7 +1304,7 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status, - bool test) + bool test, bool reset) { u32 val; int ret; @@ -1325,7 +1339,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, iwl_pcie_set_pwr(trans, false); - if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) { + if (!reset) { iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); } else { @@ -1348,6 +1362,153 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, return 0; } +struct iwl_causes_list { + u32 cause_num; + u32 mask_reg; + u8 addr; +}; + +static struct iwl_causes_list causes_list[] = { + {MSIX_FH_INT_CAUSES_D2S_CH0_NUM, CSR_MSIX_FH_INT_MASK_AD, 0}, + {MSIX_FH_INT_CAUSES_D2S_CH1_NUM, CSR_MSIX_FH_INT_MASK_AD, 0x1}, + {MSIX_FH_INT_CAUSES_S2D, CSR_MSIX_FH_INT_MASK_AD, 0x3}, + {MSIX_FH_INT_CAUSES_FH_ERR, CSR_MSIX_FH_INT_MASK_AD, 0x5}, + {MSIX_HW_INT_CAUSES_REG_ALIVE, CSR_MSIX_HW_INT_MASK_AD, 0x10}, + {MSIX_HW_INT_CAUSES_REG_WAKEUP, CSR_MSIX_HW_INT_MASK_AD, 0x11}, + {MSIX_HW_INT_CAUSES_REG_CT_KILL, CSR_MSIX_HW_INT_MASK_AD, 0x16}, + {MSIX_HW_INT_CAUSES_REG_RF_KILL, CSR_MSIX_HW_INT_MASK_AD, 0x17}, + {MSIX_HW_INT_CAUSES_REG_PERIODIC, CSR_MSIX_HW_INT_MASK_AD, 0x18}, + {MSIX_HW_INT_CAUSES_REG_SW_ERR, CSR_MSIX_HW_INT_MASK_AD, 0x29}, + {MSIX_HW_INT_CAUSES_REG_SCD, CSR_MSIX_HW_INT_MASK_AD, 0x2A}, + {MSIX_HW_INT_CAUSES_REG_FH_TX, CSR_MSIX_HW_INT_MASK_AD, 0x2B}, + {MSIX_HW_INT_CAUSES_REG_HW_ERR, CSR_MSIX_HW_INT_MASK_AD, 0x2D}, + {MSIX_HW_INT_CAUSES_REG_HAP, CSR_MSIX_HW_INT_MASK_AD, 0x2E}, +}; + +static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie) +{ + u32 val, max_rx_vector, i; + struct iwl_trans *trans = trans_pcie->trans; + + max_rx_vector = trans_pcie->allocated_vector - 1; + + if (!trans_pcie->msix_enabled) + return; + + iwl_write_prph(trans, UREG_CHICK, UREG_CHICK_MSIX_ENABLE); + + /* + * Each cause from the list above and the RX causes is represented as + * a byte in the IVAR table. We access the first (N - 1) bytes and map + * them to the (N - 1) vectors so these vectors will be used as rx + * vectors. Then access all non rx causes and map them to the + * default queue (N'th queue). + */ + for (i = 0; i < max_rx_vector; i++) { + iwl_write8(trans, CSR_MSIX_RX_IVAR(i), MSIX_FH_INT_CAUSES_Q(i)); + iwl_clear_bit(trans, CSR_MSIX_FH_INT_MASK_AD, + BIT(MSIX_FH_INT_CAUSES_Q(i))); + } + + for (i = 0; i < ARRAY_SIZE(causes_list); i++) { + val = trans_pcie->default_irq_num | + MSIX_NON_AUTO_CLEAR_CAUSE; + iwl_write8(trans, CSR_MSIX_IVAR(causes_list[i].addr), val); + iwl_clear_bit(trans, causes_list[i].mask_reg, + causes_list[i].cause_num); + } + trans_pcie->fh_init_mask = + ~iwl_read32(trans, CSR_MSIX_FH_INT_MASK_AD); + trans_pcie->fh_mask = trans_pcie->fh_init_mask; + trans_pcie->hw_init_mask = + ~iwl_read32(trans, CSR_MSIX_HW_INT_MASK_AD); + trans_pcie->hw_mask = trans_pcie->hw_init_mask; +} + +static void iwl_pcie_set_interrupt_capa(struct pci_dev *pdev, + struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u16 pci_cmd; + int max_vector; + int ret, i; + + if (trans->cfg->mq_rx_supported) { + max_vector = min_t(u32, (num_possible_cpus() + 1), + IWL_MAX_RX_HW_QUEUES); + for (i = 0; i < max_vector; i++) + trans_pcie->msix_entries[i].entry = i; + + ret = pci_enable_msix_range(pdev, trans_pcie->msix_entries, + MSIX_MIN_INTERRUPT_VECTORS, + max_vector); + if (ret > 1) { + IWL_DEBUG_INFO(trans, + "Enable MSI-X allocate %d interrupt vector\n", + ret); + trans_pcie->allocated_vector = ret; + trans_pcie->default_irq_num = + trans_pcie->allocated_vector - 1; + trans_pcie->trans->num_rx_queues = + trans_pcie->allocated_vector - 1; + trans_pcie->msix_enabled = true; + + return; + } + IWL_DEBUG_INFO(trans, + "ret = %d %s move to msi mode\n", ret, + (ret == 1) ? + "can't allocate more than 1 interrupt vector" : + "failed to enable msi-x mode"); + pci_disable_msix(pdev); + } + + ret = pci_enable_msi(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret); + /* enable rfkill interrupt: hw bug w/a */ + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); + } + } +} + +static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, + struct iwl_trans_pcie *trans_pcie) +{ + int i, last_vector; + + last_vector = trans_pcie->trans->num_rx_queues; + + for (i = 0; i < trans_pcie->allocated_vector; i++) { + int ret; + + ret = request_threaded_irq(trans_pcie->msix_entries[i].vector, + iwl_pcie_msix_isr, + (i == last_vector) ? + iwl_pcie_irq_msix_handler : + iwl_pcie_irq_rx_msix_handler, + IRQF_SHARED, + DRV_NAME, + &trans_pcie->msix_entries[i]); + if (ret) { + int j; + + IWL_ERR(trans_pcie->trans, + "Error allocating IRQ %d\n", i); + for (j = 0; j < i; j++) + free_irq(trans_pcie->msix_entries[i].vector, + &trans_pcie->msix_entries[i]); + pci_disable_msix(pdev); + return ret; + } + } + + return 0; +} + static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1369,6 +1530,7 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) iwl_pcie_apm_init(trans); + iwl_pcie_init_msix(trans_pcie); /* From now on, the op_mode will be kept updated about RF kill state */ iwl_enable_rfkill_int(trans); @@ -1383,6 +1545,10 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power) /* ... rfkill can call stop_device and set it false if needed */ iwl_trans_pcie_rf_kill(trans, hw_rfkill); + /* Make sure we sync here, because we'll need full access later */ + if (low_power) + pm_runtime_resume(trans->dev); + return 0; } @@ -1419,7 +1585,7 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) mutex_unlock(&trans_pcie->mutex); - synchronize_irq(trans_pcie->pci_dev->irq); + iwl_pcie_synchronize_irqs(trans); } static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) @@ -1452,12 +1618,6 @@ static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); } -static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) -{ - WARN_ON(1); - return 0; -} - static void iwl_trans_pcie_configure(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg) { @@ -1486,19 +1646,13 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, trans->command_groups = trans_cfg->command_groups; trans->command_groups_size = trans_cfg->command_groups_size; - /* init ref_count to 1 (should be cleared when ucode is loaded) */ - trans_pcie->ref_count = 1; - /* Initialize NAPI here - it should be before registering to mac80211 * in the opmode but after the HW struct is allocated. * As this function may be called again in some corner cases don't * do anything if NAPI was already initialized. */ - if (!trans_pcie->napi.poll) { + if (trans_pcie->napi_dev.reg_state != NETREG_DUMMY) init_dummy_netdev(&trans_pcie->napi_dev); - netif_napi_add(&trans_pcie->napi_dev, &trans_pcie->napi, - iwl_pcie_dummy_napi_poll, 64); - } } void iwl_trans_pcie_free(struct iwl_trans *trans) @@ -1506,22 +1660,29 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int i; - synchronize_irq(trans_pcie->pci_dev->irq); + iwl_pcie_synchronize_irqs(trans); iwl_pcie_tx_free(trans); iwl_pcie_rx_free(trans); - free_irq(trans_pcie->pci_dev->irq, trans); - iwl_pcie_free_ict(trans); + if (trans_pcie->msix_enabled) { + for (i = 0; i < trans_pcie->allocated_vector; i++) + free_irq(trans_pcie->msix_entries[i].vector, + &trans_pcie->msix_entries[i]); + + pci_disable_msix(trans_pcie->pci_dev); + trans_pcie->msix_enabled = false; + } else { + free_irq(trans_pcie->pci_dev->irq, trans); + + iwl_pcie_free_ict(trans); - pci_disable_msi(trans_pcie->pci_dev); + pci_disable_msi(trans_pcie->pci_dev); + } iounmap(trans_pcie->hw_base); pci_release_regions(trans_pcie->pci_dev); pci_disable_device(trans_pcie->pci_dev); - if (trans_pcie->napi.poll) - netif_napi_del(&trans_pcie->napi); - iwl_pcie_free_fw_monitor(trans); for_each_possible_cpu(i) { @@ -1861,6 +2022,7 @@ void iwl_trans_pcie_ref(struct iwl_trans *trans) spin_lock_irqsave(&trans_pcie->ref_lock, flags); IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); trans_pcie->ref_count++; + pm_runtime_get(&trans_pcie->pci_dev->dev); spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); } @@ -1879,6 +2041,10 @@ void iwl_trans_pcie_unref(struct iwl_trans *trans) return; } trans_pcie->ref_count--; + + pm_runtime_mark_last_busy(&trans_pcie->pci_dev->dev); + pm_runtime_put_autosuspend(&trans_pcie->pci_dev->dev); + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); } @@ -2031,29 +2197,48 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rxq *rxq = &trans_pcie->rxq; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", - rxq->read); - pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", - rxq->write); - pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n", - rxq->write_actual); - pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n", - rxq->need_update); - pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", - rxq->free_count); - if (rxq->rb_stts) { - pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", - le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); - } else { - pos += scnprintf(buf + pos, bufsz - pos, - "closed_rb_num: Not Allocated\n"); + char *buf; + int pos = 0, i, ret; + size_t bufsz = sizeof(buf); + + bufsz = sizeof(char) * 121 * trans->num_rx_queues; + + if (!trans_pcie->rxq) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < trans->num_rx_queues && pos < bufsz; i++) { + struct iwl_rxq *rxq = &trans_pcie->rxq[i]; + + pos += scnprintf(buf + pos, bufsz - pos, "queue#: %2d\n", + i); + pos += scnprintf(buf + pos, bufsz - pos, "\tread: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "\twrite: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "\twrite_actual: %u\n", + rxq->write_actual); + pos += scnprintf(buf + pos, bufsz - pos, "\tneed_update: %2d\n", + rxq->need_update); + pos += scnprintf(buf + pos, bufsz - pos, "\tfree_count: %u\n", + rxq->free_count); + if (rxq->rb_stts) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tclosed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & + 0x0FFF); + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "\tclosed_rb_num: Not Allocated\n"); + } } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + + return ret; } static ssize_t iwl_dbgfs_interrupt_read(struct file *file, @@ -2218,7 +2403,8 @@ static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int max_len = PAGE_SIZE << trans_pcie->rx_page_order; - struct iwl_rxq *rxq = &trans_pcie->rxq; + /* Dump RBs is supported only for pre-9000 devices (1 queue) */ + struct iwl_rxq *rxq = &trans_pcie->rxq[0]; u32 i, r, j, rb_len = 0; spin_lock(&rxq->lock); @@ -2413,7 +2599,8 @@ static struct iwl_trans_dump_data u32 len, num_rbs; u32 monitor_len; int i, ptr; - bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status); + bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && + !trans->cfg->mq_rx_supported; /* transport dump header */ len = sizeof(*dump_data); @@ -2468,11 +2655,12 @@ static struct iwl_trans_dump_data len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND); if (dump_rbs) { + /* Dump RBs is supported only for pre-9000 devices (1 queue) */ + struct iwl_rxq *rxq = &trans_pcie->rxq[0]; /* RBs */ - num_rbs = le16_to_cpu(ACCESS_ONCE( - trans_pcie->rxq.rb_stts->closed_rb_num)) + num_rbs = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; - num_rbs = (num_rbs - trans_pcie->rxq.read) & RX_QUEUE_MASK; + num_rbs = (num_rbs - rxq->read) & RX_QUEUE_MASK; len += num_rbs * (sizeof(*data) + sizeof(struct iwl_fw_error_dump_rb) + (PAGE_SIZE << trans_pcie->rx_page_order)); @@ -2523,6 +2711,22 @@ static struct iwl_trans_dump_data return dump_data; } +#ifdef CONFIG_PM_SLEEP +static int iwl_trans_pcie_suspend(struct iwl_trans *trans) +{ + if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3) + return iwl_pci_fw_enter_d0i3(trans); + + return 0; +} + +static void iwl_trans_pcie_resume(struct iwl_trans *trans) +{ + if (trans->runtime_pm_mode == IWL_PLAT_PM_MODE_D0I3) + iwl_pci_fw_exit_d0i3(trans); +} +#endif /* CONFIG_PM_SLEEP */ + static const struct iwl_trans_ops trans_ops_pcie = { .start_hw = iwl_trans_pcie_start_hw, .op_mode_leave = iwl_trans_pcie_op_mode_leave, @@ -2533,6 +2737,11 @@ static const struct iwl_trans_ops trans_ops_pcie = { .d3_suspend = iwl_trans_pcie_d3_suspend, .d3_resume = iwl_trans_pcie_d3_resume, +#ifdef CONFIG_PM_SLEEP + .suspend = iwl_trans_pcie_suspend, + .resume = iwl_trans_pcie_resume, +#endif /* CONFIG_PM_SLEEP */ + .send_cmd = iwl_trans_pcie_send_hcmd, .tx = iwl_trans_pcie_tx, @@ -2570,8 +2779,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, { struct iwl_trans_pcie *trans_pcie; struct iwl_trans *trans; - u16 pci_cmd; - int ret; + int ret, addr_size; trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), &pdev->dev, cfg, &trans_ops_pcie, 0); @@ -2609,11 +2817,17 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, PCIE_LINK_STATE_CLKPM); } + if (cfg->mq_rx_supported) + addr_size = 64; + else + addr_size = 36; + pci_set_master(pdev); - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(addr_size)); if (!ret) - ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); + ret = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(addr_size)); if (ret) { ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!ret) @@ -2647,17 +2861,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans_pcie->pci_dev = pdev; iwl_disable_interrupts(trans); - ret = pci_enable_msi(pdev); - if (ret) { - dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret); - /* enable rfkill interrupt: hw bug w/a */ - pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); - if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { - pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); - } - } - trans->hw_rev = iwl_read32(trans, CSR_HW_REV); /* * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have @@ -2709,6 +2912,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } } + iwl_pcie_set_interrupt_capa(pdev, trans); trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); @@ -2716,19 +2920,31 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, /* Initialize the wait queue for commands */ init_waitqueue_head(&trans_pcie->wait_command_queue); - ret = iwl_pcie_alloc_ict(trans); - if (ret) - goto out_pci_disable_msi; + init_waitqueue_head(&trans_pcie->d0i3_waitq); - ret = request_threaded_irq(pdev->irq, iwl_pcie_isr, - iwl_pcie_irq_handler, - IRQF_SHARED, DRV_NAME, trans); - if (ret) { - IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); - goto out_free_ict; - } + if (trans_pcie->msix_enabled) { + if (iwl_pcie_init_msix_handler(pdev, trans_pcie)) + goto out_pci_release_regions; + } else { + ret = iwl_pcie_alloc_ict(trans); + if (ret) + goto out_pci_disable_msi; - trans_pcie->inta_mask = CSR_INI_SET_MASK; + ret = request_threaded_irq(pdev->irq, iwl_pcie_isr, + iwl_pcie_irq_handler, + IRQF_SHARED, DRV_NAME, trans); + if (ret) { + IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq); + goto out_free_ict; + } + trans_pcie->inta_mask = CSR_INI_SET_MASK; + } + +#ifdef CONFIG_IWLWIFI_PCIE_RTPM + trans->runtime_pm_mode = IWL_PLAT_PM_MODE_D0I3; +#else + trans->runtime_pm_mode = IWL_PLAT_PM_MODE_DISABLED; +#endif /* CONFIG_IWLWIFI_PCIE_RTPM */ return trans; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 5262028b5..16ad820ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1,7 +1,8 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -33,7 +34,6 @@ #include <linux/sched.h> #include <net/ip6_checksum.h> #include <net/tso.h> -#include <net/ip6_checksum.h> #include "iwl-debug.h" #include "iwl-csr.h" @@ -571,6 +571,7 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, return ret; spin_lock_init(&txq->lock); + __skb_queue_head_init(&txq->overflow_q); /* * Tell nic where to find circular buffer of Tx Frame Descriptors for @@ -621,6 +622,13 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); } txq->active = false; + + while (!skb_queue_empty(&txq->overflow_q)) { + struct sk_buff *skb = __skb_dequeue(&txq->overflow_q); + + iwl_op_mode_free_skb(trans->op_mode, skb); + } + spin_unlock_bh(&txq->lock); /* just in case - this queue may have been stopped */ @@ -1052,8 +1060,41 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, iwl_pcie_txq_progress(txq); - if (iwl_queue_space(&txq->q) > txq->q.low_mark) - iwl_wake_queue(trans, txq); + if (iwl_queue_space(&txq->q) > txq->q.low_mark && + test_bit(txq_id, trans_pcie->queue_stopped)) { + struct sk_buff_head overflow_skbs; + + __skb_queue_head_init(&overflow_skbs); + skb_queue_splice_init(&txq->overflow_q, &overflow_skbs); + + /* + * This is tricky: we are in reclaim path which is non + * re-entrant, so noone will try to take the access the + * txq data from that path. We stopped tx, so we can't + * have tx as well. Bottom line, we can unlock and re-lock + * later. + */ + spin_unlock_bh(&txq->lock); + + while (!skb_queue_empty(&overflow_skbs)) { + struct sk_buff *skb = __skb_dequeue(&overflow_skbs); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 dev_cmd_idx = IWL_TRANS_FIRST_DRIVER_DATA + 1; + struct iwl_device_cmd *dev_cmd = + info->driver_data[dev_cmd_idx]; + + /* + * Note that we can very well be overflowing again. + * In that case, iwl_queue_space will be small again + * and we won't wake mac80211's queue. + */ + iwl_trans_pcie_tx(trans, skb, dev_cmd, txq_id); + } + spin_lock_bh(&txq->lock); + + if (iwl_queue_space(&txq->q) > txq->q.low_mark) + iwl_wake_queue(trans, txq); + } if (q->read_ptr == q->write_ptr) { IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id); @@ -1686,6 +1727,20 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, wake_up(&trans_pcie->wait_command_queue); } + if (meta->flags & CMD_MAKE_TRANS_IDLE) { + IWL_DEBUG_INFO(trans, "complete %s - mark trans as idle\n", + iwl_get_cmd_string(trans, cmd->hdr.cmd)); + set_bit(STATUS_TRANS_IDLE, &trans->status); + wake_up(&trans_pcie->d0i3_waitq); + } + + if (meta->flags & CMD_WAKE_UP_TRANS) { + IWL_DEBUG_INFO(trans, "complete %s - clear trans idle flag\n", + iwl_get_cmd_string(trans, cmd->hdr.cmd)); + clear_bit(STATUS_TRANS_IDLE, &trans->status); + wake_up(&trans_pcie->d0i3_waitq); + } + meta->flags = 0; spin_unlock_bh(&txq->lock); @@ -2161,6 +2216,8 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, csum = skb_checksum(skb, offs, skb->len - offs, 0); *(__sum16 *)(skb->data + csum_offs) = csum_fold(csum); + + skb->ip_summed = CHECKSUM_UNNECESSARY; } if (skb_is_nonlinear(skb) && @@ -2177,6 +2234,22 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, spin_lock(&txq->lock); + if (iwl_queue_space(q) < q->high_mark) { + iwl_stop_queue(trans, txq); + + /* don't put the packet on the ring, if there is no room */ + if (unlikely(iwl_queue_space(q) < 3)) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + info->driver_data[IWL_TRANS_FIRST_DRIVER_DATA + 1] = + dev_cmd; + __skb_queue_tail(&txq->overflow_q, skb); + + spin_unlock(&txq->lock); + return 0; + } + } + /* In AGG mode, the index in the ring must correspond to the WiFi * sequence number. This is a HW requirements to help the SCD to parse * the BA. @@ -2281,12 +2354,6 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, * At this point the frame is "transmitted" successfully * and we will get a TX status notification eventually. */ - if (iwl_queue_space(q) < q->high_mark) { - if (wait_write_ptr) - iwl_pcie_txq_inc_wr_ptr(trans, txq); - else - iwl_stop_queue(trans, txq); - } spin_unlock(&txq->lock); return 0; out_err: diff --git a/drivers/net/wireless/intersil/hostap/hostap_hw.c b/drivers/net/wireless/intersil/hostap/hostap_hw.c index 6df3ee561..515aa3f99 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_hw.c +++ b/drivers/net/wireless/intersil/hostap/hostap_hw.c @@ -836,25 +836,30 @@ static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, spin_lock_bh(&local->baplock); res = hfa384x_setup_bap(dev, BAP0, rid, 0); - if (!res) - res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + if (res) + goto unlock; + + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + if (res) + goto unlock; if (le16_to_cpu(rec.len) == 0) { /* RID not available */ res = -ENODATA; + goto unlock; } rlen = (le16_to_cpu(rec.len) - 1) * 2; - if (!res && exact_len && rlen != len) { + if (exact_len && rlen != len) { printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " "rid=0x%04x, len=%d (expected %d)\n", dev->name, rid, rlen, len); res = -ENODATA; } - if (!res) - res = hfa384x_from_bap(dev, BAP0, buf, len); + res = hfa384x_from_bap(dev, BAP0, buf, len); +unlock: spin_unlock_bh(&local->baplock); mutex_unlock(&local->rid_bap_mtx); diff --git a/drivers/net/wireless/intersil/orinoco/mic.c b/drivers/net/wireless/intersil/orinoco/mic.c index fce4a843e..bc7397d70 100644 --- a/drivers/net/wireless/intersil/orinoco/mic.c +++ b/drivers/net/wireless/intersil/orinoco/mic.c @@ -6,7 +6,7 @@ #include <linux/string.h> #include <linux/if_ether.h> #include <linux/scatterlist.h> -#include <linux/crypto.h> +#include <crypto/hash.h> #include "orinoco.h" #include "mic.h" @@ -16,7 +16,8 @@ /********************************************************************/ int orinoco_mic_init(struct orinoco_private *priv) { - priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); + priv->tx_tfm_mic = crypto_alloc_ahash("michael_mic", 0, + CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_mic)) { printk(KERN_DEBUG "orinoco_mic_init: could not allocate " "crypto API michael_mic\n"); @@ -24,7 +25,8 @@ int orinoco_mic_init(struct orinoco_private *priv) return -ENOMEM; } - priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0); + priv->rx_tfm_mic = crypto_alloc_ahash("michael_mic", 0, + CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_mic)) { printk(KERN_DEBUG "orinoco_mic_init: could not allocate " "crypto API michael_mic\n"); @@ -38,18 +40,19 @@ int orinoco_mic_init(struct orinoco_private *priv) void orinoco_mic_free(struct orinoco_private *priv) { if (priv->tx_tfm_mic) - crypto_free_hash(priv->tx_tfm_mic); + crypto_free_ahash(priv->tx_tfm_mic); if (priv->rx_tfm_mic) - crypto_free_hash(priv->rx_tfm_mic); + crypto_free_ahash(priv->rx_tfm_mic); } -int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, +int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key, u8 *da, u8 *sa, u8 priority, u8 *data, size_t data_len, u8 *mic) { - struct hash_desc desc; + AHASH_REQUEST_ON_STACK(req, tfm_michael); struct scatterlist sg[2]; u8 hdr[ETH_HLEN + 2]; /* size of header + padding */ + int err; if (tfm_michael == NULL) { printk(KERN_WARNING "orinoco_mic: tfm_michael == NULL\n"); @@ -69,11 +72,13 @@ int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, sg_set_buf(&sg[0], hdr, sizeof(hdr)); sg_set_buf(&sg[1], data, data_len); - if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN)) + if (crypto_ahash_setkey(tfm_michael, key, MIC_KEYLEN)) return -1; - desc.tfm = tfm_michael; - desc.flags = 0; - return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr), - mic); + ahash_request_set_tfm(req, tfm_michael); + ahash_request_set_callback(req, 0, NULL, NULL); + ahash_request_set_crypt(req, sg, mic, data_len + sizeof(hdr)); + err = crypto_ahash_digest(req); + ahash_request_zero(req); + return err; } diff --git a/drivers/net/wireless/intersil/orinoco/mic.h b/drivers/net/wireless/intersil/orinoco/mic.h index 04d05bc56..ce731d05c 100644 --- a/drivers/net/wireless/intersil/orinoco/mic.h +++ b/drivers/net/wireless/intersil/orinoco/mic.h @@ -11,11 +11,11 @@ /* Forward declarations */ struct orinoco_private; -struct crypto_hash; +struct crypto_ahash; int orinoco_mic_init(struct orinoco_private *priv); void orinoco_mic_free(struct orinoco_private *priv); -int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key, +int orinoco_mic(struct crypto_ahash *tfm_michael, u8 *key, u8 *da, u8 *sa, u8 priority, u8 *data, size_t data_len, u8 *mic); diff --git a/drivers/net/wireless/intersil/orinoco/orinoco.h b/drivers/net/wireless/intersil/orinoco/orinoco.h index eebd2be21..2f0c84b1c 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco.h +++ b/drivers/net/wireless/intersil/orinoco/orinoco.h @@ -152,8 +152,8 @@ struct orinoco_private { u8 *wpa_ie; int wpa_ie_len; - struct crypto_hash *rx_tfm_mic; - struct crypto_hash *tx_tfm_mic; + struct crypto_ahash *rx_tfm_mic; + struct crypto_ahash *tx_tfm_mic; unsigned int wpa_enabled:1; unsigned int tkip_cm_active:1; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a28414c50..e85e07377 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -844,7 +844,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, hdr->rt_chbitmask = cpu_to_le16(flags); skb->dev = hwsim_mon; - skb_set_mac_header(skb, 0); + skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); @@ -887,7 +887,7 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, memcpy(hdr11->addr1, addr, ETH_ALEN); skb->dev = hwsim_mon; - skb_set_mac_header(skb, 0); + skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); @@ -1334,10 +1334,8 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, data->tx_bytes += skb->len; ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel); - if (ack && skb->len >= 16) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + if (ack && skb->len >= 16) mac80211_hwsim_monitor_ack(channel, hdr->addr2); - } ieee80211_tx_info_clear_status(txi); @@ -1846,10 +1844,12 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + switch (action) { case IEEE80211_AMPDU_TX_START: ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c index 86955c416..2eea76a34 100644 --- a/drivers/net/wireless/marvell/libertas/cfg.c +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -2039,6 +2039,43 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +int lbs_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, int timeout) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + if (!(priv->fwcapinfo & FW_CAPINFO_PS)) { + if (!enabled) + return 0; + else + return -EINVAL; + } + /* firmware does not work well with too long latency with power saving + * enabled, so do not enable it if there is only polling, no + * interrupts (like in some sdio hosts which can only + * poll for sdio irqs) + */ + if (priv->is_polling) { + if (!enabled) + return 0; + else + return -EINVAL; + } + if (!enabled) { + priv->psmode = LBS802_11POWERMODECAM; + if (priv->psstate != PS_STATE_FULL_POWER) + lbs_set_ps_mode(priv, + PS_MODE_ACTION_EXIT_PS, + true); + return 0; + } + if (priv->psmode != LBS802_11POWERMODECAM) + return 0; + priv->psmode = LBS802_11POWERMODEMAX_PSP; + if (priv->connect_status == LBS_CONNECTED) + lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, true); + return 0; +} /* * Initialization @@ -2057,6 +2094,7 @@ static struct cfg80211_ops lbs_cfg80211_ops = { .change_virtual_intf = lbs_change_intf, .join_ibss = lbs_join_ibss, .leave_ibss = lbs_leave_ibss, + .set_power_mgmt = lbs_set_power_mgmt, }; diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c index 0387a5b38..4ddd0e5a6 100644 --- a/drivers/net/wireless/marvell/libertas/cmd.c +++ b/drivers/net/wireless/marvell/libertas/cmd.c @@ -957,7 +957,7 @@ static void lbs_queue_cmd(struct lbs_private *priv, /* Exit_PS command needs to be queued in the header always. */ if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { - struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; + struct cmd_ds_802_11_ps_mode *psm = (void *)cmdnode->cmdbuf; if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { if (priv->psstate != PS_STATE_FULL_POWER) @@ -1387,7 +1387,7 @@ int lbs_execute_next_command(struct lbs_private *priv) * PS command. Ignore it if it is not Exit_PS. * otherwise send it down immediately. */ - struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1]; + struct cmd_ds_802_11_ps_mode *psm = (void *)cmd; lbs_deb_host( "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", @@ -1428,40 +1428,14 @@ int lbs_execute_next_command(struct lbs_private *priv) * check if in power save mode, if yes, put the device back * to PS mode */ -#ifdef TODO - /* - * This was the old code for libertas+wext. Someone that - * understands this beast should re-code it in a sane way. - * - * I actually don't understand why this is related to WPA - * and to connection status, shouldn't powering should be - * independ of such things? - */ if ((priv->psmode != LBS802_11POWERMODECAM) && (priv->psstate == PS_STATE_FULL_POWER) && - ((priv->connect_status == LBS_CONNECTED) || - lbs_mesh_connected(priv))) { - if (priv->secinfo.WPAenabled || - priv->secinfo.WPA2enabled) { - /* check for valid WPA group keys */ - if (priv->wpa_mcast_key.len || - priv->wpa_unicast_key.len) { - lbs_deb_host( - "EXEC_NEXT_CMD: WPA enabled and GTK_SET" - " go back to PS_SLEEP"); - lbs_set_ps_mode(priv, - PS_MODE_ACTION_ENTER_PS, - false); - } - } else { - lbs_deb_host( - "EXEC_NEXT_CMD: cmdpendingq empty, " - "go back to PS_SLEEP"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, - false); - } + (priv->connect_status == LBS_CONNECTED)) { + lbs_deb_host( + "EXEC_NEXT_CMD: cmdpendingq empty, go back to PS_SLEEP"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, + false); } -#endif } ret = 0; diff --git a/drivers/net/wireless/marvell/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c index e5442e895..c95bf6dc9 100644 --- a/drivers/net/wireless/marvell/libertas/cmdresp.c +++ b/drivers/net/wireless/marvell/libertas/cmdresp.c @@ -123,7 +123,10 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) priv->cmd_timed_out = 0; if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { - struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; + /* struct cmd_ds_802_11_ps_mode also contains + * the header + */ + struct cmd_ds_802_11_ps_mode *psmode = (void *)resp; u16 action = le16_to_cpu(psmode->action); lbs_deb_host( @@ -254,6 +257,10 @@ int lbs_process_event(struct lbs_private *priv, u32 event) "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); break; } + if (!list_empty(&priv->cmdpendingq)) { + lbs_deb_cmd("EVENT: commands in queue, do not sleep\n"); + break; + } priv->psstate = PS_STATE_PRE_SLEEP; lbs_ps_confirm_sleep(priv); diff --git a/drivers/net/wireless/marvell/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h index 6bd160899..edf710bc5 100644 --- a/drivers/net/wireless/marvell/libertas/dev.h +++ b/drivers/net/wireless/marvell/libertas/dev.h @@ -99,6 +99,7 @@ struct lbs_private { /* Hardware access */ void *card; bool iface_running; + u8 is_polling; /* host has to poll the card irq */ u8 fw_ready; u8 surpriseremoved; u8 setup_fw_on_resume; diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c index 2502a1e79..5b6cf1a7b 100644 --- a/drivers/net/wireless/marvell/libertas/if_sdio.c +++ b/drivers/net/wireless/marvell/libertas/if_sdio.c @@ -1254,7 +1254,7 @@ static int if_sdio_probe(struct sdio_func *func, priv->reset_card = if_sdio_reset_card; priv->power_save = if_sdio_power_save; priv->power_restore = if_sdio_power_restore; - + priv->is_polling = !(func->card->host->caps & MMC_CAP_SDIO_IRQ); ret = if_sdio_power_on(card); if (ret) goto err_activate_card; diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index 23b0afda6..f1f8c7330 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -263,6 +263,7 @@ static int if_usb_probe(struct usb_interface *intf, priv->enter_deep_sleep = NULL; priv->exit_deep_sleep = NULL; priv->reset_deep_sleep_wakeup = NULL; + priv->is_polling = false; #ifdef CONFIG_OLPC if (machine_is_olpc()) priv->reset_card = if_usb_reset_olpc_card; diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c index 8079560f4..8541cbed7 100644 --- a/drivers/net/wireless/marvell/libertas/main.c +++ b/drivers/net/wireless/marvell/libertas/main.c @@ -1060,7 +1060,12 @@ void lbs_remove_card(struct lbs_private *priv) if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { priv->psmode = LBS802_11POWERMODECAM; - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); + /* no need to wakeup if already woken up, + * on suspend, this exit ps command is not processed + * the driver hangs + */ + if (priv->psstate != PS_STATE_FULL_POWER) + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); } if (priv->is_deep_sleep) { @@ -1113,7 +1118,8 @@ int lbs_start_card(struct lbs_private *priv) else pr_info("%s: mesh disabled\n", dev->name); - if (lbs_cfg_register(priv)) { + ret = lbs_cfg_register(priv); + if (ret) { pr_err("cannot register device\n"); goto done; } diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c index 71a1b5807..81c60d0a1 100644 --- a/drivers/net/wireless/marvell/mwifiex/11h.c +++ b/drivers/net/wireless/marvell/mwifiex/11h.c @@ -123,8 +123,7 @@ void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, void mwifiex_dfs_cac_work_queue(struct work_struct *work) { struct cfg80211_chan_def chandef; - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); + struct delayed_work *delayed_work = to_delayed_work(work); struct mwifiex_private *priv = container_of(delayed_work, struct mwifiex_private, dfs_cac_work); @@ -289,8 +288,7 @@ int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) { struct mwifiex_uap_bss_param *bss_cfg; - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); + struct delayed_work *delayed_work = to_delayed_work(work); struct mwifiex_private *priv = container_of(delayed_work, struct mwifiex_private, dfs_chan_sw_work); diff --git a/drivers/net/wireless/marvell/mwifiex/README b/drivers/net/wireless/marvell/mwifiex/README index 8c7f733d4..d31e089fe 100644 --- a/drivers/net/wireless/marvell/mwifiex/README +++ b/drivers/net/wireless/marvell/mwifiex/README @@ -236,4 +236,14 @@ device_dump cat fw_dump +verext + This command is used to get extended firmware version string using + different configuration parameters. + + Usage: + echo "[version_str_sel]" > verext + cat verext + + [version_str_sel]: firmware support several extend version + string cases, include 0/1/10/20/21/99 =============================================================================== diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index e7adef72c..bb7235e1b 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -20,6 +20,7 @@ #include "cfg80211.h" #include "main.h" #include "11n.h" +#include "wmm.h" static char *reg_alpha2; module_param(reg_alpha2, charp, 0); @@ -676,7 +677,7 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) } break; - case MWIFIEX_BSS_ROLE_STA: + case MWIFIEX_BSS_ROLE_STA: if (priv->media_connected) { mwifiex_dbg(adapter, ERROR, "cannot change wiphy params when connected"); @@ -1962,6 +1963,9 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + if (!mwifiex_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy); + if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; @@ -2217,6 +2221,9 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, "info: Trying to associate to %s and bssid %pM\n", (char *)sme->ssid, sme->bssid); + if (!mwifiex_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy); + ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); if (!ret) { @@ -2420,6 +2427,9 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, return -EBUSY; } + if (!mwifiex_stop_bg_scan(priv)) + cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy); + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); if (!user_scan_cfg) return -ENOMEM; @@ -2487,6 +2497,125 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, return 0; } +/* CFG802.11 operation handler for sched_scan_start. + * + * This function issues a bgscan config request to the firmware based upon + * the user specified sched_scan configuration. On successful completion, + * firmware will generate BGSCAN_REPORT event, driver should issue bgscan + * query command to get sched_scan results from firmware. + */ +static int +mwifiex_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int i, offset; + struct ieee80211_channel *chan; + struct mwifiex_bg_scan_cfg *bgscan_cfg; + struct ieee_types_header *ie; + + if (!request || (!request->n_ssids && !request->n_match_sets)) { + wiphy_err(wiphy, "%s : Invalid Sched_scan parameters", + __func__); + return -EINVAL; + } + + wiphy_info(wiphy, "sched_scan start : n_ssids=%d n_match_sets=%d ", + request->n_ssids, request->n_match_sets); + wiphy_info(wiphy, "n_channels=%d interval=%d ie_len=%d\n", + request->n_channels, request->scan_plans->interval, + (int)request->ie_len); + + bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); + if (!bgscan_cfg) + return -ENOMEM; + + if (priv->scan_request || priv->scan_aborting) + bgscan_cfg->start_later = true; + + bgscan_cfg->num_ssids = request->n_match_sets; + bgscan_cfg->ssid_list = request->match_sets; + + if (request->ie && request->ie_len) { + offset = 0; + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_BGSCAN; + ie = (struct ieee_types_header *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len); + offset += sizeof(*ie) + ie->len; + + if (offset >= request->ie_len) + break; + } + } + + for (i = 0; i < min_t(u32, request->n_channels, + MWIFIEX_BG_SCAN_CHAN_MAX); i++) { + chan = request->channels[i]; + bgscan_cfg->chan_list[i].chan_number = chan->hw_value; + bgscan_cfg->chan_list[i].radio_type = chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + bgscan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_PASSIVE; + else + bgscan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_ACTIVE; + + bgscan_cfg->chan_list[i].scan_time = 0; + } + + bgscan_cfg->chan_per_scan = min_t(u32, request->n_channels, + MWIFIEX_BG_SCAN_CHAN_MAX); + + /* Use at least 15 second for per scan cycle */ + bgscan_cfg->scan_interval = (request->scan_plans->interval > + MWIFIEX_BGSCAN_INTERVAL) ? + request->scan_plans->interval : + MWIFIEX_BGSCAN_INTERVAL; + + bgscan_cfg->repeat_count = MWIFIEX_BGSCAN_REPEAT_COUNT; + bgscan_cfg->report_condition = MWIFIEX_BGSCAN_SSID_MATCH | + MWIFIEX_BGSCAN_WAIT_ALL_CHAN_DONE; + bgscan_cfg->bss_type = MWIFIEX_BSS_MODE_INFRA; + bgscan_cfg->action = MWIFIEX_BGSCAN_ACT_SET; + bgscan_cfg->enable = true; + if (request->min_rssi_thold != NL80211_SCAN_RSSI_THOLD_OFF) { + bgscan_cfg->report_condition |= MWIFIEX_BGSCAN_SSID_RSSI_MATCH; + bgscan_cfg->rssi_threshold = request->min_rssi_thold; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_CONFIG, + HostCmd_ACT_GEN_SET, 0, bgscan_cfg, true)) { + kfree(bgscan_cfg); + return -EFAULT; + } + + priv->sched_scanning = true; + + kfree(bgscan_cfg); + return 0; +} + +/* CFG802.11 operation handler for sched_scan_stop. + * + * This function issues a bgscan config command to disable + * previous bgscan configuration in the firmware + */ +static int mwifiex_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + wiphy_info(wiphy, "sched scan stop!"); + mwifiex_stop_bg_scan(priv); + + return 0; +} + static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info, struct mwifiex_private *priv) { @@ -2848,6 +2977,9 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) mwifiex_dev_debugfs_remove(priv); #endif + if (priv->sched_scanning) + priv->sched_scanning = false; + mwifiex_stop_net_dev_queue(priv->netdev, adapter); skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) @@ -3044,10 +3176,12 @@ static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, sizeof(byte_seq)); mef_entry->filter[filt_num].filt_type = TYPE_EQ; - if (first_pat) + if (first_pat) { first_pat = false; - else + mwifiex_dbg(priv->adapter, INFO, "Wake on patterns\n"); + } else { mef_entry->filter[filt_num].filt_action = TYPE_AND; + } filt_num++; } @@ -3073,6 +3207,7 @@ static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, mef_entry->filter[filt_num].offset = 56; mef_entry->filter[filt_num].filt_type = TYPE_EQ; mef_entry->filter[filt_num].filt_action = TYPE_OR; + mwifiex_dbg(priv->adapter, INFO, "Wake on magic packet\n"); } return ret; } @@ -3125,7 +3260,7 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_ds_hs_cfg hs_cfg; - int i, ret = 0; + int i, ret = 0, retry_num = 10; struct mwifiex_private *priv; for (i = 0; i < adapter->priv_num; i++) { @@ -3135,6 +3270,21 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mwifiex_cancel_all_pending_cmd(adapter); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv && priv->netdev) + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + } + + for (i = 0; i < retry_num; i++) { + if (!mwifiex_wmm_lists_empty(adapter) || + !mwifiex_bypass_txlist_empty(adapter) || + !skb_queue_empty(&adapter->tx_data_q)) + usleep_range(10000, 15000); + else + break; + } + if (!wowlan) { mwifiex_dbg(adapter, ERROR, "None of the WOWLAN triggers enabled\n"); @@ -3143,7 +3293,7 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - if (!priv->media_connected) { + if (!priv->media_connected && !wowlan->nd_config) { mwifiex_dbg(adapter, ERROR, "Can not configure WOWLAN in disconnected state\n"); return 0; @@ -3155,19 +3305,30 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, return ret; } + memset(&hs_cfg, 0, sizeof(hs_cfg)); + hs_cfg.conditions = le32_to_cpu(adapter->hs_cfg.conditions); + + if (wowlan->nd_config) { + mwifiex_dbg(adapter, INFO, "Wake on net detect\n"); + hs_cfg.conditions |= HS_CFG_COND_MAC_EVENT; + mwifiex_cfg80211_sched_scan_start(wiphy, priv->netdev, + wowlan->nd_config); + } + if (wowlan->disconnect) { - memset(&hs_cfg, 0, sizeof(hs_cfg)); - hs_cfg.is_invoke_hostcmd = false; - hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; - hs_cfg.gpio = adapter->hs_cfg.gpio; - hs_cfg.gap = adapter->hs_cfg.gap; - ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - MWIFIEX_SYNC_CMD, &hs_cfg); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Failed to set HS params\n"); - return ret; - } + hs_cfg.conditions |= HS_CFG_COND_MAC_EVENT; + mwifiex_dbg(priv->adapter, INFO, "Wake on device disconnect\n"); + } + + hs_cfg.is_invoke_hostcmd = false; + hs_cfg.gpio = adapter->hs_cfg.gpio; + hs_cfg.gap = adapter->hs_cfg.gap; + ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hs_cfg); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Failed to set HS params\n"); + return ret; } return ret; @@ -3175,6 +3336,70 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, static int mwifiex_cfg80211_resume(struct wiphy *wiphy) { + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_ds_wakeup_reason wakeup_reason; + struct cfg80211_wowlan_wakeup wakeup_report; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv && priv->netdev) + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + } + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + mwifiex_get_wakeup_reason(priv, HostCmd_ACT_GEN_GET, MWIFIEX_SYNC_CMD, + &wakeup_reason); + memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup)); + + wakeup_report.pattern_idx = -1; + + switch (wakeup_reason.hs_wakeup_reason) { + case NO_HSWAKEUP_REASON: + break; + case BCAST_DATA_MATCHED: + break; + case MCAST_DATA_MATCHED: + break; + case UCAST_DATA_MATCHED: + break; + case MASKTABLE_EVENT_MATCHED: + break; + case NON_MASKABLE_EVENT_MATCHED: + if (wiphy->wowlan_config->disconnect) + wakeup_report.disconnect = true; + if (wiphy->wowlan_config->nd_config) + wakeup_report.net_detect = adapter->nd_info; + break; + case NON_MASKABLE_CONDITION_MATCHED: + break; + case MAGIC_PATTERN_MATCHED: + if (wiphy->wowlan_config->magic_pkt) + wakeup_report.magic_pkt = true; + if (wiphy->wowlan_config->n_patterns) + wakeup_report.pattern_idx = 1; + break; + case CONTROL_FRAME_MATCHED: + break; + case MANAGEMENT_FRAME_MATCHED: + break; + default: + break; + } + + if ((wakeup_reason.hs_wakeup_reason > 0) && + (wakeup_reason.hs_wakeup_reason <= 7)) + cfg80211_report_wowlan_wakeup(&priv->wdev, &wakeup_report, + GFP_KERNEL); + + if (adapter->nd_info) { + for (i = 0 ; i < adapter->nd_info->n_matches ; i++) + kfree(adapter->nd_info->matches[i]); + kfree(adapter->nd_info); + adapter->nd_info = NULL; + } + return 0; } @@ -3590,8 +3815,8 @@ static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy, freq = ieee80211_channel_to_frequency(curr_bss->channel, band); chan = ieee80211_get_channel(wiphy, freq); - if (curr_bss->bcn_ht_oper) { - second_chan_offset = curr_bss->bcn_ht_oper->ht_param & + if (priv->ht_param_present) { + second_chan_offset = priv->assoc_resp_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; chan_type = mwifiex_sec_chan_offset_to_chan_type (second_chan_offset); @@ -3701,6 +3926,8 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, .set_antenna = mwifiex_cfg80211_set_antenna, .del_station = mwifiex_cfg80211_del_station, + .sched_scan_start = mwifiex_cfg80211_sched_scan_start, + .sched_scan_stop = mwifiex_cfg80211_sched_scan_stop, #ifdef CONFIG_PM .suspend = mwifiex_cfg80211_suspend, .resume = mwifiex_cfg80211_resume, @@ -3720,11 +3947,13 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { #ifdef CONFIG_PM static const struct wiphy_wowlan_support mwifiex_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_NET_DETECT, .n_patterns = MWIFIEX_MEF_MAX_FILTERS, .pattern_min_len = 1, .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, + .max_nd_match_sets = MWIFIEX_MAX_ND_MATCH_SETS, }; #endif @@ -3829,6 +4058,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_SUPPORTS_SCHED_SCAN | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_HAS_CHANNEL_SWITCH | WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -3847,6 +4077,10 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + wiphy->max_sched_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; + wiphy->max_sched_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; + wiphy->max_match_sets = MWIFIEX_MAX_SSID_LIST_LENGTH; + wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index cb25aa7e9..a12adee77 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -1657,3 +1657,16 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, return 0; } + +/* This function handles the command response of hs wakeup reason + * command. + */ +int mwifiex_ret_wakeup_reason(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_wakeup_reason *wakeup_reason) +{ + wakeup_reason->wakeup_reason = + resp->params.hs_wakeup_reason.wakeup_reason; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c index 0b9c580af..bccf17ad5 100644 --- a/drivers/net/wireless/marvell/mwifiex/debugfs.c +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -95,8 +95,7 @@ mwifiex_info_read(struct file *file, char __user *ubuf, mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); - if (!priv->version_str[0]) - mwifiex_get_ver_ext(priv); + mwifiex_get_ver_ext(priv, 0); p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); p += sprintf(p, "driver_version = %s", fmt); @@ -583,6 +582,52 @@ done: return ret; } +/* debugfs verext file write handler. + * This function is called when the 'verext' file is opened for write + */ +static ssize_t +mwifiex_verext_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + u32 versionstrsel; + struct mwifiex_private *priv = (void *)file->private_data; + char buf[16]; + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + ret = kstrtou32(buf, 10, &versionstrsel); + if (ret) + return ret; + + priv->versionstrsel = versionstrsel; + + return count; +} + +/* Proc verext file read handler. + * This function is called when the 'verext' file is opened for reading + * This function can be used read driver exteneed verion string. + */ +static ssize_t +mwifiex_verext_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + char buf[256]; + int ret; + + mwifiex_get_ver_ext(priv, priv->versionstrsel); + ret = snprintf(buf, sizeof(buf), "version string: %s\n", + priv->version_str); + + return simple_read_from_buffer(ubuf, count, ppos, buf, ret); +} + /* Proc memrw file write handler. * This function is called when the 'memrw' file is opened for writing * This function can be used to write to a memory location. @@ -880,14 +925,12 @@ mwifiex_reset_write(struct file *file, { struct mwifiex_private *priv = file->private_data; struct mwifiex_adapter *adapter = priv->adapter; - char cmd; bool result; + int rc; - if (copy_from_user(&cmd, ubuf, sizeof(cmd))) - return -EFAULT; - - if (strtobool(&cmd, &result)) - return -EINVAL; + rc = kstrtobool_from_user(ubuf, count, &result); + if (rc) + return rc; if (!result) return -EINVAL; @@ -940,6 +983,7 @@ MWIFIEX_DFS_FILE_OPS(histogram); MWIFIEX_DFS_FILE_OPS(debug_mask); MWIFIEX_DFS_FILE_OPS(timeshare_coex); MWIFIEX_DFS_FILE_WRITE_OPS(reset); +MWIFIEX_DFS_FILE_OPS(verext); /* * This function creates the debug FS directory structure and the files. @@ -968,6 +1012,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(debug_mask); MWIFIEX_DFS_ADD_FILE(timeshare_coex); MWIFIEX_DFS_ADD_FILE(reset); + MWIFIEX_DFS_ADD_FILE(verext); } /* diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h index d9c15cd36..bec300b9c 100644 --- a/drivers/net/wireless/marvell/mwifiex/decl.h +++ b/drivers/net/wireless/marvell/mwifiex/decl.h @@ -122,6 +122,8 @@ #define BLOCK_NUMBER_OFFSET 15 #define SDIO_HEADER_OFFSET 28 +#define MWIFIEX_SIZE_4K 0x4000 + enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, @@ -270,4 +272,26 @@ struct mwifiex_11h_intf_state { bool is_11h_enabled; bool is_11h_active; } __packed; + +#define MWIFIEX_FW_DUMP_IDX 0xff +#define MWIFIEX_FW_DUMP_MAX_MEMSIZE 0x160000 +#define MWIFIEX_DRV_INFO_IDX 20 +#define FW_DUMP_MAX_NAME_LEN 8 +#define FW_DUMP_HOST_READY 0xEE +#define FW_DUMP_DONE 0xFF +#define FW_DUMP_READ_DONE 0xFE + +struct memory_type_mapping { + u8 mem_name[FW_DUMP_MAX_NAME_LEN]; + u8 *mem_ptr; + u32 mem_size; + u8 done_flag; +}; + +enum rdwr_status { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +}; + #endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index ced7af2be..c134cf865 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -96,7 +96,7 @@ enum KEY_TYPE_ID { #define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) #define MAX_POLL_TRIES 100 -#define MAX_FIRMWARE_POLL_TRIES 100 +#define MAX_FIRMWARE_POLL_TRIES 150 #define FIRMWARE_READY_SDIO 0xfedc #define FIRMWARE_READY_PCIE 0xfedcba00 @@ -144,6 +144,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) #define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_BGSCAN_START_LATER (PROPRIETARY_TLV_BASE_ID + 30) #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) #define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) #define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) @@ -177,6 +178,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148) #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) #define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) +#define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 176) #define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) #define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) #define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) @@ -331,6 +333,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D #define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b #define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e +#define HostCmd_CMD_802_11_BG_SCAN_CONFIG 0x006b #define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c #define HostCmd_CMD_WMM_GET_STATUS 0x0071 #define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 @@ -370,6 +373,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_MGMT_FRAME_REG 0x010c #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define HostCmd_CMD_11AC_CFG 0x0112 +#define HostCmd_CMD_HS_WAKEUP_REASON 0x0116 #define HostCmd_CMD_TDLS_CONFIG 0x0100 #define HostCmd_CMD_MC_POLICY 0x0121 #define HostCmd_CMD_TDLS_OPER 0x0122 @@ -523,6 +527,7 @@ enum P2P_MODES { #define EVENT_CHANNEL_REPORT_RDY 0x00000054 #define EVENT_TX_DATA_PAUSE 0x00000055 #define EVENT_EXT_SCAN_REPORT 0x00000058 +#define EVENT_BG_SCAN_STOPPED 0x00000065 #define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f #define EVENT_MULTI_CHAN_INFO 0x0000006a #define EVENT_TX_STATUS_REPORT 0x00000074 @@ -539,6 +544,8 @@ enum P2P_MODES { #define MWIFIEX_MAX_PATTERN_LEN 40 #define MWIFIEX_MAX_OFFSET_LEN 100 +#define MWIFIEX_MAX_ND_MATCH_SETS 10 + #define STACK_NBYTES 100 #define TYPE_DNUM 1 #define TYPE_BYTESEQ 2 @@ -601,6 +608,20 @@ struct mwifiex_ie_types_data { #define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 #define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20 +enum HS_WAKEUP_REASON { + NO_HSWAKEUP_REASON = 0, + BCAST_DATA_MATCHED, + MCAST_DATA_MATCHED, + UCAST_DATA_MATCHED, + MASKTABLE_EVENT_MATCHED, + NON_MASKABLE_EVENT_MATCHED, + NON_MASKABLE_CONDITION_MATCHED, + MAGIC_PATTERN_MATCHED, + CONTROL_FRAME_MATCHED, + MANAGEMENT_FRAME_MATCHED, + RESERVED +}; + struct txpd { u8 bss_type; u8 bss_num; @@ -733,6 +754,21 @@ struct mwifiex_ie_types_num_probes { __le16 num_probes; } __packed; +struct mwifiex_ie_types_repeat_count { + struct mwifiex_ie_types_header header; + __le16 repeat_count; +} __packed; + +struct mwifiex_ie_types_min_rssi_threshold { + struct mwifiex_ie_types_header header; + __le16 rssi_threshold; +} __packed; + +struct mwifiex_ie_types_bgscan_start_later { + struct mwifiex_ie_types_header header; + __le16 start_later; +} __packed; + struct mwifiex_ie_types_scan_chan_gap { struct mwifiex_ie_types_header header; /* time gap in TUs to be used between two consecutive channels scan */ @@ -1027,7 +1063,7 @@ struct ieee_types_assoc_rsp { __le16 cap_info_bitmap; __le16 status_code; __le16 a_id; - u8 ie_buffer[1]; + u8 ie_buffer[0]; } __packed; struct host_cmd_ds_802_11_associate_rsp { @@ -1425,6 +1461,36 @@ struct mwifiex_user_scan_cfg { u16 scan_chan_gap; } __packed; +#define MWIFIEX_BG_SCAN_CHAN_MAX 38 +#define MWIFIEX_BSS_MODE_INFRA 1 +#define MWIFIEX_BGSCAN_ACT_GET 0x0000 +#define MWIFIEX_BGSCAN_ACT_SET 0x0001 +#define MWIFIEX_BGSCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define MWIFIEX_BGSCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define MWIFIEX_BGSCAN_SSID_RSSI_MATCH 0x0004 +/**wait for all channel scan to complete to report scan result*/ +#define MWIFIEX_BGSCAN_WAIT_ALL_CHAN_DONE 0x80000000 + +struct mwifiex_bg_scan_cfg { + u16 action; + u8 enable; + u8 bss_type; + u8 chan_per_scan; + u32 scan_interval; + u32 report_condition; + u8 num_probes; + u8 rssi_threshold; + u8 snr_threshold; + u16 repeat_count; + u16 start_later; + struct cfg80211_match_set *ssid_list; + u8 num_ssids; + struct mwifiex_user_scan_chan chan_list[MWIFIEX_BG_SCAN_CHAN_MAX]; + u16 scan_chan_gap; +} __packed; + struct ie_body { u8 grp_key_oui[4]; u8 ptk_cnt[2]; @@ -1470,6 +1536,20 @@ struct mwifiex_ie_types_bss_scan_info { __le64 tsf; } __packed; +struct host_cmd_ds_802_11_bg_scan_config { + __le16 action; + u8 enable; + u8 bss_type; + u8 chan_per_scan; + u8 reserved; + __le16 reserved1; + __le32 scan_interval; + __le32 reserved2; + __le32 report_condition; + __le16 reserved3; + u8 tlv[0]; +} __packed; + struct host_cmd_ds_802_11_bg_scan_query { u8 flush; } __packed; @@ -2099,6 +2179,10 @@ struct host_cmd_ds_robust_coex { __le16 reserved; } __packed; +struct host_cmd_ds_wakeup_reason { + u16 wakeup_reason; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -2124,6 +2208,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_802_11_scan scan; struct host_cmd_ds_802_11_scan_ext ext_scan; struct host_cmd_ds_802_11_scan_rsp scan_resp; + struct host_cmd_ds_802_11_bg_scan_config bg_scan_config; struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; struct host_cmd_ds_802_11_associate associate; @@ -2170,6 +2255,7 @@ struct host_cmd_ds_command { struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; struct host_cmd_ds_multi_chan_policy mc_policy; struct host_cmd_ds_robust_coex coex; + struct host_cmd_ds_wakeup_reason hs_wakeup_reason; } params; } __packed; diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 6f7876ec3..517653b3a 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -741,8 +741,6 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, u32 poll_num = 1; if (adapter->if_ops.check_fw_status) { - adapter->winner = 0; - /* check if firmware is already running */ ret = adapter->if_ops.check_fw_status(adapter, poll_num); if (!ret) { @@ -750,13 +748,23 @@ int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, "WLAN FW already running! Skip FW dnld\n"); return 0; } + } + + /* check if we are the winner for downloading FW */ + if (adapter->if_ops.check_winner_status) { + adapter->winner = 0; + ret = adapter->if_ops.check_winner_status(adapter); poll_num = MAX_FIRMWARE_POLL_TRIES; + if (ret) { + mwifiex_dbg(adapter, MSG, + "WLAN read winner status failed!\n"); + return ret; + } - /* check if we are the winner for downloading FW */ if (!adapter->winner) { mwifiex_dbg(adapter, MSG, - "FW already running! Skip FW dnld\n"); + "WLAN is not the winner! Skip FW dnld\n"); goto poll_fw; } } diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h index 4f0174c64..a5a48c183 100644 --- a/drivers/net/wireless/marvell/mwifiex/ioctl.h +++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h @@ -184,6 +184,7 @@ struct mwifiex_ds_tx_ba_stream_tbl { }; #define DBG_CMD_NUM 5 +#define MWIFIEX_DBG_SDIO_MP_NUM 10 struct tdls_peer_info { u8 peer_addr[ETH_ALEN]; @@ -235,6 +236,11 @@ struct mwifiex_debug_info { u8 cmd_sent; u8 cmd_resp_received; u8 event_received; + u32 last_mp_wr_bitmap[MWIFIEX_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_ports[MWIFIEX_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_len[MWIFIEX_DBG_SDIO_MP_NUM]; + u32 last_mp_curr_wr_port[MWIFIEX_DBG_SDIO_MP_NUM]; + u8 last_sdio_mp_index; }; #define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 @@ -271,6 +277,10 @@ struct mwifiex_ds_hs_cfg { u32 gap; }; +struct mwifiex_ds_wakeup_reason { + u16 hs_wakeup_reason; +}; + #define DEEP_SLEEP_ON 1 #define DEEP_SLEEP_OFF 0 #define DEEP_SLEEP_IDLE_TIME 100 @@ -414,6 +424,7 @@ struct mwifiex_ds_mef_cfg { #define MWIFIEX_VSIE_MASK_SCAN 0x01 #define MWIFIEX_VSIE_MASK_ASSOC 0x02 #define MWIFIEX_VSIE_MASK_ADHOC 0x04 +#define MWIFIEX_VSIE_MASK_BGSCAN 0x08 enum { MWIFIEX_FUNC_INIT = 1, diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index cc09a81db..62211fca9 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -644,6 +644,8 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc; bool enable_data = true; u16 cap_info, status_code, aid; + const u8 *ie_ptr; + struct ieee80211_ht_operation *assoc_resp_ht_oper; assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; @@ -733,6 +735,19 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, = ((bss_desc->wmm_ie.qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); + /* Store the bandwidth information from assoc response */ + ie_ptr = cfg80211_find_ie(WLAN_EID_HT_OPERATION, assoc_rsp->ie_buffer, + priv->assoc_rsp_size + - sizeof(struct ieee_types_assoc_rsp)); + if (ie_ptr) { + assoc_resp_ht_oper = (struct ieee80211_ht_operation *)(ie_ptr + + sizeof(struct ieee_types_header)); + priv->assoc_resp_ht_param = assoc_resp_ht_oper->ht_param; + priv->ht_param_present = true; + } else { + priv->ht_param_present = false; + } + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: curr_pkt_filter is %#x\n", priv->curr_pkt_filter); diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index e1fc2e6be..2d40cb76c 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -132,6 +132,13 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) } } + if (adapter->nd_info) { + for (i = 0 ; i < adapter->nd_info->n_matches ; i++) + kfree(adapter->nd_info->matches[i]); + kfree(adapter->nd_info); + adapter->nd_info = NULL; + } + vfree(adapter->chan_stats); kfree(adapter); return 0; @@ -746,6 +753,13 @@ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) mwifiex_queue_main_work(priv->adapter); + if (priv->sched_scanning) { + mwifiex_dbg(priv->adapter, INFO, + "aborting bgscan on ndo_stop\n"); + mwifiex_stop_bg_scan(priv); + cfg80211_sched_scan_stopped(priv->wdev.wiphy); + } + return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 2f7f478ce..aafc4ab4e 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -198,6 +198,11 @@ do { \ buf, len, false); \ } while (0) +/** Min BGSCAN interval 15 second */ +#define MWIFIEX_BGSCAN_INTERVAL 15000 +/** default repeat count */ +#define MWIFIEX_BGSCAN_REPEAT_COUNT 6 + struct mwifiex_dbg { u32 num_cmd_host_to_card_failure; u32 num_cmd_sleep_cfm_host_to_card_failure; @@ -218,6 +223,11 @@ struct mwifiex_dbg { u16 last_cmd_resp_index; u16 last_event[DBG_CMD_NUM]; u16 last_event_index; + u32 last_mp_wr_bitmap[MWIFIEX_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_ports[MWIFIEX_DBG_SDIO_MP_NUM]; + u32 last_mp_wr_len[MWIFIEX_DBG_SDIO_MP_NUM]; + u32 last_mp_curr_wr_port[MWIFIEX_DBG_SDIO_MP_NUM]; + u8 last_sdio_mp_index; }; enum MWIFIEX_HARDWARE_STATUS { @@ -293,6 +303,7 @@ struct mwifiex_tid_tbl { #define WMM_HIGHEST_PRIORITY 7 #define HIGH_PRIO_TID 7 #define LOW_PRIO_TID 0 +#define MWIFIEX_WMM_DRV_DELAY_MAX 510 struct mwifiex_wmm_desc { struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; @@ -483,26 +494,6 @@ struct mwifiex_roc_cfg { struct ieee80211_channel chan; }; -#define MWIFIEX_FW_DUMP_IDX 0xff -#define MWIFIEX_DRV_INFO_IDX 20 -#define FW_DUMP_MAX_NAME_LEN 8 -#define FW_DUMP_HOST_READY 0xEE -#define FW_DUMP_DONE 0xFF -#define FW_DUMP_READ_DONE 0xFE - -struct memory_type_mapping { - u8 mem_name[FW_DUMP_MAX_NAME_LEN]; - u8 *mem_ptr; - u32 mem_size; - u8 done_flag; -}; - -enum rdwr_status { - RDWR_STATUS_SUCCESS = 0, - RDWR_STATUS_FAILURE = 1, - RDWR_STATUS_DONE = 2 -}; - enum mwifiex_iface_work_flags { MWIFIEX_IFACE_WORK_DEVICE_DUMP, MWIFIEX_IFACE_WORK_CARD_RESET, @@ -616,6 +607,7 @@ struct mwifiex_private { spinlock_t curr_bcn_buf_lock; struct wireless_dev wdev; struct mwifiex_chan_freq_power cfp; + u32 versionstrsel; char version_str[128]; #ifdef CONFIG_DEBUG_FS struct dentry *dfs_dev_dir; @@ -640,6 +632,7 @@ struct mwifiex_private { u32 mgmt_frame_mask; struct mwifiex_roc_cfg roc_cfg; bool scan_aborting; + u8 sched_scanning; u8 csa_chan; unsigned long csa_expire_time; u8 del_list_idx; @@ -667,6 +660,8 @@ struct mwifiex_private { struct mwifiex_ds_mem_rw mem_rw; struct sk_buff_head bypass_txq; struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX]; + u8 assoc_resp_ht_param; + bool ht_param_present; }; @@ -791,6 +786,7 @@ struct mwifiex_if_ops { int (*init_if) (struct mwifiex_adapter *); void (*cleanup_if) (struct mwifiex_adapter *); int (*check_fw_status) (struct mwifiex_adapter *, u32); + int (*check_winner_status)(struct mwifiex_adapter *); int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); int (*register_dev) (struct mwifiex_adapter *); void (*unregister_dev) (struct mwifiex_adapter *); @@ -994,6 +990,7 @@ struct mwifiex_adapter { u8 active_scan_triggered; bool usb_mc_status; bool usb_mc_setup; + struct cfg80211_wowlan_nd_info *nd_info; }; void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); @@ -1196,6 +1193,10 @@ int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, void *buf); +int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_stop_bg_scan(struct mwifiex_private *priv); /* * This function checks if the queuing is RA based or not. @@ -1417,7 +1418,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); -int mwifiex_get_ver_ext(struct mwifiex_private *priv); +int mwifiex_get_ver_ext(struct mwifiex_private *priv, u32 version_str_sel); int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, @@ -1586,6 +1587,12 @@ void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter); void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); +int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action, + int cmd_type, + struct mwifiex_ds_wakeup_reason *wakeup_reason); +int mwifiex_ret_wakeup_reason(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_wakeup_reason *wakeup_reason); void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter); void mwifiex_11n_delba(struct mwifiex_private *priv, int tid); int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy); diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 8e334e592..106717dd8 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -37,17 +37,6 @@ static struct mwifiex_if_ops pcie_ops; static struct semaphore add_remove_card_sem; -static struct memory_type_mapping mem_type_mapping_tbl[] = { - {"ITCM", NULL, 0, 0xF0}, - {"DTCM", NULL, 0, 0xF1}, - {"SQRAM", NULL, 0, 0xF2}, - {"IRAM", NULL, 0, 0xF3}, - {"APU", NULL, 0, 0xF4}, - {"CIU", NULL, 0, 0xF5}, - {"ICU", NULL, 0, 0xF6}, - {"MAC", NULL, 0, 0xF7}, -}; - static int mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, size_t size, int flags) @@ -206,6 +195,8 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, card->pcie.blksz_fw_dl = data->blksz_fw_dl; card->pcie.tx_buf_size = data->tx_buf_size; card->pcie.can_dump_fw = data->can_dump_fw; + card->pcie.mem_type_mapping_tbl = data->mem_type_mapping_tbl; + card->pcie.num_mem_types = data->num_mem_types; card->pcie.can_ext_scan = data->can_ext_scan; } @@ -323,6 +314,8 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) struct pcie_service_card *card = adapter->card; *data = ioread32(card->pci_mmap1 + reg); + if (*data == 0xffffffff) + return 0xffffffff; return 0; } @@ -1408,7 +1401,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) return -1; } - if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) + if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) return -1; buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); @@ -2007,14 +2000,12 @@ done: /* * This function checks the firmware status in card. - * - * The winner interface is also determined by this function. */ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) { int ret = 0; - u32 firmware_stat, winner_status; + u32 firmware_stat; struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; u32 tries; @@ -2054,19 +2045,28 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) } } - if (ret) { - if (mwifiex_read_reg(adapter, reg->fw_status, - &winner_status)) - ret = -1; - else if (!winner_status) { - mwifiex_dbg(adapter, INFO, - "PCI-E is the winner\n"); - adapter->winner = 1; - } else { - mwifiex_dbg(adapter, ERROR, - "PCI-E is not the winner <%#x,%d>, exit dnld\n", - ret, adapter->winner); - } + return ret; +} + +/* This function checks if WLAN is the winner. + */ +static int +mwifiex_check_winner_status(struct mwifiex_adapter *adapter) +{ + u32 winner = 0; + int ret = 0; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (mwifiex_read_reg(adapter, reg->fw_status, &winner)) { + ret = -1; + } else if (!winner) { + mwifiex_dbg(adapter, INFO, "PCI-E is the winner\n"); + adapter->winner = 1; + } else { + mwifiex_dbg(adapter, ERROR, + "PCI-E is not the winner <%#x,%d>, exit dnld\n", + ret, adapter->winner); } return ret; @@ -2075,20 +2075,28 @@ mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) /* * This function reads the interrupt status from card. */ -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter, + int msg_id) { u32 pcie_ireg; unsigned long flags; + struct pcie_service_card *card = adapter->card; if (!mwifiex_pcie_ok_to_access_hw(adapter)) return; - if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, "Read register failed\n"); - return; - } + if (card->msix_enable && msg_id >= 0) { + pcie_ireg = BIT(msg_id); + } else { + if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, + &pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, "Read register failed\n"); + return; + } + + if ((pcie_ireg == 0xFFFFFFFF) || !pcie_ireg) + return; - if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { mwifiex_pcie_disable_host_int(adapter); @@ -2099,21 +2107,24 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) "Write register failed\n"); return; } - spin_lock_irqsave(&adapter->int_lock, flags); - adapter->int_status |= pcie_ireg; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - if (!adapter->pps_uapsd_mode && - adapter->ps_state == PS_STATE_SLEEP && - mwifiex_pcie_ok_to_access_hw(adapter)) { - /* Potentially for PCIe we could get other - * interrupts like shared. Don't change power - * state until cookie is set */ - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - } } + + if (!adapter->pps_uapsd_mode && + adapter->ps_state == PS_STATE_SLEEP && + mwifiex_pcie_ok_to_access_hw(adapter)) { + /* Potentially for PCIe we could get other + * interrupts like shared. Don't change power + * state until cookie is set + */ + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + } + + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= pcie_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + mwifiex_dbg(adapter, INTR, "ireg: 0x%08x\n", pcie_ireg); } /* @@ -2124,7 +2135,8 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) */ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) { - struct pci_dev *pdev = (struct pci_dev *)context; + struct mwifiex_msix_context *ctx = context; + struct pci_dev *pdev = ctx->dev; struct pcie_service_card *card; struct mwifiex_adapter *adapter; @@ -2144,7 +2156,11 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) if (adapter->surprise_removed) goto exit; - mwifiex_interrupt_status(adapter); + if (card->msix_enable) + mwifiex_interrupt_status(adapter, ctx->msg_id); + else + mwifiex_interrupt_status(adapter, -1); + mwifiex_queue_main_work(adapter); exit: @@ -2164,7 +2180,7 @@ exit: * In case of Rx packets received, the packets are uploaded from card to * host and processed accordingly. */ -static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) +static int mwifiex_process_pcie_int(struct mwifiex_adapter *adapter) { int ret; u32 pcie_ireg; @@ -2244,6 +2260,69 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) return 0; } +static int mwifiex_process_msix_int(struct mwifiex_adapter *adapter) +{ + int ret; + u32 pcie_ireg; + unsigned long flags; + + spin_lock_irqsave(&adapter->int_lock, flags); + /* Clear out unused interrupts */ + pcie_ireg = adapter->int_status; + adapter->int_status = 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + if (pcie_ireg & HOST_INTR_DNLD_DONE) { + mwifiex_dbg(adapter, INTR, + "info: TX DNLD Done\n"); + ret = mwifiex_pcie_send_data_complete(adapter); + if (ret) + return ret; + } + if (pcie_ireg & HOST_INTR_UPLD_RDY) { + mwifiex_dbg(adapter, INTR, + "info: Rx DATA\n"); + ret = mwifiex_pcie_process_recv_data(adapter); + if (ret) + return ret; + } + if (pcie_ireg & HOST_INTR_EVENT_RDY) { + mwifiex_dbg(adapter, INTR, + "info: Rx EVENT\n"); + ret = mwifiex_pcie_process_event_ready(adapter); + if (ret) + return ret; + } + + if (pcie_ireg & HOST_INTR_CMD_DONE) { + if (adapter->cmd_sent) { + mwifiex_dbg(adapter, INTR, + "info: CMD sent Interrupt\n"); + adapter->cmd_sent = false; + } + /* Handle command response */ + ret = mwifiex_pcie_process_cmd_complete(adapter); + if (ret) + return ret; + } + + mwifiex_dbg(adapter, INTR, + "info: cmd_sent=%d data_sent=%d\n", + adapter->cmd_sent, adapter->data_sent); + + return 0; +} + +static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + + if (card->msix_enable) + return mwifiex_process_msix_int(adapter); + else + return mwifiex_process_pcie_int(adapter); +} + /* * This function downloads data from driver to card. * @@ -2278,10 +2357,15 @@ mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) { int ret, tries; u8 ctrl_data; + u32 fw_status; struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); + if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) + return RDWR_STATUS_FAILURE; + + ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, + reg->fw_dump_host_ready); if (ret) { mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); @@ -2294,11 +2378,11 @@ mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) return RDWR_STATUS_SUCCESS; if (doneflag && ctrl_data == doneflag) return RDWR_STATUS_DONE; - if (ctrl_data != FW_DUMP_HOST_READY) { + if (ctrl_data != reg->fw_dump_host_ready) { mwifiex_dbg(adapter, WARN, "The ctrl reg was changed, re-try again!\n"); ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, - FW_DUMP_HOST_READY); + reg->fw_dump_host_ready); if (ret) { mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); @@ -2318,7 +2402,8 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; unsigned int reg, reg_start, reg_end; - u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + u8 *dbg_ptr, *end_ptr, *tmp_ptr, fw_dump_num, dump_num; + u8 idx, i, read_reg, doneflag = 0; enum rdwr_status stat; u32 memory_size; int ret; @@ -2326,8 +2411,9 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) if (!card->pcie.can_dump_fw) return; - for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; if (entry->mem_ptr) { vfree(entry->mem_ptr); @@ -2336,7 +2422,7 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) entry->mem_size = 0; } - mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n"); + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); /* Read the number of the memories which will dump */ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); @@ -2344,28 +2430,38 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) return; reg = creg->fw_dump_start; - mwifiex_read_reg_byte(adapter, reg, &dump_num); + mwifiex_read_reg_byte(adapter, reg, &fw_dump_num); + + /* W8997 chipset firmware dump will be restore in single region*/ + if (fw_dump_num == 0) + dump_num = 1; + else + dump_num = fw_dump_num; /* Read the length of every memory which will dump */ for (idx = 0; idx < dump_num; idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - return; - + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; memory_size = 0; - reg = creg->fw_dump_start; - for (i = 0; i < 4; i++) { - mwifiex_read_reg_byte(adapter, reg, &read_reg); - memory_size |= (read_reg << (i * 8)); - reg++; + if (fw_dump_num != 0) { + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + return; + + reg = creg->fw_dump_start; + for (i = 0; i < 4; i++) { + mwifiex_read_reg_byte(adapter, reg, &read_reg); + memory_size |= (read_reg << (i * 8)); + reg++; + } + } else { + memory_size = MWIFIEX_FW_DUMP_MAX_MEMSIZE; } if (memory_size == 0) { mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n"); ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl, - FW_DUMP_READ_DONE); + creg->fw_dump_read_done); if (ret) { mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); return; @@ -2400,11 +2496,21 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) mwifiex_read_reg_byte(adapter, reg, dbg_ptr); if (dbg_ptr < end_ptr) { dbg_ptr++; - } else { - mwifiex_dbg(adapter, ERROR, - "Allocated buf not enough\n"); - return; + continue; } + mwifiex_dbg(adapter, ERROR, + "pre-allocated buf not enough\n"); + tmp_ptr = + vzalloc(memory_size + MWIFIEX_SIZE_4K); + if (!tmp_ptr) + return; + memcpy(tmp_ptr, entry->mem_ptr, memory_size); + vfree(entry->mem_ptr); + entry->mem_ptr = tmp_ptr; + tmp_ptr = NULL; + dbg_ptr = entry->mem_ptr + memory_size; + memory_size += MWIFIEX_SIZE_4K; + end_ptr = entry->mem_ptr + memory_size; } if (stat != RDWR_STATUS_DONE) @@ -2416,7 +2522,7 @@ static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) break; } while (true); } - mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n"); + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); } static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) @@ -2595,10 +2701,43 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter) { - int ret; + int ret, i, j; struct pcie_service_card *card = adapter->card; struct pci_dev *pdev = card->dev; + if (card->pcie.reg->msix_support) { + for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) + card->msix_entries[i].entry = i; + ret = pci_enable_msix_exact(pdev, card->msix_entries, + MWIFIEX_NUM_MSIX_VECTORS); + if (!ret) { + for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) { + card->msix_ctx[i].dev = pdev; + card->msix_ctx[i].msg_id = i; + + ret = request_irq(card->msix_entries[i].vector, + mwifiex_pcie_interrupt, 0, + "MWIFIEX_PCIE_MSIX", + &card->msix_ctx[i]); + if (ret) + break; + } + + if (ret) { + mwifiex_dbg(adapter, INFO, "request_irq fail: %d\n", + ret); + for (j = 0; j < i; j++) + free_irq(card->msix_entries[j].vector, + &card->msix_ctx[i]); + pci_disable_msix(pdev); + } else { + mwifiex_dbg(adapter, MSG, "MSIx enabled!"); + card->msix_enable = 1; + return 0; + } + } + } + if (pci_enable_msi(pdev) != 0) pci_disable_msi(pdev); else @@ -2606,8 +2745,10 @@ static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter) mwifiex_dbg(adapter, INFO, "msi_enable = %d\n", card->msi_enable); + card->share_irq_ctx.dev = pdev; + card->share_irq_ctx.msg_id = -1; ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, - "MRVL_PCIE", pdev); + "MRVL_PCIE", &card->share_irq_ctx); if (ret) { pr_err("request_irq failed: ret=%d\n", ret); adapter->card = NULL; @@ -2635,8 +2776,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) return -1; adapter->tx_buf_size = card->pcie.tx_buf_size; - adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; - adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); + adapter->mem_type_mapping_tbl = card->pcie.mem_type_mapping_tbl; + adapter->num_mem_types = card->pcie.num_mem_types; strcpy(adapter->fw_name, card->pcie.firmware); adapter->ext_scan = card->pcie.can_ext_scan; @@ -2653,11 +2794,28 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg; + struct pci_dev *pdev = card->dev; + int i; if (card) { - mwifiex_dbg(adapter, INFO, - "%s(): calling free_irq()\n", __func__); - free_irq(card->dev->irq, card->dev); + if (card->msix_enable) { + for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) + synchronize_irq(card->msix_entries[i].vector); + + for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) + free_irq(card->msix_entries[i].vector, + &card->msix_ctx[i]); + + card->msix_enable = 0; + pci_disable_msix(pdev); + } else { + mwifiex_dbg(adapter, INFO, + "%s(): calling free_irq()\n", __func__); + free_irq(card->dev->irq, &card->share_irq_ctx); + + if (card->msi_enable) + pci_disable_msi(pdev); + } reg = card->pcie.reg; if (reg->sleep_cookie) @@ -2675,6 +2833,7 @@ static struct mwifiex_if_ops pcie_ops = { .init_if = mwifiex_pcie_init, .cleanup_if = mwifiex_pcie_cleanup, .check_fw_status = mwifiex_check_fw_status, + .check_winner_status = mwifiex_check_winner_status, .prog_fw = mwifiex_prog_fw_w_helper, .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index eee9ea722..e55002806 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -26,6 +26,7 @@ #include <linux/pcieport_if.h> #include <linux/interrupt.h> +#include "decl.h" #include "main.h" #define PCIE8766_DEFAULT_FW_NAME "/*(DEBLOBBED)*/" @@ -135,6 +136,9 @@ struct mwifiex_pcie_card_reg { u16 fw_dump_ctrl; u16 fw_dump_start; u16 fw_dump_end; + u8 fw_dump_host_ready; + u8 fw_dump_read_done; + u8 msix_support; }; static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { @@ -166,6 +170,7 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { .ring_tx_start_ptr = 0, .pfu_enabled = 0, .sleep_cookie = 1, + .msix_support = 0, }; static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { @@ -200,6 +205,9 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { .fw_dump_ctrl = 0xcf4, .fw_dump_start = 0xcf8, .fw_dump_end = 0xcff, + .fw_dump_host_ready = 0xee, + .fw_dump_read_done = 0xfe, + .msix_support = 0, }; static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { @@ -231,6 +239,27 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, .pfu_enabled = 1, .sleep_cookie = 0, + .fw_dump_ctrl = 0xcf4, + .fw_dump_start = 0xcf8, + .fw_dump_end = 0xcff, + .fw_dump_host_ready = 0xcc, + .fw_dump_read_done = 0xdd, + .msix_support = 1, +}; + +static struct memory_type_mapping mem_type_mapping_tbl_w8897[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"IRAM", NULL, 0, 0xF3}, + {"APU", NULL, 0, 0xF4}, + {"CIU", NULL, 0, 0xF5}, + {"ICU", NULL, 0, 0xF6}, + {"MAC", NULL, 0, 0xF7}, +}; + +static struct memory_type_mapping mem_type_mapping_tbl_w8997[] = { + {"DUMP", NULL, 0, 0xDD}, }; struct mwifiex_pcie_device { @@ -239,6 +268,8 @@ struct mwifiex_pcie_device { u16 blksz_fw_dl; u16 tx_buf_size; bool can_dump_fw; + struct memory_type_mapping *mem_type_mapping_tbl; + u8 num_mem_types; bool can_ext_scan; }; @@ -257,6 +288,8 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = { .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, .can_dump_fw = true, + .mem_type_mapping_tbl = mem_type_mapping_tbl_w8897, + .num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl_w8897), .can_ext_scan = true, }; @@ -265,7 +298,9 @@ static const struct mwifiex_pcie_device mwifiex_pcie8997 = { .reg = &mwifiex_reg_8997, .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .can_dump_fw = false, + .can_dump_fw = true, + .mem_type_mapping_tbl = mem_type_mapping_tbl_w8997, + .num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl_w8997), .can_ext_scan = true, }; @@ -290,6 +325,13 @@ struct mwifiex_pfu_buf_desc { u32 reserved; } __packed; +#define MWIFIEX_NUM_MSIX_VECTORS 4 + +struct mwifiex_msix_context { + struct pci_dev *dev; + u16 msg_id; +}; + struct pcie_service_card { struct pci_dev *dev; struct mwifiex_adapter *adapter; @@ -327,6 +369,12 @@ struct pcie_service_card { void __iomem *pci_mmap; void __iomem *pci_mmap1; int msi_enable; + int msix_enable; +#ifdef CONFIG_PCI + struct msix_entry msix_entries[MWIFIEX_NUM_MSIX_VECTORS]; +#endif + struct mwifiex_msix_context msix_ctx[MWIFIEX_NUM_MSIX_VECTORS]; + struct mwifiex_msix_context share_irq_ctx; }; static inline int diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index c20017ced..489f7a911 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -547,6 +547,61 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, return chan_idx; } +/* This function creates a channel list tlv for bgscan config, based + * on region/band information. + */ +static int +mwifiex_bgscan_create_channel_list(struct mwifiex_private *priv, + const struct mwifiex_bg_scan_cfg + *bgscan_cfg_in, + struct mwifiex_chan_scan_param_set + *scan_chan_list) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct mwifiex_adapter *adapter = priv->adapter; + int chan_idx = 0, i; + + for (band = 0; (band < IEEE80211_NUM_BANDS); band++) { + if (!priv->wdev.wiphy->bands[band]) + continue; + + sband = priv->wdev.wiphy->bands[band]; + + for (i = 0; (i < sband->n_channels) ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_chan_list[chan_idx].radio_type = band; + + if (bgscan_cfg_in->chan_list[0].scan_time) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16((u16)bgscan_cfg_in-> + chan_list[0].scan_time); + else if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter-> + specific_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bitmap + |= MWIFIEX_PASSIVE_SCAN; + else + scan_chan_list[chan_idx].chan_scan_mode_bitmap + &= ~MWIFIEX_PASSIVE_SCAN; + + scan_chan_list[chan_idx].chan_number = + (u32)ch->hw_value; + chan_idx++; + } + } + return chan_idx; +} + /* This function appends rate TLV to scan config command. */ static int mwifiex_append_rate_tlv(struct mwifiex_private *priv, @@ -2037,6 +2092,8 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, u8 is_bgscan_resp; __le64 fw_tsf = 0; u8 *radio_type; + struct cfg80211_wowlan_nd_match *pmatch; + struct cfg80211_sched_scan_request *nd_config = NULL; is_bgscan_resp = (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_BG_SCAN_QUERY); @@ -2099,6 +2156,21 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, (struct mwifiex_ie_types_data **) &chan_band_tlv); +#ifdef CONFIG_PM + if (priv->wdev.wiphy->wowlan_config) + nd_config = priv->wdev.wiphy->wowlan_config->nd_config; +#endif + + if (nd_config) { + adapter->nd_info = + kzalloc(sizeof(struct cfg80211_wowlan_nd_match) + + sizeof(struct cfg80211_wowlan_nd_match *) * + scan_rsp->number_of_sets, GFP_ATOMIC); + + if (adapter->nd_info) + adapter->nd_info->n_matches = scan_rsp->number_of_sets; + } + for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { /* * If the TSF TLV was appended to the scan results, save this @@ -2117,6 +2189,23 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, radio_type = NULL; } + if (chan_band_tlv && adapter->nd_info) { + adapter->nd_info->matches[idx] = + kzalloc(sizeof(*pmatch) + + sizeof(u32), GFP_ATOMIC); + + pmatch = adapter->nd_info->matches[idx]; + + if (pmatch) { + memset(pmatch, 0, sizeof(*pmatch)); + if (chan_band_tlv) { + pmatch->n_channels = 1; + pmatch->channels[0] = + chan_band->chan_number; + } + } + } + ret = mwifiex_parse_single_response_buf(priv, &bss_info, &bytes_left, le64_to_cpu(fw_tsf), @@ -2155,6 +2244,227 @@ int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, return 0; } +/* This function prepares an background scan config command to be sent + * to the firmware + */ +int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_bg_scan_config *bgscan_config = + &cmd->params.bg_scan_config; + struct mwifiex_bg_scan_cfg *bgscan_cfg_in = data_buf; + u8 *tlv_pos = bgscan_config->tlv; + u8 num_probes; + u32 ssid_len, chan_idx, scan_type, scan_dur, chan_num; + int i; + struct mwifiex_ie_types_num_probes *num_probes_tlv; + struct mwifiex_ie_types_repeat_count *repeat_count_tlv; + struct mwifiex_ie_types_min_rssi_threshold *rssi_threshold_tlv; + struct mwifiex_ie_types_bgscan_start_later *start_later_tlv; + struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct mwifiex_ie_types_chan_list_param_set *chan_list_tlv; + struct mwifiex_chan_scan_param_set *temp_chan; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG); + cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN); + + bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action); + bgscan_config->enable = bgscan_cfg_in->enable; + bgscan_config->bss_type = bgscan_cfg_in->bss_type; + bgscan_config->scan_interval = + cpu_to_le32(bgscan_cfg_in->scan_interval); + bgscan_config->report_condition = + cpu_to_le32(bgscan_cfg_in->report_condition); + + /* stop sched scan */ + if (!bgscan_config->enable) + return 0; + + bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan; + + num_probes = (bgscan_cfg_in->num_probes ? bgscan_cfg_in-> + num_probes : priv->adapter->scan_probes); + + if (num_probes) { + num_probes_tlv = (struct mwifiex_ie_types_num_probes *)tlv_pos; + num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len = + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes); + + tlv_pos += sizeof(num_probes_tlv->header) + + le16_to_cpu(num_probes_tlv->header.len); + } + + if (bgscan_cfg_in->repeat_count) { + repeat_count_tlv = + (struct mwifiex_ie_types_repeat_count *)tlv_pos; + repeat_count_tlv->header.type = + cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + repeat_count_tlv->header.len = + cpu_to_le16(sizeof(repeat_count_tlv->repeat_count)); + repeat_count_tlv->repeat_count = + cpu_to_le16(bgscan_cfg_in->repeat_count); + + tlv_pos += sizeof(repeat_count_tlv->header) + + le16_to_cpu(repeat_count_tlv->header.len); + } + + if (bgscan_cfg_in->rssi_threshold) { + rssi_threshold_tlv = + (struct mwifiex_ie_types_min_rssi_threshold *)tlv_pos; + rssi_threshold_tlv->header.type = + cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_threshold_tlv->header.len = + cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold)); + rssi_threshold_tlv->rssi_threshold = + cpu_to_le16(bgscan_cfg_in->rssi_threshold); + + tlv_pos += sizeof(rssi_threshold_tlv->header) + + le16_to_cpu(rssi_threshold_tlv->header.len); + } + + for (i = 0; i < bgscan_cfg_in->num_ssids; i++) { + ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len; + + wildcard_ssid_tlv = + (struct mwifiex_ie_types_wildcard_ssid_params *)tlv_pos; + wildcard_ssid_tlv->header.type = + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len = cpu_to_le16( + (u16)(ssid_len + sizeof(wildcard_ssid_tlv-> + max_ssid_length))); + + /* max_ssid_length = 0 tells firmware to perform + * specific scan for the SSID filled, whereas + * max_ssid_length = IEEE80211_MAX_SSID_LEN is for + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length = 0; + else + wildcard_ssid_tlv->max_ssid_length = + IEEE80211_MAX_SSID_LEN; + + memcpy(wildcard_ssid_tlv->ssid, + bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len); + + tlv_pos += (sizeof(wildcard_ssid_tlv->header) + + le16_to_cpu(wildcard_ssid_tlv->header.len)); + } + + chan_list_tlv = (struct mwifiex_ie_types_chan_list_param_set *)tlv_pos; + + if (bgscan_cfg_in->chan_list[0].chan_number) { + dev_dbg(priv->adapter->dev, "info: bgscan: Using supplied channel list\n"); + + chan_list_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + for (chan_idx = 0; + chan_idx < MWIFIEX_BG_SCAN_CHAN_MAX && + bgscan_cfg_in->chan_list[chan_idx].chan_number; + chan_idx++) { + temp_chan = chan_list_tlv->chan_scan_param + chan_idx; + + /* Increment the TLV header length by size appended */ + le16_add_cpu(&chan_list_tlv->header.len, + sizeof(chan_list_tlv->chan_scan_param)); + + temp_chan->chan_number = + bgscan_cfg_in->chan_list[chan_idx].chan_number; + temp_chan->radio_type = + bgscan_cfg_in->chan_list[chan_idx].radio_type; + + scan_type = + bgscan_cfg_in->chan_list[chan_idx].scan_type; + + if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) + temp_chan->chan_scan_mode_bitmap + |= MWIFIEX_PASSIVE_SCAN; + else + temp_chan->chan_scan_mode_bitmap + &= ~MWIFIEX_PASSIVE_SCAN; + + if (bgscan_cfg_in->chan_list[chan_idx].scan_time) { + scan_dur = (u16)bgscan_cfg_in-> + chan_list[chan_idx].scan_time; + } else { + scan_dur = (scan_type == + MWIFIEX_SCAN_TYPE_PASSIVE) ? + priv->adapter->passive_scan_time : + priv->adapter->specific_scan_time; + } + + temp_chan->min_scan_time = cpu_to_le16(scan_dur); + temp_chan->max_scan_time = cpu_to_le16(scan_dur); + } + } else { + dev_dbg(priv->adapter->dev, + "info: bgscan: Creating full region channel list\n"); + chan_num = + mwifiex_bgscan_create_channel_list(priv, bgscan_cfg_in, + chan_list_tlv-> + chan_scan_param); + le16_add_cpu(&chan_list_tlv->header.len, + chan_num * + sizeof(chan_list_tlv->chan_scan_param[0])); + } + + tlv_pos += (sizeof(chan_list_tlv->header) + + le16_to_cpu(chan_list_tlv->header.len)); + + if (bgscan_cfg_in->start_later) { + start_later_tlv = + (struct mwifiex_ie_types_bgscan_start_later *)tlv_pos; + start_later_tlv->header.type = + cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER); + start_later_tlv->header.len = + cpu_to_le16(sizeof(start_later_tlv->start_later)); + start_later_tlv->start_later = + cpu_to_le16(bgscan_cfg_in->start_later); + + tlv_pos += sizeof(start_later_tlv->header) + + le16_to_cpu(start_later_tlv->header.len); + } + + /* Append vendor specific IE TLV */ + mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_BGSCAN, &tlv_pos); + + le16_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv); + + return 0; +} + +int mwifiex_stop_bg_scan(struct mwifiex_private *priv) +{ + struct mwifiex_bg_scan_cfg *bgscan_cfg; + + if (!priv->sched_scanning) { + dev_dbg(priv->adapter->dev, "bgscan already stopped!\n"); + return 0; + } + + bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL); + if (!bgscan_cfg) + return -ENOMEM; + + bgscan_cfg->bss_type = MWIFIEX_BSS_MODE_INFRA; + bgscan_cfg->action = MWIFIEX_BGSCAN_ACT_SET; + bgscan_cfg->enable = false; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_CONFIG, + HostCmd_ACT_GEN_SET, 0, bgscan_cfg, true)) { + kfree(bgscan_cfg); + return -EFAULT; + } + + kfree(bgscan_cfg); + priv->sched_scanning = false; + + return 0; +} + static void mwifiex_update_chan_statistics(struct mwifiex_private *priv, struct mwifiex_ietypes_chanstats *tlv_stat) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index a679ab30d..6b281ea03 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -181,7 +181,7 @@ static int mwifiex_sdio_resume(struct device *dev) /* Disable Host Sleep */ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_ASYNC_CMD); + MWIFIEX_SYNC_CMD); return 0; } @@ -1039,19 +1039,14 @@ done: /* * This function checks the firmware status in card. - * - * The winner interface is also determined by this function. */ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) { - struct sdio_mmc_card *card = adapter->card; int ret = 0; u16 firmware_stat; u32 tries; - u8 winner_status; - /* Wait for firmware initialization event */ for (tries = 0; tries < poll_num; tries++) { ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); if (ret) @@ -1065,16 +1060,25 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, } } - if (ret) { - if (mwifiex_read_reg - (adapter, card->reg->status_reg_0, &winner_status)) - winner_status = 0; + return ret; +} + +/* This function checks if WLAN is the winner. + */ +static int mwifiex_check_winner_status(struct mwifiex_adapter *adapter) +{ + int ret = 0; + u8 winner = 0; + struct sdio_mmc_card *card = adapter->card; + + if (mwifiex_read_reg(adapter, card->reg->status_reg_0, &winner)) + return -1; + + if (winner) + adapter->winner = 0; + else + adapter->winner = 1; - if (winner_status) - adapter->winner = 0; - else - adapter->winner = 1; - } return ret; } @@ -1351,6 +1355,9 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, card->mpa_rx.start_port; } + if (card->mpa_rx.pkt_cnt == 1) + mport = adapter->ioport + port; + if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, card->mpa_rx.buf_len, mport, 1)) goto error; @@ -1680,6 +1687,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, s32 f_precopy_cur_buf = 0; s32 f_postcopy_cur_buf = 0; u32 mport; + int index; if (!card->mpa_tx.enabled || (card->has_control_mask && (port == CTRL_PORT)) || @@ -1781,9 +1789,21 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, card->mpa_tx.start_port; } + if (card->mpa_tx.pkt_cnt == 1) + mport = adapter->ioport + port; + ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, card->mpa_tx.buf_len, mport); + /* Save the last multi port tx aggreagation info to debug log */ + index = adapter->dbg.last_sdio_mp_index; + index = (index + 1) % MWIFIEX_DBG_SDIO_MP_NUM; + adapter->dbg.last_sdio_mp_index = index; + adapter->dbg.last_mp_wr_ports[index] = mport; + adapter->dbg.last_mp_wr_bitmap[index] = card->mp_wr_bitmap; + adapter->dbg.last_mp_wr_len[index] = card->mpa_tx.buf_len; + adapter->dbg.last_mp_curr_wr_port[index] = card->curr_wr_port; + MP_TX_AGGR_BUF_RESET(card); } @@ -2620,6 +2640,7 @@ static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, .check_fw_status = mwifiex_check_fw_status, + .check_winner_status = mwifiex_check_winner_status, .prog_fw = mwifiex_prog_fw_w_helper, .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index e486867a4..30f152601 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1813,6 +1813,22 @@ static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd, return 0; } +/* This function prepares command to get HS wakeup reason. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_get_wakeup_reason(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_HS_WAKEUP_REASON); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wakeup_reason) + + S_DS_GEN); + + return 0; +} + /* * This function prepares the commands before sending them to the firmware. * @@ -1873,6 +1889,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_802_11_SCAN: ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); break; + case HostCmd_CMD_802_11_BG_SCAN_CONFIG: + ret = mwifiex_cmd_802_11_bg_scan_config(priv, cmd_ptr, + data_buf); + break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); break; @@ -2063,6 +2083,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, data_buf); break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = mwifiex_cmd_get_wakeup_reason(priv, cmd_ptr); + break; case HostCmd_CMD_MC_POLICY: ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action, data_buf); diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index 9ac7aa243..d96523e10 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -1076,9 +1076,12 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_ret_802_11_scan(priv, resp); + cfg80211_sched_scan_results(priv->wdev.wiphy); mwifiex_dbg(adapter, CMD, "info: CMD_RESP: BG_SCAN result is ready!\n"); break; + case HostCmd_CMD_802_11_BG_SCAN_CONFIG: + break; case HostCmd_CMD_TXPWR_CFG: ret = mwifiex_ret_tx_power_cfg(priv, resp); break; @@ -1233,6 +1236,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp); break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = mwifiex_ret_wakeup_reason(priv, resp, data_buf); + break; case HostCmd_CMD_TDLS_CONFIG: break; case HostCmd_CMD_ROBUST_COEX: diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c index 23bae87d4..070bce401 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_event.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -92,6 +92,9 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) priv->is_data_rate_auto = true; priv->data_rate = 0; + priv->assoc_resp_ht_param = 0; + priv->ht_param_present = false; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) mwifiex_hist_data_reset(priv); @@ -688,6 +691,13 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) HostCmd_ACT_GEN_GET, 0, NULL, false); break; + case EVENT_BG_SCAN_STOPPED: + dev_dbg(adapter->dev, "event: BGS_STOPPED\n"); + cfg80211_sched_scan_stopped(priv->wdev.wiphy); + if (priv->sched_scanning) + priv->sched_scanning = false; + break; + case EVENT_PORT_RELEASE: mwifiex_dbg(adapter, EVENT, "event: PORT RELEASE\n"); priv->port_open = true; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index d7db6f23e..d5c56eb9e 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -505,6 +505,20 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) } } + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + if (priv && priv->sched_scanning) { +#ifdef CONFIG_PM + if (!priv->wdev.wiphy->wowlan_config->nd_config) { +#endif + mwifiex_dbg(adapter, CMD, "aborting bgscan!\n"); + mwifiex_stop_bg_scan(priv); + cfg80211_sched_scan_stopped(priv->wdev.wiphy); +#ifdef CONFIG_PM + } +#endif + } + if (adapter->hs_activated) { mwifiex_dbg(adapter, CMD, "cmd: HS Already activated\n"); @@ -1115,11 +1129,12 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, * with requisite parameters and calls the IOCTL handler. */ int -mwifiex_get_ver_ext(struct mwifiex_private *priv) +mwifiex_get_ver_ext(struct mwifiex_private *priv, u32 version_str_sel) { struct mwifiex_ver_ext ver_ext; memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); + ver_ext.version_str_sel = version_str_sel; if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, HostCmd_ACT_GEN_GET, 0, &ver_ext, true)) return -1; @@ -1451,3 +1466,19 @@ mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) return 0; } + +/* This function get Host Sleep wake up reason. + * + */ +int mwifiex_get_wakeup_reason(struct mwifiex_private *priv, u16 action, + int cmd_type, + struct mwifiex_ds_wakeup_reason *wakeup_reason) +{ + int status = 0; + + status = mwifiex_send_cmd(priv, HostCmd_CMD_HS_WAKEUP_REASON, + HostCmd_ACT_GEN_GET, 0, wakeup_reason, + cmd_type == MWIFIEX_SYNC_CMD); + + return status; +} diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 9275f9c3f..150649602 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -680,6 +680,13 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, __net_timestamp(skb); mwifiex_queue_tx_pkt(priv, skb); + /* Delay 10ms to make sure tdls setup confirm/teardown frame + * is received by peer + */ + if (action_code == WLAN_TDLS_SETUP_CONFIRM || + action_code == WLAN_TDLS_TEARDOWN) + msleep_interruptible(10); + return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index e791166d9..16d95b22f 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -192,7 +192,7 @@ mwifiex_set_ht_params(struct mwifiex_private *priv, } priv->ap_11n_enabled = 1; } else { - memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); + memset(&bss_cfg->ht_cap, 0, sizeof(struct ieee80211_ht_cap)); bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; } diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 9f4a4e947..3ae9be7e4 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -244,9 +244,9 @@ setup_for_next: if (card->rx_cmd_ep == context->ep) { mwifiex_usb_submit_rx_urb(context, size); } else { - if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){ + if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING) { mwifiex_usb_submit_rx_urb(context, size); - }else{ + } else { context->skb = NULL; } } diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c index 0cec8a644..6681be051 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.c +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -78,6 +78,16 @@ static struct mwifiex_debug_data items[] = { item_addr(last_event), DBG_CMD_NUM}, {"last_event_index", item_size(last_event_index), item_addr(last_event_index), 1}, + {"last_mp_wr_bitmap", item_size(last_mp_wr_bitmap), + item_addr(last_mp_wr_bitmap), MWIFIEX_DBG_SDIO_MP_NUM}, + {"last_mp_wr_ports", item_size(last_mp_wr_ports), + item_addr(last_mp_wr_ports), MWIFIEX_DBG_SDIO_MP_NUM}, + {"last_mp_wr_len", item_size(last_mp_wr_len), + item_addr(last_mp_wr_len), MWIFIEX_DBG_SDIO_MP_NUM}, + {"last_mp_curr_wr_port", item_size(last_mp_curr_wr_port), + item_addr(last_mp_curr_wr_port), MWIFIEX_DBG_SDIO_MP_NUM}, + {"last_sdio_mp_index", item_size(last_sdio_mp_index), + item_addr(last_sdio_mp_index), 1}, {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), item_addr(num_cmd_host_to_card_failure), 1}, {"num_cmd_sleep_cfm_fail", @@ -233,6 +243,16 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, memcpy(info->last_event, adapter->dbg.last_event, sizeof(adapter->dbg.last_event)); info->last_event_index = adapter->dbg.last_event_index; + memcpy(info->last_mp_wr_bitmap, adapter->dbg.last_mp_wr_bitmap, + sizeof(adapter->dbg.last_mp_wr_bitmap)); + memcpy(info->last_mp_wr_ports, adapter->dbg.last_mp_wr_ports, + sizeof(adapter->dbg.last_mp_wr_ports)); + memcpy(info->last_mp_curr_wr_port, + adapter->dbg.last_mp_curr_wr_port, + sizeof(adapter->dbg.last_mp_curr_wr_port)); + memcpy(info->last_mp_wr_len, adapter->dbg.last_mp_wr_len, + sizeof(adapter->dbg.last_mp_wr_len)); + info->last_sdio_mp_index = adapter->dbg.last_sdio_mp_index; info->data_sent = adapter->data_sent; info->cmd_sent = adapter->cmd_sent; info->cmd_resp_received = adapter->cmd_resp_received; diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 499e5a741..0eb246502 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -438,6 +438,7 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) mwifiex_set_ba_params(priv); mwifiex_reset_11n_rx_seq_num(priv); + priv->wmm.drv_pkt_delay_max = MWIFIEX_WMM_DRV_DELAY_MAX; atomic_set(&priv->wmm.tx_pkts_queued, 0); atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); } diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index e10089602..427b961b7 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -5421,11 +5421,13 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, static int mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { - + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; int i, rc = 0; struct mwl8k_priv *priv = hw->priv; struct mwl8k_ampdu_stream *stream; diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c index f715eee39..e70dd9523 100644 --- a/drivers/net/wireless/mediatek/mt7601u/main.c +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -334,11 +334,13 @@ static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value) static int mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, - bool amsdu) + struct ieee80211_ampdu_params *params) { struct mt7601u_dev *dev = hw->priv; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; WARN_ON(msta->wcid.idx > GROUP_WCID(0)); diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c index 200f7f68b..f0cfea9e7 100644 --- a/drivers/net/wireless/mediatek/mt7601u/mcu.c +++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c @@ -362,7 +362,9 @@ mt7601u_upload_firmware(struct mt7601u_dev *dev, const struct mt76_fw *fw) int i, ret; ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL); - if (!ivb || mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) { + if (!ivb) + return -ENOMEM; + if (mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) { ret = -ENOMEM; goto error; } diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index a26afcab0..7fa0128de 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -7936,10 +7936,11 @@ u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) EXPORT_SYMBOL_GPL(rt2800_get_tsf); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; int ret = 0; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 440790b92..83f1a44fb 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -218,9 +218,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, const struct ieee80211_tx_queue_params *params); u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); int rt2800_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey); void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 26427140a..6418620f9 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -107,7 +107,7 @@ * amount of bytes needed to move the data. */ #define ALIGN_SIZE(__skb, __header) \ - ( ((unsigned long)((__skb)->data + (__header))) & 3 ) + (((unsigned long)((__skb)->data + (__header))) & 3) /* * Constants for extra TX headroom for alignment purposes. @@ -128,14 +128,14 @@ #define SLOT_TIME 20 #define SHORT_SLOT_TIME 9 #define SIFS 10 -#define PIFS ( SIFS + SLOT_TIME ) -#define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) -#define DIFS ( PIFS + SLOT_TIME ) -#define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) -#define EIFS ( SIFS + DIFS + \ - GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) -#define SHORT_EIFS ( SIFS + SHORT_DIFS + \ - GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) +#define PIFS (SIFS + SLOT_TIME) +#define SHORT_PIFS (SIFS + SHORT_SLOT_TIME) +#define DIFS (PIFS + SLOT_TIME) +#define SHORT_DIFS (SHORT_PIFS + SHORT_SLOT_TIME) +#define EIFS (SIFS + DIFS + \ + GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10)) +#define SHORT_EIFS (SIFS + SHORT_DIFS + \ + GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10)) enum rt2x00_chip_intf { RT2X00_CHIP_INTF_PCI, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c index 90fdb02b5..72ae530e4 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00debug.c @@ -478,7 +478,7 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \ { \ struct rt2x00debug_intf *intf = file->private_data; \ const struct rt2x00debug *debug = intf->debug; \ - char line[16]; \ + char line[17]; \ size_t size; \ unsigned int index = intf->offset_##__name; \ __type value; \ @@ -494,7 +494,8 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \ \ if (copy_from_user(line, buf, length)) \ return -EFAULT; \ - \ + line[16] = 0; \ + \ size = strlen(line); \ value = simple_strtoul(line, NULL, 0); \ \ @@ -629,7 +630,7 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, data += sprintf(data, "register\tbase\twords\twordsize\n"); #define RT2X00DEBUGFS_SPRINTF_REGISTER(__name) \ { \ - if(debug->__name.read) \ + if (debug->__name.read) \ data += sprintf(data, __stringify(__name) \ "\t%d\t%d\t%d\n", \ debug->__name.word_base, \ @@ -699,7 +700,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) #define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ ({ \ - if(debug->__name.read) { \ + if (debug->__name.read) { \ (__intf)->__name##_off_entry = \ debugfs_create_u32(__stringify(__name) "_offset", \ S_IRUSR | S_IWUSR, \ diff --git a/drivers/net/wireless/ralink/rt2x00/rt61pci.h b/drivers/net/wireless/ralink/rt2x00/rt61pci.h index 5218f678f..2bbfb8ea0 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt61pci.h +++ b/drivers/net/wireless/ralink/rt2x00/rt61pci.h @@ -138,14 +138,14 @@ #define PAIRWISE_TA_TABLE_BASE 0x1a00 #define SHARED_KEY_ENTRY(__idx) \ - ( SHARED_KEY_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_key_entry)) ) + (SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry))) #define PAIRWISE_KEY_ENTRY(__idx) \ - ( PAIRWISE_KEY_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_key_entry)) ) + (PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry))) #define PAIRWISE_TA_ENTRY(__idx) \ - ( PAIRWISE_TA_TABLE_BASE + \ - ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + (PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry))) struct hw_key_entry { u8 key[16]; @@ -180,7 +180,7 @@ struct hw_pairwise_ta_entry { #define HW_BEACON_BASE3 0x2f00 #define HW_BEACON_OFFSET(__index) \ - ( HW_BEACON_BASE0 + (__index * 0x0100) ) + (HW_BEACON_BASE0 + (__index * 0x0100)) /* * HOST-MCU shared memory. @@ -1287,9 +1287,9 @@ struct hw_pairwise_ta_entry { /* * DMA descriptor defines. */ -#define TXD_DESC_SIZE ( 16 * sizeof(__le32) ) -#define TXINFO_SIZE ( 6 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 16 * sizeof(__le32) ) +#define TXD_DESC_SIZE (16 * sizeof(__le32)) +#define TXINFO_SIZE (6 * sizeof(__le32)) +#define RXD_DESC_SIZE (16 * sizeof(__le32)) /* * TX descriptor format for TX, PRIO and Beacon Ring. diff --git a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig index dd4d626ae..8f053c350 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/Kconfig +++ b/drivers/net/wireless/realtek/rtl8xxxu/Kconfig @@ -13,7 +13,7 @@ config RTL8XXXU This driver is under development and has a limited feature set. In particular it does not yet support 40MHz channels and power management. However it should have a smaller - memory footprint than the vendor drivers and benetifs + memory footprint than the vendor drivers and benefits from the in kernel mac80211 stack. It can coexist with drivers from drivers/staging/rtl8723au, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c index b9c25b682..a275ff145 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.c @@ -42,7 +42,7 @@ #define DRIVER_NAME "rtl8xxxu" -static int rtl8xxxu_debug; +static int rtl8xxxu_debug = RTL8XXXU_DEBUG_EFUSE; static bool rtl8xxxu_ht40_2g; MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@redhat.com>"); @@ -145,6 +145,37 @@ static struct rtl8xxxu_reg8val rtl8723a_mac_init_table[] = { {0x70a, 0x65}, {0x70b, 0x87}, {0xffff, 0xff}, }; +static struct rtl8xxxu_reg8val rtl8723b_mac_init_table[] = { + {0x02f, 0x30}, {0x035, 0x00}, {0x039, 0x08}, {0x04e, 0xe0}, + {0x064, 0x00}, {0x067, 0x20}, {0x428, 0x0a}, {0x429, 0x10}, + {0x430, 0x00}, {0x431, 0x00}, + {0x432, 0x00}, {0x433, 0x01}, {0x434, 0x04}, {0x435, 0x05}, + {0x436, 0x07}, {0x437, 0x08}, {0x43c, 0x04}, {0x43d, 0x05}, + {0x43e, 0x07}, {0x43f, 0x08}, {0x440, 0x5d}, {0x441, 0x01}, + {0x442, 0x00}, {0x444, 0x10}, {0x445, 0x00}, {0x446, 0x00}, + {0x447, 0x00}, {0x448, 0x00}, {0x449, 0xf0}, {0x44a, 0x0f}, + {0x44b, 0x3e}, {0x44c, 0x10}, {0x44d, 0x00}, {0x44e, 0x00}, + {0x44f, 0x00}, {0x450, 0x00}, {0x451, 0xf0}, {0x452, 0x0f}, + {0x453, 0x00}, {0x456, 0x5e}, {0x460, 0x66}, {0x461, 0x66}, + {0x4c8, 0xff}, {0x4c9, 0x08}, {0x4cc, 0xff}, + {0x4cd, 0xff}, {0x4ce, 0x01}, {0x500, 0x26}, {0x501, 0xa2}, + {0x502, 0x2f}, {0x503, 0x00}, {0x504, 0x28}, {0x505, 0xa3}, + {0x506, 0x5e}, {0x507, 0x00}, {0x508, 0x2b}, {0x509, 0xa4}, + {0x50a, 0x5e}, {0x50b, 0x00}, {0x50c, 0x4f}, {0x50d, 0xa4}, + {0x50e, 0x00}, {0x50f, 0x00}, {0x512, 0x1c}, {0x514, 0x0a}, + {0x516, 0x0a}, {0x525, 0x4f}, + {0x550, 0x10}, {0x551, 0x10}, {0x559, 0x02}, {0x55c, 0x50}, + {0x55d, 0xff}, {0x605, 0x30}, {0x608, 0x0e}, {0x609, 0x2a}, + {0x620, 0xff}, {0x621, 0xff}, {0x622, 0xff}, {0x623, 0xff}, + {0x624, 0xff}, {0x625, 0xff}, {0x626, 0xff}, {0x627, 0xff}, + {0x638, 0x50}, {0x63c, 0x0a}, {0x63d, 0x0a}, {0x63e, 0x0e}, + {0x63f, 0x0e}, {0x640, 0x40}, {0x642, 0x40}, {0x643, 0x00}, + {0x652, 0xc8}, {0x66e, 0x05}, {0x700, 0x21}, {0x701, 0x43}, + {0x702, 0x65}, {0x703, 0x87}, {0x708, 0x21}, {0x709, 0x43}, + {0x70a, 0x65}, {0x70b, 0x87}, {0x765, 0x18}, {0x76e, 0x04}, + {0xffff, 0xff}, +}; + static struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = { {0x800, 0x80040000}, {0x804, 0x00000003}, {0x808, 0x0000fc00}, {0x80c, 0x0000000a}, @@ -243,6 +274,107 @@ static struct rtl8xxxu_reg32val rtl8723a_phy_1t_init_table[] = { {0xffff, 0xffffffff}, }; +static struct rtl8xxxu_reg32val rtl8723b_phy_1t_init_table[] = { + {0x800, 0x80040000}, {0x804, 0x00000003}, + {0x808, 0x0000fc00}, {0x80c, 0x0000000a}, + {0x810, 0x10001331}, {0x814, 0x020c3d10}, + {0x818, 0x02200385}, {0x81c, 0x00000000}, + {0x820, 0x01000100}, {0x824, 0x00190204}, + {0x828, 0x00000000}, {0x82c, 0x00000000}, + {0x830, 0x00000000}, {0x834, 0x00000000}, + {0x838, 0x00000000}, {0x83c, 0x00000000}, + {0x840, 0x00010000}, {0x844, 0x00000000}, + {0x848, 0x00000000}, {0x84c, 0x00000000}, + {0x850, 0x00000000}, {0x854, 0x00000000}, + {0x858, 0x569a11a9}, {0x85c, 0x01000014}, + {0x860, 0x66f60110}, {0x864, 0x061f0649}, + {0x868, 0x00000000}, {0x86c, 0x27272700}, + {0x870, 0x07000760}, {0x874, 0x25004000}, + {0x878, 0x00000808}, {0x87c, 0x00000000}, + {0x880, 0xb0000c1c}, {0x884, 0x00000001}, + {0x888, 0x00000000}, {0x88c, 0xccc000c0}, + {0x890, 0x00000800}, {0x894, 0xfffffffe}, + {0x898, 0x40302010}, {0x89c, 0x00706050}, + {0x900, 0x00000000}, {0x904, 0x00000023}, + {0x908, 0x00000000}, {0x90c, 0x81121111}, + {0x910, 0x00000002}, {0x914, 0x00000201}, + {0xa00, 0x00d047c8}, {0xa04, 0x80ff800c}, + {0xa08, 0x8c838300}, {0xa0c, 0x2e7f120f}, + {0xa10, 0x9500bb78}, {0xa14, 0x1114d028}, + {0xa18, 0x00881117}, {0xa1c, 0x89140f00}, + {0xa20, 0x1a1b0000}, {0xa24, 0x090e1317}, + {0xa28, 0x00000204}, {0xa2c, 0x00d30000}, + {0xa70, 0x101fbf00}, {0xa74, 0x00000007}, + {0xa78, 0x00000900}, {0xa7c, 0x225b0606}, + {0xa80, 0x21806490}, {0xb2c, 0x00000000}, + {0xc00, 0x48071d40}, {0xc04, 0x03a05611}, + {0xc08, 0x000000e4}, {0xc0c, 0x6c6c6c6c}, + {0xc10, 0x08800000}, {0xc14, 0x40000100}, + {0xc18, 0x08800000}, {0xc1c, 0x40000100}, + {0xc20, 0x00000000}, {0xc24, 0x00000000}, + {0xc28, 0x00000000}, {0xc2c, 0x00000000}, + {0xc30, 0x69e9ac44}, {0xc34, 0x469652af}, + {0xc38, 0x49795994}, {0xc3c, 0x0a97971c}, + {0xc40, 0x1f7c403f}, {0xc44, 0x000100b7}, + {0xc48, 0xec020107}, {0xc4c, 0x007f037f}, + {0xc50, 0x69553420}, {0xc54, 0x43bc0094}, + {0xc58, 0x00013149}, {0xc5c, 0x00250492}, + {0xc60, 0x00000000}, {0xc64, 0x7112848b}, + {0xc68, 0x47c00bff}, {0xc6c, 0x00000036}, + {0xc70, 0x2c7f000d}, {0xc74, 0x020610db}, + {0xc78, 0x0000001f}, {0xc7c, 0x00b91612}, + {0xc80, 0x390000e4}, {0xc84, 0x20f60000}, + {0xc88, 0x40000100}, {0xc8c, 0x20200000}, + {0xc90, 0x00020e1a}, {0xc94, 0x00000000}, + {0xc98, 0x00020e1a}, {0xc9c, 0x00007f7f}, + {0xca0, 0x00000000}, {0xca4, 0x000300a0}, + {0xca8, 0x00000000}, {0xcac, 0x00000000}, + {0xcb0, 0x00000000}, {0xcb4, 0x00000000}, + {0xcb8, 0x00000000}, {0xcbc, 0x28000000}, + {0xcc0, 0x00000000}, {0xcc4, 0x00000000}, + {0xcc8, 0x00000000}, {0xccc, 0x00000000}, + {0xcd0, 0x00000000}, {0xcd4, 0x00000000}, + {0xcd8, 0x64b22427}, {0xcdc, 0x00766932}, + {0xce0, 0x00222222}, {0xce4, 0x00000000}, + {0xce8, 0x37644302}, {0xcec, 0x2f97d40c}, + {0xd00, 0x00000740}, {0xd04, 0x40020401}, + {0xd08, 0x0000907f}, {0xd0c, 0x20010201}, + {0xd10, 0xa0633333}, {0xd14, 0x3333bc53}, + {0xd18, 0x7a8f5b6f}, {0xd2c, 0xcc979975}, + {0xd30, 0x00000000}, {0xd34, 0x80608000}, + {0xd38, 0x00000000}, {0xd3c, 0x00127353}, + {0xd40, 0x00000000}, {0xd44, 0x00000000}, + {0xd48, 0x00000000}, {0xd4c, 0x00000000}, + {0xd50, 0x6437140a}, {0xd54, 0x00000000}, + {0xd58, 0x00000282}, {0xd5c, 0x30032064}, + {0xd60, 0x4653de68}, {0xd64, 0x04518a3c}, + {0xd68, 0x00002101}, {0xd6c, 0x2a201c16}, + {0xd70, 0x1812362e}, {0xd74, 0x322c2220}, + {0xd78, 0x000e3c24}, {0xe00, 0x2d2d2d2d}, + {0xe04, 0x2d2d2d2d}, {0xe08, 0x0390272d}, + {0xe10, 0x2d2d2d2d}, {0xe14, 0x2d2d2d2d}, + {0xe18, 0x2d2d2d2d}, {0xe1c, 0x2d2d2d2d}, + {0xe28, 0x00000000}, {0xe30, 0x1000dc1f}, + {0xe34, 0x10008c1f}, {0xe38, 0x02140102}, + {0xe3c, 0x681604c2}, {0xe40, 0x01007c00}, + {0xe44, 0x01004800}, {0xe48, 0xfb000000}, + {0xe4c, 0x000028d1}, {0xe50, 0x1000dc1f}, + {0xe54, 0x10008c1f}, {0xe58, 0x02140102}, + {0xe5c, 0x28160d05}, {0xe60, 0x00000008}, + {0xe68, 0x001b2556}, {0xe6c, 0x00c00096}, + {0xe70, 0x00c00096}, {0xe74, 0x01000056}, + {0xe78, 0x01000014}, {0xe7c, 0x01000056}, + {0xe80, 0x01000014}, {0xe84, 0x00c00096}, + {0xe88, 0x01000056}, {0xe8c, 0x00c00096}, + {0xed0, 0x00c00096}, {0xed4, 0x00c00096}, + {0xed8, 0x00c00096}, {0xedc, 0x000000d6}, + {0xee0, 0x000000d6}, {0xeec, 0x01c00016}, + {0xf14, 0x00000003}, {0xf4c, 0x00000000}, + {0xf00, 0x00000300}, + {0x820, 0x01000100}, {0x800, 0x83040000}, + {0xffff, 0xffffffff}, +}; + static struct rtl8xxxu_reg32val rtl8192cu_phy_2t_init_table[] = { {0x024, 0x0011800f}, {0x028, 0x00ffdb83}, {0x800, 0x80040002}, {0x804, 0x00000003}, @@ -608,6 +740,77 @@ static struct rtl8xxxu_reg32val rtl8xxx_agc_highpa_table[] = { {0xffff, 0xffffffff} }; +static struct rtl8xxxu_reg32val rtl8xxx_agc_8723bu_table[] = { + {0xc78, 0xfd000001}, {0xc78, 0xfc010001}, + {0xc78, 0xfb020001}, {0xc78, 0xfa030001}, + {0xc78, 0xf9040001}, {0xc78, 0xf8050001}, + {0xc78, 0xf7060001}, {0xc78, 0xf6070001}, + {0xc78, 0xf5080001}, {0xc78, 0xf4090001}, + {0xc78, 0xf30a0001}, {0xc78, 0xf20b0001}, + {0xc78, 0xf10c0001}, {0xc78, 0xf00d0001}, + {0xc78, 0xef0e0001}, {0xc78, 0xee0f0001}, + {0xc78, 0xed100001}, {0xc78, 0xec110001}, + {0xc78, 0xeb120001}, {0xc78, 0xea130001}, + {0xc78, 0xe9140001}, {0xc78, 0xe8150001}, + {0xc78, 0xe7160001}, {0xc78, 0xe6170001}, + {0xc78, 0xe5180001}, {0xc78, 0xe4190001}, + {0xc78, 0xe31a0001}, {0xc78, 0xa51b0001}, + {0xc78, 0xa41c0001}, {0xc78, 0xa31d0001}, + {0xc78, 0x671e0001}, {0xc78, 0x661f0001}, + {0xc78, 0x65200001}, {0xc78, 0x64210001}, + {0xc78, 0x63220001}, {0xc78, 0x4a230001}, + {0xc78, 0x49240001}, {0xc78, 0x48250001}, + {0xc78, 0x47260001}, {0xc78, 0x46270001}, + {0xc78, 0x45280001}, {0xc78, 0x44290001}, + {0xc78, 0x432a0001}, {0xc78, 0x422b0001}, + {0xc78, 0x292c0001}, {0xc78, 0x282d0001}, + {0xc78, 0x272e0001}, {0xc78, 0x262f0001}, + {0xc78, 0x0a300001}, {0xc78, 0x09310001}, + {0xc78, 0x08320001}, {0xc78, 0x07330001}, + {0xc78, 0x06340001}, {0xc78, 0x05350001}, + {0xc78, 0x04360001}, {0xc78, 0x03370001}, + {0xc78, 0x02380001}, {0xc78, 0x01390001}, + {0xc78, 0x013a0001}, {0xc78, 0x013b0001}, + {0xc78, 0x013c0001}, {0xc78, 0x013d0001}, + {0xc78, 0x013e0001}, {0xc78, 0x013f0001}, + {0xc78, 0xfc400001}, {0xc78, 0xfb410001}, + {0xc78, 0xfa420001}, {0xc78, 0xf9430001}, + {0xc78, 0xf8440001}, {0xc78, 0xf7450001}, + {0xc78, 0xf6460001}, {0xc78, 0xf5470001}, + {0xc78, 0xf4480001}, {0xc78, 0xf3490001}, + {0xc78, 0xf24a0001}, {0xc78, 0xf14b0001}, + {0xc78, 0xf04c0001}, {0xc78, 0xef4d0001}, + {0xc78, 0xee4e0001}, {0xc78, 0xed4f0001}, + {0xc78, 0xec500001}, {0xc78, 0xeb510001}, + {0xc78, 0xea520001}, {0xc78, 0xe9530001}, + {0xc78, 0xe8540001}, {0xc78, 0xe7550001}, + {0xc78, 0xe6560001}, {0xc78, 0xe5570001}, + {0xc78, 0xe4580001}, {0xc78, 0xe3590001}, + {0xc78, 0xa65a0001}, {0xc78, 0xa55b0001}, + {0xc78, 0xa45c0001}, {0xc78, 0xa35d0001}, + {0xc78, 0x675e0001}, {0xc78, 0x665f0001}, + {0xc78, 0x65600001}, {0xc78, 0x64610001}, + {0xc78, 0x63620001}, {0xc78, 0x62630001}, + {0xc78, 0x61640001}, {0xc78, 0x48650001}, + {0xc78, 0x47660001}, {0xc78, 0x46670001}, + {0xc78, 0x45680001}, {0xc78, 0x44690001}, + {0xc78, 0x436a0001}, {0xc78, 0x426b0001}, + {0xc78, 0x286c0001}, {0xc78, 0x276d0001}, + {0xc78, 0x266e0001}, {0xc78, 0x256f0001}, + {0xc78, 0x24700001}, {0xc78, 0x09710001}, + {0xc78, 0x08720001}, {0xc78, 0x07730001}, + {0xc78, 0x06740001}, {0xc78, 0x05750001}, + {0xc78, 0x04760001}, {0xc78, 0x03770001}, + {0xc78, 0x02780001}, {0xc78, 0x01790001}, + {0xc78, 0x017a0001}, {0xc78, 0x017b0001}, + {0xc78, 0x017c0001}, {0xc78, 0x017d0001}, + {0xc78, 0x017e0001}, {0xc78, 0x017f0001}, + {0xc50, 0x69553422}, + {0xc50, 0x69553420}, + {0x824, 0x00390204}, + {0xffff, 0xffffffff} +}; + static struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00039c63}, @@ -683,6 +886,75 @@ static struct rtl8xxxu_rfregval rtl8723au_radioa_1t_init_table[] = { {0xff, 0xffffffff} }; +static struct rtl8xxxu_rfregval rtl8723bu_radioa_1t_init_table[] = { + {0x00, 0x00010000}, {0xb0, 0x000dffe0}, + {0xfe, 0x00000000}, {0xfe, 0x00000000}, + {0xfe, 0x00000000}, {0xb1, 0x00000018}, + {0xfe, 0x00000000}, {0xfe, 0x00000000}, + {0xfe, 0x00000000}, {0xb2, 0x00084c00}, + {0xb5, 0x0000d2cc}, {0xb6, 0x000925aa}, + {0xb7, 0x00000010}, {0xb8, 0x0000907f}, + {0x5c, 0x00000002}, {0x7c, 0x00000002}, + {0x7e, 0x00000005}, {0x8b, 0x0006fc00}, + {0xb0, 0x000ff9f0}, {0x1c, 0x000739d2}, + {0x1e, 0x00000000}, {0xdf, 0x00000780}, + {0x50, 0x00067435}, + /* + * The 8723bu vendor driver indicates that bit 8 should be set in + * 0x51 for package types TFBGA90, TFBGA80, and TFBGA79. However + * they never actually check the package type - and just default + * to not setting it. + */ + {0x51, 0x0006b04e}, + {0x52, 0x000007d2}, {0x53, 0x00000000}, + {0x54, 0x00050400}, {0x55, 0x0004026e}, + {0xdd, 0x0000004c}, {0x70, 0x00067435}, + /* + * 0x71 has same package type condition as for register 0x51 + */ + {0x71, 0x0006b04e}, + {0x72, 0x000007d2}, {0x73, 0x00000000}, + {0x74, 0x00050400}, {0x75, 0x0004026e}, + {0xef, 0x00000100}, {0x34, 0x0000add7}, + {0x35, 0x00005c00}, {0x34, 0x00009dd4}, + {0x35, 0x00005000}, {0x34, 0x00008dd1}, + {0x35, 0x00004400}, {0x34, 0x00007dce}, + {0x35, 0x00003800}, {0x34, 0x00006cd1}, + {0x35, 0x00004400}, {0x34, 0x00005cce}, + {0x35, 0x00003800}, {0x34, 0x000048ce}, + {0x35, 0x00004400}, {0x34, 0x000034ce}, + {0x35, 0x00003800}, {0x34, 0x00002451}, + {0x35, 0x00004400}, {0x34, 0x0000144e}, + {0x35, 0x00003800}, {0x34, 0x00000051}, + {0x35, 0x00004400}, {0xef, 0x00000000}, + {0xef, 0x00000100}, {0xed, 0x00000010}, + {0x44, 0x0000add7}, {0x44, 0x00009dd4}, + {0x44, 0x00008dd1}, {0x44, 0x00007dce}, + {0x44, 0x00006cc1}, {0x44, 0x00005cce}, + {0x44, 0x000044d1}, {0x44, 0x000034ce}, + {0x44, 0x00002451}, {0x44, 0x0000144e}, + {0x44, 0x00000051}, {0xef, 0x00000000}, + {0xed, 0x00000000}, {0x7f, 0x00020080}, + {0xef, 0x00002000}, {0x3b, 0x000380ef}, + {0x3b, 0x000302fe}, {0x3b, 0x00028ce6}, + {0x3b, 0x000200bc}, {0x3b, 0x000188a5}, + {0x3b, 0x00010fbc}, {0x3b, 0x00008f71}, + {0x3b, 0x00000900}, {0xef, 0x00000000}, + {0xed, 0x00000001}, {0x40, 0x000380ef}, + {0x40, 0x000302fe}, {0x40, 0x00028ce6}, + {0x40, 0x000200bc}, {0x40, 0x000188a5}, + {0x40, 0x00010fbc}, {0x40, 0x00008f71}, + {0x40, 0x00000900}, {0xed, 0x00000000}, + {0x82, 0x00080000}, {0x83, 0x00008000}, + {0x84, 0x00048d80}, {0x85, 0x00068000}, + {0xa2, 0x00080000}, {0xa3, 0x00008000}, + {0xa4, 0x00048d80}, {0xa5, 0x00068000}, + {0xed, 0x00000002}, {0xef, 0x00000002}, + {0x56, 0x00000032}, {0x76, 0x00000032}, + {0x01, 0x00000780}, + {0xff, 0xffffffff} +}; + static struct rtl8xxxu_rfregval rtl8192cu_radioa_2t_init_table[] = { {0x00, 0x00030159}, {0x01, 0x00031284}, {0x02, 0x00098000}, {0x03, 0x00018c63}, @@ -1161,6 +1433,11 @@ static u32 rtl8xxxu_read_rfreg(struct rtl8xxxu_priv *priv, return retval; } +/* + * The RTL8723BU driver indicates that registers 0xb2 and 0xb6 can + * have write issues in high temperature conditions. We may have to + * retry writing them. + */ static int rtl8xxxu_write_rfreg(struct rtl8xxxu_priv *priv, enum rtl8xxxu_rfpath path, u8 reg, u32 data) { @@ -1186,7 +1463,8 @@ static int rtl8xxxu_write_rfreg(struct rtl8xxxu_priv *priv, return retval; } -static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c) +static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, + struct h2c_cmd *h2c, int len) { struct device *dev = &priv->udev->dev; int mbox_nr, retry, retval = 0; @@ -1197,7 +1475,8 @@ static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c) mbox_nr = priv->next_mbox; mbox_reg = REG_HMBOX_0 + (mbox_nr * 4); - mbox_ext_reg = REG_HMBOX_EXT_0 + (mbox_nr * 2); + mbox_ext_reg = priv->fops->mbox_ext_reg + + (mbox_nr * priv->fops->mbox_ext_width); /* * MBOX ready? @@ -1210,7 +1489,7 @@ static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c) } while (retry--); if (!retry) { - dev_dbg(dev, "%s: Mailbox busy\n", __func__); + dev_info(dev, "%s: Mailbox busy\n", __func__); retval = -EBUSY; goto error; } @@ -1218,12 +1497,20 @@ static int rtl8723a_h2c_cmd(struct rtl8xxxu_priv *priv, struct h2c_cmd *h2c) /* * Need to swap as it's being swapped again by rtl8xxxu_write16/32() */ - if (h2c->cmd.cmd & H2C_EXT) { - rtl8xxxu_write16(priv, mbox_ext_reg, - le16_to_cpu(h2c->raw.ext)); - if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C) - dev_info(dev, "H2C_EXT %04x\n", - le16_to_cpu(h2c->raw.ext)); + if (len > sizeof(u32)) { + if (priv->fops->mbox_ext_width == 4) { + rtl8xxxu_write32(priv, mbox_ext_reg, + le32_to_cpu(h2c->raw_wide.ext)); + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C) + dev_info(dev, "H2C_EXT %08x\n", + le32_to_cpu(h2c->raw_wide.ext)); + } else { + rtl8xxxu_write16(priv, mbox_ext_reg, + le16_to_cpu(h2c->raw.ext)); + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C) + dev_info(dev, "H2C_EXT %04x\n", + le16_to_cpu(h2c->raw.ext)); + } } rtl8xxxu_write32(priv, mbox_reg, le32_to_cpu(h2c->raw.data)); if (rtl8xxxu_debug & RTL8XXXU_DEBUG_H2C) @@ -1236,6 +1523,27 @@ error: return retval; } +static void rtl8723bu_write_btreg(struct rtl8xxxu_priv *priv, u8 reg, u8 data) +{ + struct h2c_cmd h2c; + int reqnum = 0; + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.bt_mp_oper.cmd = H2C_8723B_BT_MP_OPER; + h2c.bt_mp_oper.operreq = 0 | (reqnum << 4); + h2c.bt_mp_oper.opcode = BT_MP_OP_WRITE_REG_VALUE; + h2c.bt_mp_oper.data = data; + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_mp_oper)); + + reqnum++; + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.bt_mp_oper.cmd = H2C_8723B_BT_MP_OPER; + h2c.bt_mp_oper.operreq = 0 | (reqnum << 4); + h2c.bt_mp_oper.opcode = BT_MP_OP_WRITE_REG_VALUE; + h2c.bt_mp_oper.addr = reg; + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_mp_oper)); +} + static void rtl8723a_enable_rf(struct rtl8xxxu_priv *priv) { u8 val8; @@ -1360,6 +1668,24 @@ static int rtl8723a_channel_to_group(int channel) return group; } +static int rtl8723b_channel_to_group(int channel) +{ + int group; + + if (channel < 3) + group = 0; + else if (channel < 6) + group = 1; + else if (channel < 9) + group = 2; + else if (channel < 12) + group = 3; + else + group = 4; + + return group; +} + static void rtl8723au_config_channel(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; @@ -1482,6 +1808,136 @@ static void rtl8723au_config_channel(struct ieee80211_hw *hw) } } +static void rtl8723bu_config_channel(struct ieee80211_hw *hw) +{ + struct rtl8xxxu_priv *priv = hw->priv; + u32 val32, rsr; + u8 val8, subchannel; + u16 rf_mode_bw; + bool ht = true; + int sec_ch_above, channel; + int i; + + rf_mode_bw = rtl8xxxu_read16(priv, REG_WMAC_TRXPTCL_CTL); + rf_mode_bw &= ~WMAC_TRXPTCL_CTL_BW_MASK; + rsr = rtl8xxxu_read32(priv, REG_RESPONSE_RATE_SET); + channel = hw->conf.chandef.chan->hw_value; + +/* Hack */ + subchannel = 0; + + switch (hw->conf.chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + ht = false; + case NL80211_CHAN_WIDTH_20: + rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_20; + subchannel = 0; + + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 &= ~FPGA_RF_MODE; + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE); + val32 &= ~FPGA_RF_MODE; + rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32); + + val32 = rtl8xxxu_read32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT); + val32 &= ~(BIT(30) | BIT(31)); + rtl8xxxu_write32(priv, REG_OFDM0_TX_PSDO_NOISE_WEIGHT, val32); + + break; + case NL80211_CHAN_WIDTH_40: + rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_40; + + if (hw->conf.chandef.center_freq1 > + hw->conf.chandef.chan->center_freq) { + sec_ch_above = 1; + channel += 2; + } else { + sec_ch_above = 0; + channel -= 2; + } + + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + val32 |= FPGA_RF_MODE; + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + + val32 = rtl8xxxu_read32(priv, REG_FPGA1_RF_MODE); + val32 |= FPGA_RF_MODE; + rtl8xxxu_write32(priv, REG_FPGA1_RF_MODE, val32); + + /* + * Set Control channel to upper or lower. These settings + * are required only for 40MHz + */ + val32 = rtl8xxxu_read32(priv, REG_CCK0_SYSTEM); + val32 &= ~CCK0_SIDEBAND; + if (!sec_ch_above) + val32 |= CCK0_SIDEBAND; + rtl8xxxu_write32(priv, REG_CCK0_SYSTEM, val32); + + val32 = rtl8xxxu_read32(priv, REG_OFDM1_LSTF); + val32 &= ~OFDM_LSTF_PRIME_CH_MASK; /* 0xc00 */ + if (sec_ch_above) + val32 |= OFDM_LSTF_PRIME_CH_LOW; + else + val32 |= OFDM_LSTF_PRIME_CH_HIGH; + rtl8xxxu_write32(priv, REG_OFDM1_LSTF, val32); + + val32 = rtl8xxxu_read32(priv, REG_FPGA0_POWER_SAVE); + val32 &= ~(FPGA0_PS_LOWER_CHANNEL | FPGA0_PS_UPPER_CHANNEL); + if (sec_ch_above) + val32 |= FPGA0_PS_UPPER_CHANNEL; + else + val32 |= FPGA0_PS_LOWER_CHANNEL; + rtl8xxxu_write32(priv, REG_FPGA0_POWER_SAVE, val32); + break; + case NL80211_CHAN_WIDTH_80: + rf_mode_bw |= WMAC_TRXPTCL_CTL_BW_80; + break; + default: + break; + } + + for (i = RF_A; i < priv->rf_paths; i++) { + val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG); + val32 &= ~MODE_AG_CHANNEL_MASK; + val32 |= channel; + rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32); + } + + rtl8xxxu_write16(priv, REG_WMAC_TRXPTCL_CTL, rf_mode_bw); + rtl8xxxu_write8(priv, REG_DATA_SUBCHANNEL, subchannel); + + if (ht) + val8 = 0x0e; + else + val8 = 0x0a; + + rtl8xxxu_write8(priv, REG_SIFS_CCK + 1, val8); + rtl8xxxu_write8(priv, REG_SIFS_OFDM + 1, val8); + + rtl8xxxu_write16(priv, REG_R2T_SIFS, 0x0808); + rtl8xxxu_write16(priv, REG_T2T_SIFS, 0x0a0a); + + for (i = RF_A; i < priv->rf_paths; i++) { + val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG); + val32 &= ~MODE_AG_BW_MASK; + switch(hw->conf.chandef.width) { + case NL80211_CHAN_WIDTH_80: + val32 |= MODE_AG_BW_80MHZ_8723B; + break; + case NL80211_CHAN_WIDTH_40: + val32 |= MODE_AG_BW_40MHZ_8723B; + break; + default: + val32 |= MODE_AG_BW_20MHZ_8723B; + break; + } + rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32); + } +} + static void rtl8723a_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40) { @@ -1591,12 +2047,51 @@ rtl8723a_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40) } } +static void +rtl8723b_set_tx_power(struct rtl8xxxu_priv *priv, int channel, bool ht40) +{ + u32 val32, ofdm, mcs; + u8 cck, ofdmbase, mcsbase; + int group, tx_idx; + + tx_idx = 0; + group = rtl8723b_channel_to_group(channel); + + cck = priv->cck_tx_power_index_B[group]; + val32 = rtl8xxxu_read32(priv, REG_TX_AGC_A_CCK1_MCS32); + val32 &= 0xffff00ff; + val32 |= (cck << 8); + rtl8xxxu_write32(priv, REG_TX_AGC_A_CCK1_MCS32, val32); + + val32 = rtl8xxxu_read32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11); + val32 &= 0xff; + val32 |= ((cck << 8) | (cck << 16) | (cck << 24)); + rtl8xxxu_write32(priv, REG_TX_AGC_B_CCK11_A_CCK2_11, val32); + + ofdmbase = priv->ht40_1s_tx_power_index_B[group]; + ofdmbase += priv->ofdm_tx_power_diff[tx_idx].b; + ofdm = ofdmbase | ofdmbase << 8 | ofdmbase << 16 | ofdmbase << 24; + + rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE18_06, ofdm); + rtl8xxxu_write32(priv, REG_TX_AGC_A_RATE54_24, ofdm); + + mcsbase = priv->ht40_1s_tx_power_index_B[group]; + if (ht40) + mcsbase += priv->ht40_tx_power_diff[tx_idx++].b; + else + mcsbase += priv->ht20_tx_power_diff[tx_idx++].b; + mcs = mcsbase | mcsbase << 8 | mcsbase << 16 | mcsbase << 24; + + rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS03_MCS00, mcs); + rtl8xxxu_write32(priv, REG_TX_AGC_A_MCS07_MCS04, mcs); +} + static void rtl8xxxu_set_linktype(struct rtl8xxxu_priv *priv, enum nl80211_iftype linktype) { - u16 val8; + u8 val8; - val8 = rtl8xxxu_read16(priv, REG_MSR); + val8 = rtl8xxxu_read8(priv, REG_MSR); val8 &= ~MSR_LINKTYPE_MASK; switch (linktype) { @@ -1657,16 +2152,24 @@ static void rtl8xxxu_print_chipinfo(struct rtl8xxxu_priv *priv) case 1: cut = "B"; break; + case 2: + cut = "C"; + break; + case 3: + cut = "D"; + break; + case 4: + cut = "E"; + break; default: cut = "unknown"; } dev_info(dev, "RTL%s rev %s (%s) %iT%iR, TX queues %i, WiFi=%i, BT=%i, GPS=%i, HI PA=%i\n", - priv->chip_name, cut, priv->vendor_umc ? "UMC" : "TSMC", - priv->tx_paths, priv->rx_paths, priv->ep_tx_count, - priv->has_wifi, priv->has_bluetooth, priv->has_gps, - priv->hi_pa); + priv->chip_name, cut, priv->chip_vendor, priv->tx_paths, + priv->rx_paths, priv->ep_tx_count, priv->has_wifi, + priv->has_bluetooth, priv->has_gps, priv->hi_pa); dev_info(dev, "RTL%s MAC: %pM\n", priv->chip_name, priv->mac_addr); } @@ -1686,11 +2189,18 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) } if (val32 & SYS_CFG_BT_FUNC) { - sprintf(priv->chip_name, "8723AU"); + if (priv->chip_cut >= 3) { + sprintf(priv->chip_name, "8723BU"); + priv->rtlchip = 0x8723b; + } else { + sprintf(priv->chip_name, "8723AU"); + priv->usb_interrupts = 1; + priv->rtlchip = 0x8723a; + } + priv->rf_paths = 1; priv->rx_paths = 1; priv->tx_paths = 1; - priv->rtlchip = 0x8723a; val32 = rtl8xxxu_read32(priv, REG_MULTI_FUNC_CTRL); if (val32 & MULTI_WIFI_FUNC_EN) @@ -1699,20 +2209,37 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) priv->has_bluetooth = 1; if (val32 & MULTI_GPS_FUNC_EN) priv->has_gps = 1; + priv->is_multi_func = 1; } else if (val32 & SYS_CFG_TYPE_ID) { bonding = rtl8xxxu_read32(priv, REG_HPON_FSM); bonding &= HPON_FSM_BONDING_MASK; - if (bonding == HPON_FSM_BONDING_1T2R) { + if (priv->chip_cut >= 3) { + if (bonding == HPON_FSM_BONDING_1T2R) { + sprintf(priv->chip_name, "8191EU"); + priv->rf_paths = 2; + priv->rx_paths = 2; + priv->tx_paths = 1; + priv->rtlchip = 0x8191e; + } else { + sprintf(priv->chip_name, "8192EU"); + priv->rf_paths = 2; + priv->rx_paths = 2; + priv->tx_paths = 2; + priv->rtlchip = 0x8192e; + } + } else if (bonding == HPON_FSM_BONDING_1T2R) { sprintf(priv->chip_name, "8191CU"); priv->rf_paths = 2; priv->rx_paths = 2; priv->tx_paths = 1; + priv->usb_interrupts = 1; priv->rtlchip = 0x8191c; } else { sprintf(priv->chip_name, "8192CU"); priv->rf_paths = 2; priv->rx_paths = 2; priv->tx_paths = 2; + priv->usb_interrupts = 1; priv->rtlchip = 0x8192c; } priv->has_wifi = 1; @@ -1722,11 +2249,38 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) priv->rx_paths = 1; priv->tx_paths = 1; priv->rtlchip = 0x8188c; + priv->usb_interrupts = 1; priv->has_wifi = 1; } - if (val32 & SYS_CFG_VENDOR_ID) - priv->vendor_umc = 1; + switch (priv->rtlchip) { + case 0x8188e: + case 0x8192e: + case 0x8723b: + switch (val32 & SYS_CFG_VENDOR_EXT_MASK) { + case SYS_CFG_VENDOR_ID_TSMC: + sprintf(priv->chip_vendor, "TSMC"); + break; + case SYS_CFG_VENDOR_ID_SMIC: + sprintf(priv->chip_vendor, "SMIC"); + priv->vendor_smic = 1; + break; + case SYS_CFG_VENDOR_ID_UMC: + sprintf(priv->chip_vendor, "UMC"); + priv->vendor_umc = 1; + break; + default: + sprintf(priv->chip_vendor, "unknown"); + } + break; + default: + if (val32 & SYS_CFG_VENDOR_ID) { + sprintf(priv->chip_vendor, "UMC"); + priv->vendor_umc = 1; + } else { + sprintf(priv->chip_vendor, "TSMC"); + } + } val32 = rtl8xxxu_read32(priv, REG_GPIO_OUTSTS); priv->rom_rev = (val32 & GPIO_RF_RL_ID) >> 28; @@ -1752,6 +2306,7 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) */ if (!priv->ep_tx_count) { switch (priv->nr_out_eps) { + case 4: case 3: priv->ep_tx_low_queue = 1; priv->ep_tx_count++; @@ -1773,43 +2328,126 @@ static int rtl8xxxu_identify_chip(struct rtl8xxxu_priv *priv) static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv) { - if (priv->efuse_wifi.efuse8723.rtl_id != cpu_to_le16(0x8129)) + struct rtl8723au_efuse *efuse = &priv->efuse_wifi.efuse8723; + + if (efuse->rtl_id != cpu_to_le16(0x8129)) return -EINVAL; - ether_addr_copy(priv->mac_addr, priv->efuse_wifi.efuse8723.mac_addr); + ether_addr_copy(priv->mac_addr, efuse->mac_addr); memcpy(priv->cck_tx_power_index_A, - priv->efuse_wifi.efuse8723.cck_tx_power_index_A, - sizeof(priv->cck_tx_power_index_A)); + efuse->cck_tx_power_index_A, + sizeof(efuse->cck_tx_power_index_A)); memcpy(priv->cck_tx_power_index_B, - priv->efuse_wifi.efuse8723.cck_tx_power_index_B, - sizeof(priv->cck_tx_power_index_B)); + efuse->cck_tx_power_index_B, + sizeof(efuse->cck_tx_power_index_B)); memcpy(priv->ht40_1s_tx_power_index_A, - priv->efuse_wifi.efuse8723.ht40_1s_tx_power_index_A, - sizeof(priv->ht40_1s_tx_power_index_A)); + efuse->ht40_1s_tx_power_index_A, + sizeof(efuse->ht40_1s_tx_power_index_A)); memcpy(priv->ht40_1s_tx_power_index_B, - priv->efuse_wifi.efuse8723.ht40_1s_tx_power_index_B, - sizeof(priv->ht40_1s_tx_power_index_B)); + efuse->ht40_1s_tx_power_index_B, + sizeof(efuse->ht40_1s_tx_power_index_B)); memcpy(priv->ht20_tx_power_index_diff, - priv->efuse_wifi.efuse8723.ht20_tx_power_index_diff, - sizeof(priv->ht20_tx_power_index_diff)); + efuse->ht20_tx_power_index_diff, + sizeof(efuse->ht20_tx_power_index_diff)); memcpy(priv->ofdm_tx_power_index_diff, - priv->efuse_wifi.efuse8723.ofdm_tx_power_index_diff, - sizeof(priv->ofdm_tx_power_index_diff)); + efuse->ofdm_tx_power_index_diff, + sizeof(efuse->ofdm_tx_power_index_diff)); memcpy(priv->ht40_max_power_offset, - priv->efuse_wifi.efuse8723.ht40_max_power_offset, - sizeof(priv->ht40_max_power_offset)); + efuse->ht40_max_power_offset, + sizeof(efuse->ht40_max_power_offset)); memcpy(priv->ht20_max_power_offset, - priv->efuse_wifi.efuse8723.ht20_max_power_offset, - sizeof(priv->ht20_max_power_offset)); + efuse->ht20_max_power_offset, + sizeof(efuse->ht20_max_power_offset)); + if (priv->efuse_wifi.efuse8723.version >= 0x01) { + priv->has_xtalk = 1; + priv->xtalk = priv->efuse_wifi.efuse8723.xtal_k & 0x3f; + } dev_info(&priv->udev->dev, "Vendor: %.7s\n", - priv->efuse_wifi.efuse8723.vendor_name); + efuse->vendor_name); dev_info(&priv->udev->dev, "Product: %.41s\n", - priv->efuse_wifi.efuse8723.device_name); + efuse->device_name); + return 0; +} + +static int rtl8723bu_parse_efuse(struct rtl8xxxu_priv *priv) +{ + struct rtl8723bu_efuse *efuse = &priv->efuse_wifi.efuse8723bu; + int i; + + if (efuse->rtl_id != cpu_to_le16(0x8129)) + return -EINVAL; + + ether_addr_copy(priv->mac_addr, efuse->mac_addr); + + memcpy(priv->cck_tx_power_index_A, efuse->tx_power_index_A.cck_base, + sizeof(efuse->tx_power_index_A.cck_base)); + memcpy(priv->cck_tx_power_index_B, efuse->tx_power_index_B.cck_base, + sizeof(efuse->tx_power_index_B.cck_base)); + + memcpy(priv->ht40_1s_tx_power_index_A, + efuse->tx_power_index_A.ht40_base, + sizeof(efuse->tx_power_index_A.ht40_base)); + memcpy(priv->ht40_1s_tx_power_index_B, + efuse->tx_power_index_B.ht40_base, + sizeof(efuse->tx_power_index_B.ht40_base)); + + priv->ofdm_tx_power_diff[0].a = + efuse->tx_power_index_A.ht20_ofdm_1s_diff.a; + priv->ofdm_tx_power_diff[0].b = + efuse->tx_power_index_B.ht20_ofdm_1s_diff.a; + + priv->ht20_tx_power_diff[0].a = + efuse->tx_power_index_A.ht20_ofdm_1s_diff.b; + priv->ht20_tx_power_diff[0].b = + efuse->tx_power_index_B.ht20_ofdm_1s_diff.b; + + priv->ht40_tx_power_diff[0].a = 0; + priv->ht40_tx_power_diff[0].b = 0; + + for (i = 1; i < RTL8723B_TX_COUNT; i++) { + priv->ofdm_tx_power_diff[i].a = + efuse->tx_power_index_A.pwr_diff[i - 1].ofdm; + priv->ofdm_tx_power_diff[i].b = + efuse->tx_power_index_B.pwr_diff[i - 1].ofdm; + + priv->ht20_tx_power_diff[i].a = + efuse->tx_power_index_A.pwr_diff[i - 1].ht20; + priv->ht20_tx_power_diff[i].b = + efuse->tx_power_index_B.pwr_diff[i - 1].ht20; + + priv->ht40_tx_power_diff[i].a = + efuse->tx_power_index_A.pwr_diff[i - 1].ht40; + priv->ht40_tx_power_diff[i].b = + efuse->tx_power_index_B.pwr_diff[i - 1].ht40; + } + + priv->has_xtalk = 1; + priv->xtalk = priv->efuse_wifi.efuse8723bu.xtal_k & 0x3f; + + dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name); + dev_info(&priv->udev->dev, "Product: %.41s\n", efuse->device_name); + + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) { + int i; + unsigned char *raw = priv->efuse_wifi.raw; + + dev_info(&priv->udev->dev, + "%s: dumping efuse (0x%02zx bytes):\n", + __func__, sizeof(struct rtl8723bu_efuse)); + for (i = 0; i < sizeof(struct rtl8723bu_efuse); i += 8) { + dev_info(&priv->udev->dev, "%02x: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, + raw[i], raw[i + 1], raw[i + 2], + raw[i + 3], raw[i + 4], raw[i + 5], + raw[i + 6], raw[i + 7]); + } + } + return 0; } @@ -1817,50 +2455,51 @@ static int rtl8723au_parse_efuse(struct rtl8xxxu_priv *priv) static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv) { + struct rtl8192cu_efuse *efuse = &priv->efuse_wifi.efuse8192; int i; - if (priv->efuse_wifi.efuse8192.rtl_id != cpu_to_le16(0x8129)) + if (efuse->rtl_id != cpu_to_le16(0x8129)) return -EINVAL; - ether_addr_copy(priv->mac_addr, priv->efuse_wifi.efuse8192.mac_addr); + ether_addr_copy(priv->mac_addr, efuse->mac_addr); memcpy(priv->cck_tx_power_index_A, - priv->efuse_wifi.efuse8192.cck_tx_power_index_A, - sizeof(priv->cck_tx_power_index_A)); + efuse->cck_tx_power_index_A, + sizeof(efuse->cck_tx_power_index_A)); memcpy(priv->cck_tx_power_index_B, - priv->efuse_wifi.efuse8192.cck_tx_power_index_B, - sizeof(priv->cck_tx_power_index_B)); + efuse->cck_tx_power_index_B, + sizeof(efuse->cck_tx_power_index_B)); memcpy(priv->ht40_1s_tx_power_index_A, - priv->efuse_wifi.efuse8192.ht40_1s_tx_power_index_A, - sizeof(priv->ht40_1s_tx_power_index_A)); + efuse->ht40_1s_tx_power_index_A, + sizeof(efuse->ht40_1s_tx_power_index_A)); memcpy(priv->ht40_1s_tx_power_index_B, - priv->efuse_wifi.efuse8192.ht40_1s_tx_power_index_B, - sizeof(priv->ht40_1s_tx_power_index_B)); + efuse->ht40_1s_tx_power_index_B, + sizeof(efuse->ht40_1s_tx_power_index_B)); memcpy(priv->ht40_2s_tx_power_index_diff, - priv->efuse_wifi.efuse8192.ht40_2s_tx_power_index_diff, - sizeof(priv->ht40_2s_tx_power_index_diff)); + efuse->ht40_2s_tx_power_index_diff, + sizeof(efuse->ht40_2s_tx_power_index_diff)); memcpy(priv->ht20_tx_power_index_diff, - priv->efuse_wifi.efuse8192.ht20_tx_power_index_diff, - sizeof(priv->ht20_tx_power_index_diff)); + efuse->ht20_tx_power_index_diff, + sizeof(efuse->ht20_tx_power_index_diff)); memcpy(priv->ofdm_tx_power_index_diff, - priv->efuse_wifi.efuse8192.ofdm_tx_power_index_diff, - sizeof(priv->ofdm_tx_power_index_diff)); + efuse->ofdm_tx_power_index_diff, + sizeof(efuse->ofdm_tx_power_index_diff)); memcpy(priv->ht40_max_power_offset, - priv->efuse_wifi.efuse8192.ht40_max_power_offset, - sizeof(priv->ht40_max_power_offset)); + efuse->ht40_max_power_offset, + sizeof(efuse->ht40_max_power_offset)); memcpy(priv->ht20_max_power_offset, - priv->efuse_wifi.efuse8192.ht20_max_power_offset, - sizeof(priv->ht20_max_power_offset)); + efuse->ht20_max_power_offset, + sizeof(efuse->ht20_max_power_offset)); dev_info(&priv->udev->dev, "Vendor: %.7s\n", - priv->efuse_wifi.efuse8192.vendor_name); + efuse->vendor_name); dev_info(&priv->udev->dev, "Product: %.20s\n", - priv->efuse_wifi.efuse8192.device_name); + efuse->device_name); - if (priv->efuse_wifi.efuse8192.rf_regulatory & 0x20) { + if (efuse->rf_regulatory & 0x20) { sprintf(priv->chip_name, "8188RU"); priv->hi_pa = 1; } @@ -1884,6 +2523,44 @@ static int rtl8192cu_parse_efuse(struct rtl8xxxu_priv *priv) #endif +static int rtl8192eu_parse_efuse(struct rtl8xxxu_priv *priv) +{ + struct rtl8192eu_efuse *efuse = &priv->efuse_wifi.efuse8192eu; + int i; + + if (efuse->rtl_id != cpu_to_le16(0x8129)) + return -EINVAL; + + ether_addr_copy(priv->mac_addr, efuse->mac_addr); + + priv->has_xtalk = 1; + priv->xtalk = priv->efuse_wifi.efuse8192eu.xtal_k & 0x3f; + + dev_info(&priv->udev->dev, "Vendor: %.7s\n", efuse->vendor_name); + dev_info(&priv->udev->dev, "Product: %.11s\n", efuse->device_name); + dev_info(&priv->udev->dev, "Serial: %.11s\n", efuse->serial); + + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE) { + unsigned char *raw = priv->efuse_wifi.raw; + + dev_info(&priv->udev->dev, + "%s: dumping efuse (0x%02zx bytes):\n", + __func__, sizeof(struct rtl8192eu_efuse)); + for (i = 0; i < sizeof(struct rtl8192eu_efuse); i += 8) { + dev_info(&priv->udev->dev, "%02x: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", i, + raw[i], raw[i + 1], raw[i + 2], + raw[i + 3], raw[i + 4], raw[i + 5], + raw[i + 6], raw[i + 7]); + } + } + /* + * Temporarily disable 8192eu support + */ + return -EINVAL; + return 0; +} + static int rtl8xxxu_read_efuse8(struct rtl8xxxu_priv *priv, u16 offset, u8 *data) { @@ -1933,9 +2610,11 @@ static int rtl8xxxu_read_efuse(struct rtl8xxxu_priv *priv) if (val16 & EEPROM_BOOT) priv->boot_eeprom = 1; - val32 = rtl8xxxu_read32(priv, REG_EFUSE_TEST); - val32 = (val32 & ~EFUSE_SELECT_MASK) | EFUSE_WIFI_SELECT; - rtl8xxxu_write32(priv, REG_EFUSE_TEST, val32); + if (priv->is_multi_func) { + val32 = rtl8xxxu_read32(priv, REG_EFUSE_TEST); + val32 = (val32 & ~EFUSE_SELECT_MASK) | EFUSE_WIFI_SELECT; + rtl8xxxu_write32(priv, REG_EFUSE_TEST, val32); + } dev_dbg(dev, "Booting from %s\n", priv->boot_eeprom ? "EEPROM" : "EFUSE"); @@ -1965,10 +2644,12 @@ static int rtl8xxxu_read_efuse(struct rtl8xxxu_priv *priv) } /* Default value is 0xff */ - memset(priv->efuse_wifi.raw, 0xff, EFUSE_MAP_LEN_8723A); + memset(priv->efuse_wifi.raw, 0xff, EFUSE_MAP_LEN); efuse_addr = 0; while (efuse_addr < EFUSE_REAL_CONTENT_LEN_8723A) { + u16 map_addr; + ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &header); if (ret || header == 0xff) goto exit; @@ -1991,45 +2672,34 @@ static int rtl8xxxu_read_efuse(struct rtl8xxxu_priv *priv) word_mask = header & 0x0f; } - if (offset < EFUSE_MAX_SECTION_8723A) { - u16 map_addr; - /* Get word enable value from PG header */ + /* Get word enable value from PG header */ - /* We have 8 bits to indicate validity */ - map_addr = offset * 8; - if (map_addr >= EFUSE_MAP_LEN_8723A) { - dev_warn(dev, "%s: Illegal map_addr (%04x), " - "efuse corrupt!\n", - __func__, map_addr); - ret = -EINVAL; - goto exit; - } - for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { - /* Check word enable condition in the section */ - if (!(word_mask & BIT(i))) { - ret = rtl8xxxu_read_efuse8(priv, - efuse_addr++, - &val8); - if (ret) - goto exit; - priv->efuse_wifi.raw[map_addr++] = val8; - - ret = rtl8xxxu_read_efuse8(priv, - efuse_addr++, - &val8); - if (ret) - goto exit; - priv->efuse_wifi.raw[map_addr++] = val8; - } else - map_addr += 2; - } - } else { - dev_warn(dev, - "%s: Illegal offset (%04x), efuse corrupt!\n", - __func__, offset); + /* We have 8 bits to indicate validity */ + map_addr = offset * 8; + if (map_addr >= EFUSE_MAP_LEN) { + dev_warn(dev, "%s: Illegal map_addr (%04x), " + "efuse corrupt!\n", + __func__, map_addr); ret = -EINVAL; goto exit; } + for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { + /* Check word enable condition in the section */ + if (word_mask & BIT(i)) { + map_addr += 2; + continue; + } + + ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &val8); + if (ret) + goto exit; + priv->efuse_wifi.raw[map_addr++] = val8; + + ret = rtl8xxxu_read_efuse8(priv, efuse_addr++, &val8); + if (ret) + goto exit; + priv->efuse_wifi.raw[map_addr++] = val8; + } } exit: @@ -2038,6 +2708,56 @@ exit: return ret; } +static void rtl8xxxu_reset_8051(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 sys_func; + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); + val8 &= ~BIT(0); + rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); + + sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC); + sys_func &= ~SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func); + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); + val8 |= BIT(0); + rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); + + sys_func |= SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func); +} + +static void rtl8723bu_reset_8051(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 sys_func; + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); + val8 &= ~BIT(1); + rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); + val8 &= ~BIT(0); + rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); + + sys_func = rtl8xxxu_read16(priv, REG_SYS_FUNC); + sys_func &= ~SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func); + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); + val8 &= ~BIT(1); + rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1); + val8 |= BIT(0); + rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8); + + sys_func |= SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, sys_func); +} + static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv) { struct device *dev = &priv->udev->dev; @@ -2062,6 +2782,12 @@ static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv) val32 &= ~MCU_WINT_INIT_READY; rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32); + /* + * Reset the 8051 in order for the firmware to start running, + * otherwise it won't come up on the 8192eu + */ + priv->fops->reset_8051(priv); + /* Wait for firmware to become ready */ for (i = 0; i < RTL8XXXU_FIRMWARE_POLL_MAX; i++) { val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL); @@ -2077,6 +2803,11 @@ static int rtl8xxxu_start_firmware(struct rtl8xxxu_priv *priv) goto exit; } + /* + * Init H2C command + */ + if (priv->rtlchip == 0x8723b) + rtl8xxxu_write8(priv, REG_HMTFR, 0x0f); exit: return ret; } @@ -2095,19 +2826,30 @@ static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv) /* 8051 enable */ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); - rtl8xxxu_write16(priv, REG_SYS_FUNC, val16 | SYS_FUNC_CPU_ENABLE); + val16 |= SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + + val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL); + if (val8 & MCU_FW_RAM_SEL) { + pr_info("do the RAM reset\n"); + rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); + priv->fops->reset_8051(priv); + } /* MCU firmware download enable */ val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL); - rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8 | MCU_FW_DL_ENABLE); + val8 |= MCU_FW_DL_ENABLE; + rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8); /* 8051 reset */ val32 = rtl8xxxu_read32(priv, REG_MCU_FW_DL); - rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32 & ~BIT(19)); + val32 &= ~BIT(19); + rtl8xxxu_write32(priv, REG_MCU_FW_DL, val32); /* Reset firmware download checksum */ val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL); - rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8 | MCU_FW_DL_CSUM_REPORT); + val8 |= MCU_FW_DL_CSUM_REPORT; + rtl8xxxu_write8(priv, REG_MCU_FW_DL, val8); pages = priv->fw_size / RTL_FW_PAGE_SIZE; remainder = priv->fw_size % RTL_FW_PAGE_SIZE; @@ -2116,7 +2858,8 @@ static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv) for (i = 0; i < pages; i++) { val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL + 2) & 0xF8; - rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8 | i); + val8 |= i; + rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8); ret = rtl8xxxu_writeN(priv, REG_FW_START_ADDRESS, fwptr, RTL_FW_PAGE_SIZE); @@ -2130,7 +2873,8 @@ static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv) if (remainder) { val8 = rtl8xxxu_read8(priv, REG_MCU_FW_DL + 2) & 0xF8; - rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8 | i); + val8 |= i; + rtl8xxxu_write8(priv, REG_MCU_FW_DL + 2, val8); ret = rtl8xxxu_writeN(priv, REG_FW_START_ADDRESS, fwptr, remainder); if (ret != remainder) { @@ -2143,8 +2887,8 @@ static int rtl8xxxu_download_firmware(struct rtl8xxxu_priv *priv) fw_abort: /* MCU firmware download disable */ val16 = rtl8xxxu_read16(priv, REG_MCU_FW_DL); - rtl8xxxu_write16(priv, REG_MCU_FW_DL, - val16 & (~MCU_FW_DL_ENABLE & 0xff)); + val16 &= ~MCU_FW_DL_ENABLE; + rtl8xxxu_write16(priv, REG_MCU_FW_DL, val16); return ret; } @@ -2169,12 +2913,18 @@ static int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, char *fw_name) } priv->fw_data = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (!priv->fw_data) { + ret = -ENOMEM; + goto exit; + } priv->fw_size = fw->size - sizeof(struct rtl8xxxu_firmware_header); signature = le16_to_cpu(priv->fw_data->signature); switch (signature & 0xfff0) { + case 0x92e0: case 0x92c0: case 0x88c0: + case 0x5300: case 0x2300: break; default: @@ -2216,6 +2966,20 @@ static int rtl8723au_load_firmware(struct rtl8xxxu_priv *priv) return ret; } +static int rtl8723bu_load_firmware(struct rtl8xxxu_priv *priv) +{ + char *fw_name; + int ret; + + if (priv->enable_bluetooth) + fw_name = "/*(DEBLOBBED)*/"; + else + fw_name = "/*(DEBLOBBED)*/"; + + ret = rtl8xxxu_load_firmware(priv, fw_name); + return ret; +} + #ifdef CONFIG_RTL8XXXU_UNTESTED static int rtl8192cu_load_firmware(struct rtl8xxxu_priv *priv) @@ -2237,6 +3001,18 @@ static int rtl8192cu_load_firmware(struct rtl8xxxu_priv *priv) #endif +static int rtl8192eu_load_firmware(struct rtl8xxxu_priv *priv) +{ + char *fw_name; + int ret; + + fw_name = "/*(DEBLOBBED)*/"; + + ret = rtl8xxxu_load_firmware(priv, fw_name); + + return ret; +} + static void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv) { u16 val16; @@ -2264,6 +3040,44 @@ static void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv) } } +static void rtl8723bu_phy_init_antenna_selection(struct rtl8xxxu_priv *priv) +{ + u32 val32; + + val32 = rtl8xxxu_read32(priv, 0x64); + val32 &= ~(BIT(20) | BIT(24)); + rtl8xxxu_write32(priv, 0x64, val32); + + val32 = rtl8xxxu_read32(priv, REG_GPIO_MUXCFG); + val32 &= ~BIT(4); + rtl8xxxu_write32(priv, REG_GPIO_MUXCFG, val32); + + val32 = rtl8xxxu_read32(priv, REG_GPIO_MUXCFG); + val32 |= BIT(3); + rtl8xxxu_write32(priv, REG_GPIO_MUXCFG, val32); + + val32 = rtl8xxxu_read32(priv, REG_LEDCFG0); + val32 |= BIT(24); + rtl8xxxu_write32(priv, REG_LEDCFG0, val32); + + val32 = rtl8xxxu_read32(priv, REG_LEDCFG0); + val32 &= ~BIT(23); + rtl8xxxu_write32(priv, REG_LEDCFG0, val32); + + val32 = rtl8xxxu_read32(priv, REG_RFE_BUFFER); + val32 |= (BIT(0) | BIT(1)); + rtl8xxxu_write32(priv, REG_RFE_BUFFER, val32); + + val32 = rtl8xxxu_read32(priv, REG_RFE_CTRL_ANTA_SRC); + val32 &= 0xffffff00; + val32 |= 0x77; + rtl8xxxu_write32(priv, REG_RFE_CTRL_ANTA_SRC, val32); + + val32 = rtl8xxxu_read32(priv, REG_PWR_DATA); + val32 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN; + rtl8xxxu_write32(priv, REG_PWR_DATA, val32); +} + static int rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv, struct rtl8xxxu_reg8val *array) { @@ -2286,7 +3100,8 @@ rtl8xxxu_init_mac(struct rtl8xxxu_priv *priv, struct rtl8xxxu_reg8val *array) } } - rtl8xxxu_write8(priv, REG_MAX_AGGR_NUM, 0x0a); + if (priv->rtlchip != 0x8723b) + rtl8xxxu_write8(priv, REG_MAX_AGGR_NUM, 0x0a); return 0; } @@ -2323,6 +3138,7 @@ static int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv, static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) { u8 val8, ldoa15, ldov12d, lpldo, ldohci12; + u16 val16; u32 val32; /* @@ -2330,25 +3146,36 @@ static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) * addresses, which is initialized here. Do we need this? */ - val8 = rtl8xxxu_read8(priv, REG_AFE_PLL_CTRL); - udelay(2); - val8 |= AFE_PLL_320_ENABLE; - rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL, val8); - udelay(2); + if (priv->rtlchip == 0x8723b) { + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB | + SYS_FUNC_DIO_RF; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); - rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL + 1, 0xff); - udelay(2); + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00); + } else { + val8 = rtl8xxxu_read8(priv, REG_AFE_PLL_CTRL); + udelay(2); + val8 |= AFE_PLL_320_ENABLE; + rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL, val8); + udelay(2); - val8 = rtl8xxxu_read8(priv, REG_SYS_FUNC); - val8 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB; - rtl8xxxu_write8(priv, REG_SYS_FUNC, val8); + rtl8xxxu_write8(priv, REG_AFE_PLL_CTRL + 1, 0xff); + udelay(2); + + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 |= SYS_FUNC_BB_GLB_RSTN | SYS_FUNC_BBRSTB; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + } - /* AFE_XTAL_RF_GATE (bit 14) if addressing as 32 bit register */ - val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL); - val32 &= ~AFE_XTAL_RF_GATE; - if (priv->has_bluetooth) - val32 &= ~AFE_XTAL_BT_GATE; - rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32); + if (priv->rtlchip != 0x8723b) { + /* AFE_XTAL_RF_GATE (bit 14) if addressing as 32 bit register */ + val32 = rtl8xxxu_read32(priv, REG_AFE_XTAL_CTRL); + val32 &= ~AFE_XTAL_RF_GATE; + if (priv->has_bluetooth) + val32 &= ~AFE_XTAL_BT_GATE; + rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, val32); + } /* 6. 0x1f[7:0] = 0x07 */ val8 = RF_ENABLE | RF_RSTB | RF_SDMRSTB; @@ -2358,7 +3185,14 @@ static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) rtl8xxxu_init_phy_regs(priv, rtl8188ru_phy_1t_highpa_table); else if (priv->tx_paths == 2) rtl8xxxu_init_phy_regs(priv, rtl8192cu_phy_2t_init_table); - else + else if (priv->rtlchip == 0x8723b) { + /* + * Why? + */ + rtl8xxxu_write8(priv, REG_SYS_FUNC, 0xe3); + rtl8xxxu_write8(priv, REG_AFE_XTAL_CTRL + 1, 0x80); + rtl8xxxu_init_phy_regs(priv, rtl8723b_phy_1t_init_table); + } else rtl8xxxu_init_phy_regs(priv, rtl8723a_phy_1t_init_table); @@ -2424,29 +3258,33 @@ static int rtl8xxxu_init_phy_bb(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_TX_TO_TX, val32); } - if (priv->hi_pa) + if (priv->rtlchip == 0x8723b) + rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_8723bu_table); + else if (priv->hi_pa) rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_highpa_table); else rtl8xxxu_init_phy_regs(priv, rtl8xxx_agc_standard_table); - if (priv->rtlchip == 0x8723a && - priv->efuse_wifi.efuse8723.version >= 0x01) { + if (priv->has_xtalk) { val32 = rtl8xxxu_read32(priv, REG_MAC_PHY_CTRL); - val8 = priv->efuse_wifi.efuse8723.xtal_k & 0x3f; + val8 = priv->xtalk; val32 &= 0xff000fff; val32 |= ((val8 | (val8 << 6)) << 12); rtl8xxxu_write32(priv, REG_MAC_PHY_CTRL, val32); } - ldoa15 = LDOA15_ENABLE | LDOA15_OBUF; - ldov12d = LDOV12D_ENABLE | BIT(2) | (2 << LDOV12D_VADJ_SHIFT); - ldohci12 = 0x57; - lpldo = 1; - val32 = (lpldo << 24) | (ldohci12 << 16) | (ldov12d << 8) | ldoa15; + if (priv->rtlchip != 0x8723bu) { + ldoa15 = LDOA15_ENABLE | LDOA15_OBUF; + ldov12d = LDOV12D_ENABLE | BIT(2) | (2 << LDOV12D_VADJ_SHIFT); + ldohci12 = 0x57; + lpldo = 1; + val32 = (lpldo << 24) | (ldohci12 << 16) | + (ldov12d << 8) | ldoa15; - rtl8xxxu_write32(priv, REG_LDOA15_CTRL, val32); + rtl8xxxu_write32(priv, REG_LDOA15_CTRL, val32); + } return 0; } @@ -2487,8 +3325,6 @@ static int rtl8xxxu_init_rf_regs(struct rtl8xxxu_priv *priv, continue; } - reg &= 0x3f; - ret = rtl8xxxu_write_rfreg(priv, path, reg, val); if (ret) { dev_warn(&priv->udev->dev, @@ -2618,6 +3454,31 @@ exit: return ret; } +static int rtl8xxxu_auto_llt_table(struct rtl8xxxu_priv *priv, u8 last_tx_page) +{ + u32 val32; + int ret = 0; + int i; + + val32 = rtl8xxxu_read32(priv, REG_AUTO_LLT); + val32 |= AUTO_LLT_INIT_LLT; + rtl8xxxu_write32(priv, REG_AUTO_LLT, val32); + + for (i = 500; i; i--) { + val32 = rtl8xxxu_read32(priv, REG_AUTO_LLT); + if (!(val32 & AUTO_LLT_INIT_LLT)) + break; + usleep_range(2, 4); + } + + if (!i) { + ret = -EBUSY; + dev_warn(&priv->udev->dev, "LLT table init failed\n"); + } + + return ret; +} + static int rtl8xxxu_init_queue_priority(struct rtl8xxxu_priv *priv) { u16 val16, hi, lo; @@ -2949,6 +3810,91 @@ static bool rtl8xxxu_simularity_compare(struct rtl8xxxu_priv *priv, return false; } +static bool rtl8723bu_simularity_compare(struct rtl8xxxu_priv *priv, + int result[][8], int c1, int c2) +{ + u32 i, j, diff, simubitmap, bound = 0; + int candidate[2] = {-1, -1}; /* for path A and path B */ + int tmp1, tmp2; + bool retval = true; + + if (priv->tx_paths > 1) + bound = 8; + else + bound = 4; + + simubitmap = 0; + + for (i = 0; i < bound; i++) { + if (i & 1) { + if ((result[c1][i] & 0x00000200)) + tmp1 = result[c1][i] | 0xfffffc00; + else + tmp1 = result[c1][i]; + + if ((result[c2][i]& 0x00000200)) + tmp2 = result[c2][i] | 0xfffffc00; + else + tmp2 = result[c2][i]; + } else { + tmp1 = result[c1][i]; + tmp2 = result[c2][i]; + } + + diff = (tmp1 > tmp2) ? (tmp1 - tmp2) : (tmp2 - tmp1); + + if (diff > MAX_TOLERANCE) { + if ((i == 2 || i == 6) && !simubitmap) { + if (result[c1][i] + result[c1][i + 1] == 0) + candidate[(i / 4)] = c2; + else if (result[c2][i] + result[c2][i + 1] == 0) + candidate[(i / 4)] = c1; + else + simubitmap = simubitmap | (1 << i); + } else { + simubitmap = simubitmap | (1 << i); + } + } + } + + if (simubitmap == 0) { + for (i = 0; i < (bound / 4); i++) { + if (candidate[i] >= 0) { + for (j = i * 4; j < (i + 1) * 4 - 2; j++) + result[3][j] = result[candidate[i]][j]; + retval = false; + } + } + return retval; + } else { + if (!(simubitmap & 0x03)) { + /* path A TX OK */ + for (i = 0; i < 2; i++) + result[3][i] = result[c1][i]; + } + + if (!(simubitmap & 0x0c)) { + /* path A RX OK */ + for (i = 2; i < 4; i++) + result[3][i] = result[c1][i]; + } + + if (!(simubitmap & 0x30) && priv->tx_paths > 1) { + /* path B RX OK */ + for (i = 4; i < 6; i++) + result[3][i] = result[c1][i]; + } + + if (!(simubitmap & 0x30) && priv->tx_paths > 1) { + /* path B RX OK */ + for (i = 6; i < 8; i++) + result[3][i] = result[c1][i]; + } + } + + return false; +} + static void rtl8xxxu_save_mac_regs(struct rtl8xxxu_priv *priv, const u32 *reg, u32 *backup) { @@ -2996,11 +3942,13 @@ static void rtl8xxxu_path_adda_on(struct rtl8xxxu_priv *priv, const u32 *regs, u32 path_on; int i; - path_on = path_a_on ? 0x04db25a4 : 0x0b1b25a4; if (priv->tx_paths == 1) { - path_on = 0x0bdb25a0; - rtl8xxxu_write32(priv, regs[0], 0x0b1b25a0); + path_on = priv->fops->adda_1t_path_on; + rtl8xxxu_write32(priv, regs[0], priv->fops->adda_1t_init); } else { + path_on = path_a_on ? priv->fops->adda_2t_path_on_a : + priv->fops->adda_2t_path_on_b; + rtl8xxxu_write32(priv, regs[0], path_on); } @@ -3114,6 +4062,369 @@ out: return result; } +static int rtl8723bu_iqk_path_a(struct rtl8xxxu_priv *priv) +{ + u32 reg_eac, reg_e94, reg_e9c, path_sel, val32; + int result = 0; + + path_sel = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH); + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * Enable path A PA in TX IQK mode + */ + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x20000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0003f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xc7f87); + + /* + * Tx IQK setting + */ + rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00); + rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800); + + /* path-A IQK setting */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x18008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x38008c1c); + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); + + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x821403ea); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28110000); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82110000); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28110000); + + /* LO calibration setting */ + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x00462911); + + /* + * Enter IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * The vendor driver indicates the USB module is always using + * S0S1 path 1 for the 8723bu. This may be different for 8192eu + */ + if (priv->rf_paths > 1) + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000000); + else + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000280); + + /* + * Bit 12 seems to be BT_GRANT, and is only found in the 8723bu. + * No trace of this in the 8192eu or 8188eu vendor drivers. + */ + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00000800); + + /* One shot, path A LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); + + mdelay(1); + + /* Restore Ant Path */ + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel); +#ifdef RTL8723BU_BT + /* GNT_BT = 1 */ + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00001800); +#endif + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A); + reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A); + + val32 = (reg_e9c >> 16) & 0x3ff; + if (val32 & 0x200) + val32 = 0x400 - val32; + + if (!(reg_eac & BIT(28)) && + ((reg_e94 & 0x03ff0000) != 0x01420000) && + ((reg_e9c & 0x03ff0000) != 0x00420000) && + ((reg_e94 & 0x03ff0000) < 0x01100000) && + ((reg_e94 & 0x03ff0000) > 0x00f00000) && + val32 < 0xf) + result |= 0x01; + else /* If TX not OK, ignore RX */ + goto out; + +out: + return result; +} + +static int rtl8723bu_rx_iqk_path_a(struct rtl8xxxu_priv *priv) +{ + u32 reg_ea4, reg_eac, reg_e94, reg_e9c, path_sel, val32; + int result = 0; + + path_sel = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH); + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * Enable path A PA in TX IQK mode + */ + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7fb7); + + /* + * Tx IQK setting + */ + rtl8xxxu_write32(priv, REG_TX_IQK, 0x01007c00); + rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800); + + /* path-A IQK setting */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x18008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x38008c1c); + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); + + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82160ff0); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x28110000); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82110000); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28110000); + + /* LO calibration setting */ + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a911); + + /* + * Enter IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* + * The vendor driver indicates the USB module is always using + * S0S1 path 1 for the 8723bu. This may be different for 8192eu + */ + if (priv->rf_paths > 1) + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000000); + else + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000280); + + /* + * Bit 12 seems to be BT_GRANT, and is only found in the 8723bu. + * No trace of this in the 8192eu or 8188eu vendor drivers. + */ + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00000800); + + /* One shot, path A LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); + + mdelay(1); + + /* Restore Ant Path */ + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel); +#ifdef RTL8723BU_BT + /* GNT_BT = 1 */ + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00001800); +#endif + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_e94 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_A); + reg_e9c = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_A); + + val32 = (reg_e9c >> 16) & 0x3ff; + if (val32 & 0x200) + val32 = 0x400 - val32; + + if (!(reg_eac & BIT(28)) && + ((reg_e94 & 0x03ff0000) != 0x01420000) && + ((reg_e9c & 0x03ff0000) != 0x00420000) && + ((reg_e94 & 0x03ff0000) < 0x01100000) && + ((reg_e94 & 0x03ff0000) > 0x00f00000) && + val32 < 0xf) + result |= 0x01; + else /* If TX not OK, ignore RX */ + goto out; + + val32 = 0x80007c00 | (reg_e94 &0x3ff0000) | + ((reg_e9c & 0x3ff0000) >> 16); + rtl8xxxu_write32(priv, REG_TX_IQK, val32); + + /* + * Modify RX IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7d77); + + /* + * PA, PAD setting + */ + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0xf80); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_55, 0x4021f); + + /* + * RX IQK setting + */ + rtl8xxxu_write32(priv, REG_RX_IQK, 0x01004800); + + /* path-A IQK setting */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x38008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x18008c1c); + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_B, 0x38008c1c); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_B, 0x38008c1c); + + rtl8xxxu_write32(priv, REG_TX_IQK_PI_A, 0x82110000); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_A, 0x2816001f); + rtl8xxxu_write32(priv, REG_TX_IQK_PI_B, 0x82110000); + rtl8xxxu_write32(priv, REG_RX_IQK_PI_B, 0x28110000); + + /* LO calibration setting */ + rtl8xxxu_write32(priv, REG_IQK_AGC_RSP, 0x0046a8d1); + + /* + * Enter IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + if (priv->rf_paths > 1) + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000000); + else + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00000280); + + /* + * Disable BT + */ + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00000800); + + /* One shot, path A LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf9000000); + rtl8xxxu_write32(priv, REG_IQK_AGC_PTS, 0xf8000000); + + mdelay(1); + + /* Restore Ant Path */ + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, path_sel); +#ifdef RTL8723BU_BT + /* GNT_BT = 1 */ + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, 0x00001800); +#endif + + /* + * Leave IQK mode + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_ea4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_A_2); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_DF, 0x780); + + val32 = (reg_eac >> 16) & 0x3ff; + if (val32 & 0x200) + val32 = 0x400 - val32; + + if (!(reg_eac & BIT(27)) && + ((reg_ea4 & 0x03ff0000) != 0x01320000) && + ((reg_eac & 0x03ff0000) != 0x00360000) && + ((reg_ea4 & 0x03ff0000) < 0x01100000) && + ((reg_ea4 & 0x03ff0000) > 0x00f00000) && + val32 < 0xf) + result |= 0x02; + else /* If TX not OK, ignore RX */ + goto out; +out: + return result; +} + +#ifdef RTL8723BU_PATH_B +static int rtl8723bu_iqk_path_b(struct rtl8xxxu_priv *priv) +{ + u32 reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc, path_sel; + int result = 0; + + path_sel = rtl8xxxu_read32(priv, REG_S0S1_PATH_SWITCH); + + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* One shot, path B LOK & IQK */ + rtl8xxxu_write32(priv, REG_IQK_AGC_CONT, 0x00000002); + rtl8xxxu_write32(priv, REG_IQK_AGC_CONT, 0x00000000); + + mdelay(1); + + /* Check failed */ + reg_eac = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_A_2); + reg_eb4 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B); + reg_ebc = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B); + reg_ec4 = rtl8xxxu_read32(priv, REG_RX_POWER_BEFORE_IQK_B_2); + reg_ecc = rtl8xxxu_read32(priv, REG_RX_POWER_AFTER_IQK_B_2); + + if (!(reg_eac & BIT(31)) && + ((reg_eb4 & 0x03ff0000) != 0x01420000) && + ((reg_ebc & 0x03ff0000) != 0x00420000)) + result |= 0x01; + else + goto out; + + if (!(reg_eac & BIT(30)) && + (((reg_ec4 & 0x03ff0000) >> 16) != 0x132) && + (((reg_ecc & 0x03ff0000) >> 16) != 0x36)) + result |= 0x02; + else + dev_warn(&priv->udev->dev, "%s: Path B RX IQK failed!\n", + __func__); +out: + return result; +} +#endif + static void rtl8xxxu_phy_iqcalibrate(struct rtl8xxxu_priv *priv, int result[][8], int t) { @@ -3316,7 +4627,249 @@ static void rtl8xxxu_phy_iqcalibrate(struct rtl8xxxu_priv *priv, } } -static void rtl8723a_phy_iq_calibrate(struct rtl8xxxu_priv *priv) +static void rtl8723bu_phy_iqcalibrate(struct rtl8xxxu_priv *priv, + int result[][8], int t) +{ + struct device *dev = &priv->udev->dev; + u32 i, val32; + int path_a_ok /*, path_b_ok */; + int retry = 2; + const u32 adda_regs[RTL8XXXU_ADDA_REGS] = { + REG_FPGA0_XCD_SWITCH_CTRL, REG_BLUETOOTH, + REG_RX_WAIT_CCA, REG_TX_CCK_RFON, + REG_TX_CCK_BBON, REG_TX_OFDM_RFON, + REG_TX_OFDM_BBON, REG_TX_TO_RX, + REG_TX_TO_TX, REG_RX_CCK, + REG_RX_OFDM, REG_RX_WAIT_RIFS, + REG_RX_TO_RX, REG_STANDBY, + REG_SLEEP, REG_PMPD_ANAEN + }; + const u32 iqk_mac_regs[RTL8XXXU_MAC_REGS] = { + REG_TXPAUSE, REG_BEACON_CTRL, + REG_BEACON_CTRL_1, REG_GPIO_MUXCFG + }; + const u32 iqk_bb_regs[RTL8XXXU_BB_REGS] = { + REG_OFDM0_TRX_PATH_ENABLE, REG_OFDM0_TR_MUX_PAR, + REG_FPGA0_XCD_RF_SW_CTRL, REG_CONFIG_ANT_A, REG_CONFIG_ANT_B, + REG_FPGA0_XAB_RF_SW_CTRL, REG_FPGA0_XA_RF_INT_OE, + REG_FPGA0_XB_RF_INT_OE, REG_FPGA0_RF_MODE + }; + u8 xa_agc = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1) & 0xff; + u8 xb_agc = rtl8xxxu_read32(priv, REG_OFDM0_XB_AGC_CORE1) & 0xff; + + /* + * Note: IQ calibration must be performed after loading + * PHY_REG.txt , and radio_a, radio_b.txt + */ + + if (t == 0) { + /* Save ADDA parameters, turn Path A ADDA on */ + rtl8xxxu_save_regs(priv, adda_regs, priv->adda_backup, + RTL8XXXU_ADDA_REGS); + rtl8xxxu_save_mac_regs(priv, iqk_mac_regs, priv->mac_backup); + rtl8xxxu_save_regs(priv, iqk_bb_regs, + priv->bb_backup, RTL8XXXU_BB_REGS); + } + + rtl8xxxu_path_adda_on(priv, adda_regs, true); + + /* MAC settings */ + rtl8xxxu_mac_calibration(priv, iqk_mac_regs, priv->mac_backup); + + val32 = rtl8xxxu_read32(priv, REG_CCK0_AFE_SETTING); + val32 |= 0x0f000000; + rtl8xxxu_write32(priv, REG_CCK0_AFE_SETTING, val32); + + rtl8xxxu_write32(priv, REG_OFDM0_TRX_PATH_ENABLE, 0x03a05600); + rtl8xxxu_write32(priv, REG_OFDM0_TR_MUX_PAR, 0x000800e4); + rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_SW_CTRL, 0x22204000); + +#ifdef RTL8723BU_PATH_B + /* Set RF mode to standby Path B */ + if (priv->tx_paths > 1) + rtl8xxxu_write_rfreg(priv, RF_B, RF6052_REG_AC, 0x10000); +#endif + +#if 0 + /* Page B init */ + rtl8xxxu_write32(priv, REG_CONFIG_ANT_A, 0x0f600000); + + if (priv->tx_paths > 1) + rtl8xxxu_write32(priv, REG_CONFIG_ANT_B, 0x0f600000); +#endif + + /* + * RX IQ calibration setting for 8723B D cut large current issue + * when leaving IPS + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x30000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xf7fb7); + + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED); + val32 |= 0x20; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED, val32); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_43, 0x60fbd); + + for (i = 0; i < retry; i++) { + path_a_ok = rtl8723bu_iqk_path_a(priv); + if (path_a_ok == 0x01) { + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + +#if 0 /* Only needed in restore case, we may need this when going to suspend */ + priv->RFCalibrateInfo.TxLOK[RF_A] = + rtl8xxxu_read_rfreg(priv, RF_A, + RF6052_REG_TXM_IDAC); +#endif + + val32 = rtl8xxxu_read32(priv, + REG_TX_POWER_BEFORE_IQK_A); + result[t][0] = (val32 >> 16) & 0x3ff; + val32 = rtl8xxxu_read32(priv, + REG_TX_POWER_AFTER_IQK_A); + result[t][1] = (val32 >> 16) & 0x3ff; + + break; + } + } + + if (!path_a_ok) + dev_dbg(dev, "%s: Path A TX IQK failed!\n", __func__); + + for (i = 0; i < retry; i++) { + path_a_ok = rtl8723bu_rx_iqk_path_a(priv); + if (path_a_ok == 0x03) { + val32 = rtl8xxxu_read32(priv, + REG_RX_POWER_BEFORE_IQK_A_2); + result[t][2] = (val32 >> 16) & 0x3ff; + val32 = rtl8xxxu_read32(priv, + REG_RX_POWER_AFTER_IQK_A_2); + result[t][3] = (val32 >> 16) & 0x3ff; + + break; + } + } + + if (!path_a_ok) + dev_dbg(dev, "%s: Path A RX IQK failed!\n", __func__); + + if (priv->tx_paths > 1) { +#if 1 + dev_warn(dev, "%s: Path B not supported\n", __func__); +#else + + /* + * Path A into standby + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_AC, 0x10000); + + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + val32 |= 0x80800000; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + /* Turn Path B ADDA on */ + rtl8xxxu_path_adda_on(priv, adda_regs, false); + + for (i = 0; i < retry; i++) { + path_b_ok = rtl8xxxu_iqk_path_b(priv); + if (path_b_ok == 0x03) { + val32 = rtl8xxxu_read32(priv, REG_TX_POWER_BEFORE_IQK_B); + result[t][4] = (val32 >> 16) & 0x3ff; + val32 = rtl8xxxu_read32(priv, REG_TX_POWER_AFTER_IQK_B); + result[t][5] = (val32 >> 16) & 0x3ff; + break; + } + } + + if (!path_b_ok) + dev_dbg(dev, "%s: Path B IQK failed!\n", __func__); + + for (i = 0; i < retry; i++) { + path_b_ok = rtl8723bu_rx_iqk_path_b(priv); + if (path_a_ok == 0x03) { + val32 = rtl8xxxu_read32(priv, + REG_RX_POWER_BEFORE_IQK_B_2); + result[t][6] = (val32 >> 16) & 0x3ff; + val32 = rtl8xxxu_read32(priv, + REG_RX_POWER_AFTER_IQK_B_2); + result[t][7] = (val32 >> 16) & 0x3ff; + break; + } + } + + if (!path_b_ok) + dev_dbg(dev, "%s: Path B RX IQK failed!\n", __func__); +#endif + } + + /* Back to BB mode, load original value */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 &= 0x000000ff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + + if (t) { + /* Reload ADDA power saving parameters */ + rtl8xxxu_restore_regs(priv, adda_regs, priv->adda_backup, + RTL8XXXU_ADDA_REGS); + + /* Reload MAC parameters */ + rtl8xxxu_restore_mac_regs(priv, iqk_mac_regs, priv->mac_backup); + + /* Reload BB parameters */ + rtl8xxxu_restore_regs(priv, iqk_bb_regs, + priv->bb_backup, RTL8XXXU_BB_REGS); + + /* Restore RX initial gain */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1); + val32 &= 0xffffff00; + rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32 | 0x50); + rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, val32 | xa_agc); + + if (priv->tx_paths > 1) { + val32 = rtl8xxxu_read32(priv, REG_OFDM0_XB_AGC_CORE1); + val32 &= 0xffffff00; + rtl8xxxu_write32(priv, REG_OFDM0_XB_AGC_CORE1, + val32 | 0x50); + rtl8xxxu_write32(priv, REG_OFDM0_XB_AGC_CORE1, + val32 | xb_agc); + } + + /* Load 0xe30 IQC default value */ + rtl8xxxu_write32(priv, REG_TX_IQK_TONE_A, 0x01008c00); + rtl8xxxu_write32(priv, REG_RX_IQK_TONE_A, 0x01008c00); + } +} + +static void rtl8xxxu_prepare_calibrate(struct rtl8xxxu_priv *priv, u8 start) +{ + struct h2c_cmd h2c; + + if (priv->fops->mbox_ext_width < 4) + return; + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.bt_wlan_calibration.cmd = H2C_8723B_BT_WLAN_CALIBRATION; + h2c.bt_wlan_calibration.data = start; + + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_wlan_calibration)); +} + +static void rtl8723au_phy_iq_calibrate(struct rtl8xxxu_priv *priv) { struct device *dev = &priv->udev->dev; int result[4][8]; /* last is final result */ @@ -3327,6 +4880,8 @@ static void rtl8723a_phy_iq_calibrate(struct rtl8xxxu_priv *priv) s32 reg_tmp = 0; bool simu; + rtl8xxxu_prepare_calibrate(priv, 1); + memset(result, 0, sizeof(result)); candidate = -1; @@ -3414,6 +4969,135 @@ static void rtl8723a_phy_iq_calibrate(struct rtl8xxxu_priv *priv) rtl8xxxu_save_regs(priv, rtl8723au_iqk_phy_iq_bb_reg, priv->bb_recovery_backup, RTL8XXXU_BB_REGS); + + rtl8xxxu_prepare_calibrate(priv, 0); +} + +static void rtl8723bu_phy_iq_calibrate(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + int result[4][8]; /* last is final result */ + int i, candidate; + bool path_a_ok, path_b_ok; + u32 reg_e94, reg_e9c, reg_ea4, reg_eac; + u32 reg_eb4, reg_ebc, reg_ec4, reg_ecc; + u32 val32, bt_control; + s32 reg_tmp = 0; + bool simu; + + rtl8xxxu_prepare_calibrate(priv, 1); + + memset(result, 0, sizeof(result)); + candidate = -1; + + path_a_ok = false; + path_b_ok = false; + + bt_control = rtl8xxxu_read32(priv, REG_BT_CONTROL_8723BU); + + for (i = 0; i < 3; i++) { + rtl8723bu_phy_iqcalibrate(priv, result, i); + + if (i == 1) { + simu = rtl8723bu_simularity_compare(priv, result, 0, 1); + if (simu) { + candidate = 0; + break; + } + } + + if (i == 2) { + simu = rtl8723bu_simularity_compare(priv, result, 0, 2); + if (simu) { + candidate = 0; + break; + } + + simu = rtl8723bu_simularity_compare(priv, result, 1, 2); + if (simu) { + candidate = 1; + } else { + for (i = 0; i < 8; i++) + reg_tmp += result[3][i]; + + if (reg_tmp) + candidate = 3; + else + candidate = -1; + } + } + } + + for (i = 0; i < 4; i++) { + reg_e94 = result[i][0]; + reg_e9c = result[i][1]; + reg_ea4 = result[i][2]; + reg_eac = result[i][3]; + reg_eb4 = result[i][4]; + reg_ebc = result[i][5]; + reg_ec4 = result[i][6]; + reg_ecc = result[i][7]; + } + + if (candidate >= 0) { + reg_e94 = result[candidate][0]; + priv->rege94 = reg_e94; + reg_e9c = result[candidate][1]; + priv->rege9c = reg_e9c; + reg_ea4 = result[candidate][2]; + reg_eac = result[candidate][3]; + reg_eb4 = result[candidate][4]; + priv->regeb4 = reg_eb4; + reg_ebc = result[candidate][5]; + priv->regebc = reg_ebc; + reg_ec4 = result[candidate][6]; + reg_ecc = result[candidate][7]; + dev_dbg(dev, "%s: candidate is %x\n", __func__, candidate); + dev_dbg(dev, + "%s: e94 =%x e9c=%x ea4=%x eac=%x eb4=%x ebc=%x ec4=%x " + "ecc=%x\n ", __func__, reg_e94, reg_e9c, + reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc); + path_a_ok = true; + path_b_ok = true; + } else { + reg_e94 = reg_eb4 = priv->rege94 = priv->regeb4 = 0x100; + reg_e9c = reg_ebc = priv->rege9c = priv->regebc = 0x0; + } + + if (reg_e94 && candidate >= 0) + rtl8xxxu_fill_iqk_matrix_a(priv, path_a_ok, result, + candidate, (reg_ea4 == 0)); + + if (priv->tx_paths > 1 && reg_eb4) + rtl8xxxu_fill_iqk_matrix_b(priv, path_b_ok, result, + candidate, (reg_ec4 == 0)); + + rtl8xxxu_save_regs(priv, rtl8723au_iqk_phy_iq_bb_reg, + priv->bb_recovery_backup, RTL8XXXU_BB_REGS); + + rtl8xxxu_write32(priv, REG_BT_CONTROL_8723BU, bt_control); + + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_WE_LUT); + val32 |= 0x80000; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_WE_LUT, val32); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_RCK_OS, 0x18000); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G1, 0x0001f); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_TXPA_G2, 0xe6177); + val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED); + val32 |= 0x20; + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_UNKNOWN_ED, val32); + rtl8xxxu_write_rfreg(priv, RF_A, 0x43, 0x300bd); + + if (priv->rf_paths > 1) { + dev_dbg(dev, "%s: beware 2T not yet supported\n", __func__); +#ifdef RTL8723BU_PATH_B + if (RF_Path == 0x0) //S1 + ODM_SetIQCbyRFpath(pDM_Odm, 0); + else //S0 + ODM_SetIQCbyRFpath(pDM_Odm, 1); +#endif + } + rtl8xxxu_prepare_calibrate(priv, 0); } static void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv) @@ -3451,12 +5135,17 @@ static void rtl8723a_phy_lc_calibrate(struct rtl8xxxu_priv *priv) } /* Start LC calibration */ + if (priv->fops->has_s0s1) + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_S0S1, 0xdfbe0); val32 = rtl8xxxu_read_rfreg(priv, RF_A, RF6052_REG_MODE_AG); val32 |= 0x08000; rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, val32); msleep(100); + if (priv->fops->has_s0s1) + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_S0S1, 0xdffe0); + /* Restore original parameters */ if (lstf & OFDM_LSTF_MASK) { /* Path-A */ @@ -3579,6 +5268,64 @@ exit: return ret; } +static int rtl8723bu_active_to_emu(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 val16; + u32 val32; + int count, ret; + + /* Turn off RF */ + rtl8xxxu_write8(priv, REG_RF_CTRL, 0); + + /* Enable rising edge triggering interrupt */ + val16 = rtl8xxxu_read16(priv, REG_GPIO_INTM); + val16 &= ~GPIO_INTM_EDGE_TRIG_IRQ; + rtl8xxxu_write16(priv, REG_GPIO_INTM, val16); + + /* Release WLON reset 0x04[16]= 1*/ + val32 = rtl8xxxu_read32(priv, REG_GPIO_INTM); + val32 |= APS_FSMCO_WLON_RESET; + rtl8xxxu_write32(priv, REG_GPIO_INTM, val32); + + /* 0x0005[1] = 1 turn off MAC by HW state machine*/ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 |= BIT(1); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + if ((val8 & BIT(1)) == 0) + break; + udelay(10); + } + + if (!count) { + dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n", + __func__); + ret = -EBUSY; + goto exit; + } + + /* Enable BT control XTAL setting */ + val8 = rtl8xxxu_read8(priv, REG_AFE_MISC); + val8 &= ~AFE_MISC_WL_XTAL_CTRL; + rtl8xxxu_write8(priv, REG_AFE_MISC, val8); + + /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */ + val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL); + val8 |= SYS_ISO_ANALOG_IPS; + rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8); + + /* 0x0020[0] = 0 disable LDOA12 MACRO block*/ + val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL); + val8 &= ~LDOA15_ENABLE; + rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8); + +exit: + return ret; +} + static int rtl8xxxu_active_to_lps(struct rtl8xxxu_priv *priv) { u8 val8; @@ -3635,7 +5382,7 @@ exit: return ret; } -static void rtl8xxxu_disabled_to_emu(struct rtl8xxxu_priv *priv) +static void rtl8723a_disabled_to_emu(struct rtl8xxxu_priv *priv) { u8 val8; @@ -3655,7 +5402,82 @@ static void rtl8xxxu_disabled_to_emu(struct rtl8xxxu_priv *priv) rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); } -static int rtl8xxxu_emu_to_active(struct rtl8xxxu_priv *priv) +static void rtl8192e_disabled_to_emu(struct rtl8xxxu_priv *priv) +{ + u8 val8; + + /* Clear suspend enable and power down enable*/ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~(BIT(3) | BIT(4)); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); +} + +static int rtl8192e_emu_to_active(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u32 val32; + int count, ret = 0; + + /* disable HWPDN 0x04[15]=0*/ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~BIT(7); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* disable SW LPS 0x04[10]= 0 */ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~BIT(2); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* disable WL suspend*/ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1); + val8 &= ~(BIT(3) | BIT(4)); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8); + + /* wait till 0x04[17] = 1 power ready*/ + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if (val32 & BIT(17)) + break; + + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + + /* We should be able to optimize the following three entries into one */ + + /* release WLON reset 0x04[16]= 1*/ + val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 2); + val8 |= BIT(0); + rtl8xxxu_write8(priv, REG_APS_FSMCO + 2, val8); + + /* set, then poll until 0 */ + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + val32 |= APS_FSMCO_MAC_ENABLE; + rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); + + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) { + ret = 0; + break; + } + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + +exit: + return ret; +} + +static int rtl8723a_emu_to_active(struct rtl8xxxu_priv *priv) { u8 val8; u32 val32; @@ -3747,6 +5569,127 @@ exit: return ret; } +static int rtl8723b_emu_to_active(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u32 val32; + int count, ret = 0; + + /* 0x20[0] = 1 enable LDOA12 MACRO block for all interface */ + val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL); + val8 |= LDOA15_ENABLE; + rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8); + + /* 0x67[0] = 0 to disable BT_GPS_SEL pins*/ + val8 = rtl8xxxu_read8(priv, 0x0067); + val8 &= ~BIT(4); + rtl8xxxu_write8(priv, 0x0067, val8); + + mdelay(1); + + /* 0x00[5] = 0 release analog Ips to digital, 1:isolation */ + val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL); + val8 &= ~SYS_ISO_ANALOG_IPS; + rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8); + + /* Disable SW LPS 0x04[10]= 0 */ + val32 = rtl8xxxu_read8(priv, REG_APS_FSMCO); + val32 &= ~APS_FSMCO_SW_LPS; + rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); + + /* Wait until 0x04[17] = 1 power ready */ + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if (val32 & BIT(17)) + break; + + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + + /* We should be able to optimize the following three entries into one */ + + /* Release WLON reset 0x04[16]= 1*/ + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + val32 |= APS_FSMCO_WLON_RESET; + rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); + + /* Disable HWPDN 0x04[15]= 0*/ + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + val32 &= ~APS_FSMCO_HW_POWERDOWN; + rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); + + /* Disable WL suspend*/ + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + val32 &= ~(APS_FSMCO_HW_SUSPEND | APS_FSMCO_PCIE); + rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); + + /* Set, then poll until 0 */ + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + val32 |= APS_FSMCO_MAC_ENABLE; + rtl8xxxu_write32(priv, REG_APS_FSMCO, val32); + + for (count = RTL8XXXU_MAX_REG_POLL; count; count--) { + val32 = rtl8xxxu_read32(priv, REG_APS_FSMCO); + if ((val32 & APS_FSMCO_MAC_ENABLE) == 0) { + ret = 0; + break; + } + udelay(10); + } + + if (!count) { + ret = -EBUSY; + goto exit; + } + + /* Enable WL control XTAL setting */ + val8 = rtl8xxxu_read8(priv, REG_AFE_MISC); + val8 |= AFE_MISC_WL_XTAL_CTRL; + rtl8xxxu_write8(priv, REG_AFE_MISC, val8); + + /* Enable falling edge triggering interrupt */ + val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 1); + val8 |= BIT(1); + rtl8xxxu_write8(priv, REG_GPIO_INTM + 1, val8); + + /* Enable GPIO9 interrupt mode */ + val8 = rtl8xxxu_read8(priv, REG_GPIO_IO_SEL_2 + 1); + val8 |= BIT(1); + rtl8xxxu_write8(priv, REG_GPIO_IO_SEL_2 + 1, val8); + + /* Enable GPIO9 input mode */ + val8 = rtl8xxxu_read8(priv, REG_GPIO_IO_SEL_2); + val8 &= ~BIT(1); + rtl8xxxu_write8(priv, REG_GPIO_IO_SEL_2, val8); + + /* Enable HSISR GPIO[C:0] interrupt */ + val8 = rtl8xxxu_read8(priv, REG_HSIMR); + val8 |= BIT(0); + rtl8xxxu_write8(priv, REG_HSIMR, val8); + + /* Enable HSISR GPIO9 interrupt */ + val8 = rtl8xxxu_read8(priv, REG_HSIMR + 2); + val8 |= BIT(1); + rtl8xxxu_write8(priv, REG_HSIMR + 2, val8); + + val8 = rtl8xxxu_read8(priv, REG_MULTI_FUNC_CTRL); + val8 |= MULTI_WIFI_HW_ROF_EN; + rtl8xxxu_write8(priv, REG_MULTI_FUNC_CTRL, val8); + + /* For GPIO9 internal pull high setting BIT(14) */ + val8 = rtl8xxxu_read8(priv, REG_MULTI_FUNC_CTRL + 1); + val8 |= BIT(6); + rtl8xxxu_write8(priv, REG_MULTI_FUNC_CTRL + 1, val8); + +exit: + return ret; +} + static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv) { u8 val8; @@ -3772,6 +5715,39 @@ static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv) return 0; } +static int rtl8xxxu_flush_fifo(struct rtl8xxxu_priv *priv) +{ + struct device *dev = &priv->udev->dev; + u32 val32; + int retry, retval; + + rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff); + + val32 = rtl8xxxu_read32(priv, REG_RXPKT_NUM); + val32 |= RXPKT_NUM_RW_RELEASE_EN; + rtl8xxxu_write32(priv, REG_RXPKT_NUM, val32); + + retry = 100; + retval = -EBUSY; + + do { + val32 = rtl8xxxu_read32(priv, REG_RXPKT_NUM); + if (val32 & RXPKT_NUM_RXDMA_IDLE) { + retval = 0; + break; + } + } while (retry--); + + rtl8xxxu_write16(priv, REG_RQPN_NPQ, 0); + rtl8xxxu_write32(priv, REG_RQPN, 0x80000000); + mdelay(2); + + if (!retry) + dev_warn(dev, "Failed to flush FIFO\n"); + + return retval; +} + static int rtl8723au_power_on(struct rtl8xxxu_priv *priv) { u8 val8; @@ -3784,9 +5760,9 @@ static int rtl8723au_power_on(struct rtl8xxxu_priv *priv) */ rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0); - rtl8xxxu_disabled_to_emu(priv); + rtl8723a_disabled_to_emu(priv); - ret = rtl8xxxu_emu_to_active(priv); + ret = rtl8723a_emu_to_active(priv); if (ret) goto exit; @@ -3818,6 +5794,62 @@ exit: return ret; } +static int rtl8723bu_power_on(struct rtl8xxxu_priv *priv) +{ + u8 val8; + u16 val16; + u32 val32; + int ret; + + rtl8723a_disabled_to_emu(priv); + + ret = rtl8723b_emu_to_active(priv); + if (ret) + goto exit; + + /* + * Enable MAC DMA/WMAC/SCHEDULE/SEC block + * Set CR bit10 to enable 32k calibration. + */ + val16 = rtl8xxxu_read16(priv, REG_CR); + val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE | + CR_TXDMA_ENABLE | CR_RXDMA_ENABLE | + CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE | + CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE | + CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE); + rtl8xxxu_write16(priv, REG_CR, val16); + + /* + * BT coexist power on settings. This is identical for 1 and 2 + * antenna parts. + */ + rtl8xxxu_write8(priv, REG_PAD_CTRL1 + 3, 0x20); + + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 |= SYS_FUNC_BBRSTB | SYS_FUNC_BB_GLB_RSTN; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + + rtl8xxxu_write8(priv, REG_BT_CONTROL_8723BU + 1, 0x18); + rtl8xxxu_write8(priv, REG_WLAN_ACT_CONTROL_8723B, 0x04); + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00); + /* Antenna inverse */ + rtl8xxxu_write8(priv, 0xfe08, 0x01); + + val16 = rtl8xxxu_read16(priv, REG_PWR_DATA); + val16 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN; + rtl8xxxu_write16(priv, REG_PWR_DATA, val16); + + val32 = rtl8xxxu_read32(priv, REG_LEDCFG0); + val32 |= LEDCFG0_DPDT_SELECT; + rtl8xxxu_write32(priv, REG_LEDCFG0, val32); + + val8 = rtl8xxxu_read8(priv, REG_PAD_CTRL1); + val8 &= ~PAD_CTRL1_SW_DPDT_SEL_DATA; + rtl8xxxu_write8(priv, REG_PAD_CTRL1, val8); +exit: + return ret; +} + #ifdef CONFIG_RTL8XXXU_UNTESTED static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv) @@ -3925,6 +5957,52 @@ static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv) #endif +static int rtl8192eu_power_on(struct rtl8xxxu_priv *priv) +{ + u16 val16; + u32 val32; + int ret; + + ret = 0; + + val32 = rtl8xxxu_read32(priv, REG_SYS_CFG); + if (val32 & SYS_CFG_SPS_LDO_SEL) { + rtl8xxxu_write8(priv, REG_LDO_SW_CTRL, 0xc3); + } else { + /* + * Raise 1.2V voltage + */ + val32 = rtl8xxxu_read32(priv, REG_8192E_LDOV12_CTRL); + val32 &= 0xff0fffff; + val32 |= 0x00500000; + rtl8xxxu_write32(priv, REG_8192E_LDOV12_CTRL, val32); + rtl8xxxu_write8(priv, REG_LDO_SW_CTRL, 0x83); + } + + rtl8192e_disabled_to_emu(priv); + + ret = rtl8192e_emu_to_active(priv); + if (ret) + goto exit; + + rtl8xxxu_write16(priv, REG_CR, 0x0000); + + /* + * Enable MAC DMA/WMAC/SCHEDULE/SEC block + * Set CR bit10 to enable 32k calibration. + */ + val16 = rtl8xxxu_read16(priv, REG_CR); + val16 |= (CR_HCI_TXDMA_ENABLE | CR_HCI_RXDMA_ENABLE | + CR_TXDMA_ENABLE | CR_RXDMA_ENABLE | + CR_PROTOCOL_ENABLE | CR_SCHEDULE_ENABLE | + CR_MAC_TX_ENABLE | CR_MAC_RX_ENABLE | + CR_SECURITY_ENABLE | CR_CALTIMER_ENABLE); + rtl8xxxu_write16(priv, REG_CR, val16); + +exit: + return ret; +} + static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv) { u8 val8; @@ -3940,6 +6018,8 @@ static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv) rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32); } + rtl8xxxu_flush_fifo(priv); + rtl8xxxu_active_to_lps(priv); /* Turn off RF */ @@ -3973,10 +6053,215 @@ static void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv) rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e); } -static void rtl8xxxu_init_bt(struct rtl8xxxu_priv *priv) +static void rtl8723bu_power_off(struct rtl8xxxu_priv *priv) { - if (!priv->has_bluetooth) - return; + u8 val8; + u16 val16; + + rtl8xxxu_flush_fifo(priv); + + /* + * Disable TX report timer + */ + val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL); + val8 &= ~TX_REPORT_CTRL_TIMER_ENABLE; + rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8); + + rtl8xxxu_write16(priv, REG_CR, 0x0000); + + rtl8xxxu_active_to_lps(priv); + + /* Reset Firmware if running in RAM */ + if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL) + rtl8xxxu_firmware_self_reset(priv); + + /* Reset MCU */ + val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC); + val16 &= ~SYS_FUNC_CPU_ENABLE; + rtl8xxxu_write16(priv, REG_SYS_FUNC, val16); + + /* Reset MCU ready status */ + rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00); + + rtl8723bu_active_to_emu(priv); + rtl8xxxu_emu_to_disabled(priv); +} + +#ifdef NEED_PS_TDMA +static void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv, + u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5) +{ + struct h2c_cmd h2c; + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.b_type_dma.cmd = H2C_8723B_B_TYPE_TDMA; + h2c.b_type_dma.data1 = arg1; + h2c.b_type_dma.data2 = arg2; + h2c.b_type_dma.data3 = arg3; + h2c.b_type_dma.data4 = arg4; + h2c.b_type_dma.data5 = arg5; + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.b_type_dma)); +} +#endif + +static void rtl8723b_enable_rf(struct rtl8xxxu_priv *priv) +{ + struct h2c_cmd h2c; + u32 val32; + u8 val8; + + /* + * No indication anywhere as to what 0x0790 does. The 2 antenna + * vendor code preserves bits 6-7 here. + */ + rtl8xxxu_write8(priv, 0x0790, 0x05); + /* + * 0x0778 seems to be related to enabling the number of antennas + * In the vendor driver halbtc8723b2ant_InitHwConfig() sets it + * to 0x03, while halbtc8723b1ant_InitHwConfig() sets it to 0x01 + */ + rtl8xxxu_write8(priv, 0x0778, 0x01); + + val8 = rtl8xxxu_read8(priv, REG_GPIO_MUXCFG); + val8 |= BIT(5); + rtl8xxxu_write8(priv, REG_GPIO_MUXCFG, val8); + + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_IQADJ_G1, 0x780); + + rtl8723bu_write_btreg(priv, 0x3c, 0x15); /* BT TRx Mask on */ + + /* + * Set BT grant to low + */ + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.bt_grant.cmd = H2C_8723B_BT_GRANT; + h2c.bt_grant.data = 0; + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_grant)); + + /* + * WLAN action by PTA + */ + rtl8xxxu_write8(priv, REG_WLAN_ACT_CONTROL_8723B, 0x04); + + /* + * BT select S0/S1 controlled by WiFi + */ + val8 = rtl8xxxu_read8(priv, 0x0067); + val8 |= BIT(5); + rtl8xxxu_write8(priv, 0x0067, val8); + + val32 = rtl8xxxu_read32(priv, REG_PWR_DATA); + val32 |= PWR_DATA_EEPRPAD_RFE_CTRL_EN; + rtl8xxxu_write32(priv, REG_PWR_DATA, val32); + + /* + * Bits 6/7 are marked in/out ... but for what? + */ + rtl8xxxu_write8(priv, 0x0974, 0xff); + + val32 = rtl8xxxu_read32(priv, REG_RFE_BUFFER); + val32 |= (BIT(0) | BIT(1)); + rtl8xxxu_write32(priv, REG_RFE_BUFFER, val32); + + rtl8xxxu_write8(priv, REG_RFE_CTRL_ANTA_SRC, 0x77); + + val32 = rtl8xxxu_read32(priv, REG_LEDCFG0); + val32 &= ~BIT(24); + val32 |= BIT(23); + rtl8xxxu_write32(priv, REG_LEDCFG0, val32); + + /* + * Fix external switch Main->S1, Aux->S0 + */ + val8 = rtl8xxxu_read8(priv, REG_PAD_CTRL1); + val8 &= ~BIT(0); + rtl8xxxu_write8(priv, REG_PAD_CTRL1, val8); + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.ant_sel_rsv.cmd = H2C_8723B_ANT_SEL_RSV; + h2c.ant_sel_rsv.ant_inverse = 1; + h2c.ant_sel_rsv.int_switch_type = 0; + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ant_sel_rsv)); + + /* + * 0x280, 0x00, 0x200, 0x80 - not clear + */ + rtl8xxxu_write32(priv, REG_S0S1_PATH_SWITCH, 0x00); + + /* + * Software control, antenna at WiFi side + */ +#ifdef NEED_PS_TDMA + rtl8723bu_set_ps_tdma(priv, 0x08, 0x00, 0x00, 0x00, 0x00); +#endif + + rtl8xxxu_write32(priv, REG_BT_COEX_TABLE1, 0x55555555); + rtl8xxxu_write32(priv, REG_BT_COEX_TABLE2, 0x55555555); + rtl8xxxu_write32(priv, REG_BT_COEX_TABLE3, 0x00ffffff); + rtl8xxxu_write8(priv, REG_BT_COEX_TABLE4, 0x03); + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.bt_info.cmd = H2C_8723B_BT_INFO; + h2c.bt_info.data = BIT(0); + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.bt_info)); + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.ignore_wlan.cmd = H2C_8723B_BT_IGNORE_WLANACT; + h2c.ignore_wlan.data = 0; + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ignore_wlan)); +} + +static void rtl8723b_disable_rf(struct rtl8xxxu_priv *priv) +{ + u32 val32; + + rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff); + + val32 = rtl8xxxu_read32(priv, REG_RX_WAIT_CCA); + val32 &= ~(BIT(22) | BIT(23)); + rtl8xxxu_write32(priv, REG_RX_WAIT_CCA, val32); +} + +static void rtl8723bu_init_aggregation(struct rtl8xxxu_priv *priv) +{ + u32 agg_rx; + u8 agg_ctrl; + + /* + * For now simply disable RX aggregation + */ + agg_ctrl = rtl8xxxu_read8(priv, REG_TRXDMA_CTRL); + agg_ctrl &= ~TRXDMA_CTRL_RXDMA_AGG_EN; + + agg_rx = rtl8xxxu_read32(priv, REG_RXDMA_AGG_PG_TH); + agg_rx &= ~RXDMA_USB_AGG_ENABLE; + agg_rx &= ~0xff0f; + + rtl8xxxu_write8(priv, REG_TRXDMA_CTRL, agg_ctrl); + rtl8xxxu_write32(priv, REG_RXDMA_AGG_PG_TH, agg_rx); +} + +static void rtl8723bu_init_statistics(struct rtl8xxxu_priv *priv) +{ + u32 val32; + + /* Time duration for NHM unit: 4us, 0x2710=40ms */ + rtl8xxxu_write16(priv, REG_NHM_TIMER_8723B + 2, 0x2710); + rtl8xxxu_write16(priv, REG_NHM_TH9_TH10_8723B + 2, 0xffff); + rtl8xxxu_write32(priv, REG_NHM_TH3_TO_TH0_8723B, 0xffffff52); + rtl8xxxu_write32(priv, REG_NHM_TH7_TO_TH4_8723B, 0xffffffff); + /* TH8 */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_IQK); + val32 |= 0xff; + rtl8xxxu_write32(priv, REG_FPGA0_IQK, val32); + /* Enable CCK */ + val32 = rtl8xxxu_read32(priv, REG_NHM_TH9_TH10_8723B); + val32 |= BIT(8) | BIT(9) | BIT(10); + rtl8xxxu_write32(priv, REG_NHM_TH9_TH10_8723B, val32); + /* Max power amongst all RX antennas */ + val32 = rtl8xxxu_read32(priv, REG_OFDM0_FA_RSTC); + val32 |= BIT(7); + rtl8xxxu_write32(priv, REG_OFDM0_FA_RSTC, val32); } static int rtl8xxxu_init_device(struct ieee80211_hw *hw) @@ -4010,11 +6295,30 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) dev_dbg(dev, "%s: macpower %i\n", __func__, macpower); if (!macpower) { - ret = rtl8xxxu_init_llt_table(priv, TX_TOTAL_PAGE_NUM); + ret = priv->fops->llt_init(priv, TX_TOTAL_PAGE_NUM); if (ret) { dev_warn(dev, "%s: LLT table init failed\n", __func__); goto exit; } + + /* + * Presumably this is for 8188EU as well + * Enable TX report and TX report timer + */ + if (priv->rtlchip == 0x8723bu) { + val8 = rtl8xxxu_read8(priv, REG_TX_REPORT_CTRL); + val8 |= TX_REPORT_CTRL_TIMER_ENABLE; + rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL, val8); + /* Set MAX RPT MACID */ + rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, 0x02); + /* TX report Timer. Unit: 32us */ + rtl8xxxu_write16(priv, REG_TX_REPORT_TIME, 0xcdf0); + + /* tmp ps ? */ + val8 = rtl8xxxu_read8(priv, 0xa3); + val8 &= 0xf8; + rtl8xxxu_write8(priv, 0xa3, val8); + } } ret = rtl8xxxu_download_firmware(priv); @@ -4026,7 +6330,42 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; - ret = rtl8xxxu_init_mac(priv, rtl8723a_mac_init_table); + /* Solve too many protocol error on USB bus */ + /* Can't do this for 8188/8192 UMC A cut parts */ + if (priv->rtlchip == 0x8723a || + ((priv->rtlchip == 0x8192c || priv->rtlchip == 0x8191c || + priv->rtlchip == 0x8188c) && + (priv->chip_cut || !priv->vendor_umc))) { + rtl8xxxu_write8(priv, 0xfe40, 0xe6); + rtl8xxxu_write8(priv, 0xfe41, 0x94); + rtl8xxxu_write8(priv, 0xfe42, 0x80); + + rtl8xxxu_write8(priv, 0xfe40, 0xe0); + rtl8xxxu_write8(priv, 0xfe41, 0x19); + rtl8xxxu_write8(priv, 0xfe42, 0x80); + + rtl8xxxu_write8(priv, 0xfe40, 0xe5); + rtl8xxxu_write8(priv, 0xfe41, 0x91); + rtl8xxxu_write8(priv, 0xfe42, 0x80); + + rtl8xxxu_write8(priv, 0xfe40, 0xe2); + rtl8xxxu_write8(priv, 0xfe41, 0x81); + rtl8xxxu_write8(priv, 0xfe42, 0x80); + } + + if (priv->rtlchip == 0x8192e) { + rtl8xxxu_write32(priv, REG_HIMR0, 0x00); + rtl8xxxu_write32(priv, REG_HIMR1, 0x00); + } + + if (priv->fops->phy_init_antenna_selection) + priv->fops->phy_init_antenna_selection(priv); + + if (priv->rtlchip == 0x8723b) + ret = rtl8xxxu_init_mac(priv, rtl8723b_mac_init_table); + else + ret = rtl8xxxu_init_mac(priv, rtl8723a_mac_init_table); + dev_dbg(dev, "%s: init_mac %i\n", __func__, ret); if (ret) goto exit; @@ -4041,6 +6380,17 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) rftable = rtl8723au_radioa_1t_init_table; ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A); break; + case 0x8723b: + rftable = rtl8723bu_radioa_1t_init_table; + ret = rtl8xxxu_init_phy_rf(priv, rftable, RF_A); + /* + * PHY LCK + */ + rtl8xxxu_write_rfreg(priv, RF_A, 0xb0, 0xdfbe0); + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_MODE_AG, 0x8c01); + msleep(200); + rtl8xxxu_write_rfreg(priv, RF_A, 0xb0, 0xdffe0); + break; case 0x8188c: if (priv->hi_pa) rftable = rtl8188ru_radioa_1t_highpa_table; @@ -4067,27 +6417,27 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; - /* Reduce 80M spur */ - rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d); - rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83); - rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82); - rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83); - - /* RFSW Control - clear bit 14 ?? */ - rtl8xxxu_write32(priv, REG_FPGA0_TX_INFO, 0x00000003); - /* 0x07000760 */ - val32 = FPGA0_RF_TRSW | FPGA0_RF_TRSWB | FPGA0_RF_ANTSW | - FPGA0_RF_ANTSWB | FPGA0_RF_PAPE | - ((FPGA0_RF_ANTSW | FPGA0_RF_ANTSWB | FPGA0_RF_PAPE) << - FPGA0_RF_BD_CTRL_SHIFT); - rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_SW_CTRL, val32); - /* 0x860[6:5]= 00 - why? - this sets antenna B */ - rtl8xxxu_write32(priv, REG_FPGA0_XA_RF_INT_OE, 0x66F60210); - - priv->rf_mode_ag[0] = rtl8xxxu_read_rfreg(priv, RF_A, - RF6052_REG_MODE_AG); + /* + * Chip specific quirks + */ + if (priv->rtlchip == 0x8723a) { + /* Fix USB interface interference issue */ + rtl8xxxu_write8(priv, 0xfe40, 0xe0); + rtl8xxxu_write8(priv, 0xfe41, 0x8d); + rtl8xxxu_write8(priv, 0xfe42, 0x80); + rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320); + + /* Reduce 80M spur */ + rtl8xxxu_write32(priv, REG_AFE_XTAL_CTRL, 0x0381808d); + rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83); + rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff82); + rtl8xxxu_write32(priv, REG_AFE_PLL_CTRL, 0xf0ffff83); + } else { + val32 = rtl8xxxu_read32(priv, REG_TXDMA_OFFSET_CHK); + val32 |= TXDMA_OFFSET_DROP_DATA_EN; + rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32); + } - dev_dbg(dev, "%s: macpower %i\n", __func__, macpower); if (!macpower) { if (priv->ep_tx_normal_queue) val8 = TX_PAGE_NUM_NORM_PQ; @@ -4109,6 +6459,10 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) * Set TX buffer boundary */ val8 = TX_TOTAL_PAGE_NUM + 1; + + if (priv->rtlchip == 0x8723b) + val8 -= 1; + rtl8xxxu_write8(priv, REG_TXPKTBUF_BCNQ_BDNY, val8); rtl8xxxu_write8(priv, REG_TXPKTBUF_MGQ_BDNY, val8); rtl8xxxu_write8(priv, REG_TXPKTBUF_WMAC_LBK_BF_HD, val8); @@ -4121,15 +6475,37 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) if (ret) goto exit; + /* RFSW Control - clear bit 14 ?? */ + if (priv->rtlchip != 0x8723b) + rtl8xxxu_write32(priv, REG_FPGA0_TX_INFO, 0x00000003); + /* 0x07000760 */ + val32 = FPGA0_RF_TRSW | FPGA0_RF_TRSWB | FPGA0_RF_ANTSW | + FPGA0_RF_ANTSWB | FPGA0_RF_PAPE | + ((FPGA0_RF_ANTSW | FPGA0_RF_ANTSWB | FPGA0_RF_PAPE) << + FPGA0_RF_BD_CTRL_SHIFT); + rtl8xxxu_write32(priv, REG_FPGA0_XAB_RF_SW_CTRL, val32); + /* 0x860[6:5]= 00 - why? - this sets antenna B */ + rtl8xxxu_write32(priv, REG_FPGA0_XA_RF_INT_OE, 0x66F60210); + + priv->rf_mode_ag[0] = rtl8xxxu_read_rfreg(priv, RF_A, + RF6052_REG_MODE_AG); + /* * Set RX page boundary */ - rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff); + if (priv->rtlchip == 0x8723b) + rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x3f7f); + else + rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, 0x27ff); /* * Transfer page size is always 128 */ - val8 = (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_RX_SHIFT) | - (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_TX_SHIFT); + if (priv->rtlchip == 0x8723b) + val8 = (PBP_PAGE_SIZE_256 << PBP_PAGE_SIZE_RX_SHIFT) | + (PBP_PAGE_SIZE_256 << PBP_PAGE_SIZE_TX_SHIFT); + else + val8 = (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_RX_SHIFT) | + (PBP_PAGE_SIZE_128 << PBP_PAGE_SIZE_TX_SHIFT); rtl8xxxu_write8(priv, REG_PBP, val8); /* @@ -4150,7 +6526,6 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) * Configure initial WMAC settings */ val32 = RCR_ACCEPT_PHYS_MATCH | RCR_ACCEPT_MCAST | RCR_ACCEPT_BCAST | - /* RCR_CHECK_BSSID_MATCH | RCR_CHECK_BSSID_BEACON | */ RCR_ACCEPT_MGMT_FRAME | RCR_HTC_LOC_CTRL | RCR_APPEND_PHYSTAT | RCR_APPEND_ICV | RCR_APPEND_MIC; rtl8xxxu_write32(priv, REG_RCR, val32); @@ -4215,6 +6590,42 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) rtl8xxxu_write16(priv, REG_BEACON_TCFG, 0x660F); /* + * Initialize burst parameters + */ + if (priv->rtlchip == 0x8723b) { + /* + * For USB high speed set 512B packets + */ + val8 = rtl8xxxu_read8(priv, REG_RXDMA_PRO_8723B); + val8 &= ~(BIT(4) | BIT(5)); + val8 |= BIT(4); + val8 |= BIT(1) | BIT(2) | BIT(3); + rtl8xxxu_write8(priv, REG_RXDMA_PRO_8723B, val8); + + /* + * For USB high speed set 512B packets + */ + val8 = rtl8xxxu_read8(priv, REG_HT_SINGLE_AMPDU_8723B); + val8 |= BIT(7); + rtl8xxxu_write8(priv, REG_HT_SINGLE_AMPDU_8723B, val8); + + rtl8xxxu_write16(priv, REG_MAX_AGGR_NUM, 0x0c14); + rtl8xxxu_write8(priv, REG_AMPDU_MAX_TIME_8723B, 0x5e); + rtl8xxxu_write32(priv, REG_AGGLEN_LMT, 0xffffffff); + rtl8xxxu_write8(priv, REG_RX_PKT_LIMIT, 0x18); + rtl8xxxu_write8(priv, REG_PIFS, 0x00); + rtl8xxxu_write8(priv, REG_USTIME_TSF_8723B, 0x50); + rtl8xxxu_write8(priv, REG_USTIME_EDCA, 0x50); + + val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL); + val8 |= BIT(5) | BIT(6); + rtl8xxxu_write8(priv, REG_RSV_CTRL, val8); + } + + if (priv->fops->init_aggregation) + priv->fops->init_aggregation(priv); + + /* * Enable CCK and OFDM block */ val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); @@ -4229,7 +6640,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) /* * Start out with default power levels for channel 6, 20MHz */ - rtl8723a_set_tx_power(priv, 1, false); + priv->fops->set_tx_power(priv, 1, false); /* Let the 8051 take control of antenna setting */ val8 = rtl8xxxu_read8(priv, REG_LEDCFG2); @@ -4243,78 +6654,37 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) rtl8xxxu_write16(priv, REG_FAST_EDCA_CTRL, 0); - /* - * Not sure if we should get into this at all - */ - if (priv->iqk_initialized) { - rtl8xxxu_restore_regs(priv, rtl8723au_iqk_phy_iq_bb_reg, - priv->bb_recovery_backup, - RTL8XXXU_BB_REGS); - } else { - rtl8723a_phy_iq_calibrate(priv); - priv->iqk_initialized = true; - } - - /* - * This should enable thermal meter - */ - rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60); + if (priv->fops->init_statistics) + priv->fops->init_statistics(priv); rtl8723a_phy_lc_calibrate(priv); - /* fix USB interface interference issue */ - rtl8xxxu_write8(priv, 0xfe40, 0xe0); - rtl8xxxu_write8(priv, 0xfe41, 0x8d); - rtl8xxxu_write8(priv, 0xfe42, 0x80); - rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, 0xfd0320); - - /* Solve too many protocol error on USB bus */ - /* Can't do this for 8188/8192 UMC A cut parts */ - rtl8xxxu_write8(priv, 0xfe40, 0xe6); - rtl8xxxu_write8(priv, 0xfe41, 0x94); - rtl8xxxu_write8(priv, 0xfe42, 0x80); - - rtl8xxxu_write8(priv, 0xfe40, 0xe0); - rtl8xxxu_write8(priv, 0xfe41, 0x19); - rtl8xxxu_write8(priv, 0xfe42, 0x80); - - rtl8xxxu_write8(priv, 0xfe40, 0xe5); - rtl8xxxu_write8(priv, 0xfe41, 0x91); - rtl8xxxu_write8(priv, 0xfe42, 0x80); - - rtl8xxxu_write8(priv, 0xfe40, 0xe2); - rtl8xxxu_write8(priv, 0xfe41, 0x81); - rtl8xxxu_write8(priv, 0xfe42, 0x80); - - /* Init BT hw config. */ - rtl8xxxu_init_bt(priv); + priv->fops->phy_iq_calibrate(priv); /* - * Not sure if we really need to save these parameters, but the - * vendor driver does + * This should enable thermal meter */ - val32 = rtl8xxxu_read32(priv, REG_FPGA0_XA_HSSI_PARM2); - if (val32 & FPGA0_HSSI_PARM2_CCK_HIGH_PWR) - priv->path_a_hi_power = 1; - - val32 = rtl8xxxu_read32(priv, REG_OFDM0_TRX_PATH_ENABLE); - priv->path_a_rf_paths = val32 & OFDM_RF_PATH_RX_MASK; - - val32 = rtl8xxxu_read32(priv, REG_OFDM0_XA_AGC_CORE1); - priv->path_a_ig_value = val32 & OFDM0_X_AGC_CORE1_IGI_MASK; + if (priv->fops->has_s0s1) + rtl8xxxu_write_rfreg(priv, + RF_A, RF6052_REG_T_METER_8723B, 0x37cf8); + else + rtl8xxxu_write_rfreg(priv, RF_A, RF6052_REG_T_METER, 0x60); /* Set NAV_UPPER to 30000us */ val8 = ((30000 + NAV_UPPER_UNIT - 1) / NAV_UPPER_UNIT); rtl8xxxu_write8(priv, REG_NAV_UPPER, val8); - /* - * 2011/03/09 MH debug only, UMC-B cut pass 2500 S5 test, - * but we need to fin root cause. - */ - val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); - if ((val32 & 0xff000000) != 0x83000000) { - val32 |= FPGA_RF_MODE_CCK; - rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + if (priv->rtlchip == 0x8723a) { + /* + * 2011/03/09 MH debug only, UMC-B cut pass 2500 S5 test, + * but we need to find root cause. + * This is 8723au only. + */ + val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE); + if ((val32 & 0xff000000) != 0x83000000) { + val32 |= FPGA_RF_MODE_CCK; + rtl8xxxu_write32(priv, REG_FPGA0_RF_MODE, val32); + } } val32 = rtl8xxxu_read32(priv, REG_FWHW_TXQ_CTRL); @@ -4330,7 +6700,7 @@ static void rtl8xxxu_disable_device(struct ieee80211_hw *hw) { struct rtl8xxxu_priv *priv = hw->priv; - rtl8xxxu_power_off(priv); + priv->fops->power_off(priv); } static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv, @@ -4376,7 +6746,7 @@ static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv, } static void rtl8xxxu_sw_scan_start(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, const u8* mac) + struct ieee80211_vif *vif, const u8 *mac) { struct rtl8xxxu_priv *priv = hw->priv; u8 val8; @@ -4397,11 +6767,13 @@ static void rtl8xxxu_sw_scan_complete(struct ieee80211_hw *hw, rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8); } -static void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv, - u32 ramask, int sgi) +static void rtl8723au_update_rate_mask(struct rtl8xxxu_priv *priv, + u32 ramask, int sgi) { struct h2c_cmd h2c; + memset(&h2c, 0, sizeof(struct h2c_cmd)); + h2c.ramask.cmd = H2C_SET_RATE_MASK; h2c.ramask.mask_lo = cpu_to_le16(ramask & 0xffff); h2c.ramask.mask_hi = cpu_to_le16(ramask >> 16); @@ -4410,9 +6782,68 @@ static void rtl8xxxu_update_rate_mask(struct rtl8xxxu_priv *priv, if (sgi) h2c.ramask.arg |= 0x20; - dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x\n", __func__, - ramask, h2c.ramask.arg); - rtl8723a_h2c_cmd(priv, &h2c); + dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x, size %zi\n", + __func__, ramask, h2c.ramask.arg, sizeof(h2c.ramask)); + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.ramask)); +} + +static void rtl8723bu_update_rate_mask(struct rtl8xxxu_priv *priv, + u32 ramask, int sgi) +{ + struct h2c_cmd h2c; + u8 bw = 0; + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + + h2c.b_macid_cfg.cmd = H2C_8723B_MACID_CFG_RAID; + h2c.b_macid_cfg.ramask0 = ramask & 0xff; + h2c.b_macid_cfg.ramask1 = (ramask >> 8) & 0xff; + h2c.b_macid_cfg.ramask2 = (ramask >> 16) & 0xff; + h2c.b_macid_cfg.ramask3 = (ramask >> 24) & 0xff; + + h2c.ramask.arg = 0x80; + h2c.b_macid_cfg.data1 = 0; + if (sgi) + h2c.b_macid_cfg.data1 |= BIT(7); + + h2c.b_macid_cfg.data2 = bw; + + dev_dbg(&priv->udev->dev, "%s: rate mask %08x, arg %02x, size %zi\n", + __func__, ramask, h2c.ramask.arg, sizeof(h2c.b_macid_cfg)); + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.b_macid_cfg)); +} + +static void rtl8723au_report_connect(struct rtl8xxxu_priv *priv, + u8 macid, bool connect) +{ + struct h2c_cmd h2c; + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + + h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT; + + if (connect) + h2c.joinbss.data = H2C_JOIN_BSS_CONNECT; + else + h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT; + + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.joinbss)); +} + +static void rtl8723bu_report_connect(struct rtl8xxxu_priv *priv, + u8 macid, bool connect) +{ + struct h2c_cmd h2c; + + memset(&h2c, 0, sizeof(struct h2c_cmd)); + + h2c.media_status_rpt.cmd = H2C_8723B_MEDIA_STATUS_RPT; + if (connect) + h2c.media_status_rpt.parm |= BIT(0); + else + h2c.media_status_rpt.parm &= ~BIT(0); + + rtl8723a_h2c_cmd(priv, &h2c, sizeof(h2c.media_status_rpt)); } static void rtl8xxxu_set_basic_rates(struct rtl8xxxu_priv *priv, u32 rate_cfg) @@ -4447,11 +6878,8 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 val8; if (changed & BSS_CHANGED_ASSOC) { - struct h2c_cmd h2c; - dev_dbg(dev, "Changed ASSOC: %i!\n", bss_conf->assoc); - memset(&h2c, 0, sizeof(struct h2c_cmd)); rtl8xxxu_set_linktype(priv, vif->type); if (bss_conf->assoc) { @@ -4481,14 +6909,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, sgi = 1; rcu_read_unlock(); - rtl8xxxu_update_rate_mask(priv, ramask, sgi); - - val32 = rtl8xxxu_read32(priv, REG_RCR); - val32 |= RCR_CHECK_BSSID_MATCH | RCR_CHECK_BSSID_BEACON; - rtl8xxxu_write32(priv, REG_RCR, val32); - - /* Enable RX of data frames */ - rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0xffff); + priv->fops->update_rate_mask(priv, ramask, sgi); rtl8xxxu_write8(priv, REG_BCN_MAX_ERR, 0xff); @@ -4498,23 +6919,14 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtl8xxxu_write16(priv, REG_BCN_PSR_RPT, 0xc000 | bss_conf->aid); - h2c.joinbss.data = H2C_JOIN_BSS_CONNECT; + priv->fops->report_connect(priv, 0, true); } else { - val32 = rtl8xxxu_read32(priv, REG_RCR); - val32 &= ~(RCR_CHECK_BSSID_MATCH | - RCR_CHECK_BSSID_BEACON); - rtl8xxxu_write32(priv, REG_RCR, val32); - val8 = rtl8xxxu_read8(priv, REG_BEACON_CTRL); val8 |= BEACON_DISABLE_TSF_UPDATE; rtl8xxxu_write8(priv, REG_BEACON_CTRL, val8); - /* Disable RX of data frames */ - rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000); - h2c.joinbss.data = H2C_JOIN_BSS_DISCONNECT; + priv->fops->report_connect(priv, 0, false); } - h2c.joinbss.cmd = H2C_JOIN_BSS_REPORT; - rtl8723a_h2c_cmd(priv, &h2c); } if (changed & BSS_CHANGED_ERP_PREAMBLE) { @@ -4589,7 +7001,12 @@ static u32 rtl8xxxu_queue_select(struct ieee80211_hw *hw, struct sk_buff *skb) return queue; } -static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc) +/* + * Despite newer chips 8723b/8812/8821 having a larger TX descriptor + * format. The descriptor checksum is still only calculated over the + * initial 32 bytes of the descriptor! + */ +static void rtl8xxxu_calc_tx_desc_csum(struct rtl8723au_tx_desc *tx_desc) { __le16 *ptr = (__le16 *)tx_desc; u16 csum = 0; @@ -4601,7 +7018,7 @@ static void rtl8xxxu_calc_tx_desc_csum(struct rtl8xxxu_tx_desc *tx_desc) */ tx_desc->csum = cpu_to_le16(0); - for (i = 0; i < (sizeof(struct rtl8xxxu_tx_desc) / sizeof(u16)); i++) + for (i = 0; i < (sizeof(struct rtl8723au_tx_desc) / sizeof(u16)); i++) csum = csum ^ le16_to_cpu(ptr[i]); tx_desc->csum |= cpu_to_le16(csum); @@ -4670,13 +7087,15 @@ static void rtl8xxxu_tx_complete(struct urb *urb) struct sk_buff *skb = (struct sk_buff *)urb->context; struct ieee80211_tx_info *tx_info; struct ieee80211_hw *hw; + struct rtl8xxxu_priv *priv; struct rtl8xxxu_tx_urb *tx_urb = container_of(urb, struct rtl8xxxu_tx_urb, urb); tx_info = IEEE80211_SKB_CB(skb); hw = tx_info->rate_driver_data[0]; + priv = hw->priv; - skb_pull(skb, sizeof(struct rtl8xxxu_tx_desc)); + skb_pull(skb, priv->fops->tx_desc_size); ieee80211_tx_info_clear_status(tx_info); tx_info->status.rates[0].idx = -1; @@ -4687,7 +7106,7 @@ static void rtl8xxxu_tx_complete(struct urb *urb) ieee80211_tx_status_irqsafe(hw, skb); - rtl8xxxu_free_tx_urb(hw->priv, tx_urb); + rtl8xxxu_free_tx_urb(priv, tx_urb); } static void rtl8xxxu_dump_action(struct device *dev, @@ -4737,7 +7156,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_rate *tx_rate = ieee80211_get_tx_rate(hw, tx_info); struct rtl8xxxu_priv *priv = hw->priv; - struct rtl8xxxu_tx_desc *tx_desc; + struct rtl8723au_tx_desc *tx_desc; + struct rtl8723bu_tx_desc *tx_desc40; struct rtl8xxxu_tx_urb *tx_urb; struct ieee80211_sta *sta = NULL; struct ieee80211_vif *vif = tx_info->control.vif; @@ -4746,16 +7166,18 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, u16 pktlen = skb->len; u16 seq_number; u16 rate_flag = tx_info->control.rates[0].flags; + int tx_desc_size = priv->fops->tx_desc_size; int ret; + bool usedesc40, ampdu_enable; - if (skb_headroom(skb) < sizeof(struct rtl8xxxu_tx_desc)) { + if (skb_headroom(skb) < tx_desc_size) { dev_warn(dev, "%s: Not enough headroom (%i) for tx descriptor\n", __func__, skb_headroom(skb)); goto error; } - if (unlikely(skb->len > (65535 - sizeof(struct rtl8xxxu_tx_desc)))) { + if (unlikely(skb->len > (65535 - tx_desc_size))) { dev_warn(dev, "%s: Trying to send over-sized skb (%i)\n", __func__, skb->len); goto error; @@ -4774,17 +7196,17 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, if (ieee80211_is_action(hdr->frame_control)) rtl8xxxu_dump_action(dev, hdr); + usedesc40 = (tx_desc_size == 40); tx_info->rate_driver_data[0] = hw; if (control && control->sta) sta = control->sta; - tx_desc = (struct rtl8xxxu_tx_desc *) - skb_push(skb, sizeof(struct rtl8xxxu_tx_desc)); + tx_desc = (struct rtl8723au_tx_desc *)skb_push(skb, tx_desc_size); - memset(tx_desc, 0, sizeof(struct rtl8xxxu_tx_desc)); + memset(tx_desc, 0, tx_desc_size); tx_desc->pkt_size = cpu_to_le16(pktlen); - tx_desc->pkt_offset = sizeof(struct rtl8xxxu_tx_desc); + tx_desc->pkt_offset = tx_desc_size; tx_desc->txdw0 = TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT; @@ -4810,19 +7232,8 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, } } - seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - tx_desc->txdw3 = cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT); - - if (rate_flag & IEEE80211_TX_RC_MCS) - rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; - else - rate = tx_rate->hw_value; - tx_desc->txdw5 = cpu_to_le32(rate); - - if (ieee80211_is_data(hdr->frame_control)) - tx_desc->txdw5 |= cpu_to_le32(0x0001ff00); - /* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */ + ampdu_enable = false; if (ieee80211_is_data_qos(hdr->frame_control) && sta) { if (sta->ht_cap.ht_supported) { u32 ampdu, val32; @@ -4830,35 +7241,118 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw, ampdu = (u32)sta->ht_cap.ampdu_density; val32 = ampdu << TXDESC_AMPDU_DENSITY_SHIFT; tx_desc->txdw2 |= cpu_to_le32(val32); - tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE); - } else - tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK); - } else - tx_desc->txdw1 |= cpu_to_le32(TXDESC_BK); - if (ieee80211_is_data_qos(hdr->frame_control)) - tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS); - if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE || - (sta && vif && vif->bss_conf.use_short_preamble)) - tx_desc->txdw4 |= cpu_to_le32(TXDESC_SHORT_PREAMBLE); - if (rate_flag & IEEE80211_TX_RC_SHORT_GI || - (ieee80211_is_data_qos(hdr->frame_control) && - sta && sta->ht_cap.cap & - (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) { - tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI); - } - if (ieee80211_is_mgmt(hdr->frame_control)) { - tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value); - tx_desc->txdw4 |= cpu_to_le32(TXDESC_USE_DRIVER_RATE); - tx_desc->txdw5 |= cpu_to_le32(6 << TXDESC_RETRY_LIMIT_SHIFT); - tx_desc->txdw5 |= cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE); + ampdu_enable = true; + } } - if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { - /* Use RTS rate 24M - does the mac80211 tell us which to use? */ - tx_desc->txdw4 |= cpu_to_le32(DESC_RATE_24M); - tx_desc->txdw4 |= cpu_to_le32(TXDESC_RTS_CTS_ENABLE); - tx_desc->txdw4 |= cpu_to_le32(TXDESC_HW_RTS_ENABLE); + if (rate_flag & IEEE80211_TX_RC_MCS) + rate = tx_info->control.rates[0].idx + DESC_RATE_MCS0; + else + rate = tx_rate->hw_value; + + seq_number = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + if (!usedesc40) { + tx_desc->txdw5 = cpu_to_le32(rate); + + if (ieee80211_is_data(hdr->frame_control)) + tx_desc->txdw5 |= cpu_to_le32(0x0001ff00); + + tx_desc->txdw3 = + cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT_8723A); + + if (ampdu_enable) + tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_ENABLE_8723A); + else + tx_desc->txdw1 |= cpu_to_le32(TXDESC_AGG_BREAK_8723A); + + if (ieee80211_is_mgmt(hdr->frame_control)) { + tx_desc->txdw5 = cpu_to_le32(tx_rate->hw_value); + tx_desc->txdw4 |= + cpu_to_le32(TXDESC_USE_DRIVER_RATE_8723A); + tx_desc->txdw5 |= + cpu_to_le32(6 << + TXDESC_RETRY_LIMIT_SHIFT_8723A); + tx_desc->txdw5 |= + cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE_8723A); + } + + if (ieee80211_is_data_qos(hdr->frame_control)) + tx_desc->txdw4 |= cpu_to_le32(TXDESC_QOS_8723A); + + if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE || + (sta && vif && vif->bss_conf.use_short_preamble)) + tx_desc->txdw4 |= + cpu_to_le32(TXDESC_SHORT_PREAMBLE_8723A); + + if (rate_flag & IEEE80211_TX_RC_SHORT_GI || + (ieee80211_is_data_qos(hdr->frame_control) && + sta && sta->ht_cap.cap & + (IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))) { + tx_desc->txdw5 |= cpu_to_le32(TXDESC_SHORT_GI); + } + + if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { + /* + * Use RTS rate 24M - does the mac80211 tell + * us which to use? + */ + tx_desc->txdw4 |= + cpu_to_le32(DESC_RATE_24M << + TXDESC_RTS_RATE_SHIFT_8723A); + tx_desc->txdw4 |= + cpu_to_le32(TXDESC_RTS_CTS_ENABLE_8723A); + tx_desc->txdw4 |= + cpu_to_le32(TXDESC_HW_RTS_ENABLE_8723A); + } + } else { + tx_desc40 = (struct rtl8723bu_tx_desc *)tx_desc; + + tx_desc40->txdw4 = cpu_to_le32(rate); + if (ieee80211_is_data(hdr->frame_control)) { + tx_desc->txdw4 |= + cpu_to_le32(0x1f << + TXDESC_DATA_RATE_FB_SHIFT_8723B); + } + + tx_desc40->txdw9 = + cpu_to_le32((u32)seq_number << TXDESC_SEQ_SHIFT_8723B); + + if (ampdu_enable) + tx_desc40->txdw2 |= + cpu_to_le32(TXDESC_AGG_ENABLE_8723B); + else + tx_desc40->txdw2 |= cpu_to_le32(TXDESC_AGG_BREAK_8723B); + + if (ieee80211_is_mgmt(hdr->frame_control)) { + tx_desc40->txdw4 = cpu_to_le32(tx_rate->hw_value); + tx_desc40->txdw3 |= + cpu_to_le32(TXDESC_USE_DRIVER_RATE_8723B); + tx_desc40->txdw4 |= + cpu_to_le32(6 << + TXDESC_RETRY_LIMIT_SHIFT_8723B); + tx_desc40->txdw4 |= + cpu_to_le32(TXDESC_RETRY_LIMIT_ENABLE_8723B); + } + + if (rate_flag & IEEE80211_TX_RC_USE_SHORT_PREAMBLE || + (sta && vif && vif->bss_conf.use_short_preamble)) + tx_desc40->txdw5 |= + cpu_to_le32(TXDESC_SHORT_PREAMBLE_8723B); + + if (rate_flag & IEEE80211_TX_RC_USE_RTS_CTS) { + /* + * Use RTS rate 24M - does the mac80211 tell + * us which to use? + */ + tx_desc->txdw4 |= + cpu_to_le32(DESC_RATE_24M << + TXDESC_RTS_RATE_SHIFT_8723B); + tx_desc->txdw3 |= + cpu_to_le32(TXDESC_RTS_CTS_ENABLE_8723B); + tx_desc->txdw3 |= + cpu_to_le32(TXDESC_HW_RTS_ENABLE_8723B); + } } rtl8xxxu_calc_tx_desc_csum(tx_desc); @@ -4880,13 +7374,13 @@ error: static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv, struct ieee80211_rx_status *rx_status, - struct rtl8xxxu_rx_desc *rx_desc, - struct rtl8723au_phy_stats *phy_stats) + struct rtl8723au_phy_stats *phy_stats, + u32 rxmcs) { if (phy_stats->sgi_en) rx_status->flag |= RX_FLAG_SHORT_GI; - if (rx_desc->rxmcs < DESC_RATE_6M) { + if (rxmcs < DESC_RATE_6M) { /* * Handle PHY stats for CCK rates */ @@ -4997,6 +7491,138 @@ static void rtl8xxxu_rx_urb_work(struct work_struct *work) } } +static int rtl8723au_parse_rx_desc(struct rtl8xxxu_priv *priv, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct rtl8xxxu_rx_desc *rx_desc = (struct rtl8xxxu_rx_desc *)skb->data; + struct rtl8723au_phy_stats *phy_stats; + int drvinfo_sz, desc_shift; + + skb_pull(skb, sizeof(struct rtl8xxxu_rx_desc)); + + phy_stats = (struct rtl8723au_phy_stats *)skb->data; + + drvinfo_sz = rx_desc->drvinfo_sz * 8; + desc_shift = rx_desc->shift; + skb_pull(skb, drvinfo_sz + desc_shift); + + if (rx_desc->phy_stats) + rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats, + rx_desc->rxmcs); + + rx_status->mactime = le32_to_cpu(rx_desc->tsfl); + rx_status->flag |= RX_FLAG_MACTIME_START; + + if (!rx_desc->swdec) + rx_status->flag |= RX_FLAG_DECRYPTED; + if (rx_desc->crc32) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rx_desc->bw) + rx_status->flag |= RX_FLAG_40MHZ; + + if (rx_desc->rxht) { + rx_status->flag |= RX_FLAG_HT; + rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; + } else { + rx_status->rate_idx = rx_desc->rxmcs; + } + + return RX_TYPE_DATA_PKT; +} + +static int rtl8723bu_parse_rx_desc(struct rtl8xxxu_priv *priv, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct rtl8723bu_rx_desc *rx_desc = + (struct rtl8723bu_rx_desc *)skb->data; + struct rtl8723au_phy_stats *phy_stats; + int drvinfo_sz, desc_shift; + + skb_pull(skb, sizeof(struct rtl8723bu_rx_desc)); + + phy_stats = (struct rtl8723au_phy_stats *)skb->data; + + drvinfo_sz = rx_desc->drvinfo_sz * 8; + desc_shift = rx_desc->shift; + skb_pull(skb, drvinfo_sz + desc_shift); + + if (rx_desc->rpt_sel) { + struct device *dev = &priv->udev->dev; + dev_dbg(dev, "%s: C2H packet\n", __func__); + return RX_TYPE_C2H; + } + + if (rx_desc->phy_stats) + rtl8xxxu_rx_parse_phystats(priv, rx_status, phy_stats, + rx_desc->rxmcs); + + rx_status->mactime = le32_to_cpu(rx_desc->tsfl); + rx_status->flag |= RX_FLAG_MACTIME_START; + + if (!rx_desc->swdec) + rx_status->flag |= RX_FLAG_DECRYPTED; + if (rx_desc->crc32) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rx_desc->bw) + rx_status->flag |= RX_FLAG_40MHZ; + + if (rx_desc->rxmcs >= DESC_RATE_MCS0) { + rx_status->flag |= RX_FLAG_HT; + rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; + } else { + rx_status->rate_idx = rx_desc->rxmcs; + } + + return RX_TYPE_DATA_PKT; +} + +static void rtl8723bu_handle_c2h(struct rtl8xxxu_priv *priv, + struct sk_buff *skb) +{ + struct rtl8723bu_c2h *c2h = (struct rtl8723bu_c2h *)skb->data; + struct device *dev = &priv->udev->dev; + int len; + + len = skb->len - 2; + + dev_dbg(dev, "C2H ID %02x seq %02x, len %02x source %02x\n", + c2h->id, c2h->seq, len, c2h->bt_info.response_source); + + switch(c2h->id) { + case C2H_8723B_BT_INFO: + if (c2h->bt_info.response_source > + BT_INFO_SRC_8723B_BT_ACTIVE_SEND) + dev_dbg(dev, "C2H_BT_INFO WiFi only firmware\n"); + else + dev_dbg(dev, "C2H_BT_INFO BT/WiFi coexist firmware\n"); + + if (c2h->bt_info.bt_has_reset) + dev_dbg(dev, "BT has been reset\n"); + if (c2h->bt_info.tx_rx_mask) + dev_dbg(dev, "BT TRx mask\n"); + + break; + case C2H_8723B_BT_MP_INFO: + dev_dbg(dev, "C2H_MP_INFO ext ID %02x, status %02x\n", + c2h->bt_mp_info.ext_id, c2h->bt_mp_info.status); + break; + case C2H_8723B_RA_REPORT: + dev_dbg(dev, + "C2H RA RPT: rate %02x, unk %i, macid %02x, noise %i\n", + c2h->ra_report.rate, c2h->ra_report.dummy0_0, + c2h->ra_report.macid, c2h->ra_report.noisy_state); + break; + default: + dev_info(dev, "Unhandled C2H event %02x seq %02x\n", + c2h->id, c2h->seq); + print_hex_dump(KERN_INFO, "C2H content: ", DUMP_PREFIX_NONE, + 16, 1, c2h->raw.payload, len, false); + break; + } +} + static void rtl8xxxu_rx_complete(struct urb *urb) { struct rtl8xxxu_rx_urb *rx_urb = @@ -5004,59 +7630,32 @@ static void rtl8xxxu_rx_complete(struct urb *urb) struct ieee80211_hw *hw = rx_urb->hw; struct rtl8xxxu_priv *priv = hw->priv; struct sk_buff *skb = (struct sk_buff *)urb->context; - struct rtl8xxxu_rx_desc *rx_desc = (struct rtl8xxxu_rx_desc *)skb->data; - struct rtl8723au_phy_stats *phy_stats; struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_mgmt *mgmt; struct device *dev = &priv->udev->dev; __le32 *_rx_desc_le = (__le32 *)skb->data; u32 *_rx_desc = (u32 *)skb->data; - int cnt, len, drvinfo_sz, desc_shift, i; + int rx_type, i; for (i = 0; i < (sizeof(struct rtl8xxxu_rx_desc) / sizeof(u32)); i++) _rx_desc[i] = le32_to_cpu(_rx_desc_le[i]); - cnt = rx_desc->frag; - len = rx_desc->pktlen; - drvinfo_sz = rx_desc->drvinfo_sz * 8; - desc_shift = rx_desc->shift; skb_put(skb, urb->actual_length); if (urb->status == 0) { - skb_pull(skb, sizeof(struct rtl8xxxu_rx_desc)); - phy_stats = (struct rtl8723au_phy_stats *)skb->data; - - skb_pull(skb, drvinfo_sz + desc_shift); - - mgmt = (struct ieee80211_mgmt *)skb->data; - memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); - if (rx_desc->phy_stats) - rtl8xxxu_rx_parse_phystats(priv, rx_status, - rx_desc, phy_stats); + rx_type = priv->fops->parse_rx_desc(priv, skb, rx_status); rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; - rx_status->mactime = le32_to_cpu(rx_desc->tsfl); - rx_status->flag |= RX_FLAG_MACTIME_START; - - if (!rx_desc->swdec) - rx_status->flag |= RX_FLAG_DECRYPTED; - if (rx_desc->crc32) - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - if (rx_desc->bw) - rx_status->flag |= RX_FLAG_40MHZ; - - if (rx_desc->rxht) { - rx_status->flag |= RX_FLAG_HT; - rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; - } else { - rx_status->rate_idx = rx_desc->rxmcs; + if (rx_type == RX_TYPE_DATA_PKT) + ieee80211_rx_irqsafe(hw, skb); + else { + rtl8723bu_handle_c2h(priv, skb); + dev_kfree_skb(skb); } - ieee80211_rx_irqsafe(hw, skb); skb = NULL; rx_urb->urb.context = NULL; rtl8xxxu_queue_rx_urb(priv, rx_urb); @@ -5213,9 +7812,9 @@ static int rtl8xxxu_config(struct ieee80211_hw *hw, u32 changed) channel = hw->conf.chandef.chan->hw_value; - rtl8723a_set_tx_power(priv, channel, ht40); + priv->fops->set_tx_power(priv, channel, ht40); - rtl8723au_config_channel(hw); + priv->fops->config_channel(hw); } exit: @@ -5279,11 +7878,56 @@ static void rtl8xxxu_configure_filter(struct ieee80211_hw *hw, unsigned int *total_flags, u64 multicast) { struct rtl8xxxu_priv *priv = hw->priv; + u32 rcr = rtl8xxxu_read32(priv, REG_RCR); dev_dbg(&priv->udev->dev, "%s: changed_flags %08x, total_flags %08x\n", __func__, changed_flags, *total_flags); - *total_flags &= (FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC); + /* + * FIF_ALLMULTI ignored as all multicast frames are accepted (REG_MAR) + */ + + if (*total_flags & FIF_FCSFAIL) + rcr |= RCR_ACCEPT_CRC32; + else + rcr &= ~RCR_ACCEPT_CRC32; + + /* + * FIF_PLCPFAIL not supported? + */ + + if (*total_flags & FIF_BCN_PRBRESP_PROMISC) + rcr &= ~RCR_CHECK_BSSID_BEACON; + else + rcr |= RCR_CHECK_BSSID_BEACON; + + if (*total_flags & FIF_CONTROL) + rcr |= RCR_ACCEPT_CTRL_FRAME; + else + rcr &= ~RCR_ACCEPT_CTRL_FRAME; + + if (*total_flags & FIF_OTHER_BSS) { + rcr |= RCR_ACCEPT_AP; + rcr &= ~RCR_CHECK_BSSID_MATCH; + } else { + rcr &= ~RCR_ACCEPT_AP; + rcr |= RCR_CHECK_BSSID_MATCH; + } + + if (*total_flags & FIF_PSPOLL) + rcr |= RCR_ACCEPT_PM; + else + rcr &= ~RCR_ACCEPT_PM; + + /* + * FIF_PROBE_REQ ignored as probe requests always seem to be accepted + */ + + rtl8xxxu_write32(priv, REG_RCR, rcr); + + *total_flags &= (FIF_ALLMULTI | FIF_FCSFAIL | FIF_BCN_PRBRESP_PROMISC | + FIF_CONTROL | FIF_OTHER_BSS | FIF_PSPOLL | + FIF_PROBE_REQ); } static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts) @@ -5370,13 +8014,13 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, static int rtl8xxxu_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, - bool amsdu) + struct ieee80211_ampdu_params *params) { struct rtl8xxxu_priv *priv = hw->priv; struct device *dev = &priv->udev->dev; u8 ampdu_factor, ampdu_density; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; switch (action) { case IEEE80211_AMPDU_TX_START: @@ -5426,10 +8070,12 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw) init_usb_anchor(&priv->tx_anchor); init_usb_anchor(&priv->int_anchor); - rtl8723a_enable_rf(priv); - ret = rtl8xxxu_submit_int_urb(hw); - if (ret) - goto exit; + priv->fops->enable_rf(priv); + if (priv->usb_interrupts) { + ret = rtl8xxxu_submit_int_urb(hw); + if (ret) + goto exit; + } for (i = 0; i < RTL8XXXU_TX_URBS; i++) { tx_urb = kmalloc(sizeof(struct rtl8xxxu_tx_urb), GFP_KERNEL); @@ -5468,12 +8114,9 @@ static int rtl8xxxu_start(struct ieee80211_hw *hw) } exit: /* - * Disable all data frames - */ - rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0x0000); - /* - * Accept all mgmt frames + * Accept all data and mgmt frames */ + rtl8xxxu_write16(priv, REG_RXFLTMAP2, 0xffff); rtl8xxxu_write16(priv, REG_RXFLTMAP0, 0xffff); rtl8xxxu_write32(priv, REG_OFDM0_XA_AGC_CORE1, 0x6954341e); @@ -5507,14 +8150,16 @@ static void rtl8xxxu_stop(struct ieee80211_hw *hw) usb_kill_anchored_urbs(&priv->rx_anchor); usb_kill_anchored_urbs(&priv->tx_anchor); - usb_kill_anchored_urbs(&priv->int_anchor); + if (priv->usb_interrupts) + usb_kill_anchored_urbs(&priv->int_anchor); - rtl8723a_disable_rf(priv); + priv->fops->disable_rf(priv); /* * Disable interrupts */ - rtl8xxxu_write32(priv, REG_USB_HIMR, 0); + if (priv->usb_interrupts) + rtl8xxxu_write32(priv, REG_USB_HIMR, 0); rtl8xxxu_free_rx_resources(priv); rtl8xxxu_free_tx_resources(priv); @@ -5645,7 +8290,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, } if (untested) { - rtl8xxxu_debug = RTL8XXXU_DEBUG_EFUSE; + rtl8xxxu_debug |= RTL8XXXU_DEBUG_EFUSE; dev_info(&udev->dev, "This Realtek USB WiFi dongle (0x%04x:0x%04x) is untested!\n", id->idVendor, id->idProduct); @@ -5739,7 +8384,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface, SET_IEEE80211_DEV(priv->hw, &interface->dev); SET_IEEE80211_PERM_ADDR(hw, priv->mac_addr); - hw->extra_tx_headroom = sizeof(struct rtl8xxxu_tx_desc); + hw->extra_tx_headroom = priv->fops->tx_desc_size; ieee80211_hw_set(hw, SIGNAL_DBM); /* * The firmware handles rate control @@ -5787,7 +8432,54 @@ static struct rtl8xxxu_fileops rtl8723au_fops = { .parse_efuse = rtl8723au_parse_efuse, .load_firmware = rtl8723au_load_firmware, .power_on = rtl8723au_power_on, + .power_off = rtl8xxxu_power_off, + .reset_8051 = rtl8xxxu_reset_8051, + .llt_init = rtl8xxxu_init_llt_table, + .phy_iq_calibrate = rtl8723au_phy_iq_calibrate, + .config_channel = rtl8723au_config_channel, + .parse_rx_desc = rtl8723au_parse_rx_desc, + .enable_rf = rtl8723a_enable_rf, + .disable_rf = rtl8723a_disable_rf, + .set_tx_power = rtl8723a_set_tx_power, + .update_rate_mask = rtl8723au_update_rate_mask, + .report_connect = rtl8723au_report_connect, + .writeN_block_size = 1024, + .mbox_ext_reg = REG_HMBOX_EXT_0, + .mbox_ext_width = 2, + .tx_desc_size = sizeof(struct rtl8723au_tx_desc), + .adda_1t_init = 0x0b1b25a0, + .adda_1t_path_on = 0x0bdb25a0, + .adda_2t_path_on_a = 0x04db25a4, + .adda_2t_path_on_b = 0x0b1b25a4, +}; + +static struct rtl8xxxu_fileops rtl8723bu_fops = { + .parse_efuse = rtl8723bu_parse_efuse, + .load_firmware = rtl8723bu_load_firmware, + .power_on = rtl8723bu_power_on, + .power_off = rtl8723bu_power_off, + .reset_8051 = rtl8723bu_reset_8051, + .llt_init = rtl8xxxu_auto_llt_table, + .phy_init_antenna_selection = rtl8723bu_phy_init_antenna_selection, + .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate, + .config_channel = rtl8723bu_config_channel, + .parse_rx_desc = rtl8723bu_parse_rx_desc, + .init_aggregation = rtl8723bu_init_aggregation, + .init_statistics = rtl8723bu_init_statistics, + .enable_rf = rtl8723b_enable_rf, + .disable_rf = rtl8723b_disable_rf, + .set_tx_power = rtl8723b_set_tx_power, + .update_rate_mask = rtl8723bu_update_rate_mask, + .report_connect = rtl8723bu_report_connect, .writeN_block_size = 1024, + .mbox_ext_reg = REG_HMBOX_EXT0_8723B, + .mbox_ext_width = 4, + .tx_desc_size = sizeof(struct rtl8723bu_tx_desc), + .has_s0s1 = 1, + .adda_1t_init = 0x01c00014, + .adda_1t_path_on = 0x01c00014, + .adda_2t_path_on_a = 0x01c00014, + .adda_2t_path_on_b = 0x01c00014, }; #ifdef CONFIG_RTL8XXXU_UNTESTED @@ -5796,11 +8488,55 @@ static struct rtl8xxxu_fileops rtl8192cu_fops = { .parse_efuse = rtl8192cu_parse_efuse, .load_firmware = rtl8192cu_load_firmware, .power_on = rtl8192cu_power_on, + .power_off = rtl8xxxu_power_off, + .reset_8051 = rtl8xxxu_reset_8051, + .llt_init = rtl8xxxu_init_llt_table, + .phy_iq_calibrate = rtl8723au_phy_iq_calibrate, + .config_channel = rtl8723au_config_channel, + .parse_rx_desc = rtl8723au_parse_rx_desc, + .enable_rf = rtl8723a_enable_rf, + .disable_rf = rtl8723a_disable_rf, + .set_tx_power = rtl8723a_set_tx_power, + .update_rate_mask = rtl8723au_update_rate_mask, + .report_connect = rtl8723au_report_connect, .writeN_block_size = 128, + .mbox_ext_reg = REG_HMBOX_EXT_0, + .mbox_ext_width = 2, + .tx_desc_size = sizeof(struct rtl8723au_tx_desc), + .adda_1t_init = 0x0b1b25a0, + .adda_1t_path_on = 0x0bdb25a0, + .adda_2t_path_on_a = 0x04db25a4, + .adda_2t_path_on_b = 0x0b1b25a4, }; #endif +static struct rtl8xxxu_fileops rtl8192eu_fops = { + .parse_efuse = rtl8192eu_parse_efuse, + .load_firmware = rtl8192eu_load_firmware, + .power_on = rtl8192eu_power_on, + .power_off = rtl8xxxu_power_off, + .reset_8051 = rtl8xxxu_reset_8051, + .llt_init = rtl8xxxu_auto_llt_table, + .phy_iq_calibrate = rtl8723bu_phy_iq_calibrate, + .config_channel = rtl8723bu_config_channel, + .parse_rx_desc = rtl8723bu_parse_rx_desc, + .enable_rf = rtl8723b_enable_rf, + .disable_rf = rtl8723b_disable_rf, + .set_tx_power = rtl8723b_set_tx_power, + .update_rate_mask = rtl8723au_update_rate_mask, + .report_connect = rtl8723au_report_connect, + .writeN_block_size = 128, + .mbox_ext_reg = REG_HMBOX_EXT0_8723B, + .mbox_ext_width = 4, + .tx_desc_size = sizeof(struct rtl8723au_tx_desc), + .has_s0s1 = 1, + .adda_1t_init = 0x0fc01616, + .adda_1t_path_on = 0x0fc01616, + .adda_2t_path_on_a = 0x0fc01616, + .adda_2t_path_on_b = 0x0fc01616, +}; + static struct usb_device_id dev_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8724, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8723au_fops}, @@ -5808,6 +8544,10 @@ static struct usb_device_id dev_table[] = { .driver_info = (unsigned long)&rtl8723au_fops}, {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x0724, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8723au_fops}, +{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x818b, 0xff, 0xff, 0xff), + .driver_info = (unsigned long)&rtl8192eu_fops}, +{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0xb720, 0xff, 0xff, 0xff), + .driver_info = (unsigned long)&rtl8723bu_fops}, #ifdef CONFIG_RTL8XXXU_UNTESTED /* Still supported by rtlwifi */ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff), @@ -5886,8 +8626,6 @@ static struct usb_device_id dev_table[] = { .driver_info = (unsigned long)&rtl8192cu_fops}, {USB_DEVICE_AND_INTERFACE_INFO(0xcdab, 0x8010, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8192cu_fops}, -{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x317f, 0xff, 0xff, 0xff), - .driver_info = (unsigned long)&rtl8192cu_fops}, /* Netcore 8188RU */ {USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff7, 0xff, 0xff, 0xff), .driver_info = (unsigned long)&rtl8192cu_fops}, {USB_DEVICE_AND_INTERFACE_INFO(0x04f2, 0xaff9, 0xff, 0xff, 0xff), diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h index f2a1bac6c..7b73654e1 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h @@ -34,7 +34,7 @@ #define RTL8XXXU_MAX_REG_POLL 500 #define USB_INTR_CONTENT_LENGTH 56 -#define RTL8XXXU_OUT_ENDPOINTS 3 +#define RTL8XXXU_OUT_ENDPOINTS 4 #define REALTEK_USB_READ 0xc0 #define REALTEK_USB_WRITE 0x40 @@ -53,14 +53,24 @@ #define RTL8723A_CHANNEL_GROUPS 3 #define RTL8723A_MAX_RF_PATHS 2 +#define RTL8723B_CHANNEL_GROUPS 6 +#define RTL8723B_TX_COUNT 4 +#define RTL8723B_MAX_RF_PATHS 4 +#define RTL8XXXU_MAX_CHANNEL_GROUPS 6 #define RF6052_MAX_TX_PWR 0x3f -#define EFUSE_MAP_LEN_8723A 256 -#define EFUSE_MAX_SECTION_8723A 32 +#define EFUSE_MAP_LEN 512 +#define EFUSE_MAX_SECTION_8723A 64 #define EFUSE_REAL_CONTENT_LEN_8723A 512 #define EFUSE_BT_MAP_LEN_8723A 1024 #define EFUSE_MAX_WORD_UNIT 4 +enum rtl8xxxu_rx_type { + RX_TYPE_DATA_PKT = 0, + RX_TYPE_C2H = 1, + RX_TYPE_ERROR = -1 +}; + struct rtl8xxxu_rx_desc { #ifdef __LITTLE_ENDIAN u32 pktlen:14; @@ -197,7 +207,146 @@ struct rtl8xxxu_rx_desc { #endif }; -struct rtl8xxxu_tx_desc { +struct rtl8723bu_rx_desc { +#ifdef __LITTLE_ENDIAN + u32 pktlen:14; + u32 crc32:1; + u32 icverr:1; + u32 drvinfo_sz:4; + u32 security:3; + u32 qos:1; + u32 shift:2; + u32 phy_stats:1; + u32 swdec:1; + u32 ls:1; + u32 fs:1; + u32 eor:1; + u32 own:1; + + u32 macid:7; + u32 dummy1_0:1; + u32 tid:4; + u32 dummy1_1:1; + u32 amsdu:1; + u32 rxid_match:1; + u32 paggr:1; + u32 a1fit:4; /* 16 */ + u32 chkerr:1; + u32 ipver:1; + u32 tcpudp:1; + u32 chkvld:1; + u32 pam:1; + u32 pwr:1; + u32 more_data:1; + u32 more_frag:1; + u32 type:2; + u32 mc:1; + u32 bc:1; + + u32 seq:12; + u32 frag:4; + u32 rx_is_qos:1; /* 16 */ + u32 dummy2_0:1; + u32 wlanhd_iv_len:6; + u32 dummy2_1:4; + u32 rpt_sel:1; + u32 dummy2_2:3; + + u32 rxmcs:7; + u32 dummy3_0:3; + u32 htc:1; + u32 eosp:1; + u32 bssidfit:2; + u32 dummy3_1:2; + u32 usb_agg_pktnum:8; /* 16 */ + u32 dummy3_2:5; + u32 pattern_match:1; + u32 unicast_match:1; + u32 magic_match:1; + + u32 splcp:1; + u32 ldcp:1; + u32 stbc:1; + u32 dummy4_0:1; + u32 bw:2; + u32 dummy4_1:26; +#else + u32 own:1; + u32 eor:1; + u32 fs:1; + u32 ls:1; + u32 swdec:1; + u32 phy_stats:1; + u32 shift:2; + u32 qos:1; + u32 security:3; + u32 drvinfo_sz:4; + u32 icverr:1; + u32 crc32:1; + u32 pktlen:14; + + u32 bc:1; + u32 mc:1; + u32 type:2; + u32 mf:1; + u32 md:1; + u32 pwr:1; + u32 pam:1; + u32 a2fit:4; + u32 a1fit:4; + u32 faggr:1; + u32 paggr:1; + u32 amsdu:1; + u32 hwrsvd:4; + u32 tid:4; + u32 macid:5; + + u32 dummy2_2:3; + u32 rpt_sel:1; + u32 dummy2_1:4; + u32 wlanhd_iv_len:6; + u32 dummy2_0:1; + u32 rx_is_qos:1; + u32 frag:4; /* 16 */ + u32 seq:12; + + u32 magic_match:1; + u32 unicast_match:1; + u32 pattern_match:1; + u32 dummy3_2:5; + u32 usb_agg_pktnum:8; + u32 dummy3_1:2; /* 16 */ + u32 bssidfit:2; + u32 eosp:1; + u32 htc:1; + u32 dummy3_0:3; + u32 rxmcs:7; + + u32 dumm4_1:26; + u32 bw:2; + u32 dummy4_0:1; + u32 stbc:1; + u32 ldcp:1; + u32 splcp:1; +#endif + __le32 tsfl; +}; + +struct rtl8723au_tx_desc { + __le16 pkt_size; + u8 pkt_offset; + u8 txdw0; + __le32 txdw1; + __le32 txdw2; + __le32 txdw3; + __le32 txdw4; + __le32 txdw5; + __le32 txdw6; + __le16 csum; + __le16 txdw7; +}; + +struct rtl8723bu_tx_desc { __le16 pkt_size; u8 pkt_offset; u8 txdw0; @@ -209,6 +358,8 @@ struct rtl8xxxu_tx_desc { __le32 txdw6; __le16 csum; __le16 txdw7; + __le32 txdw8; + __le32 txdw9; }; /* CCK Rates, TxHT = 0 */ @@ -256,15 +407,25 @@ struct rtl8xxxu_tx_desc { #define TXDESC_OWN BIT(31) #else #define TXDESC_BROADMULTICAST BIT(0) +#define TXDESC_HTC BIT(1) #define TXDESC_LAST_SEGMENT BIT(2) #define TXDESC_FIRST_SEGMENT BIT(3) +#define TXDESC_LINIP BIT(4) +#define TXDESC_NO_ACM BIT(5) +#define TXDESC_GF BIT(6) #define TXDESC_OWN BIT(7) #endif /* Word 1 */ +/* + * Bits 0-7 differ dependent on chip generation. For 8723au bits 5/6 are + * aggregation enable and break respectively. For 8723bu, bits 0-7 are macid. + */ #define TXDESC_PKT_OFFSET_SZ 0 -#define TXDESC_AGG_ENABLE BIT(5) -#define TXDESC_BK BIT(6) +#define TXDESC_AGG_ENABLE_8723A BIT(5) +#define TXDESC_AGG_BREAK_8723A BIT(6) +#define TXDESC_MACID_SHIFT_8723B 0 +#define TXDESC_MACID_MASK_8723B 0x00f0 #define TXDESC_QUEUE_SHIFT 8 #define TXDESC_QUEUE_MASK 0x1f00 #define TXDESC_QUEUE_BK 0x2 @@ -276,6 +437,9 @@ struct rtl8xxxu_tx_desc { #define TXDESC_QUEUE_MGNT 0x12 #define TXDESC_QUEUE_CMD 0x13 #define TXDESC_QUEUE_MAX (TXDESC_QUEUE_CMD + 1) +#define TXDESC_RDG_NAV_EXT_8723B BIT(13) +#define TXDESC_LSIG_TXOP_ENABLE_8723B BIT(14) +#define TXDESC_PIFS_8723B BIT(15) #define DESC_RATE_ID_SHIFT 16 #define DESC_RATE_ID_MASK 0xf @@ -287,41 +451,72 @@ struct rtl8xxxu_tx_desc { #define TXDESC_HWPC BIT(31) /* Word 2 */ -#define TXDESC_ACK_REPORT BIT(19) +#define TXDESC_PAID_SHIFT_8723B 0 +#define TXDESC_PAID_MASK_8723B 0x1ff +#define TXDESC_CCA_RTS_SHIFT_8723B 10 +#define TXDESC_CCA_RTS_MASK_8723B 0xc00 +#define TXDESC_AGG_ENABLE_8723B BIT(12) +#define TXDESC_RDG_ENABLE_8723B BIT(13) +#define TXDESC_AGG_BREAK_8723B BIT(16) +#define TXDESC_MORE_FRAG_8723B BIT(17) +#define TXDESC_RAW_8723B BIT(18) +#define TXDESC_ACK_REPORT_8723A BIT(19) +#define TXDESC_SPE_RPT_8723B BIT(19) #define TXDESC_AMPDU_DENSITY_SHIFT 20 +#define TXDESC_BT_INT_8723B BIT(23) +#define TXDESC_GID_8723B BIT(24) /* Word 3 */ -#define TXDESC_SEQ_SHIFT 16 -#define TXDESC_SEQ_MASK 0x0fff0000 +#define TXDESC_USE_DRIVER_RATE_8723B BIT(8) +#define TXDESC_CTS_SELF_ENABLE_8723B BIT(11) +#define TXDESC_RTS_CTS_ENABLE_8723B BIT(12) +#define TXDESC_HW_RTS_ENABLE_8723B BIT(13) +#define TXDESC_SEQ_SHIFT_8723A 16 +#define TXDESC_SEQ_MASK_8723A 0x0fff0000 /* Word 4 */ -#define TXDESC_QOS BIT(6) -#define TXDESC_HW_SEQ_ENABLE BIT(7) -#define TXDESC_USE_DRIVER_RATE BIT(8) +#define TXDESC_RTS_RATE_SHIFT_8723A 0 +#define TXDESC_RTS_RATE_MASK_8723A 0x3f +#define TXDESC_QOS_8723A BIT(6) +#define TXDESC_HW_SEQ_ENABLE_8723A BIT(7) +#define TXDESC_USE_DRIVER_RATE_8723A BIT(8) #define TXDESC_DISABLE_DATA_FB BIT(10) -#define TXDESC_CTS_SELF_ENABLE BIT(11) -#define TXDESC_RTS_CTS_ENABLE BIT(12) -#define TXDESC_HW_RTS_ENABLE BIT(13) +#define TXDESC_CTS_SELF_ENABLE_8723A BIT(11) +#define TXDESC_RTS_CTS_ENABLE_8723A BIT(12) +#define TXDESC_HW_RTS_ENABLE_8723A BIT(13) #define TXDESC_PRIME_CH_OFF_LOWER BIT(20) #define TXDESC_PRIME_CH_OFF_UPPER BIT(21) -#define TXDESC_SHORT_PREAMBLE BIT(24) +#define TXDESC_SHORT_PREAMBLE_8723A BIT(24) #define TXDESC_DATA_BW BIT(25) #define TXDESC_RTS_DATA_BW BIT(27) #define TXDESC_RTS_PRIME_CH_OFF_LOWER BIT(28) #define TXDESC_RTS_PRIME_CH_OFF_UPPER BIT(29) +#define TXDESC_DATA_RATE_FB_SHIFT_8723B 8 +#define TXDESC_DATA_RATE_FB_MASK_8723B 0x00001f00 +#define TXDESC_RETRY_LIMIT_ENABLE_8723B BIT(17) +#define TXDESC_RETRY_LIMIT_SHIFT_8723B 18 +#define TXDESC_RETRY_LIMIT_MASK_8723B 0x00fc0000 +#define TXDESC_RTS_RATE_SHIFT_8723B 24 +#define TXDESC_RTS_RATE_MASK_8723B 0x3f000000 /* Word 5 */ -#define TXDESC_RTS_RATE_SHIFT 0 -#define TXDESC_RTS_RATE_MASK 0x3f +#define TXDESC_SHORT_PREAMBLE_8723B BIT(4) #define TXDESC_SHORT_GI BIT(6) #define TXDESC_CCX_TAG BIT(7) -#define TXDESC_RETRY_LIMIT_ENABLE BIT(17) -#define TXDESC_RETRY_LIMIT_SHIFT 18 -#define TXDESC_RETRY_LIMIT_MASK 0x00fc0000 +#define TXDESC_RETRY_LIMIT_ENABLE_8723A BIT(17) +#define TXDESC_RETRY_LIMIT_SHIFT_8723A 18 +#define TXDESC_RETRY_LIMIT_MASK_8723A 0x00fc0000 /* Word 6 */ #define TXDESC_MAX_AGG_SHIFT 11 +/* Word 8 */ +#define TXDESC_HW_SEQ_ENABLE_8723B BIT(15) + +/* Word 9 */ +#define TXDESC_SEQ_SHIFT_8723B 12 +#define TXDESC_SEQ_MASK_8723B 0x00fff000 + struct phy_rx_agc_info { #ifdef __LITTLE_ENDIAN u8 gain:7, trsw:1; @@ -500,6 +695,125 @@ struct rtl8192cu_efuse { u8 customer_id; }; +struct rtl8723bu_pwr_idx { +#ifdef __LITTLE_ENDIAN + int ht20:4; + int ht40:4; + int ofdm:4; + int cck:4; +#else + int cck:4; + int ofdm:4; + int ht40:4; + int ht20:4; +#endif +} __attribute__((packed)); + +struct rtl8723bu_efuse_tx_power { + u8 cck_base[6]; + u8 ht40_base[5]; + struct rtl8723au_idx ht20_ofdm_1s_diff; + struct rtl8723bu_pwr_idx pwr_diff[3]; + u8 dummy5g[24]; /* max channel group (14) + power diff offset (10) */ +}; + +struct rtl8723bu_efuse { + __le16 rtl_id; + u8 res0[0x0e]; + struct rtl8723bu_efuse_tx_power tx_power_index_A; /* 0x10 */ + struct rtl8723bu_efuse_tx_power tx_power_index_B; /* 0x3a */ + struct rtl8723bu_efuse_tx_power tx_power_index_C; /* 0x64 */ + struct rtl8723bu_efuse_tx_power tx_power_index_D; /* 0x8e */ + u8 channel_plan; /* 0xb8 */ + u8 xtal_k; + u8 thermal_meter; + u8 iqk_lck; + u8 pa_type; /* 0xbc */ + u8 lna_type_2g; /* 0xbd */ + u8 res2[3]; + u8 rf_board_option; + u8 rf_feature_option; + u8 rf_bt_setting; + u8 eeprom_version; + u8 eeprom_customer_id; + u8 res3[2]; + u8 tx_pwr_calibrate_rate; + u8 rf_antenna_option; /* 0xc9 */ + u8 rfe_option; + u8 res4[9]; + u8 usb_optional_function; + u8 res5[0x1e]; + u8 res6[2]; + u8 serial[0x0b]; /* 0xf5 */ + u8 vid; /* 0x100 */ + u8 res7; + u8 pid; + u8 res8[4]; + u8 mac_addr[ETH_ALEN]; /* 0x107 */ + u8 res9[2]; + u8 vendor_name[0x07]; + u8 res10[2]; + u8 device_name[0x14]; + u8 res11[0xcf]; + u8 package_type; /* 0x1fb */ + u8 res12[0x4]; +}; + +struct rtl8192eu_efuse_tx_power { + u8 cck_base[6]; + u8 ht40_base[5]; + struct rtl8723au_idx ht20_ofdm_1s_diff; + struct rtl8723au_idx ht40_ht20_2s_diff; + struct rtl8723au_idx ofdm_cck_2s_diff; /* not used */ + struct rtl8723au_idx ht40_ht20_3s_diff; + struct rtl8723au_idx ofdm_cck_3s_diff; /* not used */ + struct rtl8723au_idx ht40_ht20_4s_diff; + struct rtl8723au_idx ofdm_cck_4s_diff; /* not used */ +}; + +struct rtl8192eu_efuse { + __le16 rtl_id; + u8 res0[0x0e]; + struct rtl8192eu_efuse_tx_power tx_power_index_A; /* 0x10 */ + struct rtl8192eu_efuse_tx_power tx_power_index_B; /* 0x22 */ + struct rtl8192eu_efuse_tx_power tx_power_index_C; /* 0x34 */ + struct rtl8192eu_efuse_tx_power tx_power_index_D; /* 0x46 */ + u8 res1[0x60]; + u8 channel_plan; /* 0xb8 */ + u8 xtal_k; + u8 thermal_meter; + u8 iqk_lck; + u8 pa_type; /* 0xbc */ + u8 lna_type_2g; /* 0xbd */ + u8 res2[1]; + u8 lna_type_5g; /* 0xbf */ + u8 res13[1]; + u8 rf_board_option; + u8 rf_feature_option; + u8 rf_bt_setting; + u8 eeprom_version; + u8 eeprom_customer_id; + u8 res3[3]; + u8 rf_antenna_option; /* 0xc9 */ + u8 res4[6]; + u8 vid; /* 0xd0 */ + u8 res5[1]; + u8 pid; /* 0xd2 */ + u8 res6[1]; + u8 usb_optional_function; + u8 res7[2]; + u8 mac_addr[ETH_ALEN]; /* 0xd7 */ + u8 res8[2]; + u8 vendor_name[7]; + u8 res9[2]; + u8 device_name[0x0b]; /* 0xe8 */ + u8 res10[2]; + u8 serial[0x0b]; /* 0xf5 */ + u8 res11[0x30]; + u8 unknown[0x0d]; /* 0x130 */ + u8 res12[0xc3]; +}; + struct rtl8xxxu_reg8val { u16 reg; u8 val; @@ -531,27 +845,110 @@ struct rtl8xxxu_rfregs { #define H2C_MAX_MBOX 4 #define H2C_EXT BIT(7) -#define H2C_SET_POWER_MODE 1 -#define H2C_JOIN_BSS_REPORT 2 #define H2C_JOIN_BSS_DISCONNECT 0 #define H2C_JOIN_BSS_CONNECT 1 -#define H2C_SET_RSSI 5 -#define H2C_SET_RATE_MASK (6 | H2C_EXT) + +/* + * H2C (firmware) commands differ between the older generation chips + * 8188[cr]u, 819[12]cu, and 8723au, and the more recent chips 8723bu, + * 8192[de]u, 8192eu, and 8812. + */ +enum h2c_cmd_8723a { + H2C_SET_POWER_MODE = 1, + H2C_JOIN_BSS_REPORT = 2, + H2C_SET_RSSI = 5, + H2C_SET_RATE_MASK = (6 | H2C_EXT), +}; + +enum h2c_cmd_8723b { + /* + * Common Class: 000 + */ + H2C_8723B_RSVD_PAGE = 0x00, + H2C_8723B_MEDIA_STATUS_RPT = 0x01, + H2C_8723B_SCAN_ENABLE = 0x02, + H2C_8723B_KEEP_ALIVE = 0x03, + H2C_8723B_DISCON_DECISION = 0x04, + H2C_8723B_PSD_OFFLOAD = 0x05, + H2C_8723B_AP_OFFLOAD = 0x08, + H2C_8723B_BCN_RSVDPAGE = 0x09, + H2C_8723B_PROBERSP_RSVDPAGE = 0x0A, + H2C_8723B_FCS_RSVDPAGE = 0x10, + H2C_8723B_FCS_INFO = 0x11, + H2C_8723B_AP_WOW_GPIO_CTRL = 0x13, + + /* + * PoweSave Class: 001 + */ + H2C_8723B_SET_PWR_MODE = 0x20, + H2C_8723B_PS_TUNING_PARA = 0x21, + H2C_8723B_PS_TUNING_PARA2 = 0x22, + H2C_8723B_P2P_LPS_PARAM = 0x23, + H2C_8723B_P2P_PS_OFFLOAD = 0x24, + H2C_8723B_PS_SCAN_ENABLE = 0x25, + H2C_8723B_SAP_PS_ = 0x26, + H2C_8723B_INACTIVE_PS_ = 0x27, + H2C_8723B_FWLPS_IN_IPS_ = 0x28, + + /* + * Dynamic Mechanism Class: 010 + */ + H2C_8723B_MACID_CFG_RAID = 0x40, + H2C_8723B_TXBF = 0x41, + H2C_8723B_RSSI_SETTING = 0x42, + H2C_8723B_AP_REQ_TXRPT = 0x43, + H2C_8723B_INIT_RATE_COLLECT = 0x44, + + /* + * BT Class: 011 + */ + H2C_8723B_B_TYPE_TDMA = 0x60, + H2C_8723B_BT_INFO = 0x61, + H2C_8723B_FORCE_BT_TXPWR = 0x62, + H2C_8723B_BT_IGNORE_WLANACT = 0x63, + H2C_8723B_DAC_SWING_VALUE = 0x64, + H2C_8723B_ANT_SEL_RSV = 0x65, + H2C_8723B_WL_OPMODE = 0x66, + H2C_8723B_BT_MP_OPER = 0x67, + H2C_8723B_BT_CONTROL = 0x68, + H2C_8723B_BT_WIFI_CTRL = 0x69, + H2C_8723B_BT_FW_PATCH = 0x6a, + H2C_8723B_BT_WLAN_CALIBRATION = 0x6d, + H2C_8723B_BT_GRANT = 0x6e, + + /* + * WOWLAN Class: 100 + */ + H2C_8723B_WOWLAN = 0x80, + H2C_8723B_REMOTE_WAKE_CTRL = 0x81, + H2C_8723B_AOAC_GLOBAL_INFO = 0x82, + H2C_8723B_AOAC_RSVD_PAGE = 0x83, + H2C_8723B_AOAC_RSVD_PAGE2 = 0x84, + H2C_8723B_D0_SCAN_OFFLOAD_CTRL = 0x85, + H2C_8723B_D0_SCAN_OFFLOAD_INFO = 0x86, + H2C_8723B_CHNL_SWITCH_OFFLOAD = 0x87, + + H2C_8723B_RESET_TSF = 0xC0, +}; + struct h2c_cmd { union { struct { u8 cmd; - u8 data[5]; + u8 data[7]; } __packed cmd; struct { __le32 data; __le16 ext; } __packed raw; struct { + __le32 data; + __le32 ext; + } __packed raw_wide; + struct { u8 cmd; u8 data; - u8 pad[4]; } __packed joinbss; struct { u8 cmd; @@ -559,6 +956,182 @@ struct h2c_cmd { u8 arg; __le16 mask_lo; } __packed ramask; + struct { + u8 cmd; + u8 parm; + u8 macid; + u8 macid_end; + } __packed media_status_rpt; + struct { + u8 cmd; + u8 macid; + /* + * [0:4] - RAID + * [7] - SGI + */ + u8 data1; + /* + * [0:1] - Bandwidth + * [3] - No Update + * [4:5] - VHT enable + * [6] - DISPT + * [7] - DISRA + */ + u8 data2; + u8 ramask0; + u8 ramask1; + u8 ramask2; + u8 ramask3; + } __packed b_macid_cfg; + struct { + u8 cmd; + u8 data1; + u8 data2; + u8 data3; + u8 data4; + u8 data5; + } __packed b_type_dma; + struct { + u8 cmd; + u8 data; + } __packed bt_info; + struct { + u8 cmd; + u8 operreq; + u8 opcode; + u8 data; + u8 addr; + } __packed bt_mp_oper; + struct { + u8 cmd; + u8 data; + } __packed bt_wlan_calibration; + struct { + u8 cmd; + u8 data; + } __packed ignore_wlan; + struct { + u8 cmd; + u8 ant_inverse; + u8 int_switch_type; + } __packed ant_sel_rsv; + struct { + u8 cmd; + u8 data; + } __packed bt_grant; + }; +}; + +enum c2h_evt_8723b { + C2H_8723B_DEBUG = 0, + C2H_8723B_TSF = 1, + C2H_8723B_AP_RPT_RSP = 2, + C2H_8723B_CCX_TX_RPT = 3, + C2H_8723B_BT_RSSI = 4, + C2H_8723B_BT_OP_MODE = 5, + C2H_8723B_EXT_RA_RPT = 6, + C2H_8723B_BT_INFO = 9, + C2H_8723B_HW_INFO_EXCH = 0x0a, + C2H_8723B_BT_MP_INFO = 0x0b, + C2H_8723B_RA_REPORT = 0x0c, + C2H_8723B_FW_DEBUG = 0xff, +}; + +enum bt_info_src_8723b { + BT_INFO_SRC_8723B_WIFI_FW = 0x0, + BT_INFO_SRC_8723B_BT_RSP = 0x1, + BT_INFO_SRC_8723B_BT_ACTIVE_SEND = 0x2, +}; + +enum bt_mp_oper_opcode_8723b { + BT_MP_OP_GET_BT_VERSION = 0x00, + BT_MP_OP_RESET = 0x01, + BT_MP_OP_TEST_CTRL = 0x02, + BT_MP_OP_SET_BT_MODE = 0x03, + BT_MP_OP_SET_CHNL_TX_GAIN = 0x04, + BT_MP_OP_SET_PKT_TYPE_LEN = 0x05, + BT_MP_OP_SET_PKT_CNT_L_PL_TYPE = 0x06, + BT_MP_OP_SET_PKT_CNT_H_PKT_INTV = 0x07, + BT_MP_OP_SET_PKT_HEADER = 0x08, + BT_MP_OP_SET_WHITENCOEFF = 0x09, + BT_MP_OP_SET_BD_ADDR_L = 0x0a, + BT_MP_OP_SET_BD_ADDR_H = 0x0b, + BT_MP_OP_WRITE_REG_ADDR = 0x0c, + BT_MP_OP_WRITE_REG_VALUE = 0x0d, + BT_MP_OP_GET_BT_STATUS = 0x0e, + BT_MP_OP_GET_BD_ADDR_L = 0x0f, + BT_MP_OP_GET_BD_ADDR_H = 0x10, + BT_MP_OP_READ_REG = 0x11, + BT_MP_OP_SET_TARGET_BD_ADDR_L = 0x12, + BT_MP_OP_SET_TARGET_BD_ADDR_H = 0x13, + BT_MP_OP_SET_TX_POWER_CALIBRATION = 0x14, + BT_MP_OP_GET_RX_PKT_CNT_L = 0x15, + BT_MP_OP_GET_RX_PKT_CNT_H = 0x16, + BT_MP_OP_GET_RX_ERROR_BITS_L = 0x17, + BT_MP_OP_GET_RX_ERROR_BITS_H = 0x18, + BT_MP_OP_GET_RSSI = 0x19, + BT_MP_OP_GET_CFO_HDR_QUALITY_L = 0x1a, + BT_MP_OP_GET_CFO_HDR_QUALITY_H = 0x1b, + BT_MP_OP_GET_TARGET_BD_ADDR_L = 0x1c, + BT_MP_OP_GET_TARGET_BD_ADDR_H = 0x1d, + BT_MP_OP_GET_AFH_MAP_L = 0x1e, + BT_MP_OP_GET_AFH_MAP_M = 0x1f, + BT_MP_OP_GET_AFH_MAP_H = 0x20, + BT_MP_OP_GET_AFH_STATUS = 0x21, + BT_MP_OP_SET_TRACKING_INTERVAL = 0x22, + BT_MP_OP_SET_THERMAL_METER = 0x23, + BT_MP_OP_ENABLE_CFO_TRACKING = 0x24, +}; + +struct rtl8723bu_c2h { + u8 id; + u8 seq; + union { + struct { + u8 payload[0]; + } __packed raw; + struct { + u8 ext_id; + u8 status:4; + u8 retlen:4; + u8 opcode_ver:4; + u8 req_num:4; + u8 payload[2]; + } __packed bt_mp_info; + struct { + u8 response_source:4; + u8 dummy0_0:4; + + u8 bt_info; + + u8 retry_count:4; + u8 dummy2_0:1; + u8 bt_page:1; + u8 tx_rx_mask:1; + u8 dummy2_2:1; + + u8 rssi; + + u8 basic_rate:1; + u8 bt_has_reset:1; + u8 dummy4_1:1;; + u8 ignore_wlan:1; + u8 auto_report:1; + u8 dummy4_2:3; + + u8 a4; + u8 a5; + } __packed bt_info; + struct { + u8 rate:7; + u8 dummy0_0:1; + u8 macid; + u8 ldpc:1; + u8 txbf:1; + u8 noisy_state:1; + u8 dummy2_0:5; + u8 dummy3_0; + } __packed ra_report; }; }; @@ -582,40 +1155,51 @@ struct rtl8xxxu_priv { u8 mac_addr[ETH_ALEN]; char chip_name[8]; - u8 cck_tx_power_index_A[3]; /* 0x10 */ - u8 cck_tx_power_index_B[3]; - u8 ht40_1s_tx_power_index_A[3]; /* 0x16 */ - u8 ht40_1s_tx_power_index_B[3]; + char chip_vendor[8]; + u8 cck_tx_power_index_A[RTL8XXXU_MAX_CHANNEL_GROUPS]; + u8 cck_tx_power_index_B[RTL8XXXU_MAX_CHANNEL_GROUPS]; + u8 ht40_1s_tx_power_index_A[RTL8XXXU_MAX_CHANNEL_GROUPS]; + u8 ht40_1s_tx_power_index_B[RTL8XXXU_MAX_CHANNEL_GROUPS]; /* * The following entries are half-bytes split as: * bits 0-3: path A, bits 4-7: path B, all values 4 bits signed */ - struct rtl8723au_idx ht40_2s_tx_power_index_diff[3]; - struct rtl8723au_idx ht20_tx_power_index_diff[3]; - struct rtl8723au_idx ofdm_tx_power_index_diff[3]; - struct rtl8723au_idx ht40_max_power_offset[3]; - struct rtl8723au_idx ht20_max_power_offset[3]; + struct rtl8723au_idx ht40_2s_tx_power_index_diff[ + RTL8723A_CHANNEL_GROUPS]; + struct rtl8723au_idx ht20_tx_power_index_diff[RTL8723A_CHANNEL_GROUPS]; + struct rtl8723au_idx ofdm_tx_power_index_diff[RTL8723A_CHANNEL_GROUPS]; + struct rtl8723au_idx ht40_max_power_offset[RTL8723A_CHANNEL_GROUPS]; + struct rtl8723au_idx ht20_max_power_offset[RTL8723A_CHANNEL_GROUPS]; + /* + * Newer generation chips only keep power diffs per TX count, + * not per channel group. + */ + struct rtl8723au_idx ofdm_tx_power_diff[RTL8723B_TX_COUNT]; + struct rtl8723au_idx ht20_tx_power_diff[RTL8723B_TX_COUNT]; + struct rtl8723au_idx ht40_tx_power_diff[RTL8723B_TX_COUNT]; u32 chip_cut:4; u32 rom_rev:4; + u32 is_multi_func:1; u32 has_wifi:1; u32 has_bluetooth:1; u32 enable_bluetooth:1; u32 has_gps:1; u32 hi_pa:1; u32 vendor_umc:1; + u32 vendor_smic:1; u32 has_polarity_ctrl:1; u32 has_eeprom:1; u32 boot_eeprom:1; + u32 usb_interrupts:1; u32 ep_tx_high_queue:1; u32 ep_tx_normal_queue:1; u32 ep_tx_low_queue:1; - u32 path_a_hi_power:1; - u32 path_a_rf_paths:4; + u32 has_xtalk:1; + u8 xtalk; unsigned int pipe_interrupt; unsigned int pipe_in; unsigned int pipe_out[TXDESC_QUEUE_MAX]; u8 out_ep[RTL8XXXU_OUT_ENDPOINTS]; - u8 path_a_ig_value; u8 ep_tx_count; u8 rf_paths; u8 rx_paths; @@ -642,9 +1226,11 @@ struct rtl8xxxu_priv { u8 val8; } usb_buf; union { - u8 raw[EFUSE_MAP_LEN_8723A]; + u8 raw[EFUSE_MAP_LEN]; struct rtl8723au_efuse efuse8723; + struct rtl8723bu_efuse efuse8723bu; struct rtl8192cu_efuse efuse8192; + struct rtl8192eu_efuse efuse8192eu; } efuse_wifi; u32 adda_backup[RTL8XXXU_ADDA_REGS]; u32 mac_backup[RTL8XXXU_MAC_REGS]; @@ -652,7 +1238,6 @@ struct rtl8xxxu_priv { u32 bb_recovery_backup[RTL8XXXU_BB_REGS]; u32 rtlchip; u8 pi_enabled:1; - u8 iqk_initialized:1; u8 int_buf[USB_INTR_CONTENT_LENGTH]; }; @@ -672,5 +1257,31 @@ struct rtl8xxxu_fileops { int (*parse_efuse) (struct rtl8xxxu_priv *priv); int (*load_firmware) (struct rtl8xxxu_priv *priv); int (*power_on) (struct rtl8xxxu_priv *priv); + void (*power_off) (struct rtl8xxxu_priv *priv); + void (*reset_8051) (struct rtl8xxxu_priv *priv); + int (*llt_init) (struct rtl8xxxu_priv *priv, u8 last_tx_page); + void (*phy_init_antenna_selection) (struct rtl8xxxu_priv *priv); + void (*phy_iq_calibrate) (struct rtl8xxxu_priv *priv); + void (*config_channel) (struct ieee80211_hw *hw); + int (*parse_rx_desc) (struct rtl8xxxu_priv *priv, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status); + void (*init_aggregation) (struct rtl8xxxu_priv *priv); + void (*init_statistics) (struct rtl8xxxu_priv *priv); + void (*enable_rf) (struct rtl8xxxu_priv *priv); + void (*disable_rf) (struct rtl8xxxu_priv *priv); + void (*set_tx_power) (struct rtl8xxxu_priv *priv, int channel, + bool ht40); + void (*update_rate_mask) (struct rtl8xxxu_priv *priv, + u32 ramask, int sgi); + void (*report_connect) (struct rtl8xxxu_priv *priv, + u8 macid, bool connect); int writeN_block_size; + u16 mbox_ext_reg; + char mbox_ext_width; + char tx_desc_size; + char has_s0s1; + u32 adda_1t_init; + u32 adda_1t_path_on; + u32 adda_2t_path_on_a; + u32 adda_2t_path_on_b; }; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h index 23208f79b..e545e849f 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_regs.h @@ -45,6 +45,7 @@ #define APS_FSMCO_ENABLE_POWERDOWN BIT(4) #define APS_FSMCO_MAC_ENABLE BIT(8) #define APS_FSMCO_MAC_OFF BIT(9) +#define APS_FSMCO_SW_LPS BIT(10) #define APS_FSMCO_HW_SUSPEND BIT(11) #define APS_FSMCO_PCIE BIT(12) #define APS_FSMCO_HW_POWERDOWN BIT(15) @@ -69,8 +70,11 @@ #define REG_EE_VPD 0x000c #define REG_AFE_MISC 0x0010 +#define AFE_MISC_WL_XTAL_CTRL BIT(6) + #define REG_SPS0_CTRL 0x0011 #define REG_SPS_OCP_CFG 0x0018 +#define REG_8192E_LDOV12_CTRL 0x0014 #define REG_RSV_CTRL 0x001c #define REG_RF_CTRL 0x001f @@ -131,6 +135,8 @@ #define EFUSE_ACCESS_DISABLE 0x00 /* RTL8723 only */ #define REG_PWR_DATA 0x0038 +#define PWR_DATA_EEPRPAD_RFE_CTRL_EN BIT(11) + #define REG_CAL_TIMER 0x003c #define REG_ACLK_MON 0x003e #define REG_GPIO_MUXCFG 0x0040 @@ -138,7 +144,10 @@ #define REG_MAC_PINMUX_CFG 0x0043 #define REG_GPIO_PIN_CTRL 0x0044 #define REG_GPIO_INTM 0x0048 +#define GPIO_INTM_EDGE_TRIG_IRQ BIT(9) + #define REG_LEDCFG0 0x004c +#define LEDCFG0_DPDT_SELECT BIT(23) #define REG_LEDCFG1 0x004d #define REG_LEDCFG2 0x004e #define LEDCFG2_DPDT_SELECT BIT(7) @@ -152,6 +161,12 @@ #define REG_GPIO_PIN_CTRL_2 0x0060 /* RTL8723 WIFI/BT/GPS Multi-Function GPIO Select. */ #define REG_GPIO_IO_SEL_2 0x0062 +#define GPIO_IO_SEL_2_GPIO09_INPUT BIT(1) +#define GPIO_IO_SEL_2_GPIO09_IRQ BIT(9) + +/* RTL8723B */ +#define REG_PAD_CTRL1 0x0064 +#define PAD_CTRL1_SW_DPDT_SEL_DATA BIT(0) /* RTL8723 only WIFI/BT/GPS Multi-Function control source. */ #define REG_MULTI_FUNC_CTRL 0x0068 @@ -177,6 +192,8 @@ control */ #define MULTI_GPS_FUNC_EN BIT(22) /* GPS function enable */ +#define REG_LDO_SW_CTRL 0x007c /* 8192eu */ + #define REG_MCU_FW_DL 0x0080 #define MCU_FW_DL_ENABLE BIT(0) #define MCU_FW_DL_READY BIT(1) @@ -192,6 +209,12 @@ #define REG_HMBOX_EXT_1 0x008a #define REG_HMBOX_EXT_2 0x008c #define REG_HMBOX_EXT_3 0x008e +/* Interrupt registers for 8192e/8723bu/8812 */ +#define REG_HIMR0 0x00b0 +#define REG_HISR0 0x00b4 +#define REG_HIMR1 0x00b8 +#define REG_HISR1 0x00bc + /* Host suspend counter on FPGA platform */ #define REG_HOST_SUSP_CNT 0x00bc /* Efuse access protection for RTL8723 */ @@ -213,6 +236,7 @@ #define SYS_CFG_PCIRSTB BIT(4) #define SYS_CFG_V15_VLD BIT(5) #define SYS_CFG_TRP_B15V_EN BIT(7) +#define SYS_CFG_SW_OFFLOAD_EN BIT(7) /* For chips with IOL support */ #define SYS_CFG_SIC_IDLE BIT(8) #define SYS_CFG_BD_MAC2 BIT(9) #define SYS_CFG_BD_MAC1 BIT(10) @@ -220,9 +244,14 @@ #define SYS_CFG_CHIP_VER (BIT(12) | BIT(13) | BIT(14) | BIT(15)) #define SYS_CFG_BT_FUNC BIT(16) #define SYS_CFG_VENDOR_ID BIT(19) +#define SYS_CFG_VENDOR_EXT_MASK (BIT(18) | BIT(19)) +#define SYS_CFG_VENDOR_ID_TSMC 0 +#define SYS_CFG_VENDOR_ID_SMIC BIT(18) +#define SYS_CFG_VENDOR_ID_UMC BIT(19) #define SYS_CFG_PAD_HWPD_IDN BIT(22) #define SYS_CFG_TRP_VAUX_EN BIT(23) #define SYS_CFG_TRP_BT_EN BIT(24) +#define SYS_CFG_SPS_LDO_SEL BIT(24) /* 8192eu */ #define SYS_CFG_BD_PKG_SEL BIT(25) #define SYS_CFG_BD_HCI_SEL BIT(26) #define SYS_CFG_TYPE_ID BIT(27) @@ -255,6 +284,8 @@ #define GPIO_USB_SUSEN BIT(23) #define GPIO_RF_RL_ID (BIT(31) | BIT(30) | BIT(29) | BIT(28)) +#define REG_SYS_CFG2 0x00fc /* 8192eu */ + /* 0x0100 ~ 0x01FF MACTOP General Configuration */ #define REG_CR 0x0100 #define CR_HCI_TXDMA_ENABLE BIT(0) @@ -287,6 +318,7 @@ #define PBP_PAGE_SIZE_1024 0x4 #define REG_TRXDMA_CTRL 0x010c +#define TRXDMA_CTRL_RXDMA_AGG_EN BIT(2) #define TRXDMA_CTRL_VOQ_SHIFT 4 #define TRXDMA_CTRL_VIQ_SHIFT 6 #define TRXDMA_CTRL_BEQ_SHIFT 8 @@ -321,6 +353,8 @@ #define REG_MBIST_DONE 0x0178 #define REG_MBIST_FAIL 0x017c #define REG_C2HEVT_MSG_NORMAL 0x01a0 +/* 8192EU/8723BU/8812 */ +#define REG_C2HEVT_CMD_ID_8723B 0x01ae #define REG_C2HEVT_CLEAR 0x01af #define REG_C2HEVT_MSG_TEST 0x01b8 #define REG_MCUTST_1 0x01c0 @@ -340,6 +374,11 @@ #define REG_BB_ACCEESS_CTRL 0x01e8 #define REG_BB_ACCESS_DATA 0x01ec +#define REG_HMBOX_EXT0_8723B 0x01f0 +#define REG_HMBOX_EXT1_8723B 0x01f4 +#define REG_HMBOX_EXT2_8723B 0x01f8 +#define REG_HMBOX_EXT3_8723B 0x01fc + /* 0x0200 ~ 0x027F TXDMA Configuration */ #define REG_RQPN 0x0200 #define RQPN_HI_PQ_SHIFT 0 @@ -350,14 +389,29 @@ #define REG_FIFOPAGE 0x0204 #define REG_TDECTRL 0x0208 #define REG_TXDMA_OFFSET_CHK 0x020c +#define TXDMA_OFFSET_DROP_DATA_EN BIT(9) #define REG_TXDMA_STATUS 0x0210 #define REG_RQPN_NPQ 0x0214 +#define RQPN_NPQ_SHIFT 0 +#define RQPN_EPQ_SHIFT 16 + +#define REG_AUTO_LLT 0x0224 +#define AUTO_LLT_INIT_LLT BIT(16) + +#define REG_DWBCN1_CTRL_8723B 0x0228 /* 0x0280 ~ 0x02FF RXDMA Configuration */ #define REG_RXDMA_AGG_PG_TH 0x0280 +#define RXDMA_USB_AGG_ENABLE BIT(31) #define REG_RXPKT_NUM 0x0284 +#define RXPKT_NUM_RXDMA_IDLE BIT(17) +#define RXPKT_NUM_RW_RELEASE_EN BIT(18) #define REG_RXDMA_STATUS 0x0288 +/* Presumably only found on newer chips such as 8723bu */ +#define REG_RX_DMA_CTRL_8723B 0x0286 +#define REG_RXDMA_PRO_8723B 0x0290 + #define REG_RF_BB_CMD_ADDR 0x02c0 #define REG_RF_BB_CMD_DATA 0x02c4 @@ -429,20 +483,26 @@ #define REG_ARFR1 0x0448 #define REG_ARFR2 0x044c #define REG_ARFR3 0x0450 +#define REG_AMPDU_MAX_TIME_8723B 0x0456 #define REG_AGGLEN_LMT 0x0458 #define REG_AMPDU_MIN_SPACE 0x045c #define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045d #define REG_FAST_EDCA_CTRL 0x0460 #define REG_RD_RESP_PKT_TH 0x0463 #define REG_INIRTS_RATE_SEL 0x0480 +/* 8723bu */ +#define REG_DATA_SUBCHANNEL 0x0483 +/* 8723au */ #define REG_INIDATA_RATE_SEL 0x0484 #define REG_POWER_STATUS 0x04a4 #define REG_POWER_STAGE1 0x04b4 #define REG_POWER_STAGE2 0x04b8 +#define REG_AMPDU_BURST_MODE_8723B 0x04bc #define REG_PKT_VO_VI_LIFE_TIME 0x04c0 #define REG_PKT_BE_BK_LIFE_TIME 0x04c2 #define REG_STBC_SETTING 0x04c4 +#define REG_HT_SINGLE_AMPDU_8723B 0x04c7 #define REG_PROT_MODE_CTRL 0x04c8 #define REG_MAX_AGGR_NUM 0x04ca #define REG_RTS_MAX_AGGR_NUM 0x04cb @@ -453,6 +513,10 @@ #define REG_NEED_CPU_HANDLE 0x04e0 #define REG_PKT_LOSE_RPT 0x04e1 #define REG_PTCL_ERR_STATUS 0x04e2 +#define REG_TX_REPORT_CTRL 0x04ec +#define TX_REPORT_CTRL_TIMER_ENABLE BIT(1) + +#define REG_TX_REPORT_TIME 0x04f0 #define REG_DUMMY 0x04fc /* 0x0500 ~ 0x05FF EDCA Configuration */ @@ -505,6 +569,7 @@ #define BEACON_DMA_ATIME_INT_TIME 2 #define REG_ATIMWND 0x055a +#define REG_USTIME_TSF_8723B 0x055c #define REG_BCN_MAX_ERR 0x055d #define REG_RXTSF_OFFSET_CCK 0x055e #define REG_RXTSF_OFFSET_OFDM 0x055f @@ -559,13 +624,25 @@ (Rx beacon, probe rsp) */ #define RCR_ACCEPT_CRC32 BIT(8) /* Accept CRC32 error packet */ #define RCR_ACCEPT_ICV BIT(9) /* Accept ICV error packet */ -#define RCR_ACCEPT_DATA_FRAME BIT(11) -#define RCR_ACCEPT_CTRL_FRAME BIT(12) -#define RCR_ACCEPT_MGMT_FRAME BIT(13) +#define RCR_ACCEPT_DATA_FRAME BIT(11) /* Accept all data pkt or use + REG_RXFLTMAP2 */ +#define RCR_ACCEPT_CTRL_FRAME BIT(12) /* Accept all control pkt or use + REG_RXFLTMAP1 */ +#define RCR_ACCEPT_MGMT_FRAME BIT(13) /* Accept all mgmt pkt or use + REG_RXFLTMAP0 */ #define RCR_HTC_LOC_CTRL BIT(14) /* MFC<--HTC=1 MFC-->HTC=0 */ +#define RCR_UC_DATA_PKT_INT_ENABLE BIT(16) /* Enable unicast data packet + interrupt */ +#define RCR_BM_DATA_PKT_INT_ENABLE BIT(17) /* Enable broadcast data packet + interrupt */ +#define RCR_TIM_PARSER_ENABLE BIT(18) /* Enable RX beacon TIM parser*/ #define RCR_MFBEN BIT(22) -#define RCR_LSIGEN BIT(23) +#define RCR_LSIG_ENABLE BIT(23) /* Enable LSIG TXOP Protection + function. Search KEYCAM for + each rx packet to check if + LSIGEN bit is set. */ #define RCR_MULTI_BSSID_ENABLE BIT(24) /* Enable Multiple BssId */ +#define RCR_FORCE_ACK BIT(26) #define RCR_ACCEPT_BA_SSN BIT(27) /* Accept BA SSN */ #define RCR_APPEND_PHYSTAT BIT(28) #define RCR_APPEND_ICV BIT(29) @@ -605,6 +682,10 @@ #define REG_FWDLY 0x0661 #define REG_RXERR_RPT 0x0664 #define REG_WMAC_TRXPTCL_CTL 0x0668 +#define WMAC_TRXPTCL_CTL_BW_MASK (BIT(7) | BIT(8)) +#define WMAC_TRXPTCL_CTL_BW_20 0 +#define WMAC_TRXPTCL_CTL_BW_40 BIT(7) +#define WMAC_TRXPTCL_CTL_BW_80 BIT(8) /* Security */ #define REG_CAM_CMD 0x0670 @@ -632,18 +713,40 @@ #define REG_LPNAV_CTRL 0x0694 #define REG_WKFMCAM_CMD 0x0698 #define REG_WKFMCAM_RWD 0x069c -#define REG_RXFLTMAP0 0x06a0 -#define REG_RXFLTMAP1 0x06a2 -#define REG_RXFLTMAP2 0x06a4 + +/* + * RX Filters: each bit corresponds to the numerical value of the subtype. + * If it is set the subtype frame type is passed. The filter is only used when + * the RCR_ACCEPT_DATA_FRAME, RCR_ACCEPT_CTRL_FRAME, RCR_ACCEPT_MGMT_FRAME bit + * in the RCR are low. + * + * Example: Beacon subtype is binary 1000 which is decimal 8 so we have to set + * bit 8 (0x100) in REG_RXFLTMAP0 to enable reception. + */ +#define REG_RXFLTMAP0 0x06a0 /* Management frames */ +#define REG_RXFLTMAP1 0x06a2 /* Control frames */ +#define REG_RXFLTMAP2 0x06a4 /* Data frames */ + #define REG_BCN_PSR_RPT 0x06a8 #define REG_CALB32K_CTRL 0x06ac #define REG_PKT_MON_CTRL 0x06b4 -#define REG_BT_COEX_TABLE 0x06c0 +#define REG_BT_COEX_TABLE1 0x06c0 +#define REG_BT_COEX_TABLE2 0x06c4 +#define REG_BT_COEX_TABLE3 0x06c8 +#define REG_BT_COEX_TABLE4 0x06cc #define REG_WMAC_RESP_TXINFO 0x06d8 #define REG_MACID1 0x0700 #define REG_BSSID1 0x0708 +/* + * This seems to be 8723bu specific + */ +#define REG_BT_CONTROL_8723BU 0x0764 +#define BT_CONTROL_BT_GRANT BIT(12) + +#define REG_WLAN_ACT_CONTROL_8723B 0x076e + #define REG_FPGA0_RF_MODE 0x0800 #define FPGA_RF_MODE BIT(0) #define FPGA_RF_MODE_JAPAN BIT(1) @@ -734,6 +837,11 @@ #define REG_FPGA0_ANALOG3 0x0888 #define REG_FPGA0_ANALOG4 0x088c +#define REG_NHM_TH9_TH10_8723B 0x0890 +#define REG_NHM_TIMER_8723B 0x0894 +#define REG_NHM_TH3_TO_TH0_8723B 0x0898 +#define REG_NHM_TH7_TO_TH4_8723B 0x089c + #define REG_FPGA0_XA_LSSI_READBACK 0x08a0 /* Tranceiver LSSI Readback */ #define REG_FPGA0_XB_LSSI_READBACK 0x08a4 #define REG_HSPI_XA_READBACK 0x08b8 /* Transceiver A HSPI read */ @@ -742,6 +850,11 @@ #define REG_FPGA1_RF_MODE 0x0900 #define REG_FPGA1_TX_INFO 0x090c +#define REG_DPDT_CTRL 0x092c /* 8723BU */ +#define REG_RFE_CTRL_ANTA_SRC 0x0930 /* 8723BU */ +#define REG_RFE_PATH_SELECT 0x0940 /* 8723BU */ +#define REG_RFE_BUFFER 0x0944 /* 8723BU */ +#define REG_S0S1_PATH_SWITCH 0x0948 /* 8723BU */ #define REG_CCK0_SYSTEM 0x0a00 #define CCK0_SIDEBAND BIT(4) @@ -765,11 +878,16 @@ #define REG_OFDM0_TR_MUX_PAR 0x0c08 +#define REG_OFDM0_FA_RSTC 0x0c0c + #define REG_OFDM0_XA_RX_IQ_IMBALANCE 0x0c14 #define REG_OFDM0_XB_RX_IQ_IMBALANCE 0x0c1c #define REG_OFDM0_ENERGY_CCA_THRES 0x0c4c +#define REG_OFDM0_RX_D_SYNC_PATH 0x0c40 +#define OFDM0_SYNC_PATH_NOTCH_FILTER BIT(1) + #define REG_OFDM0_XA_AGC_CORE1 0x0c50 #define REG_OFDM0_XA_AGC_CORE2 0x0c54 #define REG_OFDM0_XB_AGC_CORE1 0x0c58 @@ -794,6 +912,9 @@ #define REG_OFDM0_RX_IQ_EXT_ANTA 0x0ca0 +/* 8723bu */ +#define REG_OFDM0_TX_PSDO_NOISE_WEIGHT 0x0ce4 + #define REG_OFDM1_LSTF 0x0d00 #define OFDM_LSTF_PRIME_CH_LOW BIT(10) #define OFDM_LSTF_PRIME_CH_HIGH BIT(11) @@ -952,6 +1073,10 @@ #define RF6052_REG_MODE_AG 0x18 /* RF channel and BW switch */ #define MODE_AG_CHANNEL_MASK 0x3ff #define MODE_AG_CHANNEL_20MHZ BIT(10) +#define MODE_AG_BW_MASK (BIT(10) | BIT(11)) +#define MODE_AG_BW_20MHZ_8723B (BIT(10) | BIT(11)) +#define MODE_AG_BW_40MHZ_8723B BIT(10) +#define MODE_AG_BW_80MHZ_8723B 0 #define RF6052_REG_TOP 0x19 #define RF6052_REG_RX_G1 0x1a @@ -979,3 +1104,14 @@ #define RF6052_REG_TXPA_G1 0x31 /* RF TX PA control */ #define RF6052_REG_TXPA_G2 0x32 /* RF TX PA control */ #define RF6052_REG_TXPA_G3 0x33 /* RF TX PA control */ + +/* + * NextGen regs: 8723BU + */ +#define RF6052_REG_T_METER_8723B 0x42 +#define RF6052_REG_UNKNOWN_43 0x43 +#define RF6052_REG_UNKNOWN_55 0x55 +#define RF6052_REG_S0S1 0xb0 +#define RF6052_REG_UNKNOWN_DF 0xdf +#define RF6052_REG_UNKNOWN_ED 0xed +#define RF6052_REG_WE_LUT 0xef diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 0517a4f2d..7a40d8dff 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1660,9 +1660,9 @@ void rtl_watchdog_wq_callback(void *data) if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || (rtlpriv->link_info.num_rx_inperiod > 2)) - rtl_lps_enter(hw); - else rtl_lps_leave(hw); + else + rtl_lps_enter(hw); } rtlpriv->link_info.num_rx_inperiod = 0; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index c43ab59a6..77cbd10e8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1203,7 +1203,6 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, /* Force GNT_BT to low */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0); - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { /* tell firmware "no antenna inverse" */ @@ -1211,19 +1210,25 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, h2c_parameter[1] = 1; /* ext switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); } else { /* tell firmware "antenna inverse" */ h2c_parameter[0] = 1; h2c_parameter[1] = 1; /* ext switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); } } /* ext switch setting */ if (use_ext_switch) { /* fixed internal switch S1->WiFi, S0->BT */ - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + else + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); + switch (antpos_type) { case BTC_ANT_WIFI_AT_MAIN: /* ext switch main at wifi */ diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c index b2791c893..babd1490f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c @@ -965,13 +965,38 @@ void exhalbtc_set_chip_type(u8 chip_type) } } -void exhalbtc_set_ant_num(u8 type, u8 ant_num) +void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num) { if (BT_COEX_ANT_TYPE_PG == type) { gl_bt_coexist.board_info.pg_ant_num = ant_num; gl_bt_coexist.board_info.btdm_ant_num = ant_num; + /* The antenna position: + * Main (default) or Aux for pgAntNum=2 && btdmAntNum =1. + * The antenna position should be determined by + * auto-detect mechanism. + * The following is assumed to main, + * and those must be modified + * if y auto-detect mechanism is ready + */ + if ((gl_bt_coexist.board_info.pg_ant_num == 2) && + (gl_bt_coexist.board_info.btdm_ant_num == 1)) + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; + else + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; } else if (BT_COEX_ANT_TYPE_ANTDIV == type) { gl_bt_coexist.board_info.btdm_ant_num = ant_num; + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; + } else if (type == BT_COEX_ANT_TYPE_DETECTED) { + gl_bt_coexist.board_info.btdm_ant_num = ant_num; + if (rtlpriv->cfg->mod_params->ant_sel == 1) + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_AUX_PORT; + else + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; } } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index 0a903ea17..f41ca57dd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -535,7 +535,7 @@ void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version); void exhalbtc_update_min_bt_rssi(char bt_rssi); void exhalbtc_set_bt_exist(bool bt_exist); void exhalbtc_set_chip_type(u8 chip_type); -void exhalbtc_set_ant_num(u8 type, u8 ant_num); +void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num); void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist); void exhalbtc_signal_compensation(struct btc_coexist *btcoexist, u8 *rssi_wifi, u8 *rssi_bt); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c index b9b0cb7af..d3fd9211b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c @@ -72,7 +72,10 @@ void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv) __func__, bt_type); exhalbtc_set_chip_type(bt_type); - exhalbtc_set_ant_num(BT_COEX_ANT_TYPE_PG, ant_num); + if (rtlpriv->cfg->mod_params->ant_sel == 1) + exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_DETECTED, 1); + else + exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_PG, ant_num); } void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv) diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index 5d6df57d7..e6e81296c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -35,38 +35,44 @@ #include <linux/export.h> #include <net/cfg80211.h> +u8 channel5g[CHANNEL_MAX_NUMBER_5G] = { + 36, 38, 40, 42, 44, 46, 48, /* Band 1 */ + 52, 54, 56, 58, 60, 62, 64, /* Band 2 */ + 100, 102, 104, 106, 108, 110, 112, /* Band 3 */ + 116, 118, 120, 122, 124, 126, 128, /* Band 3 */ + 132, 134, 136, 138, 140, 142, 144, /* Band 3 */ + 149, 151, 153, 155, 157, 159, 161, /* Band 4 */ + 165, 167, 169, 171, 173, 175, 177 /* Band 4 */ +}; +EXPORT_SYMBOL(channel5g); + +u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = { + 42, 58, 106, 122, 138, 155, 171 +}; +EXPORT_SYMBOL(channel5g_80m); + void rtl_addr_delay(u32 addr) { if (addr == 0xfe) mdelay(50); else if (addr == 0xfd) - mdelay(5); + msleep(5); else if (addr == 0xfc) - mdelay(1); + msleep(1); else if (addr == 0xfb) - udelay(50); + usleep_range(50, 100); else if (addr == 0xfa) - udelay(5); + usleep_range(5, 10); else if (addr == 0xf9) - udelay(1); + usleep_range(1, 2); } EXPORT_SYMBOL(rtl_addr_delay); void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, u32 mask, u32 data) { - if (addr == 0xfe) { - mdelay(50); - } else if (addr == 0xfd) { - mdelay(5); - } else if (addr == 0xfc) { - mdelay(1); - } else if (addr == 0xfb) { - udelay(50); - } else if (addr == 0xfa) { - udelay(5); - } else if (addr == 0xf9) { - udelay(1); + if (addr >= 0xf9 && addr <= 0xfe) { + rtl_addr_delay(addr); } else { rtl_set_rfreg(hw, rfpath, addr, mask, data); udelay(1); @@ -76,18 +82,8 @@ EXPORT_SYMBOL(rtl_rfreg_delay); void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data) { - if (addr == 0xfe) { - mdelay(50); - } else if (addr == 0xfd) { - mdelay(5); - } else if (addr == 0xfc) { - mdelay(1); - } else if (addr == 0xfb) { - udelay(50); - } else if (addr == 0xfa) { - udelay(5); - } else if (addr == 0xf9) { - udelay(1); + if (addr >= 0xf9 && addr <= 0xfe) { + rtl_addr_delay(addr); } else { rtl_set_bbreg(hw, addr, MASKDWORD, data); udelay(1); @@ -1371,11 +1367,13 @@ static void rtl_op_sta_notify(struct ieee80211_hw *hw, static int rtl_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; switch (action) { case IEEE80211_AMPDU_TX_START: diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 7f471bff4..e5037d13b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -855,7 +855,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) } /* handle command packet here */ if (rtlpriv->cfg->ops->rx_command_packet && - rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) { + rtlpriv->cfg->ops->rx_command_packet(hw, &stats, skb)) { dev_kfree_skb_any(skb); goto new_trx_end; } @@ -1573,7 +1573,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); + dev_kfree_skb_irq(skb); ring->idx = (ring->idx + 1) % ring->entries; } ring->idx = 0; @@ -2392,7 +2392,6 @@ void rtl_pci_disconnect(struct pci_dev *pdev) rtlpriv->cfg->ops->deinit_sw_vars(hw); if (rtlpci->irq_alloc) { - synchronize_irq(rtlpci->pdev->irq); free_irq(rtlpci->pdev->irq, hw); rtlpci->irq_alloc = 0; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c index 791efbe6b..11701064b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c @@ -851,7 +851,7 @@ void rtl88ee_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) } u32 rtl88ee_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb) { return 0; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h index eab5ae0eb..5a24d194a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.h @@ -790,7 +790,7 @@ void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); u32 rtl88ee_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c index bb06fe836..7810fe87d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c @@ -924,19 +924,11 @@ static void _rtl92d_ccxpower_index_check(struct ieee80211_hw *hw, static u8 _rtl92c_phy_get_rightchnlplace(u8 chnl) { - u8 channel_5g[59] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, - 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, - 114, 116, 118, 120, 122, 124, 126, 128, - 130, 132, 134, 136, 138, 140, 149, 151, - 153, 155, 157, 159, 161, 163, 165 - }; u8 place = chnl; if (chnl > 14) { - for (place = 14; place < sizeof(channel_5g); place++) { - if (channel_5g[place] == chnl) { + for (place = 14; place < sizeof(channel5g); place++) { + if (channel5g[place] == chnl) { place++; break; } @@ -2471,16 +2463,9 @@ static bool _rtl92d_is_legal_5g_channel(struct ieee80211_hw *hw, u8 channel) { int i; - u8 channel_5g[45] = { - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, - 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, - 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, - 134, 136, 138, 140, 149, 151, 153, 155, 157, 159, - 161, 163, 165 - }; - for (i = 0; i < sizeof(channel_5g); i++) - if (channel == channel_5g[i]) + for (i = 0; i < sizeof(channel5g); i++) + if (channel == channel5g[i]) return true; return false; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c index 5f14308e8..9fd3f1b6e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c @@ -2018,18 +2018,6 @@ static void _rtl92ee_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, struct rtl_efuse *efu = rtl_efuse(rtl_priv(hw)); struct txpower_info_2g pwr2g; struct txpower_info_5g pwr5g; - u8 channel5g[CHANNEL_MAX_NUMBER_5G] = { - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, - 56, 58, 60, 62, 64, 100, 102, 104, 106, - 108, 110, 112, 114, 116, 118, 120, 122, - 124, 126, 128, 130, 132, 134, 136, 138, - 140, 142, 144, 149, 151, 153, 155, 157, - 159, 161, 163, 165, 167, 168, 169, 171, - 173, 175, 177 - }; - u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = { - 42, 58, 106, 122, 138, 155, 171 - }; u8 rf, idx; u8 i; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c index d39ee67f6..24eff8ea4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c @@ -1105,13 +1105,13 @@ void rtl92ee_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) } u32 rtl92ee_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb) { u32 result = 0; struct rtl_priv *rtlpriv = rtl_priv(hw); - switch (status.packet_report_type) { + switch (status->packet_report_type) { case NORMAL_RX: result = 0; break; @@ -1121,7 +1121,7 @@ u32 rtl92ee_rx_command_packet(struct ieee80211_hw *hw, break; default: RT_TRACE(rtlpriv, COMP_RECV, DBG_TRACE, - "Unknown packet type %d\n", status.packet_report_type); + "Unknown packet type %d\n", status->packet_report_type); break; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h index 8f78ac9e6..a4c383452 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.h @@ -857,6 +857,6 @@ void rtl92ee_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); u32 rtl92ee_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c index 2f7c144d7..7b4a9b635 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c @@ -710,7 +710,7 @@ void rtl8723e_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) } u32 rtl8723e_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb) { return 0; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h index 017da7e19..32970bf18 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.h @@ -716,6 +716,6 @@ void rtl8723e_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); u32 rtl8723e_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c index c983d2fe1..5a3df9198 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c @@ -2684,6 +2684,7 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, bool auto_load_fail, u8 *hwinfo) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params; u8 value; u32 tmpu_32; @@ -2702,6 +2703,10 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, rtlpriv->btcoexist.btc_info.ant_num = ANT_X2; } + /* override ant_num / ant_path */ + if (mod_params->ant_sel) + rtlpriv->btcoexist.btc_info.ant_num = + (mod_params->ant_sel == 1 ? ANT_X2 : ANT_X1); } void rtl8723be_bt_reg_init(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index f24d12dff..5f3fe83b4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -273,6 +273,7 @@ static struct rtl_mod_params rtl8723be_mod_params = { .msi_support = false, .disable_watchdog = false, .debug = DBG_EMERG, + .ant_sel = 0, }; static struct rtl_hal_cfg rtl8723be_hal_cfg = { @@ -394,6 +395,7 @@ module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444); module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog, bool, 0444); +module_param_named(ant_sel, rtl8723be_mod_params.ant_sel, int, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); @@ -402,6 +404,7 @@ MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); +MODULE_PARM_DESC(ant_sel, "Set to 1 or 2 to force antenna number (default 0)\n"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c index 338ec9a9d..60345975f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c @@ -758,13 +758,13 @@ void rtl8723be_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) } u32 rtl8723be_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb) { u32 result = 0; struct rtl_priv *rtlpriv = rtl_priv(hw); - switch (status.packet_report_type) { + switch (status->packet_report_type) { case NORMAL_RX: result = 0; break; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h index 45949ac48..40c36607b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.h @@ -620,6 +620,6 @@ void rtl8723be_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); u32 rtl8723be_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c index b57cfd965..6a8245c4e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/dm.c @@ -626,7 +626,7 @@ static void rtl8821ae_dm_find_minimum_rssi(struct ieee80211_hw *hw) rtl_dm_dig->min_undec_pwdb_for_dm = rtlpriv->dm.entry_min_undec_sm_pwdb; RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, - "AP Ext Port or disconnet PWDB = 0x%x\n", + "AP Ext Port or disconnect PWDB = 0x%x\n", rtl_dm_dig->min_undec_pwdb_for_dm); } RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, @@ -2488,9 +2488,9 @@ void rtl8821ae_dm_txpower_tracking_callback_thermalmeter( for (p = RF90_PATH_A; p < MAX_PATH_NUM_8821A; p++) rtldm->swing_idx_ofdm_base[p] = rtldm->swing_idx_ofdm[p]; - RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, - "pDM_Odm->RFCalibrateInfo.ThermalValue = %d ThermalValue= %d\n", - rtldm->thermalvalue, thermal_value); + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "pDM_Odm->RFCalibrateInfo.ThermalValue = %d ThermalValue= %d\n", + rtldm->thermalvalue, thermal_value); /*Record last Power Tracking Thermal Value*/ rtldm->thermalvalue = thermal_value; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c index 525eb2346..a4fc70e8c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c @@ -271,7 +271,7 @@ int rtl8821ae_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw) err = _rtl8821ae_fw_free_to_go(hw); if (err) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG, "Firmware is not ready to run!\n"); } else { RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 5e5719b26..fe900badd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -2786,14 +2786,6 @@ static void _rtl8812ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct txpower_info_2g pwrinfo24g; struct txpower_info_5g pwrinfo5g; - u8 channel5g[CHANNEL_MAX_NUMBER_5G] = { - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, - 56, 58, 60, 62, 64, 100, 102, 104, 106, - 108, 110, 112, 114, 116, 118, 120, 122, - 124, 126, 128, 130, 132, 134, 136, 138, - 140, 142, 144, 149, 151, 153, 155, 157, - 159, 161, 163, 165, 167, 168, 169, 171, 173, 175, 177}; - u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = {42, 58, 106, 122, 138, 155, 171}; u8 rf_path, index; u8 i; @@ -2872,16 +2864,6 @@ static void _rtl8821ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct txpower_info_2g pwrinfo24g; struct txpower_info_5g pwrinfo5g; - u8 channel5g[CHANNEL_MAX_NUMBER_5G] = { - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, - 56, 58, 60, 62, 64, 100, 102, 104, 106, - 108, 110, 112, 114, 116, 118, 120, 122, - 124, 126, 128, 130, 132, 134, 136, 138, - 140, 142, 144, 149, 151, 153, 155, 157, - 159, 161, 163, 165, 167, 168, 169, 171, - 173, 175, 177}; - u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = { - 42, 58, 106, 122, 138, 155, 171}; u8 rf_path, index; u8 i; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 9b4d8a637..74165b3eb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -1472,18 +1472,13 @@ static char _rtl8812ae_phy_get_chnl_idx_of_txpwr_lmt(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); char channel_index = -1; - u8 channel_5g[CHANNEL_MAX_NUMBER_5G] = { - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, - 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, - 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 149, - 151, 153, 155, 157, 159, 161, 163, 165, 167, 168, 169, 171, - 173, 175, 177}; u8 i = 0; + if (band == BAND_ON_2_4G) channel_index = channel - 1; else if (band == BAND_ON_5G) { - for (i = 0; i < sizeof(channel_5g)/sizeof(u8); ++i) { - if (channel_5g[i] == channel) + for (i = 0; i < sizeof(channel5g)/sizeof(u8); ++i) { + if (channel5g[i] == channel) channel_index = i; } } else @@ -2240,13 +2235,6 @@ void rtl8821ae_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) static bool _rtl8821ae_phy_get_chnl_index(u8 channel, u8 *chnl_index) { - u8 channel_5g[CHANNEL_MAX_NUMBER_5G] = { - 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, - 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, - 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, - 142, 144, 149, 151, 153, 155, 157, 159, 161, 163, 165, - 167, 168, 169, 171, 173, 175, 177 - }; u8 i = 0; bool in_24g = true; @@ -2257,7 +2245,7 @@ static bool _rtl8821ae_phy_get_chnl_index(u8 channel, u8 *chnl_index) in_24g = false; for (i = 0; i < CHANNEL_MAX_NUMBER_5G; ++i) { - if (channel_5g[i] == channel) { + if (channel5g[i] == channel) { *chnl_index = i; return in_24g; } @@ -2728,13 +2716,10 @@ static u8 _rtl8821ae_get_txpower_index(struct ieee80211_hw *hw, u8 path, rate <= DESC_RATEVHT2SS_MCS9)) txpower += rtlefuse->txpwr_5g_bw40diff[path][TX_2S]; } else if (bandwidth == HT_CHANNEL_WIDTH_80) { - u8 channel_5g_80m[CHANNEL_MAX_NUMBER_5G_80M] = { - 42, 58, 106, 122, 138, 155, 171 - }; u8 i; - for (i = 0; i < sizeof(channel_5g_80m) / sizeof(u8); ++i) - if (channel_5g_80m[i] == channel) + for (i = 0; i < sizeof(channel5g_80m) / sizeof(u8); ++i) + if (channel5g_80m[i] == channel) index = i; if ((DESC_RATEMCS0 <= rate && rate <= DESC_RATEMCS15) || diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c index 174743aef..41efaa148 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c @@ -998,13 +998,13 @@ void rtl8821ae_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) } u32 rtl8821ae_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb) { u32 result = 0; struct rtl_priv *rtlpriv = rtl_priv(hw); - switch (status.packet_report_type) { + switch (status->packet_report_type) { case NORMAL_RX: result = 0; break; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h index 31409042d..ad565bebf 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.h @@ -615,6 +615,6 @@ void rtl8821ae_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool lastseg, struct sk_buff *skb); u32 rtl8821ae_rx_command_packet(struct ieee80211_hw *hw, - struct rtl_stats status, + const struct rtl_stats *status, struct sk_buff *skb); #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 84397b190..93bd7fcd2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -116,17 +116,12 @@ #define CHANNEL_MAX_NUMBER (14 + 24 + 21) /* 14 is the max channel no */ #define CHANNEL_MAX_NUMBER_2G 14 -#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to +#define CHANNEL_MAX_NUMBER_5G 49 /* Please refer to *"phy_GetChnlGroup8812A" and * "Hal_ReadTxPowerInfo8812A" */ #define CHANNEL_MAX_NUMBER_5G_80M 7 #define CHANNEL_GROUP_MAX (3 + 9) /* ch1~3, 4~9, 10~14 = three groups */ -#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to - *"phy_GetChnlGroup8812A" and - * "Hal_ReadTxPowerInfo8812A" - */ -#define CHANNEL_MAX_NUMBER_5G_80M 7 #define MAX_PG_GROUP 13 #define CHANNEL_GROUP_MAX_2G 3 #define CHANNEL_GROUP_IDX_5GL 3 @@ -2193,7 +2188,7 @@ struct rtl_hal_ops { bool (*get_btc_status) (void); bool (*is_fw_header)(struct rtlwifi_firmware_header *hdr); u32 (*rx_command_packet)(struct ieee80211_hw *hw, - struct rtl_stats status, struct sk_buff *skb); + const struct rtl_stats *status, struct sk_buff *skb); void (*add_wowlan_pattern)(struct ieee80211_hw *hw, struct rtl_wow_pattern *rtl_pattern, u8 index); @@ -2251,6 +2246,9 @@ struct rtl_mod_params { /* default 0: 1 means do not disable interrupts */ bool int_clear; + + /* select antenna */ + int ant_sel; }; struct rtl_hal_usbint_cfg { @@ -2903,6 +2901,10 @@ value to host byte ordering.*/ #define STBC_VHT_TEST_TX_ENABLE BIT(2) #define STBC_VHT_CAP_TX BIT(3) +extern u8 channel5g[CHANNEL_MAX_NUMBER_5G]; + +extern u8 channel5g_80m[CHANNEL_MAX_NUMBER_5G_80M]; + static inline u8 rtl_read_byte(struct rtl_priv *rtlpriv, u32 addr) { return rtlpriv->io.read8_sync(rtlpriv, addr); diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index b5bcc933a..4df992de7 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -659,29 +659,24 @@ static int rsi_mac80211_set_key(struct ieee80211_hw *hw, * informs the f/w regarding this. * @hw: Pointer to the ieee80211_hw structure. * @vif: Pointer to the ieee80211_vif structure. - * @action: ieee80211_ampdu_mlme_action enum. - * @sta: Pointer to the ieee80211_sta structure. - * @tid: Traffic identifier. - * @ssn: Pointer to ssn value. - * @buf_size: Buffer size (for kernel version > 2.6.38). - * @amsdu: is AMSDU in AMPDU allowed + * @params: Pointer to A-MPDU action parameters * * Return: status: 0 on success, negative error code on failure. */ static int rsi_mac80211_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, - unsigned short tid, - unsigned short *ssn, - unsigned char buf_size, - bool amsdu) + struct ieee80211_ampdu_params *params) { int status = -EOPNOTSUPP; struct rsi_hw *adapter = hw->priv; struct rsi_common *common = adapter->priv; u16 seq_no = 0; u8 ii = 0; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; for (ii = 0; ii < RSI_MAX_VIFS; ii++) { if (vif == adapter->vifs[ii]) diff --git a/drivers/net/wireless/st/cw1200/cw1200_spi.c b/drivers/net/wireless/st/cw1200/cw1200_spi.c index a74008363..63f95e9c2 100644 --- a/drivers/net/wireless/st/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/st/cw1200/cw1200_spi.c @@ -446,8 +446,7 @@ static int cw1200_spi_disconnect(struct spi_device *func) return 0; } -#ifdef CONFIG_PM -static int cw1200_spi_suspend(struct device *dev) +static int __maybe_unused cw1200_spi_suspend(struct device *dev) { struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev)); @@ -460,16 +459,12 @@ static int cw1200_spi_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL); -#endif - static struct spi_driver spi_driver = { .probe = cw1200_spi_probe, .remove = cw1200_spi_disconnect, .driver = { .name = "cw1200_wlan_spi", -#ifdef CONFIG_PM - .pm = &cw1200_pm_ops, -#endif + .pm = IS_ENABLED(CONFIG_PM) ? &cw1200_pm_ops : NULL, }, }; diff --git a/drivers/net/wireless/st/cw1200/pm.h b/drivers/net/wireless/st/cw1200/pm.h index 3ed90ff22..534548470 100644 --- a/drivers/net/wireless/st/cw1200/pm.h +++ b/drivers/net/wireless/st/cw1200/pm.h @@ -31,13 +31,18 @@ int cw1200_pm_init(struct cw1200_pm_state *pm, void cw1200_pm_deinit(struct cw1200_pm_state *pm); int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); -int cw1200_wow_resume(struct ieee80211_hw *hw); int cw1200_can_suspend(struct cw1200_common *priv); +int cw1200_wow_resume(struct ieee80211_hw *hw); void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, unsigned long tmo); #else static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, - unsigned long tmo) { + unsigned long tmo) +{ +} +static inline int cw1200_can_suspend(struct cw1200_common *priv) +{ + return 0; } #endif #endif diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c index 800e2a4ec..eda75acf6 100644 --- a/drivers/net/wireless/st/cw1200/sta.c +++ b/drivers/net/wireless/st/cw1200/sta.c @@ -2129,9 +2129,7 @@ void cw1200_mcast_timeout(unsigned long arg) int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/net/wireless/st/cw1200/sta.h b/drivers/net/wireless/st/cw1200/sta.h index bebb33790..a0bacaa39 100644 --- a/drivers/net/wireless/st/cw1200/sta.h +++ b/drivers/net/wireless/st/cw1200/sta.h @@ -109,9 +109,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, u32 changed); int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu); + struct ieee80211_ampdu_params *params); void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 4edfe2839..86ccf84ea 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -345,6 +345,69 @@ static const struct file_operations dynamic_fw_traces_ops = { .llseek = default_llseek, }; +#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS +static ssize_t radar_debug_mode_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + struct wl12xx_vif *wlvif; + unsigned long value; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 10, &value); + if (ret < 0) { + wl1271_warning("illegal radar_debug_mode value!"); + return -EINVAL; + } + + /* valid values: 0/1 */ + if (!(value == 0 || value == 1)) { + wl1271_warning("value is not in valid!"); + return -EINVAL; + } + + mutex_lock(&wl->mutex); + + wl->radar_debug_mode = value; + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl12xx_for_each_wlvif_ap(wl, wlvif) { + wlcore_cmd_generic_cfg(wl, wlvif, + WLCORE_CFG_FEATURE_RADAR_DEBUG, + wl->radar_debug_mode, 0); + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static ssize_t radar_debug_mode_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + return wl1271_format_buffer(userbuf, count, ppos, + "%d\n", wl->radar_debug_mode); +} + +static const struct file_operations radar_debug_mode_ops = { + .write = radar_debug_mode_write, + .read = radar_debug_mode_read, + .open = simple_open, + .llseek = default_llseek, +}; +#endif /* CFG80211_CERTIFICATION_ONUS */ + int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -510,6 +573,9 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(conf, moddir); DEBUGFS_ADD(radar_detection, moddir); +#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS + DEBUGFS_ADD(radar_debug_mode, moddir); +#endif DEBUGFS_ADD(dynamic_fw_traces, moddir); return 0; diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index 719907a0a..ff6e46dd6 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -146,7 +146,8 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) mbox->radar_channel, wl18xx_radar_type_decode(mbox->radar_type)); - ieee80211_radar_detected(wl->hw); + if (!wl->radar_debug_mode) + ieee80211_radar_detected(wl->hw); } if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig index 969c9d79b..8a8f1e711 100644 --- a/drivers/net/wireless/ti/wlcore/Kconfig +++ b/drivers/net/wireless/ti/wlcore/Kconfig @@ -13,7 +13,7 @@ config WLCORE config WLCORE_SPI tristate "TI wlcore SPI support" - depends on WLCORE && SPI_MASTER + depends on WLCORE && SPI_MASTER && OF select CRC7 ---help--- This module adds support for the SPI interface of adapters using diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index e92f2639a..d0b773403 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -558,6 +558,11 @@ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (ret < 0) return ret; + if (wl->radar_debug_mode) + wlcore_cmd_generic_cfg(wl, wlvif, + WLCORE_CFG_FEATURE_RADAR_DEBUG, + wl->radar_debug_mode, 0); + return 0; } diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index c098a1195..1edf6eed7 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5187,14 +5187,16 @@ out: static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; u8 hlid, *ba_bitmap; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action, tid); @@ -5493,7 +5495,7 @@ static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw, { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl1271 *wl = hw->priv; - int channel, ret = 0; + int channel, active_roc, ret = 0; channel = ieee80211_frequency_to_channel(chan->center_freq); @@ -5506,9 +5508,9 @@ static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw, goto out; /* return EBUSY if we can't ROC right now */ - if (WARN_ON(wl->roc_vif || - find_first_bit(wl->roc_map, - WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) { + active_roc = find_first_bit(wl->roc_map, WL12XX_MAX_ROLES); + if (wl->roc_vif || active_roc < WL12XX_MAX_ROLES) { + wl1271_warning("active roc on role %d", active_roc); ret = -EBUSY; goto out; } diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 44f059f7f..020ac1a4b 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -30,6 +30,8 @@ #include <linux/spi/spi.h> #include <linux/wl12xx.h> #include <linux/platform_device.h> +#include <linux/of_irq.h> +#include <linux/regulator/consumer.h> #include "wlcore.h" #include "wl12xx_80211.h" @@ -81,6 +83,7 @@ struct wl12xx_spi_glue { struct device *dev; struct platform_device *core; + struct regulator *reg; /* Power regulator */ }; static void wl12xx_spi_reset(struct device *child) @@ -318,14 +321,76 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, return 0; } +/** + * wl12xx_spi_set_power - power on/off the wl12xx unit + * @child: wl12xx device handle. + * @enable: true/false to power on/off the unit. + * + * use the WiFi enable regulator to enable/disable the WiFi unit. + */ +static int wl12xx_spi_set_power(struct device *child, bool enable) +{ + int ret = 0; + struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); + + WARN_ON(!glue->reg); + + /* Update regulator state */ + if (enable) { + ret = regulator_enable(glue->reg); + if (ret) + dev_err(child, "Power enable failure\n"); + } else { + ret = regulator_disable(glue->reg); + if (ret) + dev_err(child, "Power disable failure\n"); + } + + return ret; +} + static struct wl1271_if_operations spi_ops = { .read = wl12xx_spi_raw_read, .write = wl12xx_spi_raw_write, .reset = wl12xx_spi_reset, .init = wl12xx_spi_init, + .power = wl12xx_spi_set_power, .set_block_size = NULL, }; +static const struct of_device_id wlcore_spi_of_match_table[] = { + { .compatible = "ti,wl1271" }, + { } +}; +MODULE_DEVICE_TABLE(of, wlcore_spi_of_match_table); + +/** + * wlcore_probe_of - DT node parsing. + * @spi: SPI slave device parameters. + * @res: resource parameters. + * @glue: wl12xx SPI bus to slave device glue parameters. + * @pdev_data: wlcore device parameters + */ +static int wlcore_probe_of(struct spi_device *spi, struct wl12xx_spi_glue *glue, + struct wlcore_platdev_data *pdev_data) +{ + struct device_node *dt_node = spi->dev.of_node; + int ret; + + if (of_find_property(dt_node, "clock-xtal", NULL)) + pdev_data->ref_clock_xtal = true; + + ret = of_property_read_u32(dt_node, "ref-clock-frequency", + &pdev_data->ref_clock_freq); + if (IS_ERR_VALUE(ret)) { + dev_err(glue->dev, + "can't get reference clock frequency (%d)\n", ret); + return ret; + } + + return 0; +} + static int wl1271_probe(struct spi_device *spi) { struct wl12xx_spi_glue *glue; @@ -335,8 +400,6 @@ static int wl1271_probe(struct spi_device *spi) memset(&pdev_data, 0x00, sizeof(pdev_data)); - /* TODO: add DT parsing when needed */ - pdev_data.if_ops = &spi_ops; glue = devm_kzalloc(&spi->dev, sizeof(*glue), GFP_KERNEL); @@ -353,6 +416,21 @@ static int wl1271_probe(struct spi_device *spi) * comes from the board-peripherals file */ spi->bits_per_word = 32; + glue->reg = devm_regulator_get(&spi->dev, "vwlan"); + if (PTR_ERR(glue->reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (IS_ERR(glue->reg)) { + dev_err(glue->dev, "can't get regulator\n"); + return PTR_ERR(glue->reg); + } + + ret = wlcore_probe_of(spi, glue, &pdev_data); + if (IS_ERR_VALUE(ret)) { + dev_err(glue->dev, + "can't get device tree parameters (%d)\n", ret); + return ret; + } + ret = spi_setup(spi); if (ret < 0) { dev_err(glue->dev, "spi_setup failed\n"); @@ -370,7 +448,7 @@ static int wl1271_probe(struct spi_device *spi) memset(res, 0x00, sizeof(res)); res[0].start = spi->irq; - res[0].flags = IORESOURCE_IRQ; + res[0].flags = IORESOURCE_IRQ | irq_get_trigger_type(spi->irq); res[0].name = "irq"; ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res)); @@ -408,10 +486,10 @@ static int wl1271_remove(struct spi_device *spi) return 0; } - static struct spi_driver wl1271_spi_driver = { .driver = { .name = "wl1271_spi", + .of_match_table = of_match_ptr(wlcore_spi_of_match_table), }, .probe = wl1271_probe, diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index dda01b118..72c31a8ed 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -463,6 +463,7 @@ struct wl1271 { /* the current dfs region */ enum nl80211_dfs_regions dfs_region; + bool radar_debug_mode; /* size of the private FW status data */ size_t fw_status_len; |