ath10k: import ath10k driver

Import ISC-licensed ath10k driver assumed to be
based on Linux kvalo/ath.git master at
6bae9de622d3ef4805aba40e763eb4b0975c4f6d.

Import support to redirect fwlogs to kernel messages
from https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/389075

Complement the driver to make compile on FreeBSD
using LinuxKPI with changes covered by #ifdef (__FreeBSD__).
Further select updates were applied since the initial import
in order to keep compiling along with other LinuxKPI based
drivers.

Any other native driver using BUS_PROBE_DEFAULT will attach
ignoring this one by default given bsd_probe_return is set
to a lower priority.

Add the module build framework.

We only support PCI parts.

The firmware is provided by port net/wifi-firmware-ath10k-kmod.

Given the lack of full license texts on most files this is
imported under the draft policy for handling SPDX files (D29226). [1]

Approved by:	core (emaste, 2022-04-08) [1]
MFC after:	2 months
This commit is contained in:
Bjoern A. Zeeb 2023-04-19 13:16:17 +00:00
parent ebacd8013f
commit da8fa4e37a
65 changed files with 87527 additions and 0 deletions

View File

@ -0,0 +1,90 @@
# SPDX-License-Identifier: ISC
config ATH10K
tristate "Atheros 802.11ac wireless cards support"
depends on MAC80211 && HAS_DMA
select ATH_COMMON
select CRC32
select WANT_DEV_COREDUMP
select ATH10K_CE
help
This module adds support for wireless adapters based on
Atheros IEEE 802.11ac family of chipsets.
If you choose to build a module, it'll be called ath10k.
config ATH10K_CE
bool
config ATH10K_PCI
tristate "Atheros ath10k PCI support"
depends on 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_SDIO
tristate "Atheros ath10k SDIO support"
depends on ATH10K && MMC
help
This module adds support for SDIO/MMC bus.
config ATH10K_USB
tristate "Atheros ath10k USB support (EXPERIMENTAL)"
depends on ATH10K && USB
help
This module adds experimental support for USB bus. Currently
work in progress and will not fully work.
config ATH10K_SNOC
tristate "Qualcomm ath10k SNOC support"
depends on ATH10K
depends on ARCH_QCOM || COMPILE_TEST
select QCOM_SCM
select QCOM_QMI_HELPERS
help
This module adds support for integrated WCN3990 chip connected
to system NOC(SNOC).
config ATH10K_DEBUG
bool "Atheros ath10k debugging"
depends on ATH10K
help
Enables debug support
If unsure, say Y to make it easier to debug problems.
config ATH10K_DEBUGFS
bool "Atheros ath10k debugfs support"
depends on ATH10K && DEBUG_FS
help
Enabled debugfs support
If unsure, say Y to make it easier to debug problems.
config ATH10K_SPECTRAL
bool "Atheros ath10k spectral scan support"
depends on ATH10K_DEBUGFS
select RELAY
default n
help
Say Y to enable access to the FFT/spectral data via debugfs.
config ATH10K_TRACING
bool "Atheros ath10k tracing support"
depends on ATH10K
depends on EVENT_TRACING
help
Select this to ath10k use tracing infrastructure.
config ATH10K_DFS_CERTIFIED
bool "Atheros DFS support for certified platforms"
depends on ATH10K && CFG80211_CERTIFICATION_ONUS
default n
help
This option enables DFS support for initiating radiation on
ath10k.

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: ISC
obj-$(CONFIG_ATH10K) += ath10k_core.o
ath10k_core-y += mac.o \
debug.o \
core.o \
htc.o \
htt.o \
htt_rx.o \
htt_tx.o \
txrx.o \
wmi.o \
wmi-tlv.o \
bmi.o \
hw.o \
p2p.o \
swap.o
ath10k_core-$(CONFIG_ATH10K_SPECTRAL) += spectral.o
ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o
ath10k_core-$(CONFIG_THERMAL) += thermal.o
ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o
ath10k_core-$(CONFIG_PM) += wow.o
ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
ath10k_core-$(CONFIG_ATH10K_CE) += ce.o
ath10k_core-${CONFIG_FWLOG) += fwlog.o
obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o
ath10k_pci-y += pci.o
ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o
obj-$(CONFIG_ATH10K_SDIO) += ath10k_sdio.o
ath10k_sdio-y += sdio.o
obj-$(CONFIG_ATH10K_USB) += ath10k_usb.o
ath10k_usb-y += usb.o
obj-$(CONFIG_ATH10K_SNOC) += ath10k_snoc.o
ath10k_snoc-y += qmi.o \
qmi_wlfw_v01.o \
snoc.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)

View File

@ -0,0 +1,879 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2016-2017 Qualcomm Atheros, Inc. All rights reserved.
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*/
#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[] = {
{ .compatible = "qcom,ipq4019-wifi",
.data = (void *)ATH10K_HW_QCA4019
},
{ }
};
MODULE_DEVICE_TABLE(of, ath10k_ahb_of_match);
#define QCA4019_SRAM_ADDR 0x000C0000
#define QCA4019_SRAM_LEN 0x00040000 /* 256 kb */
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;
dev = &ar_ahb->pdev->dev;
ar_ahb->cmd_clk = devm_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));
return ar_ahb->cmd_clk ? PTR_ERR(ar_ahb->cmd_clk) : -ENODEV;
}
ar_ahb->ref_clk = devm_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));
return ar_ahb->ref_clk ? PTR_ERR(ar_ahb->ref_clk) : -ENODEV;
}
ar_ahb->rtc_clk = devm_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));
return ar_ahb->rtc_clk ? PTR_ERR(ar_ahb->rtc_clk) : -ENODEV;
}
return 0;
}
static void ath10k_ahb_clock_deinit(struct ath10k *ar)
{
struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
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);
int ret;
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);
clk_disable_unprepare(ar_ahb->cmd_clk);
clk_disable_unprepare(ar_ahb->ref_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;
dev = &ar_ahb->pdev->dev;
ar_ahb->core_cold_rst = devm_reset_control_get_exclusive(dev,
"wifi_core_cold");
if (IS_ERR(ar_ahb->core_cold_rst)) {
ath10k_err(ar, "failed to get core cold rst ctrl: %ld\n",
PTR_ERR(ar_ahb->core_cold_rst));
return PTR_ERR(ar_ahb->core_cold_rst);
}
ar_ahb->radio_cold_rst = devm_reset_control_get_exclusive(dev,
"wifi_radio_cold");
if (IS_ERR(ar_ahb->radio_cold_rst)) {
ath10k_err(ar, "failed to get radio cold rst ctrl: %ld\n",
PTR_ERR(ar_ahb->radio_cold_rst));
return PTR_ERR(ar_ahb->radio_cold_rst);
}
ar_ahb->radio_warm_rst = devm_reset_control_get_exclusive(dev,
"wifi_radio_warm");
if (IS_ERR(ar_ahb->radio_warm_rst)) {
ath10k_err(ar, "failed to get radio warm rst ctrl: %ld\n",
PTR_ERR(ar_ahb->radio_warm_rst));
return PTR_ERR(ar_ahb->radio_warm_rst);
}
ar_ahb->radio_srif_rst = devm_reset_control_get_exclusive(dev,
"wifi_radio_srif");
if (IS_ERR(ar_ahb->radio_srif_rst)) {
ath10k_err(ar, "failed to get radio srif rst ctrl: %ld\n",
PTR_ERR(ar_ahb->radio_srif_rst));
return PTR_ERR(ar_ahb->radio_srif_rst);
}
ar_ahb->cpu_init_rst = devm_reset_control_get_exclusive(dev,
"wifi_cpu_init");
if (IS_ERR(ar_ahb->cpu_init_rst)) {
ath10k_err(ar, "failed to get cpu init rst ctrl: %ld\n",
PTR_ERR(ar_ahb->cpu_init_rst));
return PTR_ERR(ar_ahb->cpu_init_rst);
}
return 0;
}
static void ath10k_ahb_rst_ctrl_deinit(struct ath10k *ar)
{
struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
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;
if (!ath10k_pci_irq_pending(ar))
return IRQ_NONE;
ath10k_pci_disable_and_clear_legacy_irq(ar);
ath10k_pci_irq_msi_fw_mask(ar);
napi_schedule(&ar->napi);
return IRQ_HANDLED;
}
static int ath10k_ahb_request_irq_legacy(struct ath10k *ar)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(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;
}
ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_LEGACY;
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 resource *res;
int ret;
pdev = ar_ahb->pdev;
ar_ahb->mem = devm_platform_get_and_ioremap_resource(pdev, 0, &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(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(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);
ret = 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%pK mem_len: %lu gcc mem: 0x%pK tcsr_mem: 0x%pK\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_core_napi_enable(ar);
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_core_napi_sync_disable(ar);
ath10k_pci_flush(ar);
}
static int ath10k_ahb_hif_power_up(struct ath10k *ar,
enum ath10k_firmware_mode fw_mode)
{
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 u32 ath10k_ahb_qca4019_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr)
{
u32 val = 0, region = addr & 0xfffff;
val = ath10k_pci_read32(ar, PCIE_BAR_REG_ADDRESS);
if (region >= QCA4019_SRAM_ADDR && region <=
(QCA4019_SRAM_ADDR + QCA4019_SRAM_LEN)) {
/* SRAM contents for QCA4019 can be directly accessed and
* no conversions are required
*/
val |= region;
} else {
val |= 0x100000 | region;
}
return val;
}
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;
struct ath10k_bus_params bus_params = {};
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->ce.bus_ops = &ath10k_ahb_bus_ops;
ar_pci->targ_cpu_to_ce_addr = ath10k_ahb_qca4019_targ_cpu_to_ce_addr;
ar->ce_priv = &ar_pci->ce;
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_napi(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);
bus_params.dev_type = ATH10K_DEV_TYPE_LL;
bus_params.chip_id = ath10k_ahb_soc_read32(ar, SOC_CHIP_ID_ADDRESS);
if (bus_params.chip_id == 0xffffffff) {
ath10k_err(ar, "failed to get chip id\n");
ret = -ENODEV;
goto err_halt_device;
}
ret = ath10k_core_register(ar, &bus_params);
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_release_resource(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;
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);
}

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2016 Qualcomm Atheros, Inc. All rights reserved.
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*/
#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_ */

View File

@ -0,0 +1,539 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc.
*/
#include "bmi.h"
#include "hif.h"
#include "debug.h"
#include "htc.h"
#include "hw.h"
void ath10k_bmi_start(struct ath10k *ar)
{
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");
ar->bmi.done_sent = false;
}
EXPORT_SYMBOL(ath10k_bmi_start);
int ath10k_bmi_done(struct ath10k *ar)
{
struct bmi_cmd cmd;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");
if (ar->bmi.done_sent) {
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");
return 0;
}
ar->bmi.done_sent = true;
cmd.id = __cpu_to_le32(BMI_DONE);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
if (ret) {
ath10k_warn(ar, "unable to write to the device: %d\n", ret);
return ret;
}
return 0;
}
int ath10k_bmi_get_target_info(struct ath10k *ar,
struct bmi_target_info *target_info)
{
struct bmi_cmd cmd;
union bmi_resp resp;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
u32 resplen = sizeof(resp.get_target_info);
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");
if (ar->bmi.done_sent) {
ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
if (ret) {
ath10k_warn(ar, "unable to get target info from device\n");
return ret;
}
if (resplen < sizeof(resp.get_target_info)) {
ath10k_warn(ar, "invalid get_target_info response length (%d)\n",
resplen);
return -EIO;
}
target_info->version = __le32_to_cpu(resp.get_target_info.version);
target_info->type = __le32_to_cpu(resp.get_target_info.type);
return 0;
}
#define TARGET_VERSION_SENTINAL 0xffffffffu
int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,
struct bmi_target_info *target_info)
{
struct bmi_cmd cmd;
union bmi_resp resp;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
u32 resplen, ver_len;
__le32 tmp;
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n");
if (ar->bmi.done_sent) {
ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
/* Step 1: Read 4 bytes of the target info and check if it is
* the special sentinal version word or the first word in the
* version response.
*/
resplen = sizeof(u32);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen);
if (ret) {
ath10k_warn(ar, "unable to read from device\n");
return ret;
}
/* Some SDIO boards have a special sentinal byte before the real
* version response.
*/
if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) {
/* Step 1b: Read the version length */
resplen = sizeof(u32);
ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp,
&resplen);
if (ret) {
ath10k_warn(ar, "unable to read from device\n");
return ret;
}
}
ver_len = __le32_to_cpu(tmp);
/* Step 2: Check the target info length */
if (ver_len != sizeof(resp.get_target_info)) {
ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n",
ver_len, sizeof(resp.get_target_info));
return -EINVAL;
}
/* Step 3: Read the rest of the version response */
resplen = sizeof(resp.get_target_info) - sizeof(u32);
ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0,
&resp.get_target_info.version,
&resplen);
if (ret) {
ath10k_warn(ar, "unable to read from device\n");
return ret;
}
target_info->version = __le32_to_cpu(resp.get_target_info.version);
target_info->type = __le32_to_cpu(resp.get_target_info.type);
return 0;
}
int ath10k_bmi_read_memory(struct ath10k *ar,
#if defined(__linux__)
u32 address, void *buffer, u32 length)
#elif defined(__FreeBSD__)
u32 address, u8 *buffer, u32 length)
#endif
{
struct bmi_cmd cmd;
union bmi_resp resp;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
u32 rxlen;
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
address, length);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "command disallowed\n");
return -EBUSY;
}
while (length) {
rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
cmd.id = __cpu_to_le32(BMI_READ_MEMORY);
cmd.read_mem.addr = __cpu_to_le32(address);
cmd.read_mem.len = __cpu_to_le32(rxlen);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
&resp, &rxlen);
if (ret) {
ath10k_warn(ar, "unable to read from the device (%d)\n",
ret);
return ret;
}
memcpy(buffer, resp.read_mem.payload, rxlen);
address += rxlen;
buffer += rxlen;
length -= rxlen;
}
return 0;
}
EXPORT_SYMBOL(ath10k_bmi_read_memory);
int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)
{
struct bmi_cmd cmd;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg);
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI,
"bmi write soc register 0x%08x val 0x%08x\n",
address, reg_val);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "bmi write soc register command in progress\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER);
cmd.write_soc_reg.addr = __cpu_to_le32(address);
cmd.write_soc_reg.value = __cpu_to_le32(reg_val);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
if (ret) {
ath10k_warn(ar, "Unable to write soc register to device: %d\n",
ret);
return ret;
}
return 0;
}
int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val)
{
struct bmi_cmd cmd;
union bmi_resp resp;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg);
u32 resplen = sizeof(resp.read_soc_reg);
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n",
address);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "bmi read soc register command in progress\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER);
cmd.read_soc_reg.addr = __cpu_to_le32(address);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
if (ret) {
ath10k_warn(ar, "Unable to read soc register from device: %d\n",
ret);
return ret;
}
*reg_val = __le32_to_cpu(resp.read_soc_reg.value);
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n",
*reg_val);
return 0;
}
int ath10k_bmi_write_memory(struct ath10k *ar,
#if defined(__linux__)
u32 address, const void *buffer, u32 length)
#elif defined(__FreeBSD__)
u32 address, const u8 *buffer, u32 length)
#endif
{
struct bmi_cmd cmd;
u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
u32 txlen;
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
address, length);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "command disallowed\n");
return -EBUSY;
}
while (length) {
txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
/* copy before roundup to avoid reading beyond buffer*/
memcpy(cmd.write_mem.payload, buffer, txlen);
txlen = roundup(txlen, 4);
cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY);
cmd.write_mem.addr = __cpu_to_le32(address);
cmd.write_mem.len = __cpu_to_le32(txlen);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
NULL, NULL);
if (ret) {
ath10k_warn(ar, "unable to write to the device (%d)\n",
ret);
return ret;
}
/* fixup roundup() so `length` zeroes out for last chunk */
txlen = min(txlen, length);
address += txlen;
buffer += txlen;
length -= txlen;
}
return 0;
}
int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)
{
struct bmi_cmd cmd;
union bmi_resp resp;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
u32 resplen = sizeof(resp.execute);
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
address, param);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "command disallowed\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_EXECUTE);
cmd.execute.addr = __cpu_to_le32(address);
cmd.execute.param = __cpu_to_le32(param);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
if (ret) {
ath10k_warn(ar, "unable to read from the device\n");
return ret;
}
if (resplen < sizeof(resp.execute)) {
ath10k_warn(ar, "invalid execute response length (%d)\n",
resplen);
return -EIO;
}
*result = __le32_to_cpu(resp.execute.result);
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
return 0;
}
#if defined(__linux__)
static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length)
#elif defined(__FreeBSD__)
static int ath10k_bmi_lz_data_large(struct ath10k *ar, const u8 *buffer, u32 length)
#endif
{
struct bmi_cmd *cmd;
u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data);
u32 txlen;
int ret;
size_t buf_len;
ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n",
buffer, length);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "command disallowed\n");
return -EBUSY;
}
buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE;
cmd = kzalloc(buf_len, GFP_KERNEL);
if (!cmd)
return -ENOMEM;
while (length) {
txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen);
WARN_ON_ONCE(txlen & 3);
cmd->id = __cpu_to_le32(BMI_LZ_DATA);
cmd->lz_data.len = __cpu_to_le32(txlen);
memcpy(cmd->lz_data.payload, buffer, txlen);
ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen,
NULL, NULL);
if (ret) {
ath10k_warn(ar, "unable to write to the device\n");
kfree(cmd);
return ret;
}
buffer += txlen;
length -= txlen;
}
kfree(cmd);
return 0;
}
#if defined(__linux__)
int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
#elif defined(__FreeBSD__)
static
int ath10k_bmi_lz_data(struct ath10k *ar, const u8 *buffer, u32 length)
#endif
{
struct bmi_cmd cmd;
u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
u32 txlen;
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n",
buffer, length);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "command disallowed\n");
return -EBUSY;
}
while (length) {
txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
WARN_ON_ONCE(txlen & 3);
cmd.id = __cpu_to_le32(BMI_LZ_DATA);
cmd.lz_data.len = __cpu_to_le32(txlen);
memcpy(cmd.lz_data.payload, buffer, txlen);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
NULL, NULL);
if (ret) {
ath10k_warn(ar, "unable to write to the device\n");
return ret;
}
buffer += txlen;
length -= txlen;
}
return 0;
}
int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
{
struct bmi_cmd cmd;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
address);
if (ar->bmi.done_sent) {
ath10k_warn(ar, "command disallowed\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START);
cmd.lz_start.addr = __cpu_to_le32(address);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
if (ret) {
ath10k_warn(ar, "unable to Start LZ Stream to the device\n");
return ret;
}
return 0;
}
int ath10k_bmi_fast_download(struct ath10k *ar,
u32 address, const void *buffer, u32 length)
{
u8 trailer[4] = {};
u32 head_len = rounddown(length, 4);
u32 trailer_len = length - head_len;
int ret;
ath10k_dbg(ar, ATH10K_DBG_BMI,
"bmi fast download address 0x%x buffer 0x%pK length %d\n",
address, buffer, length);
ret = ath10k_bmi_lz_stream_start(ar, address);
if (ret)
return ret;
/* copy the last word into a zero padded buffer */
if (trailer_len > 0)
#if defined(__linux__)
memcpy(trailer, buffer + head_len, trailer_len);
#elif defined(__FreeBSD__)
memcpy(trailer, (const u8 *)buffer + head_len, trailer_len);
#endif
if (ar->hw_params.bmi_large_size_download)
ret = ath10k_bmi_lz_data_large(ar, buffer, head_len);
else
ret = ath10k_bmi_lz_data(ar, buffer, head_len);
if (ret)
return ret;
if (trailer_len > 0)
ret = ath10k_bmi_lz_data(ar, trailer, 4);
if (ret != 0)
return ret;
/*
* Close compressed stream and open a new (fake) one.
* This serves mainly to flush Target caches.
*/
ret = ath10k_bmi_lz_stream_start(ar, 0x00);
return ret;
}
int ath10k_bmi_set_start(struct ath10k *ar, u32 address)
{
struct bmi_cmd cmd;
u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start);
int ret;
if (ar->bmi.done_sent) {
ath10k_warn(ar, "bmi set start command disallowed\n");
return -EBUSY;
}
cmd.id = __cpu_to_le32(BMI_SET_APP_START);
cmd.set_app_start.addr = __cpu_to_le32(address);
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
if (ret) {
ath10k_warn(ar, "unable to set start to the device:%d\n", ret);
return ret;
}
return 0;
}

View File

@ -0,0 +1,286 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2015,2017 Qualcomm Atheros, Inc.
*/
#ifndef _BMI_H_
#define _BMI_H_
#include "core.h"
/*
* Bootloader Messaging Interface (BMI)
*
* BMI is a very simple messaging interface used during initialization
* to read memory, write memory, execute code, and to define an
* application entry PC.
*
* It is used to download an application to QCA988x, to provide
* patches to code that is already resident on QCA988x, and generally
* to examine and modify state. The Host has an opportunity to use
* BMI only once during bootup. Once the Host issues a BMI_DONE
* command, this opportunity ends.
*
* The Host writes BMI requests to mailbox0, and reads BMI responses
* from mailbox0. BMI requests all begin with a command
* (see below for specific commands), and are followed by
* command-specific data.
*
* Flow control:
* The Host can only issue a command once the Target gives it a
* "BMI Command Credit", using AR8K Counter #4. As soon as the
* Target has completed a command, it issues another BMI Command
* Credit (so the Host can issue the next command).
*
* BMI handles all required Target-side cache flushing.
*/
/* Maximum data size used for BMI transfers */
#define BMI_MAX_DATA_SIZE 256
/* len = cmd + addr + length */
#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \
sizeof(u32) + \
sizeof(u32) + \
sizeof(u32))
/* Maximum data size used for large BMI transfers */
#define BMI_MAX_LARGE_DATA_SIZE 2048
/* len = cmd + addr + length */
#define BMI_MAX_LARGE_CMDBUF_SIZE (BMI_MAX_LARGE_DATA_SIZE + \
sizeof(u32) + \
sizeof(u32) + \
sizeof(u32))
/* BMI Commands */
enum bmi_cmd_id {
BMI_NO_COMMAND = 0,
BMI_DONE = 1,
BMI_READ_MEMORY = 2,
BMI_WRITE_MEMORY = 3,
BMI_EXECUTE = 4,
BMI_SET_APP_START = 5,
BMI_READ_SOC_REGISTER = 6,
BMI_READ_SOC_WORD = 6,
BMI_WRITE_SOC_REGISTER = 7,
BMI_WRITE_SOC_WORD = 7,
BMI_GET_TARGET_ID = 8,
BMI_GET_TARGET_INFO = 8,
BMI_ROMPATCH_INSTALL = 9,
BMI_ROMPATCH_UNINSTALL = 10,
BMI_ROMPATCH_ACTIVATE = 11,
BMI_ROMPATCH_DEACTIVATE = 12,
BMI_LZ_STREAM_START = 13, /* should be followed by LZ_DATA */
BMI_LZ_DATA = 14,
BMI_NVRAM_PROCESS = 15,
};
#define BMI_NVRAM_SEG_NAME_SZ 16
#define BMI_PARAM_GET_EEPROM_BOARD_ID 0x10
#define BMI_PARAM_GET_FLASH_BOARD_ID 0x8000
#define BMI_PARAM_FLASH_SECTION_ALL 0x10000
/* Dual-band Extended Board ID */
#define BMI_PARAM_GET_EXT_BOARD_ID 0x40000
#define ATH10K_BMI_EXT_BOARD_ID_SUPPORT 0x40000
#define ATH10K_BMI_BOARD_ID_FROM_OTP_MASK 0x7c00
#define ATH10K_BMI_BOARD_ID_FROM_OTP_LSB 10
#define ATH10K_BMI_CHIP_ID_FROM_OTP_MASK 0x18000
#define ATH10K_BMI_CHIP_ID_FROM_OTP_LSB 15
#define ATH10K_BMI_BOARD_ID_STATUS_MASK 0xff
#define ATH10K_BMI_EBOARD_ID_STATUS_MASK 0xff
struct bmi_cmd {
__le32 id; /* enum bmi_cmd_id */
union {
struct {
} done;
struct {
__le32 addr;
__le32 len;
} read_mem;
struct {
__le32 addr;
__le32 len;
u8 payload[];
} write_mem;
struct {
__le32 addr;
__le32 param;
} execute;
struct {
__le32 addr;
} set_app_start;
struct {
__le32 addr;
} read_soc_reg;
struct {
__le32 addr;
__le32 value;
} write_soc_reg;
struct {
} get_target_info;
struct {
__le32 rom_addr;
__le32 ram_addr; /* or value */
__le32 size;
__le32 activate; /* 0=install, but dont activate */
} rompatch_install;
struct {
__le32 patch_id;
} rompatch_uninstall;
struct {
__le32 count;
__le32 patch_ids[]; /* length of @count */
} rompatch_activate;
struct {
__le32 count;
__le32 patch_ids[]; /* length of @count */
} rompatch_deactivate;
struct {
__le32 addr;
} lz_start;
struct {
__le32 len; /* max BMI_MAX_DATA_SIZE */
u8 payload[]; /* length of @len */
} lz_data;
struct {
u8 name[BMI_NVRAM_SEG_NAME_SZ];
} nvram_process;
u8 payload[BMI_MAX_CMDBUF_SIZE];
};
} __packed;
union bmi_resp {
struct {
DECLARE_FLEX_ARRAY(u8, payload);
} read_mem;
struct {
__le32 result;
} execute;
struct {
__le32 value;
} read_soc_reg;
struct {
__le32 len;
__le32 version;
__le32 type;
} get_target_info;
struct {
__le32 patch_id;
} rompatch_install;
struct {
__le32 patch_id;
} rompatch_uninstall;
struct {
/* 0 = nothing executed
* otherwise = NVRAM segment return value
*/
__le32 result;
} nvram_process;
u8 payload[BMI_MAX_CMDBUF_SIZE];
} __packed;
struct bmi_target_info {
u32 version;
u32 type;
};
struct bmi_segmented_file_header {
__le32 magic_num;
__le32 file_flags;
u8 data[];
};
struct bmi_segmented_metadata {
__le32 addr;
__le32 length;
u8 data[];
};
#define BMI_SGMTFILE_MAGIC_NUM 0x544d4753 /* "SGMT" */
#define BMI_SGMTFILE_FLAG_COMPRESS 1
/* Special values for bmi_segmented_metadata.length (all have high bit set) */
/* end of segmented data */
#define BMI_SGMTFILE_DONE 0xffffffff
/* Board Data segment */
#define BMI_SGMTFILE_BDDATA 0xfffffffe
/* set beginning address */
#define BMI_SGMTFILE_BEGINADDR 0xfffffffd
/* immediate function execution */
#define BMI_SGMTFILE_EXEC 0xfffffffc
/* in jiffies */
#define BMI_COMMUNICATION_TIMEOUT_HZ (3 * HZ)
#define BMI_CE_NUM_TO_TARG 0
#define BMI_CE_NUM_TO_HOST 1
void ath10k_bmi_start(struct ath10k *ar);
int ath10k_bmi_done(struct ath10k *ar);
int ath10k_bmi_get_target_info(struct ath10k *ar,
struct bmi_target_info *target_info);
int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,
struct bmi_target_info *target_info);
#if defined(__linux__)
int ath10k_bmi_read_memory(struct ath10k *ar, u32 address,
void *buffer, u32 length);
int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
const void *buffer, u32 length);
#elif defined(__FreeBSD__)
int ath10k_bmi_read_memory(struct ath10k *ar, u32 address,
u8 *buffer, u32 length);
int ath10k_bmi_write_memory(struct ath10k *ar, u32 address,
const u8 *buffer, u32 length);
#endif
#define ath10k_bmi_read32(ar, item, val) \
({ \
int ret; \
u32 addr; \
__le32 tmp; \
\
addr = host_interest_item_address(HI_ITEM(item)); \
ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \
if (!ret) \
*val = __le32_to_cpu(tmp); \
ret; \
})
#define ath10k_bmi_write32(ar, item, val) \
({ \
int ret; \
u32 address; \
__le32 v = __cpu_to_le32(val); \
\
address = host_interest_item_address(HI_ITEM(item)); \
ret = ath10k_bmi_write_memory(ar, address, \
(u8 *)&v, sizeof(v)); \
ret; \
})
int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result);
int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address);
#if defined(__linux__)
int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length);
#endif
int ath10k_bmi_fast_download(struct ath10k *ar, u32 address,
const void *buffer, u32 length);
int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val);
int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val);
int ath10k_bmi_set_start(struct ath10k *ar, u32 address);
#endif /* _BMI_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,427 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*/
#ifndef _CE_H_
#define _CE_H_
#include "hif.h"
#define CE_HTT_H2T_MSG_SRC_NENTRIES 8192
/* Descriptor rings must be aligned to this boundary */
#define CE_DESC_RING_ALIGN 8
#define CE_SEND_FLAG_GATHER 0x00010000
/*
* Copy Engine support: low-level Target-side Copy Engine API.
* This is a hardware access layer used by code that understands
* how to use copy engines.
*/
struct ath10k_ce_pipe;
#define CE_DESC_FLAGS_GATHER (1 << 0)
#define CE_DESC_FLAGS_BYTE_SWAP (1 << 1)
#define CE_WCN3990_DESC_FLAGS_GATHER BIT(31)
#define CE_DESC_ADDR_MASK GENMASK_ULL(34, 0)
#define CE_DESC_ADDR_HI_MASK GENMASK(4, 0)
/* Following desc flags are used in QCA99X0 */
#define CE_DESC_FLAGS_HOST_INT_DIS (1 << 2)
#define CE_DESC_FLAGS_TGT_INT_DIS (1 << 3)
#define CE_DESC_FLAGS_META_DATA_MASK ar->hw_values->ce_desc_meta_data_mask
#define CE_DESC_FLAGS_META_DATA_LSB ar->hw_values->ce_desc_meta_data_lsb
#define CE_DDR_RRI_MASK GENMASK(15, 0)
#define CE_DDR_DRRI_SHIFT 16
struct ce_desc {
__le32 addr;
__le16 nbytes;
__le16 flags; /* %CE_DESC_FLAGS_ */
};
struct ce_desc_64 {
__le64 addr;
__le16 nbytes; /* length in register map */
__le16 flags; /* fw_metadata_high */
__le32 toeplitz_hash_result;
};
#define CE_DESC_SIZE sizeof(struct ce_desc)
#define CE_DESC_SIZE_64 sizeof(struct ce_desc_64)
struct ath10k_ce_ring {
/* Number of entries in this ring; must be power of 2 */
unsigned int nentries;
unsigned int nentries_mask;
/*
* For dest ring, this is the next index to be processed
* by software after it was/is received into.
*
* For src ring, this is the last descriptor that was sent
* and completion processed by software.
*
* Regardless of src or dest ring, this is an invariant
* (modulo ring size):
* write index >= read index >= sw_index
*/
unsigned int sw_index;
/* cached copy */
unsigned int write_index;
/*
* For src ring, this is the next index not yet processed by HW.
* This is a cached copy of the real HW index (read index), used
* for avoiding reading the HW index register more often than
* necessary.
* This extends the invariant:
* write index >= read index >= hw_index >= sw_index
*
* For dest ring, this is currently unused.
*/
/* cached copy */
unsigned int hw_index;
/* Start of DMA-coherent area reserved for descriptors */
/* Host address space */
void *base_addr_owner_space_unaligned;
/* CE address space */
dma_addr_t base_addr_ce_space_unaligned;
/*
* Actual start of descriptors.
* Aligned to descriptor-size boundary.
* Points into reserved DMA-coherent area, above.
*/
/* Host address space */
void *base_addr_owner_space;
/* CE address space */
dma_addr_t base_addr_ce_space;
char *shadow_base_unaligned;
struct ce_desc_64 *shadow_base;
/* keep last */
void *per_transfer_context[];
};
struct ath10k_ce_pipe {
struct ath10k *ar;
unsigned int id;
unsigned int attr_flags;
u32 ctrl_addr;
void (*send_cb)(struct ath10k_ce_pipe *);
void (*recv_cb)(struct ath10k_ce_pipe *);
unsigned int src_sz_max;
struct ath10k_ce_ring *src_ring;
struct ath10k_ce_ring *dest_ring;
const struct ath10k_ce_ops *ops;
};
/* Copy Engine settable attributes */
struct ce_attr;
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);
};
static inline struct ath10k_ce *ath10k_ce_priv(struct ath10k *ar)
{
return (struct ath10k_ce *)ar->ce_priv;
}
struct ath10k_ce {
/* protects CE info */
spinlock_t ce_lock;
const struct ath10k_bus_ops *bus_ops;
struct ath10k_ce_pipe ce_states[CE_COUNT_MAX];
u32 *vaddr_rri;
dma_addr_t paddr_rri;
};
/*==================Send====================*/
/* ath10k_ce_send flags */
#define CE_SEND_FLAG_BYTE_SWAP 1
/*
* Queue a source buffer to be sent to an anonymous destination buffer.
* ce - which copy engine to use
* buffer - address of buffer
* nbytes - number of bytes to send
* transfer_id - arbitrary ID; reflected to destination
* flags - CE_SEND_FLAG_* values
* Returns 0 on success; otherwise an error status.
*
* Note: If no flags are specified, use CE's default data swap mode.
*
* Implementation note: pushes 1 buffer to Source ring
*/
int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
void *per_transfer_send_context,
dma_addr_t buffer,
unsigned int nbytes,
/* 14 bits */
unsigned int transfer_id,
unsigned int flags);
int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
void *per_transfer_context,
dma_addr_t buffer,
unsigned int nbytes,
unsigned int transfer_id,
unsigned int flags);
void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe);
int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe);
/*==================Recv=======================*/
int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe);
int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx,
dma_addr_t paddr);
void ath10k_ce_rx_update_write_idx(struct ath10k_ce_pipe *pipe, u32 nentries);
/* recv flags */
/* Data is byte-swapped */
#define CE_RECV_FLAG_SWAPPED 1
/*
* Supply data for the next completed unprocessed receive descriptor.
* Pops buffer from Dest ring.
*/
int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
unsigned int *nbytesp);
/*
* Supply data for the next completed unprocessed send descriptor.
* Pops 1 completed send buffer from Source ring.
*/
int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp);
int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp);
/*==================CE Engine Initialization=======================*/
int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
const struct ce_attr *attr);
void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
const struct ce_attr *attr);
void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
/*==================CE Engine Shutdown=======================*/
/*
* Support clean shutdown by allowing the caller to revoke
* receive buffers. Target DMA must be stopped before using
* this API.
*/
int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
dma_addr_t *bufferp);
int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
unsigned int *nbytesp);
/*
* Support clean shutdown by allowing the caller to cancel
* pending sends. Target DMA must be stopped before using
* this API.
*/
int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
dma_addr_t *bufferp,
unsigned int *nbytesp,
unsigned int *transfer_idp);
/*==================CE Interrupt Handlers====================*/
void ath10k_ce_per_engine_service_any(struct ath10k *ar);
void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
void ath10k_ce_disable_interrupt(struct ath10k *ar, int ce_id);
void ath10k_ce_disable_interrupts(struct ath10k *ar);
void ath10k_ce_enable_interrupt(struct ath10k *ar, int ce_id);
void ath10k_ce_enable_interrupts(struct ath10k *ar);
void ath10k_ce_dump_registers(struct ath10k *ar,
struct ath10k_fw_crash_data *crash_data);
void ath10k_ce_alloc_rri(struct ath10k *ar);
void ath10k_ce_free_rri(struct ath10k *ar);
/* ce_attr.flags values */
/* Use NonSnooping PCIe accesses? */
#define CE_ATTR_NO_SNOOP BIT(0)
/* Byte swap data words */
#define CE_ATTR_BYTE_SWAP_DATA BIT(1)
/* Swizzle descriptors? */
#define CE_ATTR_SWIZZLE_DESCRIPTORS BIT(2)
/* no interrupt on copy completion */
#define CE_ATTR_DIS_INTR BIT(3)
/* no interrupt, only polling */
#define CE_ATTR_POLL BIT(4)
/* Attributes of an instance of a Copy Engine */
struct ce_attr {
/* CE_ATTR_* values */
unsigned int flags;
/* #entries in source ring - Must be a power of 2 */
unsigned int src_nentries;
/*
* Max source send size for this CE.
* This is also the minimum size of a destination buffer.
*/
unsigned int src_sz_max;
/* #entries in destination ring - Must be a power of 2 */
unsigned int dest_nentries;
void (*send_cb)(struct ath10k_ce_pipe *);
void (*recv_cb)(struct ath10k_ce_pipe *);
};
struct ath10k_ce_ops {
struct ath10k_ce_ring *(*ce_alloc_src_ring)(struct ath10k *ar,
u32 ce_id,
const struct ce_attr *attr);
struct ath10k_ce_ring *(*ce_alloc_dst_ring)(struct ath10k *ar,
u32 ce_id,
const struct ce_attr *attr);
int (*ce_rx_post_buf)(struct ath10k_ce_pipe *pipe, void *ctx,
dma_addr_t paddr);
int (*ce_completed_recv_next_nolock)(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
u32 *nbytesp);
int (*ce_revoke_recv_next)(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
dma_addr_t *nbytesp);
void (*ce_extract_desc_data)(struct ath10k *ar,
struct ath10k_ce_ring *src_ring,
u32 sw_index, dma_addr_t *bufferp,
u32 *nbytesp, u32 *transfer_idp);
void (*ce_free_pipe)(struct ath10k *ar, int ce_id);
int (*ce_send_nolock)(struct ath10k_ce_pipe *pipe,
void *per_transfer_context,
dma_addr_t buffer, u32 nbytes,
u32 transfer_id, u32 flags);
void (*ce_set_src_ring_base_addr_hi)(struct ath10k *ar,
u32 ce_ctrl_addr,
u64 addr);
void (*ce_set_dest_ring_base_addr_hi)(struct ath10k *ar,
u32 ce_ctrl_addr,
u64 addr);
int (*ce_completed_send_next_nolock)(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp);
};
static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
{
return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id;
}
#define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) (((COPY_ENGINE_BASE_ADDRESS) \
- CE0_BASE_ADDRESS) / (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS))
#define CE_SRC_RING_TO_DESC(baddr, idx) \
(&(((struct ce_desc *)baddr)[idx]))
#define CE_DEST_RING_TO_DESC(baddr, idx) \
(&(((struct ce_desc *)baddr)[idx]))
#define CE_SRC_RING_TO_DESC_64(baddr, idx) \
(&(((struct ce_desc_64 *)baddr)[idx]))
#define CE_DEST_RING_TO_DESC_64(baddr, idx) \
(&(((struct ce_desc_64 *)baddr)[idx]))
/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */
#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \
(((int)(toidx) - (int)(fromidx)) & (nentries_mask))
#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
#define CE_RING_IDX_ADD(nentries_mask, idx, num) \
(((idx) + (num)) & (nentries_mask))
#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB \
ar->regs->ce_wrap_intr_sum_host_msi_lsb
#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK \
ar->regs->ce_wrap_intr_sum_host_msi_mask
#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \
(((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000
static inline u32 ath10k_ce_interrupt_summary(struct ath10k *ar)
{
struct ath10k_ce *ce = ath10k_ce_priv(ar);
return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(
ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS +
CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS));
}
/* Host software's Copy Engine configuration. */
#define CE_ATTR_FLAGS 0
/*
* Configuration information for a Copy Engine pipe.
* Passed from Host to Target during startup (one per CE).
*
* NOTE: Structure is shared between Host software and Target firmware!
*/
struct ce_pipe_config {
__le32 pipenum;
__le32 pipedir;
__le32 nentries;
__le32 nbytes_max;
__le32 flags;
__le32 reserved;
};
/*
* Directions for interconnect pipe configuration.
* These definitions may be used during configuration and are shared
* between Host and Target.
*
* Pipe Directions are relative to the Host, so PIPEDIR_IN means
* "coming IN over air through Target to Host" as with a WiFi Rx operation.
* Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air"
* as with a WiFi Tx operation. This is somewhat awkward for the "middle-man"
* Target since things that are "PIPEDIR_OUT" are coming IN to the Target
* over the interconnect.
*/
#define PIPEDIR_NONE 0
#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */
#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */
#define PIPEDIR_INOUT 3 /* bidirectional */
/* Establish a mapping between a service/direction and a pipe. */
struct ce_service_to_pipe {
__le32 service_id;
__le32 pipedir;
__le32 pipenum;
};
#endif /* _CE_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
*/
#ifndef _COREDUMP_H_
#define _COREDUMP_H_
#include "core.h"
#define ATH10K_FW_CRASH_DUMP_VERSION 1
/**
* enum ath10k_fw_crash_dump_type - types of data in the dump file
* @ATH10K_FW_CRASH_DUMP_REGDUMP: Register crash dump in binary format
*/
enum ath10k_fw_crash_dump_type {
ATH10K_FW_CRASH_DUMP_REGISTERS = 0,
ATH10K_FW_CRASH_DUMP_CE_DATA = 1,
/* contains multiple struct ath10k_dump_ram_data_hdr */
ATH10K_FW_CRASH_DUMP_RAM_DATA = 2,
ATH10K_FW_CRASH_DUMP_MAX,
};
struct ath10k_tlv_dump_data {
/* see ath10k_fw_crash_dump_type above */
__le32 type;
/* in bytes */
__le32 tlv_len;
/* pad to 32-bit boundaries as needed */
u8 tlv_data[];
} __packed;
struct ath10k_dump_file_data {
/* dump file information */
/* "ATH10K-FW-DUMP" */
char df_magic[16];
__le32 len;
/* file dump version */
__le32 version;
/* some info we can get from ath10k struct that might help */
guid_t guid;
__le32 chip_id;
/* 0 for now, in place for later hardware */
__le32 bus_type;
__le32 target_version;
__le32 fw_version_major;
__le32 fw_version_minor;
__le32 fw_version_release;
__le32 fw_version_build;
__le32 phy_capability;
__le32 hw_min_tx_power;
__le32 hw_max_tx_power;
__le32 ht_cap_info;
__le32 vht_cap_info;
__le32 num_rf_chains;
/* firmware version string */
char fw_ver[ETHTOOL_FWVERS_LEN];
/* Kernel related information */
/* time-of-day stamp */
__le64 tv_sec;
/* time-of-day stamp, nano-seconds */
__le64 tv_nsec;
/* LINUX_VERSION_CODE */
__le32 kernel_ver_code;
/* VERMAGIC_STRING */
char kernel_ver[64];
/* room for growth w/out changing binary format */
u8 unused[128];
/* struct ath10k_tlv_dump_data + more */
u8 data[];
} __packed;
struct ath10k_dump_ram_data_hdr {
/* enum ath10k_mem_region_type */
__le32 region_type;
__le32 start;
/* length of payload data, not including this header */
__le32 length;
u8 data[];
};
/* magic number to fill the holes not copied due to sections in regions */
#define ATH10K_MAGIC_NOT_COPIED 0xAA
/* part of user space ABI */
enum ath10k_mem_region_type {
ATH10K_MEM_REGION_TYPE_REG = 1,
ATH10K_MEM_REGION_TYPE_DRAM = 2,
ATH10K_MEM_REGION_TYPE_AXI = 3,
ATH10K_MEM_REGION_TYPE_IRAM1 = 4,
ATH10K_MEM_REGION_TYPE_IRAM2 = 5,
ATH10K_MEM_REGION_TYPE_IOSRAM = 6,
ATH10K_MEM_REGION_TYPE_IOREG = 7,
ATH10K_MEM_REGION_TYPE_MSA = 8,
};
/* Define a section of the region which should be copied. As not all parts
* of the memory is possible to copy, for example some of the registers can
* be like that, sections can be used to define what is safe to copy.
*
* To minimize the size of the array, the list must obey the format:
* '{start0,stop0},{start1,stop1},{start2,stop2}....' The values below must
* also obey to 'start0 < stop0 < start1 < stop1 < start2 < ...', otherwise
* we may encouter error in the dump processing.
*/
struct ath10k_mem_section {
u32 start;
u32 end;
};
/* One region of a memory layout. If the sections field is null entire
* region is copied. If sections is non-null only the areas specified in
* sections are copied and rest of the areas are filled with
* ATH10K_MAGIC_NOT_COPIED.
*/
struct ath10k_mem_region {
enum ath10k_mem_region_type type;
u32 start;
u32 len;
const char *name;
struct {
const struct ath10k_mem_section *sections;
u32 size;
} section_table;
};
/* Contains the memory layout of a hardware version identified with the
* hardware id, split into regions.
*/
struct ath10k_hw_mem_layout {
u32 hw_id;
u32 hw_rev;
enum ath10k_bus bus;
struct {
const struct ath10k_mem_region *regions;
int size;
} region_table;
};
/* FIXME: where to put this? */
extern unsigned long ath10k_coredump_mask;
#ifdef CONFIG_DEV_COREDUMP
int ath10k_coredump_submit(struct ath10k *ar);
struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar);
int ath10k_coredump_create(struct ath10k *ar);
int ath10k_coredump_register(struct ath10k *ar);
void ath10k_coredump_unregister(struct ath10k *ar);
void ath10k_coredump_destroy(struct ath10k *ar);
const struct ath10k_hw_mem_layout *_ath10k_coredump_get_mem_layout(struct ath10k *ar);
const struct ath10k_hw_mem_layout *ath10k_coredump_get_mem_layout(struct ath10k *ar);
#else /* CONFIG_DEV_COREDUMP */
static inline int ath10k_coredump_submit(struct ath10k *ar)
{
return 0;
}
static inline struct ath10k_fw_crash_data *ath10k_coredump_new(struct ath10k *ar)
{
return NULL;
}
static inline int ath10k_coredump_create(struct ath10k *ar)
{
return 0;
}
static inline int ath10k_coredump_register(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_coredump_unregister(struct ath10k *ar)
{
}
static inline void ath10k_coredump_destroy(struct ath10k *ar)
{
}
static inline const struct ath10k_hw_mem_layout *
ath10k_coredump_get_mem_layout(struct ath10k *ar)
{
return NULL;
}
static inline const struct ath10k_hw_mem_layout *
_ath10k_coredump_get_mem_layout(struct ath10k *ar)
{
return NULL;
}
#endif /* CONFIG_DEV_COREDUMP */
#endif /* _COREDUMP_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,292 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#include <linux/types.h>
#include "trace.h"
enum ath10k_debug_mask {
ATH10K_DBG_PCI = 0x00000001,
ATH10K_DBG_WMI = 0x00000002,
ATH10K_DBG_HTC = 0x00000004,
ATH10K_DBG_HTT = 0x00000008,
ATH10K_DBG_MAC = 0x00000010,
ATH10K_DBG_BOOT = 0x00000020,
ATH10K_DBG_PCI_DUMP = 0x00000040,
ATH10K_DBG_HTT_DUMP = 0x00000080,
ATH10K_DBG_MGMT = 0x00000100,
ATH10K_DBG_DATA = 0x00000200,
ATH10K_DBG_BMI = 0x00000400,
ATH10K_DBG_REGULATORY = 0x00000800,
ATH10K_DBG_TESTMODE = 0x00001000,
ATH10K_DBG_WMI_PRINT = 0x00002000,
ATH10K_DBG_PCI_PS = 0x00004000,
ATH10K_DBG_AHB = 0x00008000,
ATH10K_DBG_SDIO = 0x00010000,
ATH10K_DBG_SDIO_DUMP = 0x00020000,
ATH10K_DBG_USB = 0x00040000,
ATH10K_DBG_USB_BULK = 0x00080000,
ATH10K_DBG_SNOC = 0x00100000,
ATH10K_DBG_QMI = 0x00200000,
ATH10K_DBG_STA = 0x00400000,
ATH10K_DBG_ANY = 0xffffffff,
};
enum ath10k_pktlog_filter {
ATH10K_PKTLOG_RX = 0x000000001,
ATH10K_PKTLOG_TX = 0x000000002,
ATH10K_PKTLOG_RCFIND = 0x000000004,
ATH10K_PKTLOG_RCUPDATE = 0x000000008,
ATH10K_PKTLOG_DBG_PRINT = 0x000000010,
ATH10K_PKTLOG_PEER_STATS = 0x000000040,
ATH10K_PKTLOG_ANY = 0x00000005f,
};
enum ath10k_dbg_aggr_mode {
ATH10K_DBG_AGGR_MODE_AUTO,
ATH10K_DBG_AGGR_MODE_MANUAL,
ATH10K_DBG_AGGR_MODE_MAX,
};
/* Types of packet log events */
enum ath_pktlog_type {
ATH_PKTLOG_TYPE_TX_CTRL = 1,
ATH_PKTLOG_TYPE_TX_STAT,
};
struct ath10k_pktlog_hdr {
__le16 flags;
__le16 missed_cnt;
__le16 log_type; /* Type of log information foll this header */
__le16 size; /* Size of variable length log information in bytes */
__le32 timestamp;
u8 payload[];
} __packed;
/* FIXME: How to calculate the buffer size sanely? */
#define ATH10K_FW_STATS_BUF_SIZE (1024 * 1024)
#define ATH10K_TX_POWER_MAX_VAL 70
#define ATH10K_TX_POWER_MIN_VAL 0
extern unsigned int ath10k_debug_mask;
__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
__printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...);
__printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...);
void ath10k_debug_print_hwfw_info(struct ath10k *ar);
void ath10k_debug_print_board_info(struct ath10k *ar);
void ath10k_debug_print_boot_info(struct ath10k *ar);
void ath10k_print_driver_info(struct ath10k *ar);
#ifdef CONFIG_ATH10K_DEBUGFS
int ath10k_debug_start(struct ath10k *ar);
void ath10k_debug_stop(struct ath10k *ar);
int ath10k_debug_create(struct ath10k *ar);
void ath10k_debug_destroy(struct ath10k *ar);
int ath10k_debug_register(struct ath10k *ar);
void ath10k_debug_unregister(struct ath10k *ar);
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
void ath10k_debug_tpc_stats_process(struct ath10k *ar,
struct ath10k_tpc_stats *tpc_stats);
void
ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
struct ath10k_tpc_stats_final *tpc_stats);
void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u32 sset, u8 *data);
int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int sset);
void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ethtool_stats *stats, u64 *data);
static inline u64 ath10k_debug_get_fw_dbglog_mask(struct ath10k *ar)
{
return ar->debug.fw_dbglog_mask;
}
static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar)
{
return ar->debug.fw_dbglog_level;
}
static inline int ath10k_debug_is_extd_tx_stats_enabled(struct ath10k *ar)
{
return ar->debug.enable_extd_tx_stats;
}
int ath10k_debug_fw_stats_request(struct ath10k *ar);
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_debug_stop(struct ath10k *ar)
{
}
static inline int ath10k_debug_create(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_debug_destroy(struct ath10k *ar)
{
}
static inline int ath10k_debug_register(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_debug_unregister(struct ath10k *ar)
{
}
static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
struct sk_buff *skb)
{
}
static inline void ath10k_debug_tpc_stats_process(struct ath10k *ar,
struct ath10k_tpc_stats *tpc_stats)
{
kfree(tpc_stats);
}
static inline void
ath10k_debug_tpc_stats_final_process(struct ath10k *ar,
struct ath10k_tpc_stats_final *tpc_stats)
{
kfree(tpc_stats);
}
static inline void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer,
int len)
{
}
static inline u64 ath10k_debug_get_fw_dbglog_mask(struct ath10k *ar)
{
return 0;
}
static inline u32 ath10k_debug_get_fw_dbglog_level(struct ath10k *ar)
{
return 0;
}
static inline int ath10k_debug_is_extd_tx_stats_enabled(struct ath10k *ar)
{
return 0;
}
static inline int ath10k_debug_fw_stats_request(struct ath10k *ar)
{
return 0;
}
#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
#define ath10k_debug_get_et_strings NULL
#define ath10k_debug_get_et_sset_count NULL
#define ath10k_debug_get_et_stats NULL
#endif /* CONFIG_ATH10K_DEBUGFS */
#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 ath10k_fw_stats *stats);
void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
unsigned long num_msdus,
enum ath10k_pkt_rx_err err,
unsigned long unchain_cnt,
unsigned long drop_cnt,
unsigned long drop_cnt_filter,
unsigned long queued_msdus);
void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar,
u16 peer_id, u8 tid,
struct htt_rx_indication_mpdu_range *ranges,
int num_ranges);
#else
static inline
void ath10k_sta_update_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats)
{
}
static inline
void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
unsigned long num_msdus,
enum ath10k_pkt_rx_err err,
unsigned long unchain_cnt,
unsigned long drop_cnt,
unsigned long drop_cnt_filter,
unsigned long queued_msdus)
{
}
static inline
void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar,
u16 peer_id, u8 tid,
struct htt_rx_indication_mpdu_range *ranges,
int num_ranges)
{
}
#endif /* CONFIG_MAC80211_DEBUGFS */
#ifdef CONFIG_ATH10K_DEBUG
__printf(3, 4) void __ath10k_dbg(struct ath10k *ar,
enum ath10k_debug_mask mask,
const char *fmt, ...);
void ath10k_dbg_dump(struct ath10k *ar,
enum ath10k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len);
#else /* CONFIG_ATH10K_DEBUG */
static inline int __ath10k_dbg(struct ath10k *ar,
enum ath10k_debug_mask dbg_mask,
const char *fmt, ...)
{
return 0;
}
static inline void ath10k_dbg_dump(struct ath10k *ar,
enum ath10k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len)
{
}
#endif /* CONFIG_ATH10K_DEBUG */
#if !defined(CONFIG_ATH10K_TRACING)
#define trace_ath10k_log_dbg_enabled() (0)
#endif
/* Avoid calling __ath10k_dbg() if debug_mask is not set and tracing
* disabled.
*/
#define ath10k_dbg(ar, dbg_mask, fmt, ...) \
do { \
if ((ath10k_debug_mask & dbg_mask) || \
trace_ath10k_log_dbg_enabled()) \
__ath10k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \
} while (0)
#endif /* _DEBUG_H_ */

View File

@ -0,0 +1,777 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#include "core.h"
#include "wmi-ops.h"
#include "txrx.h"
#include "debug.h"
static void ath10k_rx_stats_update_amsdu_subfrm(struct ath10k *ar,
struct ath10k_sta_tid_stats *stats,
u32 msdu_count)
{
if (msdu_count == 1)
stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_1]++;
else if (msdu_count == 2)
stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_2]++;
else if (msdu_count == 3)
stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_3]++;
else if (msdu_count == 4)
stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_4]++;
else if (msdu_count > 4)
stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MORE]++;
}
static void ath10k_rx_stats_update_ampdu_subfrm(struct ath10k *ar,
struct ath10k_sta_tid_stats *stats,
u32 mpdu_count)
{
if (mpdu_count <= 10)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_10]++;
else if (mpdu_count <= 20)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_20]++;
else if (mpdu_count <= 30)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_30]++;
else if (mpdu_count <= 40)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_40]++;
else if (mpdu_count <= 50)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_50]++;
else if (mpdu_count <= 60)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_60]++;
else if (mpdu_count > 60)
stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MORE]++;
}
void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid,
struct htt_rx_indication_mpdu_range *ranges,
int num_ranges)
{
struct ath10k_sta *arsta;
struct ath10k_peer *peer;
int i;
if (tid > IEEE80211_NUM_TIDS || !(ar->sta_tid_stats_mask & BIT(tid)))
return;
rcu_read_lock();
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, peer_id);
if (!peer || !peer->sta)
goto out;
arsta = (struct ath10k_sta *)peer->sta->drv_priv;
for (i = 0; i < num_ranges; i++)
ath10k_rx_stats_update_ampdu_subfrm(ar,
&arsta->tid_stats[tid],
ranges[i].mpdu_count);
out:
spin_unlock_bh(&ar->data_lock);
rcu_read_unlock();
}
void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr,
unsigned long num_msdus,
enum ath10k_pkt_rx_err err,
unsigned long unchain_cnt,
unsigned long drop_cnt,
unsigned long drop_cnt_filter,
unsigned long queued_msdus)
{
struct ieee80211_sta *sta;
struct ath10k_sta *arsta;
struct ieee80211_hdr *hdr;
struct ath10k_sta_tid_stats *stats;
u8 tid = IEEE80211_NUM_TIDS;
bool non_data_frm = false;
hdr = (struct ieee80211_hdr *)first_hdr;
if (!ieee80211_is_data(hdr->frame_control))
non_data_frm = true;
if (ieee80211_is_data_qos(hdr->frame_control))
tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
if (!(ar->sta_tid_stats_mask & BIT(tid)) || non_data_frm)
return;
rcu_read_lock();
sta = ieee80211_find_sta_by_ifaddr(ar->hw, hdr->addr2, NULL);
if (!sta)
goto exit;
arsta = (struct ath10k_sta *)sta->drv_priv;
spin_lock_bh(&ar->data_lock);
stats = &arsta->tid_stats[tid];
stats->rx_pkt_from_fw += num_msdus;
stats->rx_pkt_unchained += unchain_cnt;
stats->rx_pkt_drop_chained += drop_cnt;
stats->rx_pkt_drop_filter += drop_cnt_filter;
if (err != ATH10K_PKT_RX_ERR_MAX)
stats->rx_pkt_err[err] += queued_msdus;
stats->rx_pkt_queued_for_mac += queued_msdus;
ath10k_rx_stats_update_amsdu_subfrm(ar, &arsta->tid_stats[tid],
num_msdus);
spin_unlock_bh(&ar->data_lock);
exit:
rcu_read_unlock();
}
static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats)
{
struct ath10k_fw_extd_stats_peer *peer;
struct ieee80211_sta *sta;
struct ath10k_sta *arsta;
rcu_read_lock();
list_for_each_entry(peer, &stats->peers_extd, 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 void ath10k_sta_update_stats_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats)
{
struct ath10k_fw_stats_peer *peer;
struct ieee80211_sta *sta;
struct ath10k_sta *arsta;
rcu_read_lock();
list_for_each_entry(peer, &stats->peers, 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();
}
void ath10k_sta_update_rx_duration(struct ath10k *ar,
struct ath10k_fw_stats *stats)
{
if (stats->extended)
ath10k_sta_update_extd_stats_rx_duration(ar, stats);
else
ath10k_sta_update_stats_rx_duration(ar, stats);
}
static ssize_t ath10k_dbg_sta_read_aggr_mode(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;
struct ath10k *ar = arsta->arvif->ar;
char buf[32];
int len = 0;
mutex_lock(&ar->conf_mutex);
len = scnprintf(buf, sizeof(buf) - len, "aggregation mode: %s\n",
(arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ?
"auto" : "manual");
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath10k_dbg_sta_write_aggr_mode(struct file *file,
const 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;
struct ath10k *ar = arsta->arvif->ar;
u32 aggr_mode;
int ret;
if (kstrtouint_from_user(user_buf, count, 0, &aggr_mode))
return -EINVAL;
if (aggr_mode >= ATH10K_DBG_AGGR_MODE_MAX)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(aggr_mode == arsta->aggr_mode)) {
ret = count;
goto out;
}
ret = ath10k_wmi_addba_clear_resp(ar, arsta->arvif->vdev_id, sta->addr);
if (ret) {
ath10k_warn(ar, "failed to clear addba session ret: %d\n", ret);
goto out;
}
arsta->aggr_mode = aggr_mode;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_aggr_mode = {
.read = ath10k_dbg_sta_read_aggr_mode,
.write = ath10k_dbg_sta_write_aggr_mode,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_write_addba(struct file *file,
const 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;
struct ath10k *ar = arsta->arvif->ar;
u32 tid, buf_size;
int ret;
char buf[64] = {0};
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
user_buf, count);
if (ret <= 0)
return ret;
ret = sscanf(buf, "%u %u", &tid, &buf_size);
if (ret != 2)
return -EINVAL;
/* Valid TID values are 0 through 15 */
if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
ret = count;
goto out;
}
ret = ath10k_wmi_addba_send(ar, arsta->arvif->vdev_id, sta->addr,
tid, buf_size);
if (ret) {
ath10k_warn(ar, "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n",
arsta->arvif->vdev_id, sta->addr, tid, buf_size);
}
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_addba = {
.write = ath10k_dbg_sta_write_addba,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_write_addba_resp(struct file *file,
const 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;
struct ath10k *ar = arsta->arvif->ar;
u32 tid, status;
int ret;
char buf[64] = {0};
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
user_buf, count);
if (ret <= 0)
return ret;
ret = sscanf(buf, "%u %u", &tid, &status);
if (ret != 2)
return -EINVAL;
/* Valid TID values are 0 through 15 */
if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
ret = count;
goto out;
}
ret = ath10k_wmi_addba_set_resp(ar, arsta->arvif->vdev_id, sta->addr,
tid, status);
if (ret) {
ath10k_warn(ar, "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n",
arsta->arvif->vdev_id, sta->addr, tid, status);
}
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_addba_resp = {
.write = ath10k_dbg_sta_write_addba_resp,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_write_delba(struct file *file,
const 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;
struct ath10k *ar = arsta->arvif->ar;
u32 tid, initiator, reason;
int ret;
char buf[64] = {0};
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos,
user_buf, count);
if (ret <= 0)
return ret;
ret = sscanf(buf, "%u %u %u", &tid, &initiator, &reason);
if (ret != 3)
return -EINVAL;
/* Valid TID values are 0 through 15 */
if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if ((ar->state != ATH10K_STATE_ON) ||
(arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) {
ret = count;
goto out;
}
ret = ath10k_wmi_delba_send(ar, arsta->arvif->vdev_id, sta->addr,
tid, initiator, reason);
if (ret) {
ath10k_warn(ar, "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n",
arsta->arvif->vdev_id, sta->addr, tid, initiator,
reason);
}
ret = count;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_delba = {
.write = ath10k_dbg_sta_write_delba,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_read_peer_debug_trigger(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;
struct ath10k *ar = arsta->arvif->ar;
char buf[8];
int len = 0;
mutex_lock(&ar->conf_mutex);
len = scnprintf(buf, sizeof(buf) - len,
"Write 1 to once trigger the debug logs\n");
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t
ath10k_dbg_sta_write_peer_debug_trigger(struct file *file,
const 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;
struct ath10k *ar = arsta->arvif->ar;
u8 peer_debug_trigger;
int ret;
if (kstrtou8_from_user(user_buf, count, 0, &peer_debug_trigger))
return -EINVAL;
if (peer_debug_trigger != 1)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_ON) {
ret = -ENETDOWN;
goto out;
}
ret = ath10k_wmi_peer_set_param(ar, arsta->arvif->vdev_id, sta->addr,
ar->wmi.peer_param->debug, peer_debug_trigger);
if (ret) {
ath10k_warn(ar, "failed to set param to trigger peer tid logs for station ret: %d\n",
ret);
goto out;
}
out:
mutex_unlock(&ar->conf_mutex);
return count;
}
static const struct file_operations fops_peer_debug_trigger = {
.open = simple_open,
.read = ath10k_dbg_sta_read_peer_debug_trigger,
.write = ath10k_dbg_sta_write_peer_debug_trigger,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_read_peer_ps_state(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;
struct ath10k *ar = arsta->arvif->ar;
char buf[20];
int len = 0;
spin_lock_bh(&ar->data_lock);
len = scnprintf(buf, sizeof(buf) - len, "%d\n",
arsta->peer_ps_state);
spin_unlock_bh(&ar->data_lock);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_peer_ps_state = {
.open = simple_open,
.read = ath10k_dbg_sta_read_peer_ps_state,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static char *get_err_str(enum ath10k_pkt_rx_err i)
{
switch (i) {
case ATH10K_PKT_RX_ERR_FCS:
return "fcs_err";
case ATH10K_PKT_RX_ERR_TKIP:
return "tkip_err";
case ATH10K_PKT_RX_ERR_CRYPT:
return "crypt_err";
case ATH10K_PKT_RX_ERR_PEER_IDX_INVAL:
return "peer_idx_inval";
case ATH10K_PKT_RX_ERR_MAX:
return "unknown";
}
return "unknown";
}
static char *get_num_ampdu_subfrm_str(enum ath10k_ampdu_subfrm_num i)
{
switch (i) {
case ATH10K_AMPDU_SUBFRM_NUM_10:
return "upto 10";
case ATH10K_AMPDU_SUBFRM_NUM_20:
return "11-20";
case ATH10K_AMPDU_SUBFRM_NUM_30:
return "21-30";
case ATH10K_AMPDU_SUBFRM_NUM_40:
return "31-40";
case ATH10K_AMPDU_SUBFRM_NUM_50:
return "41-50";
case ATH10K_AMPDU_SUBFRM_NUM_60:
return "51-60";
case ATH10K_AMPDU_SUBFRM_NUM_MORE:
return ">60";
case ATH10K_AMPDU_SUBFRM_NUM_MAX:
return "0";
}
return "0";
}
static char *get_num_amsdu_subfrm_str(enum ath10k_amsdu_subfrm_num i)
{
switch (i) {
case ATH10K_AMSDU_SUBFRM_NUM_1:
return "1";
case ATH10K_AMSDU_SUBFRM_NUM_2:
return "2";
case ATH10K_AMSDU_SUBFRM_NUM_3:
return "3";
case ATH10K_AMSDU_SUBFRM_NUM_4:
return "4";
case ATH10K_AMSDU_SUBFRM_NUM_MORE:
return ">4";
case ATH10K_AMSDU_SUBFRM_NUM_MAX:
return "0";
}
return "0";
}
#define PRINT_TID_STATS(_field, _tabs) \
do { \
int k = 0; \
for (j = 0; j <= IEEE80211_NUM_TIDS; j++) { \
if (ar->sta_tid_stats_mask & BIT(j)) { \
len += scnprintf(buf + len, buf_len - len, \
"[%02d] %-10lu ", \
j, stats[j]._field); \
k++; \
if (k % 8 == 0) { \
len += scnprintf(buf + len, \
buf_len - len, "\n"); \
len += scnprintf(buf + len, \
buf_len - len, \
_tabs); \
} \
} \
} \
len += scnprintf(buf + len, buf_len - len, "\n"); \
} while (0)
static ssize_t ath10k_dbg_sta_read_tid_stats(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;
struct ath10k *ar = arsta->arvif->ar;
struct ath10k_sta_tid_stats *stats = arsta->tid_stats;
size_t len = 0, buf_len = 1048 * IEEE80211_NUM_TIDS;
char *buf;
int i, j;
ssize_t ret;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
len += scnprintf(buf + len, buf_len - len,
"\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n");
len += scnprintf(buf + len, buf_len - len,
"\t\t------------------------------------------\n");
len += scnprintf(buf + len, buf_len - len, "MSDUs from FW\t\t\t");
PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t");
len += scnprintf(buf + len, buf_len - len, "MSDUs unchained\t\t\t");
PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t");
len += scnprintf(buf + len, buf_len - len,
"MSDUs locally dropped:chained\t");
PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t");
len += scnprintf(buf + len, buf_len - len,
"MSDUs locally dropped:filtered\t");
PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t");
len += scnprintf(buf + len, buf_len - len,
"MSDUs queued for mac80211\t");
PRINT_TID_STATS(rx_pkt_queued_for_mac, "\t\t\t\t");
for (i = 0; i < ATH10K_PKT_RX_ERR_MAX; i++) {
len += scnprintf(buf + len, buf_len - len,
"MSDUs with error:%s\t", get_err_str(i));
PRINT_TID_STATS(rx_pkt_err[i], "\t\t\t\t");
}
len += scnprintf(buf + len, buf_len - len, "\n");
for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) {
len += scnprintf(buf + len, buf_len - len,
"A-MPDU num subframes %s\t",
get_num_ampdu_subfrm_str(i));
PRINT_TID_STATS(rx_pkt_ampdu[i], "\t\t\t\t");
}
len += scnprintf(buf + len, buf_len - len, "\n");
for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) {
len += scnprintf(buf + len, buf_len - len,
"A-MSDU num subframes %s\t\t",
get_num_amsdu_subfrm_str(i));
PRINT_TID_STATS(rx_pkt_amsdu[i], "\t\t\t\t");
}
spin_unlock_bh(&ar->data_lock);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct file_operations fops_tid_stats_dump = {
.open = simple_open,
.read = ath10k_dbg_sta_read_tid_stats,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t ath10k_dbg_sta_dump_tx_stats(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;
struct ath10k *ar = arsta->arvif->ar;
struct ath10k_htt_data_stats *stats;
const char *str_name[ATH10K_STATS_TYPE_MAX] = {"succ", "fail",
"retry", "ampdu"};
const char *str[ATH10K_COUNTER_TYPE_MAX] = {"bytes", "packets"};
int len = 0, i, j, k, retval = 0;
const int size = 16 * 4096;
char *buf;
buf = kzalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&ar->conf_mutex);
if (!arsta->tx_stats) {
ath10k_warn(ar, "failed to get tx stats");
mutex_unlock(&ar->conf_mutex);
kfree(buf);
return 0;
}
spin_lock_bh(&ar->data_lock);
for (k = 0; k < ATH10K_STATS_TYPE_MAX; k++) {
for (j = 0; j < ATH10K_COUNTER_TYPE_MAX; j++) {
stats = &arsta->tx_stats->stats[k];
len += scnprintf(buf + len, size - len, "%s_%s\n",
str_name[k],
str[j]);
len += scnprintf(buf + len, size - len,
" VHT MCS %s\n",
str[j]);
for (i = 0; i < ATH10K_VHT_MCS_NUM; i++)
len += scnprintf(buf + len, size - len,
" %llu ",
stats->vht[j][i]);
len += scnprintf(buf + len, size - len, "\n");
len += scnprintf(buf + len, size - len, " HT MCS %s\n",
str[j]);
for (i = 0; i < ATH10K_HT_MCS_NUM; i++)
len += scnprintf(buf + len, size - len,
" %llu ", stats->ht[j][i]);
len += scnprintf(buf + len, size - len, "\n");
len += scnprintf(buf + len, size - len,
" BW %s (20,5,10,40,80,160 MHz)\n", str[j]);
len += scnprintf(buf + len, size - len,
" %llu %llu %llu %llu %llu %llu\n",
stats->bw[j][0], stats->bw[j][1],
stats->bw[j][2], stats->bw[j][3],
stats->bw[j][4], stats->bw[j][5]);
len += scnprintf(buf + len, size - len,
" NSS %s (1x1,2x2,3x3,4x4)\n", str[j]);
len += scnprintf(buf + len, size - len,
" %llu %llu %llu %llu\n",
stats->nss[j][0], stats->nss[j][1],
stats->nss[j][2], stats->nss[j][3]);
len += scnprintf(buf + len, size - len,
" GI %s (LGI,SGI)\n",
str[j]);
len += scnprintf(buf + len, size - len, " %llu %llu\n",
stats->gi[j][0], stats->gi[j][1]);
len += scnprintf(buf + len, size - len,
" legacy rate %s (1,2 ... Mbps)\n ",
str[j]);
for (i = 0; i < ATH10K_LEGACY_NUM; i++)
len += scnprintf(buf + len, size - len, "%llu ",
stats->legacy[j][i]);
len += scnprintf(buf + len, size - len, "\n");
len += scnprintf(buf + len, size - len,
" Rate table %s (1,2 ... Mbps)\n ",
str[j]);
for (i = 0; i < ATH10K_RATE_TABLE_NUM; i++) {
len += scnprintf(buf + len, size - len, "%llu ",
stats->rate_table[j][i]);
if (!((i + 1) % 8))
len +=
scnprintf(buf + len, size - len, "\n ");
}
}
}
len += scnprintf(buf + len, size - len,
"\nTX duration\n %llu usecs\n",
arsta->tx_stats->tx_duration);
len += scnprintf(buf + len, size - len,
"BA fails\n %llu\n", arsta->tx_stats->ba_fails);
len += scnprintf(buf + len, size - len,
"ack fails\n %llu\n", arsta->tx_stats->ack_fails);
spin_unlock_bh(&ar->data_lock);
if (len > size)
len = size;
retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
mutex_unlock(&ar->conf_mutex);
return retval;
}
static const struct file_operations fops_tx_stats = {
.read = ath10k_dbg_sta_dump_tx_stats,
.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)
{
struct ath10k *ar = hw->priv;
debugfs_create_file("aggr_mode", 0644, dir, sta, &fops_aggr_mode);
debugfs_create_file("addba", 0200, dir, sta, &fops_addba);
debugfs_create_file("addba_resp", 0200, dir, sta, &fops_addba_resp);
debugfs_create_file("delba", 0200, dir, sta, &fops_delba);
debugfs_create_file("peer_debug_trigger", 0600, dir, sta,
&fops_peer_debug_trigger);
debugfs_create_file("dump_tid_stats", 0400, dir, sta,
&fops_tid_stats_dump);
if (ath10k_peer_stats_enabled(ar) &&
ath10k_debug_is_extd_tx_stats_enabled(ar))
debugfs_create_file("tx_stats", 0400, dir, sta,
&fops_tx_stats);
debugfs_create_file("peer_ps_state", 0400, dir, sta,
&fops_peer_ps_state);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
/*
* 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 _FWLOG_
#define _FWLOG_
void ath10k_fwlog_register(struct ath10k *ar);
void ath10k_fwlog_unregister(struct ath10k *ar);
void ath10k_handle_fwlog_msg(struct ath10k *ar, struct sk_buff *skb);
#endif /* _FWLOG_ */

View File

@ -0,0 +1,258 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2015,2017 Qualcomm Atheros, Inc.
*/
#ifndef _HIF_H_
#define _HIF_H_
#include <linux/kernel.h>
#include "core.h"
#include "bmi.h"
#include "debug.h"
/* Types of fw logging mode */
enum ath_dbg_mode {
ATH10K_ENABLE_FW_LOG_DIAG,
ATH10K_ENABLE_FW_LOG_CE,
};
struct ath10k_hif_sg_item {
u16 transfer_id;
void *transfer_context; /* NULL = tx completion callback not called */
void *vaddr; /* for debugging mostly */
dma_addr_t paddr;
u16 len;
};
struct ath10k_hif_ops {
/* send a scatter-gather list to the target */
int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
struct ath10k_hif_sg_item *items, int n_items);
/* read firmware memory through the diagnose interface */
int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
size_t buf_len);
int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
int nbytes);
/*
* API to handle HIF-specific BMI message exchanges, this API is
* synchronous and only allowed to be called from a context that
* can block (sleep)
*/
int (*exchange_bmi_msg)(struct ath10k *ar,
void *request, u32 request_len,
void *response, u32 *response_len);
/* Post BMI phase, after FW is loaded. Starts regular operation */
int (*start)(struct ath10k *ar);
/* Clean up what start() did. This does not revert to BMI phase. If
* desired so, call power_down() and power_up()
*/
void (*stop)(struct ath10k *ar);
int (*start_post)(struct ath10k *ar);
int (*get_htt_tx_complete)(struct ath10k *ar);
int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe);
/*
* Check if prior sends have completed.
*
* Check whether the pipe in question has any completed
* sends that have not yet been processed.
* This function is only relevant for HIF pipes that are configured
* to be polled rather than interrupt-driven.
*/
void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
u32 (*read32)(struct ath10k *ar, u32 address);
void (*write32)(struct ath10k *ar, u32 address, u32 value);
/* Power up the device and enter BMI transfer mode for FW download */
int (*power_up)(struct ath10k *ar, enum ath10k_firmware_mode fw_mode);
/* Power down the device and free up resources. stop() must be called
* before this if start() was called earlier
*/
void (*power_down)(struct ath10k *ar);
int (*suspend)(struct ath10k *ar);
int (*resume)(struct ath10k *ar);
/* fetch calibration data from target eeprom */
int (*fetch_cal_eeprom)(struct ath10k *ar, void **data,
size_t *data_len);
int (*get_target_info)(struct ath10k *ar,
struct bmi_target_info *target_info);
int (*set_target_log_mode)(struct ath10k *ar, u8 fw_log_mode);
};
static inline int ath10k_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
struct ath10k_hif_sg_item *items,
int n_items)
{
return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items);
}
static inline int ath10k_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
size_t buf_len)
{
return ar->hif.ops->diag_read(ar, address, buf, buf_len);
}
static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
const void *data, int nbytes)
{
if (!ar->hif.ops->diag_write)
return -EOPNOTSUPP;
return ar->hif.ops->diag_write(ar, address, data, nbytes);
}
static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
void *request, u32 request_len,
void *response, u32 *response_len)
{
return ar->hif.ops->exchange_bmi_msg(ar, request, request_len,
response, response_len);
}
static inline int ath10k_hif_start(struct ath10k *ar)
{
return ar->hif.ops->start(ar);
}
static inline void ath10k_hif_stop(struct ath10k *ar)
{
return ar->hif.ops->stop(ar);
}
static inline int ath10k_hif_start_post(struct ath10k *ar)
{
if (ar->hif.ops->start_post)
return ar->hif.ops->start_post(ar);
return 0;
}
static inline int ath10k_hif_get_htt_tx_complete(struct ath10k *ar)
{
if (ar->hif.ops->get_htt_tx_complete)
return ar->hif.ops->get_htt_tx_complete(ar);
return 0;
}
static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
u16 service_id,
u8 *ul_pipe, u8 *dl_pipe)
{
return ar->hif.ops->map_service_to_pipe(ar, service_id,
ul_pipe, dl_pipe);
}
static inline void ath10k_hif_get_default_pipe(struct ath10k *ar,
u8 *ul_pipe, u8 *dl_pipe)
{
ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe);
}
static inline void ath10k_hif_send_complete_check(struct ath10k *ar,
u8 pipe_id, int force)
{
if (ar->hif.ops->send_complete_check)
ar->hif.ops->send_complete_check(ar, pipe_id, force);
}
static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
u8 pipe_id)
{
return ar->hif.ops->get_free_queue_number(ar, pipe_id);
}
static inline int ath10k_hif_power_up(struct ath10k *ar,
enum ath10k_firmware_mode fw_mode)
{
return ar->hif.ops->power_up(ar, fw_mode);
}
static inline void ath10k_hif_power_down(struct ath10k *ar)
{
ar->hif.ops->power_down(ar);
}
static inline int ath10k_hif_suspend(struct ath10k *ar)
{
if (!ar->hif.ops->suspend)
return -EOPNOTSUPP;
return ar->hif.ops->suspend(ar);
}
static inline int ath10k_hif_resume(struct ath10k *ar)
{
if (!ar->hif.ops->resume)
return -EOPNOTSUPP;
return ar->hif.ops->resume(ar);
}
static inline u32 ath10k_hif_read32(struct ath10k *ar, u32 address)
{
if (!ar->hif.ops->read32) {
ath10k_warn(ar, "hif read32 not supported\n");
return 0xdeaddead;
}
return ar->hif.ops->read32(ar, address);
}
static inline void ath10k_hif_write32(struct ath10k *ar,
u32 address, u32 data)
{
if (!ar->hif.ops->write32) {
ath10k_warn(ar, "hif write32 not supported\n");
return;
}
ar->hif.ops->write32(ar, address, data);
}
static inline int ath10k_hif_fetch_cal_eeprom(struct ath10k *ar,
void **data,
size_t *data_len)
{
if (!ar->hif.ops->fetch_cal_eeprom)
return -EOPNOTSUPP;
return ar->hif.ops->fetch_cal_eeprom(ar, data, data_len);
}
static inline int ath10k_hif_get_target_info(struct ath10k *ar,
struct bmi_target_info *tgt_info)
{
if (!ar->hif.ops->get_target_info)
return -EOPNOTSUPP;
return ar->hif.ops->get_target_info(ar, tgt_info);
}
static inline int ath10k_hif_set_target_log_mode(struct ath10k *ar,
u8 fw_log_mode)
{
if (!ar->hif.ops->set_target_log_mode)
return -EOPNOTSUPP;
return ar->hif.ops->set_target_log_mode(ar, fw_log_mode);
}
#endif /* _HIF_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,435 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2016 Qualcomm Atheros, Inc.
*/
#ifndef _HTC_H_
#define _HTC_H_
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/bug.h>
#include <linux/skbuff.h>
#include <linux/timer.h>
#include <linux/bitfield.h>
struct ath10k;
/****************/
/* HTC protocol */
/****************/
/*
* HTC - host-target control protocol
*
* tx packets are generally <htc_hdr><payload>
* rx packets are more complex: <htc_hdr><payload><trailer>
*
* The payload + trailer length is stored in len.
* To get payload-only length one needs to payload - trailer_len.
*
* Trailer contains (possibly) multiple <htc_record>.
* Each record is a id-len-value.
*
* HTC header flags, control_byte0, control_byte1
* have different meaning depending whether its tx
* or rx.
*
* Alignment: htc_hdr, payload and trailer are
* 4-byte aligned.
*/
#define HTC_HOST_MAX_MSG_PER_RX_BUNDLE 32
enum ath10k_htc_tx_flags {
ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
ATH10K_HTC_FLAG_SEND_BUNDLE = 0x02
};
enum ath10k_htc_rx_flags {
ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK = 0x01,
ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02,
};
#define ATH10K_HTC_FLAG_BUNDLE_MASK GENMASK(7, 4)
/* bits 2-3 are for extra bundle count bits 4-5 */
#define ATH10K_HTC_BUNDLE_EXTRA_MASK GENMASK(3, 2)
#define ATH10K_HTC_BUNDLE_EXTRA_SHIFT 4
static inline unsigned int ath10k_htc_get_bundle_count(u8 max_msgs, u8 flags)
{
unsigned int count, extra_count = 0;
count = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, flags);
if (max_msgs > 16)
extra_count = FIELD_GET(ATH10K_HTC_BUNDLE_EXTRA_MASK, flags) <<
ATH10K_HTC_BUNDLE_EXTRA_SHIFT;
return count + extra_count;
}
struct ath10k_htc_hdr {
u8 eid; /* @enum ath10k_htc_ep_id */
u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */
__le16 len;
union {
u8 trailer_len; /* for rx */
u8 control_byte0;
} __packed;
union {
u8 seq_no; /* for tx */
u8 control_byte1;
} __packed;
union {
__le16 pad_len;
struct {
u8 pad0;
u8 pad1;
} __packed;
} __packed;
} __packed __aligned(4);
enum ath10k_ath10k_htc_msg_id {
ATH10K_HTC_MSG_READY_ID = 1,
ATH10K_HTC_MSG_CONNECT_SERVICE_ID = 2,
ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
ATH10K_HTC_MSG_SETUP_COMPLETE_ID = 4,
ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5,
ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6
};
enum ath10k_htc_version {
ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
};
enum ath10k_htc_conn_flags {
ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0,
ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1,
ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2,
ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3,
#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3
ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 1 << 2,
ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3
#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00
#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB 8
};
#define ATH10K_HTC_MSG_READY_EXT_ALT_DATA_MASK 0xFFF
enum ath10k_htc_conn_svc_status {
ATH10K_HTC_CONN_SVC_STATUS_SUCCESS = 0,
ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1,
ATH10K_HTC_CONN_SVC_STATUS_FAILED = 2,
ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4
};
#define ATH10K_MAX_MSG_PER_HTC_TX_BUNDLE 32
#define ATH10K_MIN_MSG_PER_HTC_TX_BUNDLE 2
#define ATH10K_MIN_CREDIT_PER_HTC_TX_BUNDLE 2
enum ath10k_htc_setup_complete_flags {
ATH10K_HTC_SETUP_COMPLETE_FLAGS_RX_BNDL_EN = 1
};
struct ath10k_ath10k_htc_msg_hdr {
__le16 message_id; /* @enum htc_message_id */
} __packed;
struct ath10k_htc_unknown {
u8 pad0;
u8 pad1;
} __packed;
struct ath10k_htc_ready {
__le16 credit_count;
__le16 credit_size;
u8 max_endpoints;
u8 pad0;
} __packed;
struct ath10k_htc_ready_extended {
struct ath10k_htc_ready base;
u8 htc_version; /* @enum ath10k_htc_version */
u8 max_msgs_per_htc_bundle;
union {
__le16 reserved;
struct {
u8 pad0;
u8 pad1;
} __packed;
} __packed;
} __packed;
struct ath10k_htc_conn_svc {
__le16 service_id;
__le16 flags; /* @enum ath10k_htc_conn_flags */
u8 pad0;
u8 pad1;
} __packed;
struct ath10k_htc_conn_svc_response {
__le16 service_id;
u8 status; /* @enum ath10k_htc_conn_svc_status */
u8 eid;
__le16 max_msg_size;
} __packed;
struct ath10k_htc_setup_complete_extended {
u8 pad0;
u8 pad1;
__le32 flags; /* @enum htc_setup_complete_flags */
u8 max_msgs_per_bundled_recv;
u8 pad2;
u8 pad3;
u8 pad4;
} __packed;
struct ath10k_htc_msg {
struct ath10k_ath10k_htc_msg_hdr hdr;
union {
/* host-to-target */
struct ath10k_htc_conn_svc connect_service;
struct ath10k_htc_ready ready;
struct ath10k_htc_ready_extended ready_ext;
struct ath10k_htc_unknown unknown;
struct ath10k_htc_setup_complete_extended setup_complete_ext;
/* target-to-host */
struct ath10k_htc_conn_svc_response connect_service_response;
};
} __packed __aligned(4);
enum ath10k_ath10k_htc_record_id {
ATH10K_HTC_RECORD_NULL = 0,
ATH10K_HTC_RECORD_CREDITS = 1,
ATH10K_HTC_RECORD_LOOKAHEAD = 2,
ATH10K_HTC_RECORD_LOOKAHEAD_BUNDLE = 3,
};
struct ath10k_ath10k_htc_record_hdr {
u8 id; /* @enum ath10k_ath10k_htc_record_id */
u8 len;
u8 pad0;
u8 pad1;
} __packed;
struct ath10k_htc_credit_report {
u8 eid; /* @enum ath10k_htc_ep_id */
u8 credits;
u8 pad0;
u8 pad1;
} __packed;
struct ath10k_htc_lookahead_report {
u8 pre_valid;
u8 pad0;
u8 pad1;
u8 pad2;
u8 lookahead[4];
u8 post_valid;
u8 pad3;
u8 pad4;
u8 pad5;
} __packed;
struct ath10k_htc_lookahead_bundle {
u8 lookahead[4];
} __packed;
struct ath10k_htc_record {
struct ath10k_ath10k_htc_record_hdr hdr;
union {
struct ath10k_htc_credit_report credit_report[0];
struct ath10k_htc_lookahead_report lookahead_report[0];
struct ath10k_htc_lookahead_bundle lookahead_bundle[0];
u8 pauload[0];
};
} __packed __aligned(4);
/*
* note: the trailer offset is dynamic depending
* on payload length. this is only a struct layout draft
*/
struct ath10k_htc_frame {
struct ath10k_htc_hdr hdr;
union {
struct ath10k_htc_msg msg;
u8 payload[0];
};
struct ath10k_htc_record trailer[0];
} __packed __aligned(4);
/*******************/
/* Host-side stuff */
/*******************/
enum ath10k_htc_svc_gid {
ATH10K_HTC_SVC_GRP_RSVD = 0,
ATH10K_HTC_SVC_GRP_WMI = 1,
ATH10K_HTC_SVC_GRP_NMI = 2,
ATH10K_HTC_SVC_GRP_HTT = 3,
ATH10K_LOG_SERVICE_GROUP = 6,
ATH10K_HTC_SVC_GRP_TEST = 254,
ATH10K_HTC_SVC_GRP_LAST = 255,
};
#define SVC(group, idx) \
(int)(((int)(group) << 8) | (int)(idx))
enum ath10k_htc_svc_id {
/* NOTE: service ID of 0x0000 is reserved and should never be used */
ATH10K_HTC_SVC_ID_RESERVED = 0x0000,
ATH10K_HTC_SVC_ID_UNUSED = ATH10K_HTC_SVC_ID_RESERVED,
ATH10K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH10K_HTC_SVC_GRP_RSVD, 1),
ATH10K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH10K_HTC_SVC_GRP_WMI, 0),
ATH10K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH10K_HTC_SVC_GRP_WMI, 1),
ATH10K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH10K_HTC_SVC_GRP_WMI, 2),
ATH10K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH10K_HTC_SVC_GRP_WMI, 3),
ATH10K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH10K_HTC_SVC_GRP_WMI, 4),
ATH10K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH10K_HTC_SVC_GRP_NMI, 0),
ATH10K_HTC_SVC_ID_NMI_DATA = SVC(ATH10K_HTC_SVC_GRP_NMI, 1),
ATH10K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 0),
ATH10K_HTC_SVC_ID_HTT_DATA2_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 1),
ATH10K_HTC_SVC_ID_HTT_DATA3_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 2),
ATH10K_HTC_SVC_ID_HTT_LOG_MSG = SVC(ATH10K_LOG_SERVICE_GROUP, 0),
/* raw stream service (i.e. flash, tcmd, calibration apps) */
ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0),
};
#undef SVC
enum ath10k_htc_ep_id {
ATH10K_HTC_EP_UNUSED = -1,
ATH10K_HTC_EP_0 = 0,
ATH10K_HTC_EP_1 = 1,
ATH10K_HTC_EP_2,
ATH10K_HTC_EP_3,
ATH10K_HTC_EP_4,
ATH10K_HTC_EP_5,
ATH10K_HTC_EP_6,
ATH10K_HTC_EP_7,
ATH10K_HTC_EP_8,
ATH10K_HTC_EP_COUNT,
};
struct ath10k_htc_ops {
void (*target_send_suspend_complete)(struct ath10k *ar);
};
struct ath10k_htc_ep_ops {
void (*ep_tx_complete)(struct ath10k *, struct sk_buff *);
void (*ep_rx_complete)(struct ath10k *, struct sk_buff *);
void (*ep_tx_credits)(struct ath10k *);
};
/* service connection information */
struct ath10k_htc_svc_conn_req {
u16 service_id;
struct ath10k_htc_ep_ops ep_ops;
int max_send_queue_depth;
};
/* service connection response information */
struct ath10k_htc_svc_conn_resp {
u8 buffer_len;
u8 actual_len;
enum ath10k_htc_ep_id eid;
unsigned int max_msg_len;
u8 connect_resp_code;
};
#define ATH10K_NUM_CONTROL_TX_BUFFERS 2
#define ATH10K_HTC_MAX_LEN 4096
#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256
#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1 * HZ)
#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \
sizeof(struct ath10k_htc_hdr))
#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1 * HZ)
struct ath10k_htc_ep {
struct ath10k_htc *htc;
enum ath10k_htc_ep_id eid;
enum ath10k_htc_svc_id service_id;
struct ath10k_htc_ep_ops ep_ops;
int max_tx_queue_depth;
int max_ep_message_len;
u8 ul_pipe_id;
u8 dl_pipe_id;
u8 seq_no; /* for debugging */
int tx_credits;
int tx_credit_size;
bool tx_credit_flow_enabled;
bool bundle_tx;
struct sk_buff_head tx_req_head;
struct sk_buff_head tx_complete_head;
};
struct ath10k_htc_svc_tx_credits {
u16 service_id;
u8 credit_allocation;
};
struct ath10k_htc {
struct ath10k *ar;
struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
/* protects endpoints */
spinlock_t tx_lock;
struct ath10k_htc_ops htc_ops;
u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN];
int control_resp_len;
struct completion ctl_resp;
int total_transmit_credits;
int target_credit_size;
u8 max_msgs_per_htc_bundle;
int alt_data_credit_size;
};
int ath10k_htc_init(struct ath10k *ar);
int ath10k_htc_wait_target(struct ath10k_htc *htc);
void ath10k_htc_setup_tx_req(struct ath10k_htc_ep *ep);
int ath10k_htc_start(struct ath10k_htc *htc);
int ath10k_htc_connect_service(struct ath10k_htc *htc,
struct ath10k_htc_svc_conn_req *conn_req,
struct ath10k_htc_svc_conn_resp *conn_resp);
void ath10k_htc_change_tx_credit_flow(struct ath10k_htc *htc,
enum ath10k_htc_ep_id eid,
bool enable);
int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
struct sk_buff *packet);
void ath10k_htc_stop_hl(struct ath10k *ar);
int ath10k_htc_send_hl(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
struct sk_buff *packet);
struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size);
void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
struct sk_buff *skb);
int ath10k_htc_process_trailer(struct ath10k_htc *htc,
u8 *buffer,
int length,
enum ath10k_htc_ep_id src_eid,
void *next_lookaheads,
int *next_lookaheads_len);
#endif

View File

@ -0,0 +1,436 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
*/
#include <linux/slab.h>
#include <linux/if_ether.h>
#include "htt.h"
#include "core.h"
#include "debug.h"
#include "hif.h"
static const enum htt_t2h_msg_type htt_main_t2h_msg_types[] = {
[HTT_MAIN_T2H_MSG_TYPE_VERSION_CONF] = HTT_T2H_MSG_TYPE_VERSION_CONF,
[HTT_MAIN_T2H_MSG_TYPE_RX_IND] = HTT_T2H_MSG_TYPE_RX_IND,
[HTT_MAIN_T2H_MSG_TYPE_RX_FLUSH] = HTT_T2H_MSG_TYPE_RX_FLUSH,
[HTT_MAIN_T2H_MSG_TYPE_PEER_MAP] = HTT_T2H_MSG_TYPE_PEER_MAP,
[HTT_MAIN_T2H_MSG_TYPE_PEER_UNMAP] = HTT_T2H_MSG_TYPE_PEER_UNMAP,
[HTT_MAIN_T2H_MSG_TYPE_RX_ADDBA] = HTT_T2H_MSG_TYPE_RX_ADDBA,
[HTT_MAIN_T2H_MSG_TYPE_RX_DELBA] = HTT_T2H_MSG_TYPE_RX_DELBA,
[HTT_MAIN_T2H_MSG_TYPE_TX_COMPL_IND] = HTT_T2H_MSG_TYPE_TX_COMPL_IND,
[HTT_MAIN_T2H_MSG_TYPE_PKTLOG] = HTT_T2H_MSG_TYPE_PKTLOG,
[HTT_MAIN_T2H_MSG_TYPE_STATS_CONF] = HTT_T2H_MSG_TYPE_STATS_CONF,
[HTT_MAIN_T2H_MSG_TYPE_RX_FRAG_IND] = HTT_T2H_MSG_TYPE_RX_FRAG_IND,
[HTT_MAIN_T2H_MSG_TYPE_SEC_IND] = HTT_T2H_MSG_TYPE_SEC_IND,
[HTT_MAIN_T2H_MSG_TYPE_TX_INSPECT_IND] =
HTT_T2H_MSG_TYPE_TX_INSPECT_IND,
[HTT_MAIN_T2H_MSG_TYPE_MGMT_TX_COMPL_IND] =
HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION,
[HTT_MAIN_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND] =
HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND,
[HTT_MAIN_T2H_MSG_TYPE_RX_PN_IND] = HTT_T2H_MSG_TYPE_RX_PN_IND,
[HTT_MAIN_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND] =
HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND,
[HTT_MAIN_T2H_MSG_TYPE_TEST] = HTT_T2H_MSG_TYPE_TEST,
};
static const enum htt_t2h_msg_type htt_10x_t2h_msg_types[] = {
[HTT_10X_T2H_MSG_TYPE_VERSION_CONF] = HTT_T2H_MSG_TYPE_VERSION_CONF,
[HTT_10X_T2H_MSG_TYPE_RX_IND] = HTT_T2H_MSG_TYPE_RX_IND,
[HTT_10X_T2H_MSG_TYPE_RX_FLUSH] = HTT_T2H_MSG_TYPE_RX_FLUSH,
[HTT_10X_T2H_MSG_TYPE_PEER_MAP] = HTT_T2H_MSG_TYPE_PEER_MAP,
[HTT_10X_T2H_MSG_TYPE_PEER_UNMAP] = HTT_T2H_MSG_TYPE_PEER_UNMAP,
[HTT_10X_T2H_MSG_TYPE_RX_ADDBA] = HTT_T2H_MSG_TYPE_RX_ADDBA,
[HTT_10X_T2H_MSG_TYPE_RX_DELBA] = HTT_T2H_MSG_TYPE_RX_DELBA,
[HTT_10X_T2H_MSG_TYPE_TX_COMPL_IND] = HTT_T2H_MSG_TYPE_TX_COMPL_IND,
[HTT_10X_T2H_MSG_TYPE_PKTLOG] = HTT_T2H_MSG_TYPE_PKTLOG,
[HTT_10X_T2H_MSG_TYPE_STATS_CONF] = HTT_T2H_MSG_TYPE_STATS_CONF,
[HTT_10X_T2H_MSG_TYPE_RX_FRAG_IND] = HTT_T2H_MSG_TYPE_RX_FRAG_IND,
[HTT_10X_T2H_MSG_TYPE_SEC_IND] = HTT_T2H_MSG_TYPE_SEC_IND,
[HTT_10X_T2H_MSG_TYPE_RC_UPDATE_IND] = HTT_T2H_MSG_TYPE_RC_UPDATE_IND,
[HTT_10X_T2H_MSG_TYPE_TX_INSPECT_IND] = HTT_T2H_MSG_TYPE_TX_INSPECT_IND,
[HTT_10X_T2H_MSG_TYPE_TEST] = HTT_T2H_MSG_TYPE_TEST,
[HTT_10X_T2H_MSG_TYPE_CHAN_CHANGE] = HTT_T2H_MSG_TYPE_CHAN_CHANGE,
[HTT_10X_T2H_MSG_TYPE_AGGR_CONF] = HTT_T2H_MSG_TYPE_AGGR_CONF,
[HTT_10X_T2H_MSG_TYPE_STATS_NOUPLOAD] = HTT_T2H_MSG_TYPE_STATS_NOUPLOAD,
[HTT_10X_T2H_MSG_TYPE_MGMT_TX_COMPL_IND] =
HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION,
};
static const enum htt_t2h_msg_type htt_tlv_t2h_msg_types[] = {
[HTT_TLV_T2H_MSG_TYPE_VERSION_CONF] = HTT_T2H_MSG_TYPE_VERSION_CONF,
[HTT_TLV_T2H_MSG_TYPE_RX_IND] = HTT_T2H_MSG_TYPE_RX_IND,
[HTT_TLV_T2H_MSG_TYPE_RX_FLUSH] = HTT_T2H_MSG_TYPE_RX_FLUSH,
[HTT_TLV_T2H_MSG_TYPE_PEER_MAP] = HTT_T2H_MSG_TYPE_PEER_MAP,
[HTT_TLV_T2H_MSG_TYPE_PEER_UNMAP] = HTT_T2H_MSG_TYPE_PEER_UNMAP,
[HTT_TLV_T2H_MSG_TYPE_RX_ADDBA] = HTT_T2H_MSG_TYPE_RX_ADDBA,
[HTT_TLV_T2H_MSG_TYPE_RX_DELBA] = HTT_T2H_MSG_TYPE_RX_DELBA,
[HTT_TLV_T2H_MSG_TYPE_TX_COMPL_IND] = HTT_T2H_MSG_TYPE_TX_COMPL_IND,
[HTT_TLV_T2H_MSG_TYPE_PKTLOG] = HTT_T2H_MSG_TYPE_PKTLOG,
[HTT_TLV_T2H_MSG_TYPE_STATS_CONF] = HTT_T2H_MSG_TYPE_STATS_CONF,
[HTT_TLV_T2H_MSG_TYPE_RX_FRAG_IND] = HTT_T2H_MSG_TYPE_RX_FRAG_IND,
[HTT_TLV_T2H_MSG_TYPE_SEC_IND] = HTT_T2H_MSG_TYPE_SEC_IND,
[HTT_TLV_T2H_MSG_TYPE_RC_UPDATE_IND] = HTT_T2H_MSG_TYPE_RC_UPDATE_IND,
[HTT_TLV_T2H_MSG_TYPE_TX_INSPECT_IND] = HTT_T2H_MSG_TYPE_TX_INSPECT_IND,
[HTT_TLV_T2H_MSG_TYPE_MGMT_TX_COMPL_IND] =
HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION,
[HTT_TLV_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND] =
HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND,
[HTT_TLV_T2H_MSG_TYPE_RX_PN_IND] = HTT_T2H_MSG_TYPE_RX_PN_IND,
[HTT_TLV_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND] =
HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND,
[HTT_TLV_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND] =
HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND,
[HTT_TLV_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE] =
HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE,
[HTT_TLV_T2H_MSG_TYPE_CHAN_CHANGE] = HTT_T2H_MSG_TYPE_CHAN_CHANGE,
[HTT_TLV_T2H_MSG_TYPE_RX_OFLD_PKT_ERR] =
HTT_T2H_MSG_TYPE_RX_OFLD_PKT_ERR,
[HTT_TLV_T2H_MSG_TYPE_TEST] = HTT_T2H_MSG_TYPE_TEST,
};
static const enum htt_t2h_msg_type htt_10_4_t2h_msg_types[] = {
[HTT_10_4_T2H_MSG_TYPE_VERSION_CONF] = HTT_T2H_MSG_TYPE_VERSION_CONF,
[HTT_10_4_T2H_MSG_TYPE_RX_IND] = HTT_T2H_MSG_TYPE_RX_IND,
[HTT_10_4_T2H_MSG_TYPE_RX_FLUSH] = HTT_T2H_MSG_TYPE_RX_FLUSH,
[HTT_10_4_T2H_MSG_TYPE_PEER_MAP] = HTT_T2H_MSG_TYPE_PEER_MAP,
[HTT_10_4_T2H_MSG_TYPE_PEER_UNMAP] = HTT_T2H_MSG_TYPE_PEER_UNMAP,
[HTT_10_4_T2H_MSG_TYPE_RX_ADDBA] = HTT_T2H_MSG_TYPE_RX_ADDBA,
[HTT_10_4_T2H_MSG_TYPE_RX_DELBA] = HTT_T2H_MSG_TYPE_RX_DELBA,
[HTT_10_4_T2H_MSG_TYPE_TX_COMPL_IND] = HTT_T2H_MSG_TYPE_TX_COMPL_IND,
[HTT_10_4_T2H_MSG_TYPE_PKTLOG] = HTT_T2H_MSG_TYPE_PKTLOG,
[HTT_10_4_T2H_MSG_TYPE_STATS_CONF] = HTT_T2H_MSG_TYPE_STATS_CONF,
[HTT_10_4_T2H_MSG_TYPE_RX_FRAG_IND] = HTT_T2H_MSG_TYPE_RX_FRAG_IND,
[HTT_10_4_T2H_MSG_TYPE_SEC_IND] = HTT_T2H_MSG_TYPE_SEC_IND,
[HTT_10_4_T2H_MSG_TYPE_RC_UPDATE_IND] = HTT_T2H_MSG_TYPE_RC_UPDATE_IND,
[HTT_10_4_T2H_MSG_TYPE_TX_INSPECT_IND] =
HTT_T2H_MSG_TYPE_TX_INSPECT_IND,
[HTT_10_4_T2H_MSG_TYPE_MGMT_TX_COMPL_IND] =
HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION,
[HTT_10_4_T2H_MSG_TYPE_CHAN_CHANGE] = HTT_T2H_MSG_TYPE_CHAN_CHANGE,
[HTT_10_4_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND] =
HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND,
[HTT_10_4_T2H_MSG_TYPE_RX_PN_IND] = HTT_T2H_MSG_TYPE_RX_PN_IND,
[HTT_10_4_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND] =
HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND,
[HTT_10_4_T2H_MSG_TYPE_TEST] = HTT_T2H_MSG_TYPE_TEST,
[HTT_10_4_T2H_MSG_TYPE_EN_STATS] = HTT_T2H_MSG_TYPE_EN_STATS,
[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_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_MODE_SWITCH_IND] =
HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND,
[HTT_10_4_T2H_MSG_TYPE_PEER_STATS] =
HTT_T2H_MSG_TYPE_PEER_STATS,
};
const struct ath10k_htt_rx_desc_ops qca988x_rx_desc_ops = {
.rx_desc_size = sizeof(struct htt_rx_desc_v1),
.rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v1, msdu_payload)
};
static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v1 *rx_desc = container_of(rxd,
struct htt_rx_desc_v1,
base);
return MS(__le32_to_cpu(rx_desc->msdu_end.qca99x0.info1),
RX_MSDU_END_INFO1_L3_HDR_PAD);
}
static bool ath10k_qca99x0_rx_desc_msdu_limit_error(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v1 *rx_desc = container_of(rxd,
struct htt_rx_desc_v1,
base);
return !!(rx_desc->msdu_end.common.info0 &
__cpu_to_le32(RX_MSDU_END_INFO0_MSDU_LIMIT_ERR));
}
const struct ath10k_htt_rx_desc_ops qca99x0_rx_desc_ops = {
.rx_desc_size = sizeof(struct htt_rx_desc_v1),
.rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v1, msdu_payload),
.rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes,
.rx_desc_get_msdu_limit_error = ath10k_qca99x0_rx_desc_msdu_limit_error,
};
static void ath10k_rx_desc_wcn3990_get_offsets(struct htt_rx_ring_rx_desc_offsets *off)
{
#define desc_offset(x) (offsetof(struct htt_rx_desc_v2, x) / 4)
off->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status));
off->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload));
off->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start));
off->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end));
off->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start));
off->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end));
off->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start));
off->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end));
off->rx_attention_offset = __cpu_to_le16(desc_offset(attention));
off->frag_info_offset = __cpu_to_le16(desc_offset(frag_info));
#undef desc_offset
}
static struct htt_rx_desc *
ath10k_rx_desc_wcn3990_from_raw_buffer(void *buff)
{
return &((struct htt_rx_desc_v2 *)buff)->base;
}
static struct rx_attention *
ath10k_rx_desc_wcn3990_get_attention(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->attention;
}
static struct rx_frag_info_common *
ath10k_rx_desc_wcn3990_get_frag_info(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->frag_info.common;
}
static struct rx_mpdu_start *
ath10k_rx_desc_wcn3990_get_mpdu_start(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->mpdu_start;
}
static struct rx_mpdu_end *
ath10k_rx_desc_wcn3990_get_mpdu_end(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->mpdu_end;
}
static struct rx_msdu_start_common *
ath10k_rx_desc_wcn3990_get_msdu_start(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->msdu_start.common;
}
static struct rx_msdu_end_common *
ath10k_rx_desc_wcn3990_get_msdu_end(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->msdu_end.common;
}
static struct rx_ppdu_start *
ath10k_rx_desc_wcn3990_get_ppdu_start(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->ppdu_start;
}
static struct rx_ppdu_end_common *
ath10k_rx_desc_wcn3990_get_ppdu_end(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return &rx_desc->ppdu_end.common;
}
static u8 *
ath10k_rx_desc_wcn3990_get_rx_hdr_status(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return rx_desc->rx_hdr_status;
}
static u8 *
ath10k_rx_desc_wcn3990_get_msdu_payload(struct htt_rx_desc *rxd)
{
struct htt_rx_desc_v2 *rx_desc = container_of(rxd, struct htt_rx_desc_v2, base);
return rx_desc->msdu_payload;
}
const struct ath10k_htt_rx_desc_ops wcn3990_rx_desc_ops = {
.rx_desc_size = sizeof(struct htt_rx_desc_v2),
.rx_desc_msdu_payload_offset = offsetof(struct htt_rx_desc_v2, msdu_payload),
.rx_desc_from_raw_buffer = ath10k_rx_desc_wcn3990_from_raw_buffer,
.rx_desc_get_offsets = ath10k_rx_desc_wcn3990_get_offsets,
.rx_desc_get_attention = ath10k_rx_desc_wcn3990_get_attention,
.rx_desc_get_frag_info = ath10k_rx_desc_wcn3990_get_frag_info,
.rx_desc_get_mpdu_start = ath10k_rx_desc_wcn3990_get_mpdu_start,
.rx_desc_get_mpdu_end = ath10k_rx_desc_wcn3990_get_mpdu_end,
.rx_desc_get_msdu_start = ath10k_rx_desc_wcn3990_get_msdu_start,
.rx_desc_get_msdu_end = ath10k_rx_desc_wcn3990_get_msdu_end,
.rx_desc_get_ppdu_start = ath10k_rx_desc_wcn3990_get_ppdu_start,
.rx_desc_get_ppdu_end = ath10k_rx_desc_wcn3990_get_ppdu_end,
.rx_desc_get_rx_hdr_status = ath10k_rx_desc_wcn3990_get_rx_hdr_status,
.rx_desc_get_msdu_payload = ath10k_rx_desc_wcn3990_get_msdu_payload,
};
int ath10k_htt_connect(struct ath10k_htt *htt)
{
struct ath10k_htc_svc_conn_req conn_req;
struct ath10k_htc_svc_conn_resp conn_resp;
struct ath10k *ar = htt->ar;
struct ath10k_htc_ep *ep;
int status;
memset(&conn_req, 0, sizeof(conn_req));
memset(&conn_resp, 0, sizeof(conn_resp));
conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
conn_req.ep_ops.ep_rx_complete = ath10k_htt_htc_t2h_msg_handler;
conn_req.ep_ops.ep_tx_credits = ath10k_htt_op_ep_tx_credits;
/* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req,
&conn_resp);
if (status)
return status;
htt->eid = conn_resp.eid;
if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) {
ep = &ar->htc.endpoint[htt->eid];
ath10k_htc_setup_tx_req(ep);
}
htt->disable_tx_comp = ath10k_hif_get_htt_tx_complete(htt->ar);
if (htt->disable_tx_comp)
ath10k_htc_change_tx_credit_flow(&htt->ar->htc, htt->eid, true);
return 0;
}
int ath10k_htt_init(struct ath10k *ar)
{
struct ath10k_htt *htt = &ar->htt;
htt->ar = ar;
/*
* Prefetch enough data to satisfy target
* classification engine.
* This is for LL chips. HL chips will probably
* transfer all frame in the tx fragment.
*/
htt->prefetch_len =
36 + /* 802.11 + qos + ht */
4 + /* 802.1q */
8 + /* llc snap */
2; /* ip4 dscp or ip6 priority */
switch (ar->running_fw->fw_file.htt_op_version) {
case ATH10K_FW_HTT_OP_VERSION_10_4:
ar->htt.t2h_msg_types = htt_10_4_t2h_msg_types;
ar->htt.t2h_msg_types_max = HTT_10_4_T2H_NUM_MSGS;
break;
case ATH10K_FW_HTT_OP_VERSION_10_1:
ar->htt.t2h_msg_types = htt_10x_t2h_msg_types;
ar->htt.t2h_msg_types_max = HTT_10X_T2H_NUM_MSGS;
break;
case ATH10K_FW_HTT_OP_VERSION_TLV:
ar->htt.t2h_msg_types = htt_tlv_t2h_msg_types;
ar->htt.t2h_msg_types_max = HTT_TLV_T2H_NUM_MSGS;
break;
case ATH10K_FW_HTT_OP_VERSION_MAIN:
ar->htt.t2h_msg_types = htt_main_t2h_msg_types;
ar->htt.t2h_msg_types_max = HTT_MAIN_T2H_NUM_MSGS;
break;
case ATH10K_FW_HTT_OP_VERSION_MAX:
case ATH10K_FW_HTT_OP_VERSION_UNSET:
WARN_ON(1);
return -EINVAL;
}
ath10k_htt_set_tx_ops(htt);
ath10k_htt_set_rx_ops(htt);
return 0;
}
#define HTT_TARGET_VERSION_TIMEOUT_HZ (3 * HZ)
static int ath10k_htt_verify_version(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt target version %d.%d\n",
htt->target_version_major, htt->target_version_minor);
if (htt->target_version_major != 2 &&
htt->target_version_major != 3) {
ath10k_err(ar, "unsupported htt major version %d. supported versions are 2 and 3\n",
htt->target_version_major);
return -ENOTSUPP;
}
return 0;
}
int ath10k_htt_setup(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
int status;
init_completion(&htt->target_version_received);
status = ath10k_htt_h2t_ver_req_msg(htt);
if (status)
return status;
status = wait_for_completion_timeout(&htt->target_version_received,
HTT_TARGET_VERSION_TIMEOUT_HZ);
if (status == 0) {
ath10k_warn(ar, "htt version request timed out\n");
return -ETIMEDOUT;
}
status = ath10k_htt_verify_version(htt);
if (status) {
ath10k_warn(ar, "failed to verify htt version: %d\n",
status);
return status;
}
status = ath10k_htt_send_frag_desc_bank_cfg(htt);
if (status)
return status;
status = ath10k_htt_send_rx_ring_cfg(htt);
if (status) {
ath10k_warn(ar, "failed to setup rx ring: %d\n",
status);
return status;
}
status = ath10k_htt_h2t_aggr_cfg_msg(htt,
htt->max_num_ampdu,
htt->max_num_amsdu);
if (status) {
ath10k_warn(ar, "failed to setup amsdu/ampdu limit: %d\n",
status);
return status;
}
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
*/
#ifndef _MAC_H_
#define _MAC_H_
#include <net/mac80211.h>
#include "core.h"
#define WEP_KEYID_SHIFT 6
enum wmi_tlv_tx_pause_id;
enum wmi_tlv_tx_pause_action;
struct ath10k_generic_iter {
struct ath10k *ar;
int ret;
};
struct rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctrl;
u8 snap_oui[3];
__be16 snap_type;
} __packed;
struct ath10k *ath10k_mac_create(size_t priv_size);
void ath10k_mac_destroy(struct ath10k *ar);
int ath10k_mac_register(struct ath10k *ar);
void ath10k_mac_unregister(struct ath10k *ar);
struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id);
void __ath10k_scan_finish(struct ath10k *ar);
void ath10k_scan_finish(struct ath10k *ar);
void ath10k_scan_timeout_work(struct work_struct *work);
void ath10k_offchan_tx_purge(struct ath10k *ar);
void ath10k_offchan_tx_work(struct work_struct *work);
void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
void ath10k_halt(struct ath10k *ar);
void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif);
void ath10k_drain_tx(struct ath10k *ar);
bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
u8 keyidx);
int ath10k_mac_vif_chan(struct ieee80211_vif *vif,
struct cfg80211_chan_def *def);
void ath10k_mac_handle_beacon(struct ath10k *ar, struct sk_buff *skb);
void ath10k_mac_handle_beacon_miss(struct ath10k *ar, u32 vdev_id);
void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id,
enum wmi_tlv_tx_pause_id pause_id,
enum wmi_tlv_tx_pause_action action);
u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
u8 hw_rate, bool cck);
u8 ath10k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
u32 bitrate);
void ath10k_mac_tx_lock(struct ath10k *ar, int reason);
void ath10k_mac_tx_unlock(struct ath10k *ar, int reason);
void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
void ath10k_mac_tx_push_pending(struct ath10k *ar);
int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
u16 peer_id,
u8 tid);
int ath10k_mac_ext_resource_config(struct ath10k *ar, u32 val);
void ath10k_mac_wait_tx_complete(struct ath10k *ar);
int ath10k_mac_rfkill_enable_radio(struct ath10k *ar, bool enable);
static inline void ath10k_tx_h_seq_no(struct ieee80211_vif *vif,
struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ath10k_vif *arvif = (void *)vif->drv_priv;
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
if (arvif->tx_seq_no == 0)
arvif->tx_seq_no = 0x1000;
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
arvif->tx_seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no);
}
}
#endif /* _MAC_H_ */

View File

@ -0,0 +1,145 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2015 Qualcomm Atheros, Inc.
*/
#include "core.h"
#include "wmi.h"
#include "mac.h"
#include "p2p.h"
static void ath10k_p2p_noa_ie_fill(u8 *data, size_t len,
const struct wmi_p2p_noa_info *noa)
{
struct ieee80211_p2p_noa_attr *noa_attr;
u8 ctwindow_oppps = noa->ctwindow_oppps;
u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET;
bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT);
__le16 *noa_attr_len;
u16 attr_len;
u8 noa_descriptors = noa->num_descriptors;
int i;
/* P2P IE */
data[0] = WLAN_EID_VENDOR_SPECIFIC;
data[1] = len - 2;
data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
data[5] = WLAN_OUI_TYPE_WFA_P2P;
/* NOA ATTR */
data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
noa_attr->index = noa->index;
noa_attr->oppps_ctwindow = ctwindow;
if (oppps)
noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
for (i = 0; i < noa_descriptors; i++) {
noa_attr->desc[i].count =
__le32_to_cpu(noa->descriptors[i].type_count);
noa_attr->desc[i].duration = noa->descriptors[i].duration;
noa_attr->desc[i].interval = noa->descriptors[i].interval;
noa_attr->desc[i].start_time = noa->descriptors[i].start_time;
}
attr_len = 2; /* index + oppps_ctwindow */
attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
*noa_attr_len = __cpu_to_le16(attr_len);
}
static size_t ath10k_p2p_noa_ie_len_compute(const struct wmi_p2p_noa_info *noa)
{
size_t len = 0;
if (!noa->num_descriptors &&
!(noa->ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT))
return 0;
len += 1 + 1 + 4; /* EID + len + OUI */
len += 1 + 2; /* noa attr + attr len */
len += 1 + 1; /* index + oppps_ctwindow */
len += noa->num_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
return len;
}
static void ath10k_p2p_noa_ie_assign(struct ath10k_vif *arvif, void *ie,
size_t len)
{
struct ath10k *ar = arvif->ar;
lockdep_assert_held(&ar->data_lock);
kfree(arvif->u.ap.noa_data);
arvif->u.ap.noa_data = ie;
arvif->u.ap.noa_len = len;
}
static void __ath10k_p2p_noa_update(struct ath10k_vif *arvif,
const struct wmi_p2p_noa_info *noa)
{
struct ath10k *ar = arvif->ar;
void *ie;
size_t len;
lockdep_assert_held(&ar->data_lock);
ath10k_p2p_noa_ie_assign(arvif, NULL, 0);
len = ath10k_p2p_noa_ie_len_compute(noa);
if (!len)
return;
ie = kmalloc(len, GFP_ATOMIC);
if (!ie)
return;
ath10k_p2p_noa_ie_fill(ie, len, noa);
ath10k_p2p_noa_ie_assign(arvif, ie, len);
}
void ath10k_p2p_noa_update(struct ath10k_vif *arvif,
const struct wmi_p2p_noa_info *noa)
{
struct ath10k *ar = arvif->ar;
spin_lock_bh(&ar->data_lock);
__ath10k_p2p_noa_update(arvif, noa);
spin_unlock_bh(&ar->data_lock);
}
struct ath10k_p2p_noa_arg {
u32 vdev_id;
const struct wmi_p2p_noa_info *noa;
};
static void ath10k_p2p_noa_update_vdev_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct ath10k_vif *arvif = (void *)vif->drv_priv;
struct ath10k_p2p_noa_arg *arg = data;
if (arvif->vdev_id != arg->vdev_id)
return;
ath10k_p2p_noa_update(arvif, arg->noa);
}
void ath10k_p2p_noa_update_by_vdev_id(struct ath10k *ar, u32 vdev_id,
const struct wmi_p2p_noa_info *noa)
{
struct ath10k_p2p_noa_arg arg = {
.vdev_id = vdev_id,
.noa = noa,
};
ieee80211_iterate_active_interfaces_atomic(ar->hw,
ATH10K_ITER_NORMAL_FLAGS,
ath10k_p2p_noa_update_vdev_iter,
&arg);
}

View File

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2015 Qualcomm Atheros, Inc.
*/
#ifndef _P2P_H
#define _P2P_H
struct ath10k_vif;
struct wmi_p2p_noa_info;
void ath10k_p2p_noa_update(struct ath10k_vif *arvif,
const struct wmi_p2p_noa_info *noa);
void ath10k_p2p_noa_update_by_vdev_id(struct ath10k *ar, u32 vdev_id,
const struct wmi_p2p_noa_info *noa);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,259 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
*/
#ifndef _PCI_H_
#define _PCI_H_
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include "hw.h"
#include "ce.h"
#include "ahb.h"
/*
* maximum number of bytes that can be
* handled atomically by DiagRead/DiagWrite
*/
#define DIAG_TRANSFER_LIMIT 2048
struct bmi_xfer {
bool tx_done;
bool rx_done;
bool wait_for_resp;
u32 resp_len;
};
/*
* PCI-specific Target state
*
* NOTE: Structure is shared between Host software and Target firmware!
*
* Much of this may be of interest to the Host so
* HOST_INTEREST->hi_interconnect_state points here
* (and all members are 32-bit quantities in order to
* facilitate Host access). In particular, Host software is
* required to initialize pipe_cfg_addr and svc_to_pipe_map.
*/
struct pcie_state {
/* Pipe configuration Target address */
/* NB: ce_pipe_config[CE_COUNT] */
u32 pipe_cfg_addr;
/* Service to pipe map Target address */
/* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */
u32 svc_to_pipe_map;
/* number of MSI interrupts requested */
u32 msi_requested;
/* number of MSI interrupts granted */
u32 msi_granted;
/* Message Signalled Interrupt address */
u32 msi_addr;
/* Base data */
u32 msi_data;
/*
* Data for firmware interrupt;
* MSI data for other interrupts are
* in various SoC registers
*/
u32 msi_fw_intr_data;
/* PCIE_PWR_METHOD_* */
u32 power_mgmt_method;
/* PCIE_CONFIG_FLAG_* */
u32 config_flags;
};
/* PCIE_CONFIG_FLAG definitions */
#define PCIE_CONFIG_FLAG_ENABLE_L1 0x0000001
/* Per-pipe state. */
struct ath10k_pci_pipe {
/* Handle of underlying Copy Engine */
struct ath10k_ce_pipe *ce_hdl;
/* Our pipe number; facilitiates use of pipe_info ptrs. */
u8 pipe_num;
/* Convenience back pointer to hif_ce_state. */
struct ath10k *hif_ce_state;
size_t buf_sz;
/* protects compl_free and num_send_allowed */
spinlock_t pipe_lock;
};
struct ath10k_pci_supp_chip {
u32 dev_id;
u32 rev_id;
};
enum ath10k_pci_irq_mode {
ATH10K_PCI_IRQ_AUTO = 0,
ATH10K_PCI_IRQ_LEGACY = 1,
ATH10K_PCI_IRQ_MSI = 2,
};
struct ath10k_pci {
struct pci_dev *pdev;
struct device *dev;
struct ath10k *ar;
void __iomem *mem;
size_t mem_len;
/* Operating interrupt mode */
enum ath10k_pci_irq_mode oper_irq_mode;
struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
/* Copy Engine used for Diagnostic Accesses */
struct ath10k_ce_pipe *ce_diag;
/* For protecting ce_diag */
struct mutex ce_diag_mutex;
struct work_struct dump_work;
struct ath10k_ce ce;
struct timer_list rx_post_retry;
/* Due to HW quirks it is recommended to disable ASPM during device
* bootup. To do that the original PCI-E Link Control is stored before
* device bootup is executed and re-programmed later.
*/
u16 link_ctl;
/* Protects ps_awake and ps_wake_refcount */
spinlock_t ps_lock;
/* The device has a special powersave-oriented register. When device is
* considered asleep it drains less power and driver is forbidden from
* accessing most MMIO registers. If host were to access them without
* waking up the device might scribble over host memory or return
* 0xdeadbeef readouts.
*/
unsigned long ps_wake_refcount;
/* Waking up takes some time (up to 2ms in some cases) so it can be bad
* for latency. To mitigate this the device isn't immediately allowed
* to sleep after all references are undone - instead there's a grace
* period after which the powersave register is updated unless some
* activity to/from device happened in the meantime.
*
* Also see comments on ATH10K_PCI_SLEEP_GRACE_PERIOD_MSEC.
*/
struct timer_list ps_timer;
/* MMIO registers are used to communicate with the device. With
* intensive traffic accessing powersave register would be a bit
* wasteful overhead and would needlessly stall CPU. It is far more
* efficient to rely on a variable in RAM and update it only upon
* powersave register state changes.
*/
bool ps_awake;
/* pci power save, disable for QCA988X and QCA99X0.
* Writing 'false' to this variable avoids frequent locking
* on MMIO read/write.
*/
bool pci_ps;
/* Chip specific pci reset routine used to do a safe reset */
int (*pci_soft_reset)(struct ath10k *ar);
/* Chip specific pci full reset function */
int (*pci_hard_reset)(struct ath10k *ar);
/* chip specific methods for converting target CPU virtual address
* space to CE address space
*/
u32 (*targ_cpu_to_ce_addr)(struct ath10k *ar, u32 addr);
struct ce_attr *attr;
struct ce_pipe_config *pipe_config;
struct ce_service_to_pipe *serv_to_pipe;
/* 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[];
};
static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar)
{
return (struct ath10k_pci *)ar->drv_priv;
}
#define ATH10K_PCI_RX_POST_RETRY_MS 50
#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */
#define PCIE_WAKE_TIMEOUT 30000 /* 30ms */
#define PCIE_WAKE_LATE_US 10000 /* 10ms */
#define BAR_NUM 0
#define CDC_WAR_MAGIC_STR 0xceef0000
#define CDC_WAR_DATA_CE 4
/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
#define DIAG_ACCESS_CE_TIMEOUT_US 10000 /* 10 ms */
#define DIAG_ACCESS_CE_WAIT_US 50
void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value);
void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val);
void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val);
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_rx_replenish_retry(struct timer_list *t);
void ath10k_pci_ce_deinit(struct ath10k *ar);
void ath10k_pci_init_napi(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);
void ath10k_pci_irq_msi_fw_mask(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.
*/
#define ATH10K_PCI_SLEEP_GRACE_PERIOD_MSEC 60
#endif /* _PCI_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*/
#ifndef _ATH10K_QMI_H_
#define _ATH10K_QMI_H_
#include <linux/soc/qcom/qmi.h>
#include <linux/qrtr.h>
#include "qmi_wlfw_v01.h"
#define MAX_NUM_MEMORY_REGIONS 2
#define MAX_TIMESTAMP_LEN 32
#define MAX_BUILD_ID_LEN 128
#define MAX_NUM_CAL_V01 5
enum ath10k_qmi_driver_event_type {
ATH10K_QMI_EVENT_SERVER_ARRIVE,
ATH10K_QMI_EVENT_SERVER_EXIT,
ATH10K_QMI_EVENT_FW_READY_IND,
ATH10K_QMI_EVENT_FW_DOWN_IND,
ATH10K_QMI_EVENT_MSA_READY_IND,
ATH10K_QMI_EVENT_MAX,
};
struct ath10k_msa_mem_info {
phys_addr_t addr;
u32 size;
bool secure;
};
struct ath10k_qmi_chip_info {
u32 chip_id;
u32 chip_family;
};
struct ath10k_qmi_board_info {
u32 board_id;
};
struct ath10k_qmi_soc_info {
u32 soc_id;
};
struct ath10k_qmi_cal_data {
u32 cal_id;
u32 total_size;
u8 *data;
};
struct ath10k_tgt_pipe_cfg {
__le32 pipe_num;
__le32 pipe_dir;
__le32 nentries;
__le32 nbytes_max;
__le32 flags;
__le32 reserved;
};
struct ath10k_svc_pipe_cfg {
__le32 service_id;
__le32 pipe_dir;
__le32 pipe_num;
};
struct ath10k_shadow_reg_cfg {
__le16 ce_id;
__le16 reg_offset;
};
struct ath10k_qmi_wlan_enable_cfg {
u32 num_ce_tgt_cfg;
struct ath10k_tgt_pipe_cfg *ce_tgt_cfg;
u32 num_ce_svc_pipe_cfg;
struct ath10k_svc_pipe_cfg *ce_svc_cfg;
u32 num_shadow_reg_cfg;
struct ath10k_shadow_reg_cfg *shadow_reg_cfg;
};
struct ath10k_qmi_driver_event {
struct list_head list;
enum ath10k_qmi_driver_event_type type;
void *data;
};
enum ath10k_qmi_state {
ATH10K_QMI_STATE_INIT_DONE,
ATH10K_QMI_STATE_DEINIT,
};
struct ath10k_qmi {
struct ath10k *ar;
struct qmi_handle qmi_hdl;
struct sockaddr_qrtr sq;
struct work_struct event_work;
struct workqueue_struct *event_wq;
struct list_head event_list;
spinlock_t event_lock; /* spinlock for qmi event list */
u32 nr_mem_region;
struct ath10k_msa_mem_info mem_region[MAX_NUM_MEMORY_REGIONS];
struct ath10k_qmi_chip_info chip_info;
struct ath10k_qmi_board_info board_info;
struct ath10k_qmi_soc_info soc_info;
char fw_build_id[MAX_BUILD_ID_LEN + 1];
u32 fw_version;
bool fw_ready;
char fw_build_timestamp[MAX_TIMESTAMP_LEN + 1];
struct ath10k_qmi_cal_data cal_data[MAX_NUM_CAL_V01];
bool msa_fixed_perm;
enum ath10k_qmi_state state;
};
int ath10k_qmi_wlan_enable(struct ath10k *ar,
struct ath10k_qmi_wlan_enable_cfg *config,
enum wlfw_driver_mode_enum_v01 mode,
const char *version);
int ath10k_qmi_wlan_disable(struct ath10k *ar);
int ath10k_qmi_init(struct ath10k *ar, u32 msa_size);
int ath10k_qmi_deinit(struct ath10k *ar);
int ath10k_qmi_set_fw_log_mode(struct ath10k *ar, u8 fw_log_mode);
#endif /* ATH10K_QMI_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,693 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*/
#ifndef WCN3990_QMI_SVC_V01_H
#define WCN3990_QMI_SVC_V01_H
#define WLFW_SERVICE_ID_V01 0x45
#define WLFW_SERVICE_VERS_V01 0x01
#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025
#define QMI_WLFW_MEM_READY_IND_V01 0x0037
#define QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01 0x003B
#define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A
#define QMI_WLFW_HOST_CAP_REQ_V01 0x0034
#define QMI_WLFW_M3_INFO_REQ_V01 0x003C
#define QMI_WLFW_CAP_REQ_V01 0x0024
#define QMI_WLFW_FW_INIT_DONE_IND_V01 0x0038
#define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026
#define QMI_WLFW_M3_INFO_RESP_V01 0x003C
#define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029
#define QMI_WLFW_CAL_DOWNLOAD_RESP_V01 0x0027
#define QMI_WLFW_XO_CAL_IND_V01 0x003D
#define QMI_WLFW_INI_RESP_V01 0x002F
#define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026
#define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033
#define QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01 0x0028
#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034
#define QMI_WLFW_MSA_READY_IND_V01 0x002B
#define QMI_WLFW_ATHDIAG_WRITE_RESP_V01 0x0031
#define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022
#define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020
#define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023
#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035
#define QMI_WLFW_REJUVENATE_IND_V01 0x0039
#define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B
#define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031
#define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022
#define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036
#define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C
#define QMI_WLFW_FW_READY_IND_V01 0x0021
#define QMI_WLFW_MSA_READY_RESP_V01 0x002E
#define QMI_WLFW_CAL_UPDATE_REQ_V01 0x0029
#define QMI_WLFW_INI_REQ_V01 0x002F
#define QMI_WLFW_BDF_DOWNLOAD_RESP_V01 0x0025
#define QMI_WLFW_REJUVENATE_ACK_RESP_V01 0x003A
#define QMI_WLFW_MSA_INFO_RESP_V01 0x002D
#define QMI_WLFW_MSA_READY_REQ_V01 0x002E
#define QMI_WLFW_CAP_RESP_V01 0x0024
#define QMI_WLFW_REJUVENATE_ACK_REQ_V01 0x003A
#define QMI_WLFW_ATHDIAG_READ_RESP_V01 0x0030
#define QMI_WLFW_VBATT_REQ_V01 0x0032
#define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033
#define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036
#define QMI_WLFW_VBATT_RESP_V01 0x0032
#define QMI_WLFW_MSA_INFO_REQ_V01 0x002D
#define QMI_WLFW_CAL_DOWNLOAD_REQ_V01 0x0027
#define QMI_WLFW_ATHDIAG_READ_REQ_V01 0x0030
#define QMI_WLFW_WLAN_CFG_REQ_V01 0x0023
#define QMI_WLFW_IND_REGISTER_RESP_V01 0x0020
#define QMI_WLFW_MAX_MEM_REG_V01 2
#define QMI_WLFW_MAX_NUM_MEM_SEG_V01 16
#define QMI_WLFW_MAX_NUM_CAL_V01 5
#define QMI_WLFW_MAX_DATA_SIZE_V01 6144
#define QMI_WLFW_FUNCTION_NAME_LEN_V01 128
#define QMI_WLFW_MAX_NUM_CE_V01 12
#define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32
#define QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01 6144
#define QMI_WLFW_MAX_NUM_GPIO_V01 32
#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128
#define QMI_WLFW_MAX_NUM_MEM_CFG_V01 2
#define QMI_WLFW_MAX_STR_LEN_V01 16
#define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24
#define QMI_WLFW_MAC_ADDR_SIZE_V01 6
#define QMI_WLFW_MAX_SHADOW_REG_V2 36
#define QMI_WLFW_MAX_NUM_SVC_V01 24
enum wlfw_driver_mode_enum_v01 {
QMI_WLFW_MISSION_V01 = 0,
QMI_WLFW_FTM_V01 = 1,
QMI_WLFW_EPPING_V01 = 2,
QMI_WLFW_WALTEST_V01 = 3,
QMI_WLFW_OFF_V01 = 4,
QMI_WLFW_CCPM_V01 = 5,
QMI_WLFW_QVIT_V01 = 6,
QMI_WLFW_CALIBRATION_V01 = 7,
};
enum wlfw_cal_temp_id_enum_v01 {
QMI_WLFW_CAL_TEMP_IDX_0_V01 = 0,
QMI_WLFW_CAL_TEMP_IDX_1_V01 = 1,
QMI_WLFW_CAL_TEMP_IDX_2_V01 = 2,
QMI_WLFW_CAL_TEMP_IDX_3_V01 = 3,
QMI_WLFW_CAL_TEMP_IDX_4_V01 = 4,
};
enum wlfw_pipedir_enum_v01 {
QMI_WLFW_PIPEDIR_NONE_V01 = 0,
QMI_WLFW_PIPEDIR_IN_V01 = 1,
QMI_WLFW_PIPEDIR_OUT_V01 = 2,
QMI_WLFW_PIPEDIR_INOUT_V01 = 3,
};
enum wlfw_mem_type_enum_v01 {
QMI_WLFW_MEM_TYPE_MSA_V01 = 0,
QMI_WLFW_MEM_TYPE_DDR_V01 = 1,
};
#define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00)
#define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01)
#define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02)
#define QMI_WLFW_CE_ATTR_SWIZZLE_DESCRIPTORS_V01 ((u32)0x04)
#define QMI_WLFW_CE_ATTR_DISABLE_INTR_V01 ((u32)0x08)
#define QMI_WLFW_CE_ATTR_ENABLE_POLL_V01 ((u32)0x10)
#define QMI_WLFW_ALREADY_REGISTERED_V01 ((u64)0x01ULL)
#define QMI_WLFW_FW_READY_V01 ((u64)0x02ULL)
#define QMI_WLFW_MSA_READY_V01 ((u64)0x04ULL)
#define QMI_WLFW_MEM_READY_V01 ((u64)0x08ULL)
#define QMI_WLFW_FW_INIT_DONE_V01 ((u64)0x10ULL)
#define QMI_WLFW_FW_REJUVENATE_V01 ((u64)0x01ULL)
struct wlfw_ce_tgt_pipe_cfg_s_v01 {
__le32 pipe_num;
__le32 pipe_dir;
__le32 nentries;
__le32 nbytes_max;
__le32 flags;
};
struct wlfw_ce_svc_pipe_cfg_s_v01 {
__le32 service_id;
__le32 pipe_dir;
__le32 pipe_num;
};
struct wlfw_shadow_reg_cfg_s_v01 {
u16 id;
u16 offset;
};
struct wlfw_shadow_reg_v2_cfg_s_v01 {
u32 addr;
};
struct wlfw_memory_region_info_s_v01 {
u64 region_addr;
u32 size;
u8 secure_flag;
};
struct wlfw_mem_cfg_s_v01 {
u64 offset;
u32 size;
u8 secure_flag;
};
struct wlfw_mem_seg_s_v01 {
u32 size;
enum wlfw_mem_type_enum_v01 type;
u32 mem_cfg_len;
struct wlfw_mem_cfg_s_v01 mem_cfg[QMI_WLFW_MAX_NUM_MEM_CFG_V01];
};
struct wlfw_mem_seg_resp_s_v01 {
u64 addr;
u32 size;
enum wlfw_mem_type_enum_v01 type;
};
struct wlfw_rf_chip_info_s_v01 {
u32 chip_id;
u32 chip_family;
};
struct wlfw_rf_board_info_s_v01 {
u32 board_id;
};
struct wlfw_soc_info_s_v01 {
u32 soc_id;
};
struct wlfw_fw_version_info_s_v01 {
u32 fw_version;
char fw_build_timestamp[QMI_WLFW_MAX_TIMESTAMP_LEN_V01 + 1];
};
struct wlfw_ind_register_req_msg_v01 {
u8 fw_ready_enable_valid;
u8 fw_ready_enable;
u8 initiate_cal_download_enable_valid;
u8 initiate_cal_download_enable;
u8 initiate_cal_update_enable_valid;
u8 initiate_cal_update_enable;
u8 msa_ready_enable_valid;
u8 msa_ready_enable;
u8 pin_connect_result_enable_valid;
u8 pin_connect_result_enable;
u8 client_id_valid;
u32 client_id;
u8 request_mem_enable_valid;
u8 request_mem_enable;
u8 mem_ready_enable_valid;
u8 mem_ready_enable;
u8 fw_init_done_enable_valid;
u8 fw_init_done_enable;
u8 rejuvenate_enable_valid;
u32 rejuvenate_enable;
u8 xo_cal_enable_valid;
u8 xo_cal_enable;
};
#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 50
extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[];
struct wlfw_ind_register_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 fw_status_valid;
u64 fw_status;
};
#define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18
extern struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[];
struct wlfw_fw_ready_ind_msg_v01 {
char placeholder;
};
#define WLFW_FW_READY_IND_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[];
struct wlfw_msa_ready_ind_msg_v01 {
char placeholder;
};
#define WLFW_MSA_READY_IND_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[];
struct wlfw_pin_connect_result_ind_msg_v01 {
u8 pwr_pin_result_valid;
u32 pwr_pin_result;
u8 phy_io_pin_result_valid;
u32 phy_io_pin_result;
u8 rf_pin_result_valid;
u32 rf_pin_result;
};
#define WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN 21
extern struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[];
struct wlfw_wlan_mode_req_msg_v01 {
enum wlfw_driver_mode_enum_v01 mode;
u8 hw_debug_valid;
u8 hw_debug;
};
#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11
extern struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[];
struct wlfw_wlan_mode_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[];
struct wlfw_wlan_cfg_req_msg_v01 {
u8 host_version_valid;
char host_version[QMI_WLFW_MAX_STR_LEN_V01 + 1];
u8 tgt_cfg_valid;
u32 tgt_cfg_len;
struct wlfw_ce_tgt_pipe_cfg_s_v01 tgt_cfg[QMI_WLFW_MAX_NUM_CE_V01];
u8 svc_cfg_valid;
u32 svc_cfg_len;
struct wlfw_ce_svc_pipe_cfg_s_v01 svc_cfg[QMI_WLFW_MAX_NUM_SVC_V01];
u8 shadow_reg_valid;
u32 shadow_reg_len;
struct wlfw_shadow_reg_cfg_s_v01 shadow_reg[QMI_WLFW_MAX_NUM_SHADOW_REG_V01];
u8 shadow_reg_v2_valid;
u32 shadow_reg_v2_len;
struct wlfw_shadow_reg_v2_cfg_s_v01 shadow_reg_v2[QMI_WLFW_MAX_SHADOW_REG_V2];
};
#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803
extern struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[];
struct wlfw_wlan_cfg_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[];
struct wlfw_cap_req_msg_v01 {
char placeholder;
};
#define WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_cap_req_msg_v01_ei[];
struct wlfw_cap_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 chip_info_valid;
struct wlfw_rf_chip_info_s_v01 chip_info;
u8 board_info_valid;
struct wlfw_rf_board_info_s_v01 board_info;
u8 soc_info_valid;
struct wlfw_soc_info_s_v01 soc_info;
u8 fw_version_info_valid;
struct wlfw_fw_version_info_s_v01 fw_version_info;
u8 fw_build_id_valid;
char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1];
u8 num_macs_valid;
u8 num_macs;
};
#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 207
extern struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[];
struct wlfw_bdf_download_req_msg_v01 {
u8 valid;
u8 file_id_valid;
enum wlfw_cal_temp_id_enum_v01 file_id;
u8 total_size_valid;
u32 total_size;
u8 seg_id_valid;
u32 seg_id;
u8 data_valid;
u32 data_len;
u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
u8 end_valid;
u8 end;
u8 bdf_type_valid;
u8 bdf_type;
};
#define WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6182
extern struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[];
struct wlfw_bdf_download_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[];
struct wlfw_cal_report_req_msg_v01 {
u32 meta_data_len;
enum wlfw_cal_temp_id_enum_v01 meta_data[QMI_WLFW_MAX_NUM_CAL_V01];
u8 xo_cal_data_valid;
u8 xo_cal_data;
};
#define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 28
extern struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[];
struct wlfw_cal_report_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_CAL_REPORT_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[];
struct wlfw_initiate_cal_download_ind_msg_v01 {
enum wlfw_cal_temp_id_enum_v01 cal_id;
};
#define WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[];
struct wlfw_cal_download_req_msg_v01 {
u8 valid;
u8 file_id_valid;
enum wlfw_cal_temp_id_enum_v01 file_id;
u8 total_size_valid;
u32 total_size;
u8 seg_id_valid;
u32 seg_id;
u8 data_valid;
u32 data_len;
u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
u8 end_valid;
u8 end;
};
#define WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178
extern struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[];
struct wlfw_cal_download_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[];
struct wlfw_initiate_cal_update_ind_msg_v01 {
enum wlfw_cal_temp_id_enum_v01 cal_id;
u32 total_size;
};
#define WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN 14
extern struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[];
struct wlfw_cal_update_req_msg_v01 {
enum wlfw_cal_temp_id_enum_v01 cal_id;
u32 seg_id;
};
#define WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN 14
extern struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[];
struct wlfw_cal_update_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 file_id_valid;
enum wlfw_cal_temp_id_enum_v01 file_id;
u8 total_size_valid;
u32 total_size;
u8 seg_id_valid;
u32 seg_id;
u8 data_valid;
u32 data_len;
u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
u8 end_valid;
u8 end;
};
#define WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN 6181
extern struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[];
struct wlfw_msa_info_req_msg_v01 {
u64 msa_addr;
u32 size;
};
#define WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
extern struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[];
struct wlfw_msa_info_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u32 mem_region_info_len;
struct wlfw_memory_region_info_s_v01 mem_region_info[QMI_WLFW_MAX_MEM_REG_V01];
};
#define WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN 37
extern struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[];
struct wlfw_msa_ready_req_msg_v01 {
char placeholder;
};
#define WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[];
struct wlfw_msa_ready_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[];
struct wlfw_ini_req_msg_v01 {
u8 enablefwlog_valid;
u8 enablefwlog;
};
#define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4
extern struct qmi_elem_info wlfw_ini_req_msg_v01_ei[];
struct wlfw_ini_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[];
struct wlfw_athdiag_read_req_msg_v01 {
u32 offset;
u32 mem_type;
u32 data_len;
};
#define WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN 21
extern struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[];
struct wlfw_athdiag_read_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 data_valid;
u32 data_len;
u8 data[QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01];
};
#define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 6156
extern struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[];
struct wlfw_athdiag_write_req_msg_v01 {
u32 offset;
u32 mem_type;
u32 data_len;
u8 data[QMI_WLFW_MAX_ATHDIAG_DATA_SIZE_V01];
};
#define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 6163
extern struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[];
struct wlfw_athdiag_write_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[];
struct wlfw_vbatt_req_msg_v01 {
u64 voltage_uv;
};
#define WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN 11
extern struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[];
struct wlfw_vbatt_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[];
struct wlfw_mac_addr_req_msg_v01 {
u8 mac_addr_valid;
u8 mac_addr[QMI_WLFW_MAC_ADDR_SIZE_V01];
};
#define WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN 9
extern struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[];
struct wlfw_mac_addr_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[];
#define QMI_WLFW_MAX_NUM_GPIO_V01 32
struct wlfw_host_cap_req_msg_v01 {
u8 daemon_support_valid;
u32 daemon_support;
u8 wake_msi_valid;
u32 wake_msi;
u8 gpios_valid;
u32 gpios_len;
u32 gpios[QMI_WLFW_MAX_NUM_GPIO_V01];
u8 nm_modem_valid;
u8 nm_modem;
u8 bdf_support_valid;
u8 bdf_support;
u8 bdf_cache_support_valid;
u8 bdf_cache_support;
u8 m3_support_valid;
u8 m3_support;
u8 m3_cache_support_valid;
u8 m3_cache_support;
u8 cal_filesys_support_valid;
u8 cal_filesys_support;
u8 cal_cache_support_valid;
u8 cal_cache_support;
u8 cal_done_valid;
u8 cal_done;
u8 mem_bucket_valid;
u32 mem_bucket;
u8 mem_cfg_mode_valid;
u8 mem_cfg_mode;
};
#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 189
extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[];
extern struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[];
struct wlfw_host_cap_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[];
struct wlfw_request_mem_ind_msg_v01 {
u32 mem_seg_len;
struct wlfw_mem_seg_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
};
#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 564
extern struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[];
struct wlfw_respond_mem_req_msg_v01 {
u32 mem_seg_len;
struct wlfw_mem_seg_resp_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01];
};
#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 260
extern struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[];
struct wlfw_respond_mem_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[];
struct wlfw_mem_ready_ind_msg_v01 {
char placeholder;
};
#define WLFW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[];
struct wlfw_fw_init_done_ind_msg_v01 {
char placeholder;
};
#define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[];
struct wlfw_rejuvenate_ind_msg_v01 {
u8 cause_for_rejuvenation_valid;
u8 cause_for_rejuvenation;
u8 requesting_sub_system_valid;
u8 requesting_sub_system;
u8 line_number_valid;
u16 line_number;
u8 function_name_valid;
char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
};
#define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144
extern struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[];
struct wlfw_rejuvenate_ack_req_msg_v01 {
char placeholder;
};
#define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0
extern struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[];
struct wlfw_rejuvenate_ack_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[];
struct wlfw_dynamic_feature_mask_req_msg_v01 {
u8 mask_valid;
u64 mask;
};
#define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11
extern struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[];
struct wlfw_dynamic_feature_mask_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 prev_mask_valid;
u64 prev_mask;
u8 curr_mask_valid;
u64 curr_mask;
};
#define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29
extern struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[];
struct wlfw_m3_info_req_msg_v01 {
u64 addr;
u32 size;
};
#define WLFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
extern struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[];
struct wlfw_m3_info_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[];
struct wlfw_xo_cal_ind_msg_v01 {
u8 xo_cal_data;
};
#define WLFW_XO_CAL_IND_MSG_V01_MAX_MSG_LEN 4
extern struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[];
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,236 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
* Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
* Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com>
*/
#ifndef _SDIO_H_
#define _SDIO_H_
#define ATH10K_HIF_MBOX_BLOCK_SIZE 256
#define ATH10K_SDIO_MAX_BUFFER_SIZE 4096 /*Unsure of this constant*/
/* Mailbox address in SDIO address space */
#define ATH10K_HIF_MBOX_BASE_ADDR 0x1000
#define ATH10K_HIF_MBOX_WIDTH 0x800
#define ATH10K_HIF_MBOX_TOT_WIDTH \
(ATH10K_HIF_MBOX_NUM_MAX * ATH10K_HIF_MBOX_WIDTH)
#define ATH10K_HIF_MBOX0_EXT_BASE_ADDR 0x5000
#define ATH10K_HIF_MBOX0_EXT_WIDTH (36 * 1024)
#define ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0 (56 * 1024)
#define ATH10K_HIF_MBOX1_EXT_WIDTH (36 * 1024)
#define ATH10K_HIF_MBOX_DUMMY_SPACE_SIZE (2 * 1024)
#define ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH \
(ATH10K_SDIO_MAX_BUFFER_SIZE - sizeof(struct ath10k_htc_hdr))
#define ATH10K_HIF_MBOX_NUM_MAX 4
#define ATH10K_SDIO_BUS_REQUEST_MAX_NUM 1024
#define ATH10K_SDIO_HIF_COMMUNICATION_TIMEOUT_HZ (100 * HZ)
/* HTC runs over mailbox 0 */
#define ATH10K_HTC_MAILBOX 0
#define ATH10K_HTC_MAILBOX_MASK BIT(ATH10K_HTC_MAILBOX)
/* GMBOX addresses */
#define ATH10K_HIF_GMBOX_BASE_ADDR 0x7000
#define ATH10K_HIF_GMBOX_WIDTH 0x4000
/* Modified versions of the sdio.h macros.
* The macros in sdio.h can't be used easily with the FIELD_{PREP|GET}
* macros in bitfield.h, so we define our own macros here.
*/
#define ATH10K_SDIO_DRIVE_DTSX_MASK \
(SDIO_DRIVE_DTSx_MASK << SDIO_DRIVE_DTSx_SHIFT)
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_B 0
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_A 1
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_C 2
#define ATH10K_SDIO_DRIVE_DTSX_TYPE_D 3
/* SDIO CCCR register definitions */
#define CCCR_SDIO_IRQ_MODE_REG 0xF0
#define CCCR_SDIO_IRQ_MODE_REG_SDIO3 0x16
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_ADDR 0xF2
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_A 0x02
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_C 0x04
#define CCCR_SDIO_DRIVER_STRENGTH_ENABLE_D 0x08
#define CCCR_SDIO_ASYNC_INT_DELAY_ADDRESS 0xF0
#define CCCR_SDIO_ASYNC_INT_DELAY_MASK 0xC0
/* mode to enable special 4-bit interrupt assertion without clock */
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ BIT(0)
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ_SDIO3 BIT(1)
#define ATH10K_SDIO_TARGET_DEBUG_INTR_MASK 0x01
/* The theoretical maximum number of RX messages that can be fetched
* from the mbox interrupt handler in one loop is derived in the following
* way:
*
* Let's assume that each packet in a bundle of the maximum bundle size
* (HTC_HOST_MAX_MSG_PER_RX_BUNDLE) has the HTC header bundle count set
* to the maximum value (HTC_HOST_MAX_MSG_PER_RX_BUNDLE).
*
* in this case the driver must allocate
* (HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2) skb's.
*/
#define ATH10K_SDIO_MAX_RX_MSGS \
(HTC_HOST_MAX_MSG_PER_RX_BUNDLE * 2)
#define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL 0x00000868u
#define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_OFF 0xFFFEFFFF
#define ATH10K_FIFO_TIMEOUT_AND_CHIP_CONTROL_DISABLE_SLEEP_ON 0x10000
enum sdio_mbox_state {
SDIO_MBOX_UNKNOWN_STATE = 0,
SDIO_MBOX_REQUEST_TO_SLEEP_STATE = 1,
SDIO_MBOX_SLEEP_STATE = 2,
SDIO_MBOX_AWAKE_STATE = 3,
};
#define ATH10K_CIS_READ_WAIT_4_RTC_CYCLE_IN_US 125
#define ATH10K_CIS_RTC_STATE_ADDR 0x1138
#define ATH10K_CIS_RTC_STATE_ON 0x01
#define ATH10K_CIS_XTAL_SETTLE_DURATION_IN_US 1500
#define ATH10K_CIS_READ_RETRY 10
#define ATH10K_MIN_SLEEP_INACTIVITY_TIME_MS 50
/* TODO: remove this and use skb->cb instead, much cleaner approach */
struct ath10k_sdio_bus_request {
struct list_head list;
/* sdio address */
u32 address;
struct sk_buff *skb;
enum ath10k_htc_ep_id eid;
int status;
/* Specifies if the current request is an HTC message.
* If not, the eid is not applicable an the TX completion handler
* associated with the endpoint will not be invoked.
*/
bool htc_msg;
/* Completion that (if set) will be invoked for non HTC requests
* (htc_msg == false) when the request has been processed.
*/
struct completion *comp;
};
struct ath10k_sdio_rx_data {
struct sk_buff *skb;
size_t alloc_len;
size_t act_len;
enum ath10k_htc_ep_id eid;
bool part_of_bundle;
bool last_in_bundle;
bool trailer_only;
};
struct ath10k_sdio_irq_proc_regs {
u8 host_int_status;
u8 cpu_int_status;
u8 error_int_status;
u8 counter_int_status;
u8 mbox_frame;
u8 rx_lookahead_valid;
u8 host_int_status2;
u8 gmbox_rx_avail;
__le32 rx_lookahead[2 * ATH10K_HIF_MBOX_NUM_MAX];
__le32 int_status_enable;
};
struct ath10k_sdio_irq_enable_regs {
u8 int_status_en;
u8 cpu_int_status_en;
u8 err_int_status_en;
u8 cntr_int_status_en;
};
struct ath10k_sdio_irq_data {
/* protects irq_proc_reg and irq_en_reg below.
* We use a mutex here and not a spinlock since we will have the
* mutex locked while calling the sdio_memcpy_ functions.
* These function require non atomic context, and hence, spinlocks
* can be held while calling these functions.
*/
struct mutex mtx;
struct ath10k_sdio_irq_proc_regs *irq_proc_reg;
struct ath10k_sdio_irq_enable_regs *irq_en_reg;
};
struct ath10k_mbox_ext_info {
u32 htc_ext_addr;
u32 htc_ext_sz;
};
struct ath10k_mbox_info {
u32 htc_addr;
struct ath10k_mbox_ext_info ext_info[2];
u32 block_size;
u32 block_mask;
u32 gmbox_addr;
u32 gmbox_sz;
};
struct ath10k_sdio {
struct sdio_func *func;
struct ath10k_mbox_info mbox_info;
bool swap_mbox;
u32 mbox_addr[ATH10K_HTC_EP_COUNT];
u32 mbox_size[ATH10K_HTC_EP_COUNT];
/* available bus requests */
struct ath10k_sdio_bus_request bus_req[ATH10K_SDIO_BUS_REQUEST_MAX_NUM];
/* free list of bus requests */
struct list_head bus_req_freeq;
struct sk_buff_head rx_head;
/* protects access to bus_req_freeq */
spinlock_t lock;
struct ath10k_sdio_rx_data rx_pkts[ATH10K_SDIO_MAX_RX_MSGS];
size_t n_rx_pkts;
struct ath10k *ar;
struct ath10k_sdio_irq_data irq_data;
/* temporary buffer for sdio read.
* It is allocated when probe, and used for receive bundled packets,
* the read for bundled packets is not parallel, so it does not need
* protected.
*/
u8 *vsg_buffer;
/* temporary buffer for BMI requests */
u8 *bmi_buf;
bool is_disabled;
struct workqueue_struct *workqueue;
struct work_struct wr_async_work;
struct list_head wr_asyncq;
/* protects access to wr_asyncq */
spinlock_t wr_async_lock;
struct work_struct async_work_rx;
struct timer_list sleep_timer;
enum sdio_mbox_state mbox_state;
};
static inline struct ath10k_sdio *ath10k_sdio_priv(struct ath10k *ar)
{
return (struct ath10k_sdio *)ar->drv_priv;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*/
#ifndef _SNOC_H_
#define _SNOC_H_
#include <linux/notifier.h>
#include "hw.h"
#include "ce.h"
#include "qmi.h"
struct ath10k_snoc_drv_priv {
enum ath10k_hw_rev hw_rev;
u64 dma_mask;
u32 msa_size;
};
struct snoc_state {
u32 pipe_cfg_addr;
u32 svc_to_pipe_map;
};
struct ath10k_snoc_pipe {
struct ath10k_ce_pipe *ce_hdl;
u8 pipe_num;
struct ath10k *hif_ce_state;
size_t buf_sz;
/* protect ce info */
spinlock_t pipe_lock;
struct ath10k_snoc *ar_snoc;
};
struct ath10k_snoc_target_info {
u32 target_version;
u32 target_type;
u32 target_revision;
u32 soc_version;
};
struct ath10k_snoc_ce_irq {
u32 irq_line;
};
enum ath10k_snoc_flags {
ATH10K_SNOC_FLAG_REGISTERED,
ATH10K_SNOC_FLAG_UNREGISTERING,
ATH10K_SNOC_FLAG_MODEM_STOPPED,
ATH10K_SNOC_FLAG_RECOVERY,
ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK,
};
struct clk_bulk_data;
struct regulator_bulk_data;
struct ath10k_snoc {
struct platform_device *dev;
struct ath10k *ar;
unsigned int use_tz;
struct ath10k_firmware {
struct device *dev;
dma_addr_t fw_start_addr;
struct iommu_domain *iommu_domain;
size_t mapped_mem_size;
} fw;
void __iomem *mem;
dma_addr_t mem_pa;
struct ath10k_snoc_target_info target_info;
size_t mem_len;
struct ath10k_snoc_pipe pipe_info[CE_COUNT_MAX];
struct ath10k_snoc_ce_irq ce_irqs[CE_COUNT_MAX];
struct ath10k_ce ce;
struct timer_list rx_post_retry;
struct regulator_bulk_data *vregs;
size_t num_vregs;
struct clk_bulk_data *clks;
size_t num_clks;
struct ath10k_qmi *qmi;
struct notifier_block nb;
void *notifier;
unsigned long flags;
bool xo_cal_supported;
u32 xo_cal_data;
DECLARE_BITMAP(pending_ce_irqs, CE_COUNT_MAX);
};
static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
{
return (struct ath10k_snoc *)ar->drv_priv;
}
int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type);
void ath10k_snoc_fw_crashed_dump(struct ath10k *ar);
#endif /* _SNOC_H_ */

View File

@ -0,0 +1,560 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2013-2017 Qualcomm Atheros, Inc.
*/
#include <linux/relay.h>
#include "core.h"
#include "debug.h"
#include "wmi-ops.h"
static void send_fft_sample(struct ath10k *ar,
const struct fft_sample_tlv *fft_sample_tlv)
{
int length;
if (!ar->spectral.rfs_chan_spec_scan)
return;
length = __be16_to_cpu(fft_sample_tlv->length) +
sizeof(*fft_sample_tlv);
relay_write(ar->spectral.rfs_chan_spec_scan, fft_sample_tlv, length);
}
static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len,
u8 *data)
{
int dc_pos;
u8 max_exp;
dc_pos = bin_len / 2;
/* peak index outside of bins */
if (dc_pos < max_index || -dc_pos >= max_index)
return 0;
for (max_exp = 0; max_exp < 8; max_exp++) {
if (data[dc_pos + max_index] == (max_magnitude >> max_exp))
break;
}
/* max_exp not found */
if (data[dc_pos + max_index] != (max_magnitude >> max_exp))
return 0;
return max_exp;
}
static inline size_t ath10k_spectral_fix_bin_size(struct ath10k *ar,
size_t bin_len)
{
/* some chipsets reports bin size as 2^n bytes + 'm' bytes in
* report mode 2. First 2^n bytes carries inband tones and last
* 'm' bytes carries band edge detection data mainly used in
* radar detection purpose. Strip last 'm' bytes to make bin size
* as a valid one. 'm' can take possible values of 4, 12.
*/
if (!is_power_of_2(bin_len))
bin_len -= ar->hw_params.spectral_bin_discard;
return bin_len;
}
int ath10k_spectral_process_fft(struct ath10k *ar,
struct wmi_phyerr_ev_arg *phyerr,
const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf)
{
struct fft_sample_ath10k *fft_sample;
u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS];
u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag;
u32 reg0, reg1;
u8 chain_idx, *bins;
int dc_pos;
fft_sample = (struct fft_sample_ath10k *)&buf;
bin_len = ath10k_spectral_fix_bin_size(ar, bin_len);
if (bin_len < 64 || bin_len > SPECTRAL_ATH10K_MAX_NUM_BINS)
return -EINVAL;
reg0 = __le32_to_cpu(fftr->reg0);
reg1 = __le32_to_cpu(fftr->reg1);
length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + bin_len;
fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH10K;
fft_sample->tlv.length = __cpu_to_be16(length);
/* TODO: there might be a reason why the hardware reports 20/40/80 MHz,
* but the results/plots suggest that its actually 22/44/88 MHz.
*/
switch (phyerr->chan_width_mhz) {
case 20:
fft_sample->chan_width_mhz = 22;
break;
case 40:
fft_sample->chan_width_mhz = 44;
break;
case 80:
/* TODO: As experiments with an analogue sender and various
* configurations (fft-sizes of 64/128/256 and 20/40/80 Mhz)
* show, the particular configuration of 80 MHz/64 bins does
* not match with the other samples at all. Until the reason
* for that is found, don't report these samples.
*/
if (bin_len == 64)
return -EINVAL;
fft_sample->chan_width_mhz = 88;
break;
default:
fft_sample->chan_width_mhz = phyerr->chan_width_mhz;
}
fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB);
fft_sample->avgpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB);
peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
fft_sample->max_magnitude = __cpu_to_be16(peak_mag);
fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX);
fft_sample->rssi = phyerr->rssi_combined;
total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB);
base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB);
fft_sample->total_gain_db = __cpu_to_be16(total_gain_db);
fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db);
freq1 = phyerr->freq1;
freq2 = phyerr->freq2;
fft_sample->freq1 = __cpu_to_be16(freq1);
fft_sample->freq2 = __cpu_to_be16(freq2);
chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX);
fft_sample->noise = __cpu_to_be16(phyerr->nf_chains[chain_idx]);
bins = (u8 *)fftr;
bins += sizeof(*fftr) + ar->hw_params.spectral_bin_offset;
fft_sample->tsf = __cpu_to_be64(tsf);
/* max_exp has been directly reported by previous hardware (ath9k),
* maybe its possible to get it by other means?
*/
fft_sample->max_exp = get_max_exp(fft_sample->max_index, peak_mag,
bin_len, bins);
memcpy(fft_sample->data, bins, bin_len);
/* DC value (value in the middle) is the blind spot of the spectral
* sample and invalid, interpolate it.
*/
dc_pos = bin_len / 2;
fft_sample->data[dc_pos] = (fft_sample->data[dc_pos + 1] +
fft_sample->data[dc_pos - 1]) / 2;
send_fft_sample(ar, &fft_sample->tlv);
return 0;
}
static struct ath10k_vif *ath10k_get_spectral_vdev(struct ath10k *ar)
{
struct ath10k_vif *arvif;
lockdep_assert_held(&ar->conf_mutex);
if (list_empty(&ar->arvifs))
return NULL;
/* if there already is a vif doing spectral, return that. */
list_for_each_entry(arvif, &ar->arvifs, list)
if (arvif->spectral_enabled)
return arvif;
/* otherwise, return the first vif. */
return list_first_entry(&ar->arvifs, typeof(*arvif), list);
}
static int ath10k_spectral_scan_trigger(struct ath10k *ar)
{
struct ath10k_vif *arvif;
int res;
int vdev_id;
lockdep_assert_held(&ar->conf_mutex);
arvif = ath10k_get_spectral_vdev(ar);
if (!arvif)
return -ENODEV;
vdev_id = arvif->vdev_id;
if (ar->spectral.mode == SPECTRAL_DISABLED)
return 0;
res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
WMI_SPECTRAL_ENABLE_CMD_ENABLE);
if (res < 0)
return res;
res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
WMI_SPECTRAL_ENABLE_CMD_ENABLE);
if (res < 0)
return res;
return 0;
}
static int ath10k_spectral_scan_config(struct ath10k *ar,
enum ath10k_spectral_mode mode)
{
struct wmi_vdev_spectral_conf_arg arg;
struct ath10k_vif *arvif;
int vdev_id, count, res = 0;
lockdep_assert_held(&ar->conf_mutex);
arvif = ath10k_get_spectral_vdev(ar);
if (!arvif)
return -ENODEV;
vdev_id = arvif->vdev_id;
arvif->spectral_enabled = (mode != SPECTRAL_DISABLED);
ar->spectral.mode = mode;
res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
WMI_SPECTRAL_ENABLE_CMD_DISABLE);
if (res < 0) {
ath10k_warn(ar, "failed to enable spectral scan: %d\n", res);
return res;
}
if (mode == SPECTRAL_DISABLED)
return 0;
if (mode == SPECTRAL_BACKGROUND)
count = WMI_SPECTRAL_COUNT_DEFAULT;
else
count = max_t(u8, 1, ar->spectral.config.count);
arg.vdev_id = vdev_id;
arg.scan_count = count;
arg.scan_period = WMI_SPECTRAL_PERIOD_DEFAULT;
arg.scan_priority = WMI_SPECTRAL_PRIORITY_DEFAULT;
arg.scan_fft_size = ar->spectral.config.fft_size;
arg.scan_gc_ena = WMI_SPECTRAL_GC_ENA_DEFAULT;
arg.scan_restart_ena = WMI_SPECTRAL_RESTART_ENA_DEFAULT;
arg.scan_noise_floor_ref = WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
arg.scan_init_delay = WMI_SPECTRAL_INIT_DELAY_DEFAULT;
arg.scan_nb_tone_thr = WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
arg.scan_str_bin_thr = WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
arg.scan_wb_rpt_mode = WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
arg.scan_rssi_rpt_mode = WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
arg.scan_rssi_thr = WMI_SPECTRAL_RSSI_THR_DEFAULT;
arg.scan_pwr_format = WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
arg.scan_rpt_mode = WMI_SPECTRAL_RPT_MODE_DEFAULT;
arg.scan_bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
arg.scan_dbm_adj = WMI_SPECTRAL_DBM_ADJ_DEFAULT;
arg.scan_chn_mask = WMI_SPECTRAL_CHN_MASK_DEFAULT;
res = ath10k_wmi_vdev_spectral_conf(ar, &arg);
if (res < 0) {
ath10k_warn(ar, "failed to configure spectral scan: %d\n", res);
return res;
}
return 0;
}
static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
char *mode = "";
size_t len;
enum ath10k_spectral_mode spectral_mode;
mutex_lock(&ar->conf_mutex);
spectral_mode = ar->spectral.mode;
mutex_unlock(&ar->conf_mutex);
switch (spectral_mode) {
case SPECTRAL_DISABLED:
mode = "disable";
break;
case SPECTRAL_BACKGROUND:
mode = "background";
break;
case SPECTRAL_MANUAL:
mode = "manual";
break;
}
len = strlen(mode);
return simple_read_from_buffer(user_buf, count, ppos, mode, len);
}
static ssize_t write_file_spec_scan_ctl(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
char buf[32];
ssize_t len;
int res;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
mutex_lock(&ar->conf_mutex);
if (strncmp("trigger", buf, 7) == 0) {
if (ar->spectral.mode == SPECTRAL_MANUAL ||
ar->spectral.mode == SPECTRAL_BACKGROUND) {
/* reset the configuration to adopt possibly changed
* debugfs parameters
*/
res = ath10k_spectral_scan_config(ar,
ar->spectral.mode);
if (res < 0) {
ath10k_warn(ar, "failed to reconfigure spectral scan: %d\n",
res);
}
res = ath10k_spectral_scan_trigger(ar);
if (res < 0) {
ath10k_warn(ar, "failed to trigger spectral scan: %d\n",
res);
}
} else {
res = -EINVAL;
}
} else if (strncmp("background", buf, 10) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND);
} else if (strncmp("manual", buf, 6) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
} else if (strncmp("disable", buf, 7) == 0) {
res = ath10k_spectral_scan_config(ar, SPECTRAL_DISABLED);
} else {
res = -EINVAL;
}
mutex_unlock(&ar->conf_mutex);
if (res < 0)
return res;
return count;
}
static const struct file_operations fops_spec_scan_ctl = {
.read = read_file_spec_scan_ctl,
.write = write_file_spec_scan_ctl,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t read_file_spectral_count(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
char buf[32];
size_t len;
u8 spectral_count;
mutex_lock(&ar->conf_mutex);
spectral_count = ar->spectral.config.count;
mutex_unlock(&ar->conf_mutex);
len = sprintf(buf, "%d\n", spectral_count);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_spectral_count(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val > 255)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
ar->spectral.config.count = val;
mutex_unlock(&ar->conf_mutex);
return count;
}
static const struct file_operations fops_spectral_count = {
.read = read_file_spectral_count,
.write = write_file_spectral_count,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static ssize_t read_file_spectral_bins(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
char buf[32];
unsigned int bins, fft_size, bin_scale;
size_t len;
mutex_lock(&ar->conf_mutex);
fft_size = ar->spectral.config.fft_size;
bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
bins = 1 << (fft_size - bin_scale);
mutex_unlock(&ar->conf_mutex);
len = sprintf(buf, "%d\n", bins);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_spectral_bins(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val < 64 || val > SPECTRAL_ATH10K_MAX_NUM_BINS)
return -EINVAL;
if (!is_power_of_2(val))
return -EINVAL;
mutex_lock(&ar->conf_mutex);
ar->spectral.config.fft_size = ilog2(val);
ar->spectral.config.fft_size += WMI_SPECTRAL_BIN_SCALE_DEFAULT;
mutex_unlock(&ar->conf_mutex);
return count;
}
static const struct file_operations fops_spectral_bins = {
.read = read_file_spectral_bins,
.write = write_file_spectral_bins,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static struct dentry *create_buf_file_handler(const char *filename,
struct dentry *parent,
umode_t mode,
struct rchan_buf *buf,
int *is_global)
{
struct dentry *buf_file;
buf_file = debugfs_create_file(filename, mode, parent, buf,
&relay_file_operations);
if (IS_ERR(buf_file))
return NULL;
*is_global = 1;
return buf_file;
}
static int remove_buf_file_handler(struct dentry *dentry)
{
debugfs_remove(dentry);
return 0;
}
static const struct rchan_callbacks rfs_spec_scan_cb = {
.create_buf_file = create_buf_file_handler,
.remove_buf_file = remove_buf_file_handler,
};
int ath10k_spectral_start(struct ath10k *ar)
{
struct ath10k_vif *arvif;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list)
arvif->spectral_enabled = 0;
ar->spectral.mode = SPECTRAL_DISABLED;
ar->spectral.config.count = WMI_SPECTRAL_COUNT_DEFAULT;
ar->spectral.config.fft_size = WMI_SPECTRAL_FFT_SIZE_DEFAULT;
return 0;
}
int ath10k_spectral_vif_stop(struct ath10k_vif *arvif)
{
if (!arvif->spectral_enabled)
return 0;
return ath10k_spectral_scan_config(arvif->ar, SPECTRAL_DISABLED);
}
int ath10k_spectral_create(struct ath10k *ar)
{
/* The buffer size covers whole channels in dual bands up to 128 bins.
* Scan with bigger than 128 bins needs to be run on single band each.
*/
ar->spectral.rfs_chan_spec_scan = relay_open("spectral_scan",
ar->debug.debugfs_phy,
1140, 2500,
&rfs_spec_scan_cb, NULL);
debugfs_create_file("spectral_scan_ctl",
0600,
ar->debug.debugfs_phy, ar,
&fops_spec_scan_ctl);
debugfs_create_file("spectral_count",
0600,
ar->debug.debugfs_phy, ar,
&fops_spectral_count);
debugfs_create_file("spectral_bins",
0600,
ar->debug.debugfs_phy, ar,
&fops_spectral_bins);
return 0;
}
void ath10k_spectral_destroy(struct ath10k *ar)
{
if (ar->spectral.rfs_chan_spec_scan) {
relay_close(ar->spectral.rfs_chan_spec_scan);
ar->spectral.rfs_chan_spec_scan = NULL;
}
}

View File

@ -0,0 +1,79 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2013-2015 Qualcomm Atheros, Inc.
*/
#ifndef SPECTRAL_H
#define SPECTRAL_H
#include "../spectral_common.h"
/**
* struct ath10k_spec_scan - parameters for Atheros spectral scan
*
* @count: number of scan results requested for manual mode
* @fft_size: number of bins to be requested = 2^(fft_size - bin_scale)
*/
struct ath10k_spec_scan {
u8 count;
u8 fft_size;
};
/* enum ath10k_spectral_mode:
*
* @SPECTRAL_DISABLED: spectral mode is disabled
* @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
* something else.
* @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
* is performed manually.
*/
enum ath10k_spectral_mode {
SPECTRAL_DISABLED = 0,
SPECTRAL_BACKGROUND,
SPECTRAL_MANUAL,
};
#ifdef CONFIG_ATH10K_SPECTRAL
int ath10k_spectral_process_fft(struct ath10k *ar,
struct wmi_phyerr_ev_arg *phyerr,
const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf);
int ath10k_spectral_start(struct ath10k *ar);
int ath10k_spectral_vif_stop(struct ath10k_vif *arvif);
int ath10k_spectral_create(struct ath10k *ar);
void ath10k_spectral_destroy(struct ath10k *ar);
#else
static inline int
ath10k_spectral_process_fft(struct ath10k *ar,
struct wmi_phyerr_ev_arg *phyerr,
const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf)
{
return 0;
}
static inline int ath10k_spectral_start(struct ath10k *ar)
{
return 0;
}
static inline int ath10k_spectral_vif_stop(struct ath10k_vif *arvif)
{
return 0;
}
static inline int ath10k_spectral_create(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_spectral_destroy(struct ath10k *ar)
{
}
#endif /* CONFIG_ATH10K_SPECTRAL */
#endif /* SPECTRAL_H */

View File

@ -0,0 +1,199 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2015-2016 Qualcomm Atheros, Inc.
*/
/* This file has implementation for code swap logic. With code swap feature,
* target can run the fw binary with even smaller IRAM size by using host
* memory to store some of the code segments.
*/
#include "core.h"
#include "bmi.h"
#include "debug.h"
static int ath10k_swap_code_seg_fill(struct ath10k *ar,
struct ath10k_swap_code_seg_info *seg_info,
const void *data, size_t data_len)
{
u8 *virt_addr = seg_info->virt_address[0];
u8 swap_magic[ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ] = {};
const u8 *fw_data = data;
const union ath10k_swap_code_seg_item *swap_item;
u32 length = 0;
u32 payload_len;
u32 total_payload_len = 0;
u32 size_left = data_len;
/* Parse swap bin and copy the content to host allocated memory.
* The format is Address, length and value. The last 4-bytes is
* target write address. Currently address field is not used.
*/
seg_info->target_addr = -1;
while (size_left >= sizeof(*swap_item)) {
swap_item = (const union ath10k_swap_code_seg_item *)fw_data;
payload_len = __le32_to_cpu(swap_item->tlv.length);
if ((payload_len > size_left) ||
(payload_len == 0 &&
size_left != sizeof(struct ath10k_swap_code_seg_tail))) {
ath10k_err(ar, "refusing to parse invalid tlv length %d\n",
payload_len);
return -EINVAL;
}
if (payload_len == 0) {
if (memcmp(swap_item->tail.magic_signature, swap_magic,
ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ)) {
ath10k_err(ar, "refusing an invalid swap file\n");
return -EINVAL;
}
seg_info->target_addr =
__le32_to_cpu(swap_item->tail.bmi_write_addr);
break;
}
memcpy(virt_addr, swap_item->tlv.data, payload_len);
virt_addr += payload_len;
length = payload_len + sizeof(struct ath10k_swap_code_seg_tlv);
size_left -= length;
fw_data += length;
total_payload_len += payload_len;
}
if (seg_info->target_addr == -1) {
ath10k_err(ar, "failed to parse invalid swap file\n");
return -EINVAL;
}
seg_info->seg_hw_info.swap_size = __cpu_to_le32(total_payload_len);
return 0;
}
static void
ath10k_swap_code_seg_free(struct ath10k *ar,
struct ath10k_swap_code_seg_info *seg_info)
{
u32 seg_size;
if (!seg_info)
return;
if (!seg_info->virt_address[0])
return;
seg_size = __le32_to_cpu(seg_info->seg_hw_info.size);
dma_free_coherent(ar->dev, seg_size, seg_info->virt_address[0],
seg_info->paddr[0]);
}
static struct ath10k_swap_code_seg_info *
ath10k_swap_code_seg_alloc(struct ath10k *ar, size_t swap_bin_len)
{
struct ath10k_swap_code_seg_info *seg_info;
void *virt_addr;
dma_addr_t paddr;
swap_bin_len = roundup(swap_bin_len, 2);
if (swap_bin_len > ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX) {
ath10k_err(ar, "refusing code swap bin because it is too big %zu > %d\n",
swap_bin_len, ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX);
return NULL;
}
seg_info = devm_kzalloc(ar->dev, sizeof(*seg_info), GFP_KERNEL);
if (!seg_info)
return NULL;
virt_addr = dma_alloc_coherent(ar->dev, swap_bin_len, &paddr,
GFP_KERNEL);
if (!virt_addr)
return NULL;
seg_info->seg_hw_info.bus_addr[0] = __cpu_to_le32(paddr);
seg_info->seg_hw_info.size = __cpu_to_le32(swap_bin_len);
seg_info->seg_hw_info.swap_size = __cpu_to_le32(swap_bin_len);
seg_info->seg_hw_info.num_segs =
__cpu_to_le32(ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED);
seg_info->seg_hw_info.size_log2 = __cpu_to_le32(ilog2(swap_bin_len));
seg_info->virt_address[0] = virt_addr;
seg_info->paddr[0] = paddr;
return seg_info;
}
int ath10k_swap_code_seg_configure(struct ath10k *ar,
const struct ath10k_fw_file *fw_file)
{
int ret;
struct ath10k_swap_code_seg_info *seg_info = NULL;
if (!fw_file->firmware_swap_code_seg_info)
return 0;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot found firmware code swap binary\n");
seg_info = fw_file->firmware_swap_code_seg_info;
ret = ath10k_bmi_write_memory(ar, seg_info->target_addr,
#if defined(__linux__)
&seg_info->seg_hw_info,
#elif defined(__FreeBSD__)
(void *)&seg_info->seg_hw_info,
#endif
sizeof(seg_info->seg_hw_info));
if (ret) {
ath10k_err(ar, "failed to write Code swap segment information (%d)\n",
ret);
return ret;
}
return 0;
}
void ath10k_swap_code_seg_release(struct ath10k *ar,
struct ath10k_fw_file *fw_file)
{
ath10k_swap_code_seg_free(ar, fw_file->firmware_swap_code_seg_info);
/* FIXME: these two assignments look to bein wrong place! Shouldn't
* they be in ath10k_core_free_firmware_files() like the rest?
*/
fw_file->codeswap_data = NULL;
fw_file->codeswap_len = 0;
fw_file->firmware_swap_code_seg_info = NULL;
}
int ath10k_swap_code_seg_init(struct ath10k *ar, struct ath10k_fw_file *fw_file)
{
int ret;
struct ath10k_swap_code_seg_info *seg_info;
const void *codeswap_data;
size_t codeswap_len;
codeswap_data = fw_file->codeswap_data;
codeswap_len = fw_file->codeswap_len;
if (!codeswap_len || !codeswap_data)
return 0;
seg_info = ath10k_swap_code_seg_alloc(ar, codeswap_len);
if (!seg_info) {
ath10k_err(ar, "failed to allocate fw code swap segment\n");
return -ENOMEM;
}
ret = ath10k_swap_code_seg_fill(ar, seg_info,
codeswap_data, codeswap_len);
if (ret) {
ath10k_warn(ar, "failed to initialize fw code swap segment: %d\n",
ret);
ath10k_swap_code_seg_free(ar, seg_info);
return ret;
}
fw_file->firmware_swap_code_seg_info = seg_info;
return 0;
}

View File

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2015-2016 Qualcomm Atheros, Inc.
*/
#ifndef _SWAP_H_
#define _SWAP_H_
#define ATH10K_SWAP_CODE_SEG_BIN_LEN_MAX (512 * 1024)
#define ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ 12
#define ATH10K_SWAP_CODE_SEG_NUM_MAX 16
/* Currently only one swap segment is supported */
#define ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED 1
struct ath10k_fw_file;
struct ath10k_swap_code_seg_tlv {
__le32 address;
__le32 length;
u8 data[0];
} __packed;
struct ath10k_swap_code_seg_tail {
u8 magic_signature[ATH10K_SWAP_CODE_SEG_MAGIC_BYTES_SZ];
__le32 bmi_write_addr;
} __packed;
union ath10k_swap_code_seg_item {
struct ath10k_swap_code_seg_tlv tlv;
struct ath10k_swap_code_seg_tail tail;
} __packed;
struct ath10k_swap_code_seg_hw_info {
/* Swap binary image size */
__le32 swap_size;
__le32 num_segs;
/* Swap data size */
__le32 size;
__le32 size_log2;
__le32 bus_addr[ATH10K_SWAP_CODE_SEG_NUM_MAX];
__le64 reserved[ATH10K_SWAP_CODE_SEG_NUM_MAX];
} __packed;
struct ath10k_swap_code_seg_info {
struct ath10k_swap_code_seg_hw_info seg_hw_info;
void *virt_address[ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED];
u32 target_addr;
dma_addr_t paddr[ATH10K_SWAP_CODE_SEG_NUM_SUPPORTED];
};
int ath10k_swap_code_seg_configure(struct ath10k *ar,
const struct ath10k_fw_file *fw_file);
void ath10k_swap_code_seg_release(struct ath10k *ar,
struct ath10k_fw_file *fw_file);
int ath10k_swap_code_seg_init(struct ath10k *ar,
struct ath10k_fw_file *fw_file);
#endif

View File

@ -0,0 +1,494 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2016 Qualcomm Atheros, Inc.
*/
#ifndef __TARGADDRS_H__
#define __TARGADDRS_H__
#include "hw.h"
/*
* xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the
* host_interest structure. It must match the address of the _host_interest
* symbol (see linker script).
*
* Host Interest is shared between Host and Target in order to coordinate
* between the two, and is intended to remain constant (with additions only
* at the end) across software releases.
*
* All addresses are available here so that it's possible to
* write a single binary that works with all Target Types.
* May be used in assembler code as well as C.
*/
#define QCA988X_HOST_INTEREST_ADDRESS 0x00400800
#define HOST_INTEREST_MAX_SIZE 0x200
/*
* These are items that the Host may need to access via BMI or via the
* Diagnostic Window. The position of items in this structure must remain
* constant across firmware revisions! Types for each item must be fixed
* size across target and host platforms. More items may be added at the end.
*/
struct host_interest {
/*
* Pointer to application-defined area, if any.
* Set by Target application during startup.
*/
u32 hi_app_host_interest; /* 0x00 */
/* Pointer to register dump area, valid after Target crash. */
u32 hi_failure_state; /* 0x04 */
/* Pointer to debug logging header */
u32 hi_dbglog_hdr; /* 0x08 */
u32 hi_unused0c; /* 0x0c */
/*
* General-purpose flag bits, similar to SOC_OPTION_* flags.
* Can be used by application rather than by OS.
*/
u32 hi_option_flag; /* 0x10 */
/*
* Boolean that determines whether or not to
* display messages on the serial port.
*/
u32 hi_serial_enable; /* 0x14 */
/* Start address of DataSet index, if any */
u32 hi_dset_list_head; /* 0x18 */
/* Override Target application start address */
u32 hi_app_start; /* 0x1c */
/* Clock and voltage tuning */
u32 hi_skip_clock_init; /* 0x20 */
u32 hi_core_clock_setting; /* 0x24 */
u32 hi_cpu_clock_setting; /* 0x28 */
u32 hi_system_sleep_setting; /* 0x2c */
u32 hi_xtal_control_setting; /* 0x30 */
u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */
u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */
u32 hi_ref_voltage_trim_setting; /* 0x3c */
u32 hi_clock_info; /* 0x40 */
/* Host uses BE CPU or not */
u32 hi_be; /* 0x44 */
u32 hi_stack; /* normal stack */ /* 0x48 */
u32 hi_err_stack; /* error stack */ /* 0x4c */
u32 hi_desired_cpu_speed_hz; /* 0x50 */
/* Pointer to Board Data */
u32 hi_board_data; /* 0x54 */
/*
* Indication of Board Data state:
* 0: board data is not yet initialized.
* 1: board data is initialized; unknown size
* >1: number of bytes of initialized board data
*/
u32 hi_board_data_initialized; /* 0x58 */
u32 hi_dset_ram_index_table; /* 0x5c */
u32 hi_desired_baud_rate; /* 0x60 */
u32 hi_dbglog_config; /* 0x64 */
u32 hi_end_ram_reserve_sz; /* 0x68 */
u32 hi_mbox_io_block_sz; /* 0x6c */
u32 hi_num_bpatch_streams; /* 0x70 -- unused */
u32 hi_mbox_isr_yield_limit; /* 0x74 */
u32 hi_refclk_hz; /* 0x78 */
u32 hi_ext_clk_detected; /* 0x7c */
u32 hi_dbg_uart_txpin; /* 0x80 */
u32 hi_dbg_uart_rxpin; /* 0x84 */
u32 hi_hci_uart_baud; /* 0x88 */
u32 hi_hci_uart_pin_assignments; /* 0x8C */
u32 hi_hci_uart_baud_scale_val; /* 0x90 */
u32 hi_hci_uart_baud_step_val; /* 0x94 */
u32 hi_allocram_start; /* 0x98 */
u32 hi_allocram_sz; /* 0x9c */
u32 hi_hci_bridge_flags; /* 0xa0 */
u32 hi_hci_uart_support_pins; /* 0xa4 */
u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */
/*
* 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high
* [31:16]: wakeup timeout in ms
*/
/* Pointer to extended board Data */
u32 hi_board_ext_data; /* 0xac */
u32 hi_board_ext_data_config; /* 0xb0 */
/*
* Bit [0] : valid
* Bit[31:16: size
*/
/*
* hi_reset_flag is used to do some stuff when target reset.
* such as restore app_start after warm reset or
* preserve host Interest area, or preserve ROM data, literals etc.
*/
u32 hi_reset_flag; /* 0xb4 */
/* indicate hi_reset_flag is valid */
u32 hi_reset_flag_valid; /* 0xb8 */
u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */
/* 0xbc - [31:0]: idle timeout in ms */
/* ACS flags */
u32 hi_acs_flags; /* 0xc0 */
u32 hi_console_flags; /* 0xc4 */
u32 hi_nvram_state; /* 0xc8 */
u32 hi_option_flag2; /* 0xcc */
/* If non-zero, override values sent to Host in WMI_READY event. */
u32 hi_sw_version_override; /* 0xd0 */
u32 hi_abi_version_override; /* 0xd4 */
/*
* Percentage of high priority RX traffic to total expected RX traffic
* applicable only to ar6004
*/
u32 hi_hp_rx_traffic_ratio; /* 0xd8 */
/* test applications flags */
u32 hi_test_apps_related; /* 0xdc */
/* location of test script */
u32 hi_ota_testscript; /* 0xe0 */
/* location of CAL data */
u32 hi_cal_data; /* 0xe4 */
/* Number of packet log buffers */
u32 hi_pktlog_num_buffers; /* 0xe8 */
/* wow extension configuration */
u32 hi_wow_ext_config; /* 0xec */
u32 hi_pwr_save_flags; /* 0xf0 */
/* Spatial Multiplexing Power Save (SMPS) options */
u32 hi_smps_options; /* 0xf4 */
/* Interconnect-specific state */
u32 hi_interconnect_state; /* 0xf8 */
/* Coex configuration flags */
u32 hi_coex_config; /* 0xfc */
/* Early allocation support */
u32 hi_early_alloc; /* 0x100 */
/* FW swap field */
/*
* Bits of this 32bit word will be used to pass specific swap
* instruction to FW
*/
/*
* Bit 0 -- AP Nart descriptor no swap. When this bit is set
* FW will not swap TX descriptor. Meaning packets are formed
* on the target processor.
*/
/* Bit 1 - unused */
u32 hi_fw_swap; /* 0x104 */
/* global arenas pointer address, used by host driver debug */
u32 hi_dynamic_mem_arenas_addr; /* 0x108 */
/* allocated bytes of DRAM use by allocated */
u32 hi_dynamic_mem_allocated; /* 0x10C */
/* remaining bytes of DRAM */
u32 hi_dynamic_mem_remaining; /* 0x110 */
/* memory track count, configured by host */
u32 hi_dynamic_mem_track_max; /* 0x114 */
/* minidump buffer */
u32 hi_minidump; /* 0x118 */
/* bdata's sig and key addr */
u32 hi_bd_sig_key; /* 0x11c */
} __packed;
#define HI_ITEM(item) offsetof(struct host_interest, item)
/* Bits defined in hi_option_flag */
/* Enable timer workaround */
#define HI_OPTION_TIMER_WAR 0x01
/* Limit BMI command credits */
#define HI_OPTION_BMI_CRED_LIMIT 0x02
/* Relay Dot11 hdr to/from host */
#define HI_OPTION_RELAY_DOT11_HDR 0x04
/* MAC addr method 0-locally administred 1-globally unique addrs */
#define HI_OPTION_MAC_ADDR_METHOD 0x08
/* Firmware Bridging */
#define HI_OPTION_FW_BRIDGE 0x10
/* Enable CPU profiling */
#define HI_OPTION_ENABLE_PROFILE 0x20
/* Disable debug logging */
#define HI_OPTION_DISABLE_DBGLOG 0x40
/* Skip Era Tracking */
#define HI_OPTION_SKIP_ERA_TRACKING 0x80
/* Disable PAPRD (debug) */
#define HI_OPTION_PAPRD_DISABLE 0x100
#define HI_OPTION_NUM_DEV_LSB 0x200
#define HI_OPTION_NUM_DEV_MSB 0x800
#define HI_OPTION_DEV_MODE_LSB 0x1000
#define HI_OPTION_DEV_MODE_MSB 0x8000000
/* Disable LowFreq Timer Stabilization */
#define HI_OPTION_NO_LFT_STBL 0x10000000
/* Skip regulatory scan */
#define HI_OPTION_SKIP_REG_SCAN 0x20000000
/*
* Do regulatory scan during init before
* sending WMI ready event to host
*/
#define HI_OPTION_INIT_REG_SCAN 0x40000000
/* REV6: Do not adjust memory map */
#define HI_OPTION_SKIP_MEMMAP 0x80000000
#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3
/* 2 bits of hi_option_flag are used to represent 3 modes */
#define HI_OPTION_FW_MODE_IBSS 0x0 /* IBSS Mode */
#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */
#define HI_OPTION_FW_MODE_AP 0x2 /* AP Mode */
#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */
/* 2 bits of hi_option flag are usedto represent 4 submodes */
#define HI_OPTION_FW_SUBMODE_NONE 0x0 /* Normal mode */
#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1 /* p2p device mode */
#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */
#define HI_OPTION_FW_SUBMODE_P2PGO 0x3 /* p2p go mode */
/* Num dev Mask */
#define HI_OPTION_NUM_DEV_MASK 0x7
#define HI_OPTION_NUM_DEV_SHIFT 0x9
/* firmware bridging */
#define HI_OPTION_FW_BRIDGE_SHIFT 0x04
/*
* Fw Mode/SubMode Mask
*-----------------------------------------------------------------------------
* SUB | SUB | SUB | SUB | | | |
*MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]
* (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2)
*-----------------------------------------------------------------------------
*/
#define HI_OPTION_FW_MODE_BITS 0x2
#define HI_OPTION_FW_MODE_MASK 0x3
#define HI_OPTION_FW_MODE_SHIFT 0xC
#define HI_OPTION_ALL_FW_MODE_MASK 0xFF
#define HI_OPTION_FW_SUBMODE_BITS 0x2
#define HI_OPTION_FW_SUBMODE_MASK 0x3
#define HI_OPTION_FW_SUBMODE_SHIFT 0x14
#define HI_OPTION_ALL_FW_SUBMODE_MASK 0xFF00
#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8
/* hi_option_flag2 options */
#define HI_OPTION_OFFLOAD_AMSDU 0x01
#define HI_OPTION_DFS_SUPPORT 0x02 /* Enable DFS support */
#define HI_OPTION_ENABLE_RFKILL 0x04 /* RFKill Enable Feature*/
#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */
#define HI_OPTION_EARLY_CFG_DONE 0x10 /* Early configuration is complete */
#define HI_OPTION_RF_KILL_SHIFT 0x2
#define HI_OPTION_RF_KILL_MASK 0x1
/* hi_reset_flag */
/* preserve App Start address */
#define HI_RESET_FLAG_PRESERVE_APP_START 0x01
/* preserve host interest */
#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST 0x02
/* preserve ROM data */
#define HI_RESET_FLAG_PRESERVE_ROMDATA 0x04
#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE 0x08
#define HI_RESET_FLAG_PRESERVE_BOOT_INFO 0x10
#define HI_RESET_FLAG_WARM_RESET 0x20
/* define hi_fw_swap bits */
#define HI_DESC_IN_FW_BIT 0x01
/* indicate the reset flag is valid */
#define HI_RESET_FLAG_IS_VALID 0x12345678
/* ACS is enabled */
#define HI_ACS_FLAGS_ENABLED (1 << 0)
/* Use physical WWAN device */
#define HI_ACS_FLAGS_USE_WWAN (1 << 1)
/* Use test VAP */
#define HI_ACS_FLAGS_TEST_VAP (1 << 2)
/* SDIO/mailbox ACS flag definitions */
#define HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET (1 << 0)
#define HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET (1 << 1)
#define HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE (1 << 2)
#define HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_FW_ACK (1 << 16)
#define HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK (1 << 17)
/*
* If both SDIO_CRASH_DUMP_ENHANCEMENT_HOST and SDIO_CRASH_DUMP_ENHANCEMENT_FW
* flags are set, then crashdump upload will be done using the BMI host/target
* communication channel.
*/
/* HOST to support using BMI dump FW memory when hit assert */
#define HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_HOST 0x400
/* FW to support using BMI dump FW memory when hit assert */
#define HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW 0x800
/*
* CONSOLE FLAGS
*
* Bit Range Meaning
* --------- --------------------------------
* 2..0 UART ID (0 = Default)
* 3 Baud Select (0 = 9600, 1 = 115200)
* 30..4 Reserved
* 31 Enable Console
*
*/
#define HI_CONSOLE_FLAGS_ENABLE (1 << 31)
#define HI_CONSOLE_FLAGS_UART_MASK (0x7)
#define HI_CONSOLE_FLAGS_UART_SHIFT 0
#define HI_CONSOLE_FLAGS_BAUD_SELECT (1 << 3)
/* SM power save options */
#define HI_SMPS_ALLOW_MASK (0x00000001)
#define HI_SMPS_MODE_MASK (0x00000002)
#define HI_SMPS_MODE_STATIC (0x00000000)
#define HI_SMPS_MODE_DYNAMIC (0x00000002)
#define HI_SMPS_DISABLE_AUTO_MODE (0x00000004)
#define HI_SMPS_DATA_THRESH_MASK (0x000007f8)
#define HI_SMPS_DATA_THRESH_SHIFT (3)
#define HI_SMPS_RSSI_THRESH_MASK (0x0007f800)
#define HI_SMPS_RSSI_THRESH_SHIFT (11)
#define HI_SMPS_LOWPWR_CM_MASK (0x00380000)
#define HI_SMPS_LOWPWR_CM_SHIFT (15)
#define HI_SMPS_HIPWR_CM_MASK (0x03c00000)
#define HI_SMPS_HIPWR_CM_SHIFT (19)
/*
* WOW Extension configuration
*
* Bit Range Meaning
* --------- --------------------------------
* 8..0 Size of each WOW pattern (max 511)
* 15..9 Number of patterns per list (max 127)
* 17..16 Number of lists (max 4)
* 30..18 Reserved
* 31 Enabled
*
* set values (except enable) to zeros for default settings
*/
#define HI_WOW_EXT_ENABLED_MASK (1 << 31)
#define HI_WOW_EXT_NUM_LIST_SHIFT 16
#define HI_WOW_EXT_NUM_LIST_MASK (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT)
#define HI_WOW_EXT_NUM_PATTERNS_SHIFT 9
#define HI_WOW_EXT_NUM_PATTERNS_MASK (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT)
#define HI_WOW_EXT_PATTERN_SIZE_SHIFT 0
#define HI_WOW_EXT_PATTERN_SIZE_MASK (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT)
#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \
((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \
HI_WOW_EXT_NUM_LIST_MASK) | \
(((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \
HI_WOW_EXT_NUM_PATTERNS_MASK) | \
(((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \
HI_WOW_EXT_PATTERN_SIZE_MASK))
#define HI_WOW_EXT_GET_NUM_LISTS(config) \
(((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT)
#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \
(((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \
HI_WOW_EXT_NUM_PATTERNS_SHIFT)
#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \
(((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \
HI_WOW_EXT_PATTERN_SIZE_SHIFT)
/*
* Early allocation configuration
* Support RAM bank configuration before BMI done and this eases the memory
* allocation at very early stage
* Bit Range Meaning
* --------- ----------------------------------
* [0:3] number of bank assigned to be IRAM
* [4:15] reserved
* [16:31] magic number
*
* Note:
* 1. target firmware would check magic number and if it's a match, firmware
* would consider the bits[0:15] are valid and base on that to calculate
* the end of DRAM. Early allocation would be located at that area and
* may be reclaimed when necessary
* 2. if no magic number is found, early allocation would happen at "_end"
* symbol of ROM which is located before the app-data and might NOT be
* re-claimable. If this is adopted, link script should keep this in
* mind to avoid data corruption.
*/
#define HI_EARLY_ALLOC_MAGIC 0x6d8a
#define HI_EARLY_ALLOC_MAGIC_MASK 0xffff0000
#define HI_EARLY_ALLOC_MAGIC_SHIFT 16
#define HI_EARLY_ALLOC_IRAM_BANKS_MASK 0x0000000f
#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT 0
#define HI_EARLY_ALLOC_VALID() \
((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \
HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC))
#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \
(((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \
>> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT)
/*power save flag bit definitions*/
#define HI_PWR_SAVE_LPL_ENABLED 0x1
/*b1-b3 reserved*/
/*b4-b5 : dev0 LPL type : 0 - none
* 1- Reduce Pwr Search
* 2- Reduce Pwr Listen
*/
/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/
#define HI_PWR_SAVE_LPL_DEV0_LSB 4
#define HI_PWR_SAVE_LPL_DEV_MASK 0x3
/*power save related utility macros*/
#define HI_LPL_ENABLED() \
((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED))
#define HI_DEV_LPL_TYPE_GET(_devix) \
(HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \
(HI_PWR_SAVE_LPL_DEV0_LSB + (_devix) * 2)))
#define HOST_INTEREST_SMPS_IS_ALLOWED() \
((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK))
/* Reserve 1024 bytes for extended board data */
#define QCA988X_BOARD_DATA_SZ 7168
#define QCA988X_BOARD_EXT_DATA_SZ 0
#define QCA9887_BOARD_DATA_SZ 7168
#define QCA9887_BOARD_EXT_DATA_SZ 0
#define QCA6174_BOARD_DATA_SZ 8192
#define QCA6174_BOARD_EXT_DATA_SZ 0
#define QCA9377_BOARD_DATA_SZ QCA6174_BOARD_DATA_SZ
#define QCA9377_BOARD_EXT_DATA_SZ 0
#define QCA99X0_BOARD_DATA_SZ 12288
#define QCA99X0_BOARD_EXT_DATA_SZ 0
/* Dual band extended board data */
#define QCA99X0_EXT_BOARD_DATA_SZ 2048
#define EXT_BOARD_ADDRESS_OFFSET 0x3000
#define QCA4019_BOARD_DATA_SZ 12064
#define QCA4019_BOARD_EXT_DATA_SZ 0
#endif /* __TARGADDRS_H__ */

View File

@ -0,0 +1,469 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
*/
#include "testmode.h"
#include <net/netlink.h>
#include <linux/firmware.h>
#include "debug.h"
#include "wmi.h"
#include "hif.h"
#include "hw.h"
#include "core.h"
#include "testmode_i.h"
static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
[ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },
[ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,
.len = ATH10K_TM_DATA_MAX_LEN },
[ATH10K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
[ATH10K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
[ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
};
/* Returns true if callee consumes the skb and the skb should be discarded.
* Returns false if skb is not used. Does not sleep.
*/
bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
{
struct sk_buff *nl_skb;
bool consumed;
int ret;
ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
"testmode event wmi cmd_id %d skb %pK skb->len %d\n",
cmd_id, skb, skb->len);
ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
spin_lock_bh(&ar->data_lock);
if (!ar->testmode.utf_monitor) {
consumed = false;
goto out;
}
/* Only testmode.c should be handling events from utf firmware,
* otherwise all sort of problems will arise as mac80211 operations
* are not initialised.
*/
consumed = true;
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
2 * sizeof(u32) + skb->len,
GFP_ATOMIC);
if (!nl_skb) {
ath10k_warn(ar,
"failed to allocate skb for testmode wmi event\n");
goto out;
}
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
if (ret) {
ath10k_warn(ar,
"failed to put testmode wmi event cmd attribute: %d\n",
ret);
kfree_skb(nl_skb);
goto out;
}
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
if (ret) {
ath10k_warn(ar,
"failed to put testmode wmi event cmd_id: %d\n",
ret);
kfree_skb(nl_skb);
goto out;
}
ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
if (ret) {
ath10k_warn(ar,
"failed to copy skb to testmode wmi event: %d\n",
ret);
kfree_skb(nl_skb);
goto out;
}
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
out:
spin_unlock_bh(&ar->data_lock);
return consumed;
}
static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[])
{
struct sk_buff *skb;
int ret;
ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
"testmode cmd get version_major %d version_minor %d\n",
ATH10K_TESTMODE_VERSION_MAJOR,
ATH10K_TESTMODE_VERSION_MINOR);
skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy,
nla_total_size(sizeof(u32)));
if (!skb)
return -ENOMEM;
ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MAJOR,
ATH10K_TESTMODE_VERSION_MAJOR);
if (ret) {
kfree_skb(skb);
return ret;
}
ret = nla_put_u32(skb, ATH10K_TM_ATTR_VERSION_MINOR,
ATH10K_TESTMODE_VERSION_MINOR);
if (ret) {
kfree_skb(skb);
return ret;
}
ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION,
ar->normal_mode_fw.fw_file.wmi_op_version);
if (ret) {
kfree_skb(skb);
return ret;
}
return cfg80211_testmode_reply(skb);
}
static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar,
struct ath10k_fw_file *fw_file)
{
char filename[100];
int ret;
snprintf(filename, sizeof(filename), "%s/%s",
ar->hw_params.fw.dir, ATH10K_FW_UTF_FILE);
/* load utf firmware image */
ret = firmware_request_nowarn(&fw_file->firmware, filename, ar->dev);
ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode fw request '%s': %d\n",
filename, ret);
if (ret) {
ath10k_warn(ar, "failed to retrieve utf firmware '%s': %d\n",
filename, ret);
return ret;
}
/* We didn't find FW UTF API 1 ("utf.bin") does not advertise
* firmware features. Do an ugly hack where we force the firmware
* features to match with 10.1 branch so that wmi.c will use the
* correct WMI interface.
*/
fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1;
fw_file->firmware_data = fw_file->firmware->data;
fw_file->firmware_len = fw_file->firmware->size;
return 0;
}
static int ath10k_tm_fetch_firmware(struct ath10k *ar)
{
struct ath10k_fw_components *utf_mode_fw;
int ret;
char fw_name[100];
int fw_api2 = 2;
switch (ar->hif.bus) {
case ATH10K_BUS_SDIO:
case ATH10K_BUS_USB:
scnprintf(fw_name, sizeof(fw_name), "%s-%s-%d.bin",
ATH10K_FW_UTF_FILE_BASE, ath10k_bus_str(ar->hif.bus),
fw_api2);
break;
default:
scnprintf(fw_name, sizeof(fw_name), "%s-%d.bin",
ATH10K_FW_UTF_FILE_BASE, fw_api2);
break;
}
ret = ath10k_core_fetch_firmware_api_n(ar, fw_name,
&ar->testmode.utf_mode_fw.fw_file);
if (ret == 0) {
ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using fw utf api 2");
goto out;
}
ret = ath10k_tm_fetch_utf_firmware_api_1(ar, &ar->testmode.utf_mode_fw.fw_file);
if (ret) {
ath10k_err(ar, "failed to fetch utf firmware binary: %d", ret);
return ret;
}
ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode using utf api 1");
out:
utf_mode_fw = &ar->testmode.utf_mode_fw;
/* Use the same board data file as the normal firmware uses (but
* it's still "owned" by normal_mode_fw so we shouldn't free it.
*/
utf_mode_fw->board_data = ar->normal_mode_fw.board_data;
utf_mode_fw->board_len = ar->normal_mode_fw.board_len;
if (!utf_mode_fw->fw_file.otp_data) {
ath10k_info(ar, "utf.bin didn't contain otp binary, taking it from the normal mode firmware");
utf_mode_fw->fw_file.otp_data = ar->normal_mode_fw.fw_file.otp_data;
utf_mode_fw->fw_file.otp_len = ar->normal_mode_fw.fw_file.otp_len;
}
return 0;
}
static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
{
const char *ver;
int ret;
ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf start\n");
mutex_lock(&ar->conf_mutex);
if (ar->state == ATH10K_STATE_UTF) {
ret = -EALREADY;
goto err;
}
/* start utf only when the driver is not in use */
if (ar->state != ATH10K_STATE_OFF) {
ret = -EBUSY;
goto err;
}
if (WARN_ON(ar->testmode.utf_mode_fw.fw_file.firmware != NULL)) {
/* utf image is already downloaded, it shouldn't be */
ret = -EEXIST;
goto err;
}
ret = ath10k_tm_fetch_firmware(ar);
if (ret) {
ath10k_err(ar, "failed to fetch UTF firmware: %d", ret);
goto err;
}
if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
ar->testmode.utf_mode_fw.fw_file.codeswap_len) {
ret = ath10k_swap_code_seg_init(ar,
&ar->testmode.utf_mode_fw.fw_file);
if (ret) {
ath10k_warn(ar,
"failed to init utf code swap segment: %d\n",
ret);
goto err_release_utf_mode_fw;
}
}
spin_lock_bh(&ar->data_lock);
ar->testmode.utf_monitor = true;
spin_unlock_bh(&ar->data_lock);
ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode wmi version %d\n",
ar->testmode.utf_mode_fw.fw_file.wmi_op_version);
ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_UTF);
if (ret) {
ath10k_err(ar, "failed to power up hif (testmode): %d\n", ret);
ar->state = ATH10K_STATE_OFF;
goto err_release_utf_mode_fw;
}
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
&ar->testmode.utf_mode_fw);
if (ret) {
ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
ar->state = ATH10K_STATE_OFF;
goto err_power_down;
}
ar->state = ATH10K_STATE_UTF;
if (strlen(ar->testmode.utf_mode_fw.fw_file.fw_version) > 0)
ver = ar->testmode.utf_mode_fw.fw_file.fw_version;
else
ver = "API 1";
ath10k_info(ar, "UTF firmware %s started\n", ver);
mutex_unlock(&ar->conf_mutex);
return 0;
err_power_down:
ath10k_hif_power_down(ar);
err_release_utf_mode_fw:
if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
ar->testmode.utf_mode_fw.fw_file.codeswap_len)
ath10k_swap_code_seg_release(ar,
&ar->testmode.utf_mode_fw.fw_file);
release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
err:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
{
lockdep_assert_held(&ar->conf_mutex);
ath10k_core_stop(ar);
ath10k_hif_power_down(ar);
spin_lock_bh(&ar->data_lock);
ar->testmode.utf_monitor = false;
spin_unlock_bh(&ar->data_lock);
if (ar->testmode.utf_mode_fw.fw_file.codeswap_data &&
ar->testmode.utf_mode_fw.fw_file.codeswap_len)
ath10k_swap_code_seg_release(ar,
&ar->testmode.utf_mode_fw.fw_file);
release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
ar->state = ATH10K_STATE_OFF;
}
static int ath10k_tm_cmd_utf_stop(struct ath10k *ar, struct nlattr *tb[])
{
int ret;
ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "testmode cmd utf stop\n");
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_UTF) {
ret = -ENETDOWN;
goto out;
}
__ath10k_tm_cmd_utf_stop(ar);
ret = 0;
ath10k_info(ar, "UTF firmware stopped\n");
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[])
{
struct sk_buff *skb;
int ret, buf_len;
u32 cmd_id;
void *buf;
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_UTF) {
ret = -ENETDOWN;
goto out;
}
if (!tb[ATH10K_TM_ATTR_DATA]) {
ret = -EINVAL;
goto out;
}
if (!tb[ATH10K_TM_ATTR_WMI_CMDID]) {
ret = -EINVAL;
goto out;
}
buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]);
ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
"testmode cmd wmi cmd_id %d buf %pK buf_len %d\n",
cmd_id, buf, buf_len);
ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
skb = ath10k_wmi_alloc_skb(ar, buf_len);
if (!skb) {
ret = -ENOMEM;
goto out;
}
memcpy(skb->data, buf, buf_len);
ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
if (ret) {
ath10k_warn(ar, "failed to transmit wmi command (testmode): %d\n",
ret);
goto out;
}
ret = 0;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct ath10k *ar = hw->priv;
struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1];
int ret;
ret = nla_parse_deprecated(tb, ATH10K_TM_ATTR_MAX, data, len,
ath10k_tm_policy, NULL);
if (ret)
return ret;
if (!tb[ATH10K_TM_ATTR_CMD])
return -EINVAL;
switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
case ATH10K_TM_CMD_GET_VERSION:
return ath10k_tm_cmd_get_version(ar, tb);
case ATH10K_TM_CMD_UTF_START:
return ath10k_tm_cmd_utf_start(ar, tb);
case ATH10K_TM_CMD_UTF_STOP:
return ath10k_tm_cmd_utf_stop(ar, tb);
case ATH10K_TM_CMD_WMI:
return ath10k_tm_cmd_wmi(ar, tb);
default:
return -EOPNOTSUPP;
}
}
void ath10k_testmode_destroy(struct ath10k *ar)
{
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_UTF) {
/* utf firmware is not running, nothing to do */
goto out;
}
__ath10k_tm_cmd_utf_stop(ar);
out:
mutex_unlock(&ar->conf_mutex);
}

View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2014 Qualcomm Atheros, Inc.
*/
#include "core.h"
#ifdef CONFIG_NL80211_TESTMODE
void ath10k_testmode_destroy(struct ath10k *ar);
bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb);
int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
#else
static inline void ath10k_testmode_destroy(struct ath10k *ar)
{
}
static inline bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id,
struct sk_buff *skb)
{
return false;
}
static inline int ath10k_tm_cmd(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
void *data, int len)
{
return 0;
}
#endif

View File

@ -0,0 +1,60 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
*/
/* "API" level of the ath10k testmode interface. Bump it after every
* incompatible interface change.
*/
#define ATH10K_TESTMODE_VERSION_MAJOR 1
/* Bump this after every _compatible_ interface change, for example
* addition of a new command or an attribute.
*/
#define ATH10K_TESTMODE_VERSION_MINOR 0
#define ATH10K_TM_DATA_MAX_LEN 5000
enum ath10k_tm_attr {
__ATH10K_TM_ATTR_INVALID = 0,
ATH10K_TM_ATTR_CMD = 1,
ATH10K_TM_ATTR_DATA = 2,
ATH10K_TM_ATTR_WMI_CMDID = 3,
ATH10K_TM_ATTR_VERSION_MAJOR = 4,
ATH10K_TM_ATTR_VERSION_MINOR = 5,
ATH10K_TM_ATTR_WMI_OP_VERSION = 6,
/* keep last */
__ATH10K_TM_ATTR_AFTER_LAST,
ATH10K_TM_ATTR_MAX = __ATH10K_TM_ATTR_AFTER_LAST - 1,
};
/* All ath10k testmode interface commands specified in
* ATH10K_TM_ATTR_CMD
*/
enum ath10k_tm_cmd {
/* Returns the supported ath10k testmode interface version in
* ATH10K_TM_ATTR_VERSION. Always guaranteed to work. User space
* uses this to verify it's using the correct version of the
* testmode interface
*/
ATH10K_TM_CMD_GET_VERSION = 0,
/* Boots the UTF firmware, the netdev interface must be down at the
* time.
*/
ATH10K_TM_CMD_UTF_START = 1,
/* Shuts down the UTF firmware and puts the driver back into OFF
* state.
*/
ATH10K_TM_CMD_UTF_STOP = 2,
/* The command used to transmit a WMI command to the firmware and
* the event to receive WMI events from the firmware. Without
* struct wmi_cmd_hdr header, only the WMI payload. Command id is
* provided with ATH10K_TM_ATTR_WMI_CMDID and payload in
* ATH10K_TM_ATTR_DATA.
*/
ATH10K_TM_CMD_WMI = 3,
};

View File

@ -0,0 +1,219 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
*/
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/thermal.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include "core.h"
#include "debug.h"
#include "wmi-ops.h"
static int
ath10k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = ATH10K_THERMAL_THROTTLE_MAX;
return 0;
}
static int
ath10k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct ath10k *ar = cdev->devdata;
mutex_lock(&ar->conf_mutex);
*state = ar->thermal.throttle_state;
mutex_unlock(&ar->conf_mutex);
return 0;
}
static int
ath10k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
unsigned long throttle_state)
{
struct ath10k *ar = cdev->devdata;
if (throttle_state > ATH10K_THERMAL_THROTTLE_MAX) {
ath10k_warn(ar, "throttle state %ld is exceeding the limit %d\n",
throttle_state, ATH10K_THERMAL_THROTTLE_MAX);
return -EINVAL;
}
mutex_lock(&ar->conf_mutex);
ar->thermal.throttle_state = throttle_state;
ath10k_thermal_set_throttling(ar);
mutex_unlock(&ar->conf_mutex);
return 0;
}
static const struct thermal_cooling_device_ops ath10k_thermal_ops = {
.get_max_state = ath10k_thermal_get_max_throttle_state,
.get_cur_state = ath10k_thermal_get_cur_throttle_state,
.set_cur_state = ath10k_thermal_set_cur_throttle_state,
};
static ssize_t ath10k_thermal_show_temp(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ath10k *ar = dev_get_drvdata(dev);
int ret, temperature;
unsigned long time_left;
mutex_lock(&ar->conf_mutex);
/* Can't get temperature when the card is off */
if (ar->state != ATH10K_STATE_ON) {
ret = -ENETDOWN;
goto out;
}
reinit_completion(&ar->thermal.wmi_sync);
ret = ath10k_wmi_pdev_get_temperature(ar);
if (ret) {
ath10k_warn(ar, "failed to read temperature %d\n", ret);
goto out;
}
if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
ret = -ESHUTDOWN;
goto out;
}
time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
ATH10K_THERMAL_SYNC_TIMEOUT_HZ);
if (!time_left) {
ath10k_warn(ar, "failed to synchronize thermal read\n");
ret = -ETIMEDOUT;
goto out;
}
spin_lock_bh(&ar->data_lock);
temperature = ar->thermal.temperature;
spin_unlock_bh(&ar->data_lock);
/* display in millidegree celcius */
ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000);
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature)
{
spin_lock_bh(&ar->data_lock);
ar->thermal.temperature = temperature;
spin_unlock_bh(&ar->data_lock);
complete(&ar->thermal.wmi_sync);
}
static SENSOR_DEVICE_ATTR(temp1_input, 0444, ath10k_thermal_show_temp,
NULL, 0);
static struct attribute *ath10k_hwmon_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(ath10k_hwmon);
void ath10k_thermal_set_throttling(struct ath10k *ar)
{
u32 period, duration, enabled;
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
return;
if (!ar->wmi.ops->gen_pdev_set_quiet_mode)
return;
if (ar->state != ATH10K_STATE_ON)
return;
period = ar->thermal.quiet_period;
duration = (period * ar->thermal.throttle_state) / 100;
enabled = duration ? 1 : 0;
ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration,
ATH10K_QUIET_START_OFFSET,
enabled);
if (ret) {
ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n",
period, duration, enabled, ret);
}
}
int ath10k_thermal_register(struct ath10k *ar)
{
struct thermal_cooling_device *cdev;
struct device *hwmon_dev;
int ret;
if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
return 0;
cdev = thermal_cooling_device_register("ath10k_thermal", ar,
&ath10k_thermal_ops);
if (IS_ERR(cdev)) {
ath10k_err(ar, "failed to setup thermal device result: %ld\n",
PTR_ERR(cdev));
return -EINVAL;
}
ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj,
"cooling_device");
if (ret) {
ath10k_err(ar, "failed to create cooling device symlink\n");
goto err_cooling_destroy;
}
ar->thermal.cdev = cdev;
ar->thermal.quiet_period = ATH10K_QUIET_PERIOD_DEFAULT;
/* Do not register hwmon device when temperature reading is not
* supported by firmware
*/
if (!(ar->wmi.ops->gen_pdev_get_temperature))
return 0;
/* Avoid linking error on devm_hwmon_device_register_with_groups, I
* guess linux/hwmon.h is missing proper stubs.
*/
if (!IS_REACHABLE(CONFIG_HWMON))
return 0;
hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev,
"ath10k_hwmon", ar,
ath10k_hwmon_groups);
if (IS_ERR(hwmon_dev)) {
ath10k_err(ar, "failed to register hwmon device: %ld\n",
PTR_ERR(hwmon_dev));
ret = -EINVAL;
goto err_remove_link;
}
return 0;
err_remove_link:
sysfs_remove_link(&ar->dev->kobj, "cooling_device");
err_cooling_destroy:
thermal_cooling_device_unregister(cdev);
return ret;
}
void ath10k_thermal_unregister(struct ath10k *ar)
{
if (!test_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map))
return;
sysfs_remove_link(&ar->dev->kobj, "cooling_device");
thermal_cooling_device_unregister(ar->thermal.cdev);
}

View File

@ -0,0 +1,53 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
*/
#ifndef _THERMAL_
#define _THERMAL_
#define ATH10K_QUIET_PERIOD_DEFAULT 100
#define ATH10K_QUIET_PERIOD_MIN 25
#define ATH10K_QUIET_START_OFFSET 10
#define ATH10K_HWMON_NAME_LEN 15
#define ATH10K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
#define ATH10K_THERMAL_THROTTLE_MAX 100
struct ath10k_thermal {
struct thermal_cooling_device *cdev;
struct completion wmi_sync;
/* protected by conf_mutex */
u32 throttle_state;
u32 quiet_period;
/* temperature value in Celcius degree
* protected by data_lock
*/
int temperature;
};
#if IS_REACHABLE(CONFIG_THERMAL)
int ath10k_thermal_register(struct ath10k *ar);
void ath10k_thermal_unregister(struct ath10k *ar);
void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
void ath10k_thermal_set_throttling(struct ath10k *ar);
#else
static inline int ath10k_thermal_register(struct ath10k *ar)
{
return 0;
}
static inline void ath10k_thermal_unregister(struct ath10k *ar)
{
}
static inline void ath10k_thermal_event_temperature(struct ath10k *ar,
int temperature)
{
}
static inline void ath10k_thermal_set_throttling(struct ath10k *ar)
{
}
#endif
#endif /* _THERMAL_ */

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*/
#include <linux/module.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
EXPORT_SYMBOL(__tracepoint_ath10k_log_dbg);

View File

@ -0,0 +1,537 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2016 Qualcomm Atheros, Inc.
*/
#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
#include <linux/tracepoint.h>
#include "core.h"
#if !defined(_TRACE_H_)
static inline u32 ath10k_frm_hdr_len(const void *buf, size_t len)
{
const struct ieee80211_hdr *hdr = buf;
/* In some rare cases (e.g. fcs error) device reports frame buffer
* shorter than what frame header implies (e.g. len = 0). The buffer
* can still be accessed so do a simple min() to guarantee caller
* doesn't get value greater than len.
*/
return min_t(u32, len, ieee80211_hdrlen(hdr->frame_control));
}
#endif
#define _TRACE_H_
/* create empty functions when tracing is disabled */
#if !defined(CONFIG_ATH10K_TRACING)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {} \
static inline bool trace_##name##_enabled(void) \
{ \
return false; \
}
#undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(...)
#undef DEFINE_EVENT
#define DEFINE_EVENT(evt_class, name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ath10k
#define ATH10K_MSG_MAX 400
DECLARE_EVENT_CLASS(ath10k_log_event,
TP_PROTO(struct ath10k *ar, struct va_format *vaf),
TP_ARGS(ar, vaf),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__dynamic_array(char, msg, ATH10K_MSG_MAX)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
ATH10K_MSG_MAX,
vaf->fmt,
*vaf->va) >= ATH10K_MSG_MAX);
),
TP_printk(
"%s %s %s",
__get_str(driver),
__get_str(device),
__get_str(msg)
)
);
DEFINE_EVENT(ath10k_log_event, ath10k_log_err,
TP_PROTO(struct ath10k *ar, struct va_format *vaf),
TP_ARGS(ar, vaf)
);
DEFINE_EVENT(ath10k_log_event, ath10k_log_warn,
TP_PROTO(struct ath10k *ar, struct va_format *vaf),
TP_ARGS(ar, vaf)
);
DEFINE_EVENT(ath10k_log_event, ath10k_log_info,
TP_PROTO(struct ath10k *ar, struct va_format *vaf),
TP_ARGS(ar, vaf)
);
TRACE_EVENT(ath10k_log_dbg,
TP_PROTO(struct ath10k *ar, unsigned int level, struct va_format *vaf),
TP_ARGS(ar, level, vaf),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(unsigned int, level)
__dynamic_array(char, msg, ATH10K_MSG_MAX)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->level = level;
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
ATH10K_MSG_MAX,
vaf->fmt,
*vaf->va) >= ATH10K_MSG_MAX);
),
TP_printk(
"%s %s %s",
__get_str(driver),
__get_str(device),
__get_str(msg)
)
);
TRACE_EVENT(ath10k_log_dbg_dump,
TP_PROTO(struct ath10k *ar, const char *msg, const char *prefix,
const void *buf, size_t buf_len),
TP_ARGS(ar, msg, prefix, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__string(msg, msg)
__string(prefix, prefix)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__assign_str(msg, msg);
__assign_str(prefix, prefix);
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"%s %s %s/%s\n",
__get_str(driver),
__get_str(device),
__get_str(prefix),
__get_str(msg)
)
);
TRACE_EVENT(ath10k_wmi_cmd,
TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
TP_ARGS(ar, id, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(unsigned int, id)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->id = id;
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"%s %s id %d len %zu",
__get_str(driver),
__get_str(device),
__entry->id,
__entry->buf_len
)
);
TRACE_EVENT(ath10k_wmi_event,
TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
TP_ARGS(ar, id, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(unsigned int, id)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->id = id;
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"%s %s id %d len %zu",
__get_str(driver),
__get_str(device),
__entry->id,
__entry->buf_len
)
);
TRACE_EVENT(ath10k_htt_stats,
TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
TP_ARGS(ar, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"%s %s len %zu",
__get_str(driver),
__get_str(device),
__entry->buf_len
)
);
TRACE_EVENT(ath10k_wmi_dbglog,
TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
TP_ARGS(ar, buf, buf_len),
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)
),
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 %d len %zu",
__get_str(driver),
__get_str(device),
__entry->hw_type,
__entry->buf_len
)
);
TRACE_EVENT(ath10k_htt_pktlog,
TP_PROTO(struct ath10k *ar, const void *buf, u16 buf_len),
TP_ARGS(ar, buf, buf_len),
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)
),
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 %d size %u",
__get_str(driver),
__get_str(device),
__entry->hw_type,
__entry->buf_len
)
);
TRACE_EVENT(ath10k_htt_tx,
TP_PROTO(struct ath10k *ar, u16 msdu_id, u16 msdu_len,
u8 vdev_id, u8 tid),
TP_ARGS(ar, msdu_id, msdu_len, vdev_id, tid),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(u16, msdu_id)
__field(u16, msdu_len)
__field(u8, vdev_id)
__field(u8, tid)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->msdu_id = msdu_id;
__entry->msdu_len = msdu_len;
__entry->vdev_id = vdev_id;
__entry->tid = tid;
),
TP_printk(
"%s %s msdu_id %d msdu_len %d vdev_id %d tid %d",
__get_str(driver),
__get_str(device),
__entry->msdu_id,
__entry->msdu_len,
__entry->vdev_id,
__entry->tid
)
);
TRACE_EVENT(ath10k_txrx_tx_unref,
TP_PROTO(struct ath10k *ar, u16 msdu_id),
TP_ARGS(ar, msdu_id),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(u16, msdu_id)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->msdu_id = msdu_id;
),
TP_printk(
"%s %s msdu_id %d",
__get_str(driver),
__get_str(device),
__entry->msdu_id
)
);
DECLARE_EVENT_CLASS(ath10k_hdr_event,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(size_t, len)
__dynamic_array(u8, data, ath10k_frm_hdr_len(data, len))
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->len = ath10k_frm_hdr_len(data, len);
memcpy(__get_dynamic_array(data), data, __entry->len);
),
TP_printk(
"%s %s len %zu\n",
__get_str(driver),
__get_str(device),
__entry->len
)
);
DECLARE_EVENT_CLASS(ath10k_payload_event,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(size_t, len)
__dynamic_array(u8, payload, (len -
ath10k_frm_hdr_len(data, len)))
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->len = len - ath10k_frm_hdr_len(data, len);
memcpy(__get_dynamic_array(payload),
data + ath10k_frm_hdr_len(data, len), __entry->len);
),
TP_printk(
"%s %s len %zu\n",
__get_str(driver),
__get_str(device),
__entry->len
)
);
DEFINE_EVENT(ath10k_hdr_event, ath10k_tx_hdr,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len)
);
DEFINE_EVENT(ath10k_payload_event, ath10k_tx_payload,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len)
);
DEFINE_EVENT(ath10k_hdr_event, ath10k_rx_hdr,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len)
);
DEFINE_EVENT(ath10k_payload_event, ath10k_rx_payload,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len)
);
TRACE_EVENT(ath10k_htt_rx_desc,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len),
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)
),
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 %d rxdesc len %d",
__get_str(driver),
__get_str(device),
__entry->hw_type,
__entry->len
)
);
TRACE_EVENT(ath10k_wmi_diag_container,
TP_PROTO(struct ath10k *ar,
u8 type,
u32 timestamp,
u32 code,
u16 len,
const void *data),
TP_ARGS(ar, type, timestamp, code, len, data),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(u8, type)
__field(u32, timestamp)
__field(u32, code)
__field(u16, len)
__dynamic_array(u8, data, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->type = type;
__entry->timestamp = timestamp;
__entry->code = code;
__entry->len = len;
memcpy(__get_dynamic_array(data), data, len);
),
TP_printk(
"%s %s diag container type %u timestamp %u code %u len %d",
__get_str(driver),
__get_str(device),
__entry->type,
__entry->timestamp,
__entry->code,
__entry->len
)
);
TRACE_EVENT(ath10k_wmi_diag,
TP_PROTO(struct ath10k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ar->dev))
__string(driver, dev_driver_string(ar->dev))
__field(u16, len)
__dynamic_array(u8, data, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->dev));
__assign_str(driver, dev_driver_string(ar->dev));
__entry->len = len;
memcpy(__get_dynamic_array(data), data, len);
),
TP_printk(
"%s %s tlv diag len %d",
__get_str(driver),
__get_str(device),
__entry->len
)
);
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>

View File

@ -0,0 +1,275 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2016 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#include "core.h"
#include "txrx.h"
#include "htt.h"
#include "mac.h"
#include "debug.h"
static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (likely(!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)))
return;
if (ath10k_mac_tx_frm_has_freq(ar))
return;
/* If the original wait_for_completion() timed out before
* {data,mgmt}_tx_completed() was called then we could complete
* offchan_tx_completed for a different skb. Prevent this by using
* offchan_tx_skb.
*/
spin_lock_bh(&ar->data_lock);
if (ar->offchan_tx_skb != skb) {
ath10k_warn(ar, "completed old offchannel frame\n");
goto out;
}
complete(&ar->offchan_tx_completed);
ar->offchan_tx_skb = NULL; /* just for sanity */
ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %pK\n", skb);
out:
spin_unlock_bh(&ar->data_lock);
}
int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done)
{
struct ath10k *ar = htt->ar;
struct device *dev = ar->dev;
struct ieee80211_tx_info *info;
struct ieee80211_txq *txq;
struct ath10k_skb_cb *skb_cb;
struct ath10k_txq *artxq;
struct sk_buff *msdu;
u8 flags;
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt tx completion msdu_id %u status %d\n",
tx_done->msdu_id, tx_done->status);
if (tx_done->msdu_id >= htt->max_num_pending_tx) {
ath10k_warn(ar, "warning: msdu_id %d too big, ignoring\n",
tx_done->msdu_id);
return -EINVAL;
}
spin_lock_bh(&htt->tx_lock);
msdu = idr_find(&htt->pending_tx, tx_done->msdu_id);
if (!msdu) {
ath10k_warn(ar, "received tx completion for invalid msdu_id: %d\n",
tx_done->msdu_id);
spin_unlock_bh(&htt->tx_lock);
return -ENOENT;
}
skb_cb = ATH10K_SKB_CB(msdu);
txq = skb_cb->txq;
if (txq) {
artxq = (void *)txq->drv_priv;
artxq->num_fw_queued--;
}
flags = skb_cb->flags;
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
ath10k_htt_tx_dec_pending(htt);
spin_unlock_bh(&htt->tx_lock);
rcu_read_lock();
if (txq && txq->sta && skb_cb->airtime_est)
ieee80211_sta_register_airtime(txq->sta, txq->tid,
skb_cb->airtime_est, 0);
rcu_read_unlock();
if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL)
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
ath10k_report_offchan_tx(htt->ar, msdu);
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
info->status.rates[0].idx = -1;
trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
!(flags & ATH10K_SKB_F_NOACK_TID))
info->flags |= IEEE80211_TX_STAT_ACK;
if (tx_done->status == HTT_TX_COMPL_STATE_NOACK)
info->flags &= ~IEEE80211_TX_STAT_ACK;
if ((tx_done->status == HTT_TX_COMPL_STATE_ACK) &&
((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
(flags & ATH10K_SKB_F_NOACK_TID)))
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) {
if ((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
(flags & ATH10K_SKB_F_NOACK_TID))
info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED;
else
info->flags &= ~IEEE80211_TX_STAT_ACK;
}
if (tx_done->status == HTT_TX_COMPL_STATE_ACK &&
tx_done->ack_rssi != ATH10K_INVALID_RSSI) {
info->status.ack_signal = ATH10K_DEFAULT_NOISE_FLOOR +
tx_done->ack_rssi;
info->status.is_valid_ack_signal = true;
}
ieee80211_tx_status(htt->ar->hw, msdu);
/* we do not own the msdu anymore */
return 0;
}
struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
const u8 *addr)
{
struct ath10k_peer *peer;
lockdep_assert_held(&ar->data_lock);
list_for_each_entry(peer, &ar->peers, list) {
if (peer->vdev_id != vdev_id)
continue;
if (!ether_addr_equal(peer->addr, addr))
continue;
return peer;
}
return NULL;
}
struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id)
{
struct ath10k_peer *peer;
if (peer_id >= BITS_PER_TYPE(peer->peer_ids))
return NULL;
lockdep_assert_held(&ar->data_lock);
list_for_each_entry(peer, &ar->peers, list)
if (test_bit(peer_id, peer->peer_ids))
return peer;
return NULL;
}
static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id,
const u8 *addr, bool expect_mapped)
{
long time_left;
time_left = wait_event_timeout(ar->peer_mapping_wq, ({
bool mapped;
spin_lock_bh(&ar->data_lock);
mapped = !!ath10k_peer_find(ar, vdev_id, addr);
spin_unlock_bh(&ar->data_lock);
(mapped == expect_mapped ||
test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags));
}), 3 * HZ);
if (time_left == 0)
return -ETIMEDOUT;
return 0;
}
int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr)
{
return ath10k_wait_for_peer_common(ar, vdev_id, addr, true);
}
int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, const u8 *addr)
{
return ath10k_wait_for_peer_common(ar, vdev_id, addr, false);
}
void ath10k_peer_map_event(struct ath10k_htt *htt,
struct htt_peer_map_event *ev)
{
struct ath10k *ar = htt->ar;
struct ath10k_peer *peer;
if (ev->peer_id >= ATH10K_MAX_NUM_PEER_IDS) {
ath10k_warn(ar,
"received htt peer map event with idx out of bounds: %u\n",
ev->peer_id);
return;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr);
if (!peer) {
peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
if (!peer)
goto exit;
peer->vdev_id = ev->vdev_id;
ether_addr_copy(peer->addr, ev->addr);
list_add(&peer->list, &ar->peers);
wake_up(&ar->peer_mapping_wq);
}
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
ev->vdev_id, ev->addr, ev->peer_id);
WARN_ON(ar->peer_map[ev->peer_id] && (ar->peer_map[ev->peer_id] != peer));
ar->peer_map[ev->peer_id] = peer;
set_bit(ev->peer_id, peer->peer_ids);
exit:
spin_unlock_bh(&ar->data_lock);
}
void ath10k_peer_unmap_event(struct ath10k_htt *htt,
struct htt_peer_unmap_event *ev)
{
struct ath10k *ar = htt->ar;
struct ath10k_peer *peer;
if (ev->peer_id >= ATH10K_MAX_NUM_PEER_IDS) {
ath10k_warn(ar,
"received htt peer unmap event with idx out of bounds: %u\n",
ev->peer_id);
return;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find_by_id(ar, ev->peer_id);
if (!peer) {
ath10k_warn(ar, "peer-unmap-event: unknown peer id %d\n",
ev->peer_id);
goto exit;
}
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, ev->peer_id);
ar->peer_map[ev->peer_id] = NULL;
clear_bit(ev->peer_id, peer->peer_ids);
if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
list_del(&peer->list);
kfree(peer);
wake_up(&ar->peer_mapping_wq);
}
exit:
spin_unlock_bh(&ar->data_lock);
}

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2014,2016 Qualcomm Atheros, Inc.
*/
#ifndef _TXRX_H_
#define _TXRX_H_
#include "htt.h"
int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done);
struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
const u8 *addr);
struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, int peer_id);
int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id,
const u8 *addr);
int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id,
const u8 *addr);
void ath10k_peer_map_event(struct ath10k_htt *htt,
struct htt_peer_map_event *ev);
void ath10k_peer_unmap_event(struct ath10k_htt *htt,
struct htt_peer_unmap_event *ev);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
* Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
* Copyright (c) 2016-2017 Erik Stromdahl <erik.stromdahl@gmail.com>
*/
#ifndef _USB_H_
#define _USB_H_
/* constants */
#define TX_URB_COUNT 32
#define RX_URB_COUNT 32
#define ATH10K_USB_RX_BUFFER_SIZE 4096
#define ATH10K_USB_PIPE_INVALID ATH10K_USB_PIPE_MAX
/* USB endpoint definitions */
#define ATH10K_USB_EP_ADDR_APP_CTRL_IN 0x81
#define ATH10K_USB_EP_ADDR_APP_DATA_IN 0x82
#define ATH10K_USB_EP_ADDR_APP_DATA2_IN 0x83
#define ATH10K_USB_EP_ADDR_APP_INT_IN 0x84
#define ATH10K_USB_EP_ADDR_APP_CTRL_OUT 0x01
#define ATH10K_USB_EP_ADDR_APP_DATA_LP_OUT 0x02
#define ATH10K_USB_EP_ADDR_APP_DATA_MP_OUT 0x03
#define ATH10K_USB_EP_ADDR_APP_DATA_HP_OUT 0x04
/* diagnostic command defnitions */
#define ATH10K_USB_CONTROL_REQ_SEND_BMI_CMD 1
#define ATH10K_USB_CONTROL_REQ_RECV_BMI_RESP 2
#define ATH10K_USB_CONTROL_REQ_DIAG_CMD 3
#define ATH10K_USB_CONTROL_REQ_DIAG_RESP 4
#define ATH10K_USB_CTRL_DIAG_CC_READ 0
#define ATH10K_USB_CTRL_DIAG_CC_WRITE 1
#define ATH10K_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02)
#define ATH10K_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03)
#define ATH10K_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01)
#define ATH10K_USB_IS_DIR_IN(addr) ((addr) & 0x80)
struct ath10k_usb_ctrl_diag_cmd_write {
__le32 cmd;
__le32 address;
__le32 value;
__le32 padding;
} __packed;
struct ath10k_usb_ctrl_diag_cmd_read {
__le32 cmd;
__le32 address;
} __packed;
struct ath10k_usb_ctrl_diag_resp_read {
u8 value[4];
} __packed;
/* tx/rx pipes for usb */
enum ath10k_usb_pipe_id {
ATH10K_USB_PIPE_TX_CTRL = 0,
ATH10K_USB_PIPE_TX_DATA_LP,
ATH10K_USB_PIPE_TX_DATA_MP,
ATH10K_USB_PIPE_TX_DATA_HP,
ATH10K_USB_PIPE_RX_CTRL,
ATH10K_USB_PIPE_RX_DATA,
ATH10K_USB_PIPE_RX_DATA2,
ATH10K_USB_PIPE_RX_INT,
ATH10K_USB_PIPE_MAX
};
struct ath10k_usb_pipe {
struct list_head urb_list_head;
struct usb_anchor urb_submitted;
u32 urb_alloc;
u32 urb_cnt;
u32 urb_cnt_thresh;
unsigned int usb_pipe_handle;
u32 flags;
u8 ep_address;
u8 logical_pipe_num;
struct ath10k_usb *ar_usb;
u16 max_packet_size;
struct work_struct io_complete_work;
struct sk_buff_head io_comp_queue;
struct usb_endpoint_descriptor *ep_desc;
};
#define ATH10K_USB_PIPE_FLAG_TX BIT(0)
/* usb device object */
struct ath10k_usb {
/* protects pipe->urb_list_head and pipe->urb_cnt */
spinlock_t cs_lock;
struct usb_device *udev;
struct usb_interface *interface;
struct ath10k_usb_pipe pipes[ATH10K_USB_PIPE_MAX];
u8 *diag_cmd_buffer;
u8 *diag_resp_buffer;
struct ath10k *ar;
};
/* usb urb object */
struct ath10k_urb_context {
struct list_head link;
struct ath10k_usb_pipe *pipe;
struct sk_buff *skb;
struct ath10k *ar;
};
static inline struct ath10k_usb *ath10k_usb_priv(struct ath10k *ar)
{
return (struct ath10k_usb *)ar->drv_priv;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,641 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
#include "mac.h"
#include <net/mac80211.h>
#include "hif.h"
#include "core.h"
#include "debug.h"
#include "wmi.h"
#include "wmi-ops.h"
static const struct wiphy_wowlan_support ath10k_wowlan_support = {
.flags = WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_MAGIC_PKT,
.pattern_min_len = WOW_MIN_PATTERN_SIZE,
.pattern_max_len = WOW_MAX_PATTERN_SIZE,
.max_pkt_offset = WOW_MAX_PKT_OFFSET,
};
static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
{
struct ath10k *ar = arvif->ar;
int i, ret;
for (i = 0; i < WOW_EVENT_MAX; i++) {
ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
if (ret) {
ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
wow_wakeup_event(i), arvif->vdev_id, ret);
return ret;
}
}
for (i = 0; i < ar->wow.max_num_patterns; i++) {
ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
if (ret) {
ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
i, arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath10k_wow_cleanup(struct ath10k *ar)
{
struct ath10k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath10k_wow_vif_cleanup(arvif);
if (ret) {
ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
/*
* Convert a 802.3 format to a 802.11 format.
* +------------+-----------+--------+----------------+
* 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... |
* +------------+-----------+--------+----------------+
* |__ |_______ |____________ |________
* | | | |
* +--+------------+----+-----------+---------------+-----------+
* 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... |
* +--+------------+----+-----------+---------------+-----------+
*/
static void ath10k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
const struct cfg80211_pkt_pattern *old)
{
u8 hdr_8023_pattern[ETH_HLEN] = {};
u8 hdr_8023_bit_mask[ETH_HLEN] = {};
u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
int total_len = old->pkt_offset + old->pattern_len;
int hdr_80211_end_offset;
struct ieee80211_hdr_3addr *new_hdr_pattern =
(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
struct ieee80211_hdr_3addr *new_hdr_mask =
(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
int hdr_len = sizeof(*new_hdr_pattern);
struct rfc1042_hdr *new_rfc_pattern =
(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
struct rfc1042_hdr *new_rfc_mask =
(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
int rfc_len = sizeof(*new_rfc_pattern);
memcpy(hdr_8023_pattern + old->pkt_offset,
old->pattern, ETH_HLEN - old->pkt_offset);
memcpy(hdr_8023_bit_mask + old->pkt_offset,
old->mask, ETH_HLEN - old->pkt_offset);
/* Copy destination address */
memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
/* Copy source address */
memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
/* Copy logic link type */
memcpy(&new_rfc_pattern->snap_type,
&old_hdr_pattern->h_proto,
sizeof(old_hdr_pattern->h_proto));
memcpy(&new_rfc_mask->snap_type,
&old_hdr_mask->h_proto,
sizeof(old_hdr_mask->h_proto));
/* Calculate new pkt_offset */
if (old->pkt_offset < ETH_ALEN)
new->pkt_offset = old->pkt_offset +
offsetof(struct ieee80211_hdr_3addr, addr1);
else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
new->pkt_offset = old->pkt_offset +
offsetof(struct ieee80211_hdr_3addr, addr3) -
offsetof(struct ethhdr, h_source);
else
new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
/* Calculate new hdr end offset */
if (total_len > ETH_HLEN)
hdr_80211_end_offset = hdr_len + rfc_len;
else if (total_len > offsetof(struct ethhdr, h_proto))
hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
else if (total_len > ETH_ALEN)
hdr_80211_end_offset = total_len - ETH_ALEN +
offsetof(struct ieee80211_hdr_3addr, addr3);
else
hdr_80211_end_offset = total_len +
offsetof(struct ieee80211_hdr_3addr, addr1);
new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
memcpy((u8 *)new->pattern,
hdr_80211_pattern + new->pkt_offset,
new->pattern_len);
memcpy((u8 *)new->mask,
hdr_80211_bit_mask + new->pkt_offset,
new->pattern_len);
if (total_len > ETH_HLEN) {
/* Copy frame body */
memcpy((u8 *)new->pattern + new->pattern_len,
(void *)old->pattern + ETH_HLEN - old->pkt_offset,
total_len - ETH_HLEN);
memcpy((u8 *)new->mask + new->pattern_len,
(void *)old->mask + ETH_HLEN - old->pkt_offset,
total_len - ETH_HLEN);
new->pattern_len += total_len - ETH_HLEN;
}
}
static int ath10k_wmi_pno_check(struct ath10k *ar, u32 vdev_id,
struct cfg80211_sched_scan_request *nd_config,
struct wmi_pno_scan_req *pno)
{
int i, j, ret = 0;
u8 ssid_len;
pno->enable = 1;
pno->vdev_id = vdev_id;
pno->uc_networks_count = nd_config->n_match_sets;
if (!pno->uc_networks_count ||
pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
return -EINVAL;
if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
return -EINVAL;
/* Filling per profile params */
for (i = 0; i < pno->uc_networks_count; i++) {
ssid_len = nd_config->match_sets[i].ssid.ssid_len;
if (ssid_len == 0 || ssid_len > 32)
return -EINVAL;
pno->a_networks[i].ssid.ssid_len = __cpu_to_le32(ssid_len);
memcpy(pno->a_networks[i].ssid.ssid,
nd_config->match_sets[i].ssid.ssid,
nd_config->match_sets[i].ssid.ssid_len);
pno->a_networks[i].authentication = 0;
pno->a_networks[i].encryption = 0;
pno->a_networks[i].bcast_nw_type = 0;
/*Copying list of valid channel into request */
pno->a_networks[i].channel_count = nd_config->n_channels;
pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
for (j = 0; j < nd_config->n_channels; j++) {
pno->a_networks[i].channels[j] =
nd_config->channels[j]->center_freq;
}
}
/* set scan to passive if no SSIDs are specified in the request */
if (nd_config->n_ssids == 0)
pno->do_passive_scan = true;
else
pno->do_passive_scan = false;
for (i = 0; i < nd_config->n_ssids; i++) {
j = 0;
while (j < pno->uc_networks_count) {
if (__le32_to_cpu(pno->a_networks[j].ssid.ssid_len) ==
nd_config->ssids[i].ssid_len &&
(memcmp(pno->a_networks[j].ssid.ssid,
nd_config->ssids[i].ssid,
__le32_to_cpu(pno->a_networks[j].ssid.ssid_len)) == 0)) {
pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
break;
}
j++;
}
}
if (nd_config->n_scan_plans == 2) {
pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
pno->slow_scan_period =
nd_config->scan_plans[1].interval * MSEC_PER_SEC;
} else if (nd_config->n_scan_plans == 1) {
pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
pno->fast_scan_max_cycles = 1;
pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
} else {
ath10k_warn(ar, "Invalid number of scan plans %d !!",
nd_config->n_scan_plans);
}
if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
/* enable mac randomization */
pno->enable_pno_scan_randomization = 1;
memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
}
pno->delay_start_time = nd_config->delay;
/* Current FW does not support min-max range for dwell time */
pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
return ret;
}
static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
struct cfg80211_wowlan *wowlan)
{
int ret, i;
unsigned long wow_mask = 0;
struct ath10k *ar = arvif->ar;
const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
int pattern_id = 0;
/* Setup requested WOW features */
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_IBSS:
__set_bit(WOW_BEACON_EVENT, &wow_mask);
fallthrough;
case WMI_VDEV_TYPE_AP:
__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
__set_bit(WOW_HTT_EVENT, &wow_mask);
__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
break;
case WMI_VDEV_TYPE_STA:
if (wowlan->disconnect) {
__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
__set_bit(WOW_BMISS_EVENT, &wow_mask);
__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
}
if (wowlan->magic_pkt)
__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
if (wowlan->nd_config) {
struct wmi_pno_scan_req *pno;
int ret;
pno = kzalloc(sizeof(*pno), GFP_KERNEL);
if (!pno)
return -ENOMEM;
ar->nlo_enabled = true;
ret = ath10k_wmi_pno_check(ar, arvif->vdev_id,
wowlan->nd_config, pno);
if (!ret) {
ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
}
kfree(pno);
}
break;
default:
break;
}
for (i = 0; i < wowlan->n_patterns; i++) {
u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
struct cfg80211_pkt_pattern new_pattern = {};
struct cfg80211_pkt_pattern old_pattern = patterns[i];
int j;
new_pattern.pattern = ath_pattern;
new_pattern.mask = ath_bitmask;
if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
continue;
/* convert bytemask to bitmask */
for (j = 0; j < patterns[i].pattern_len; j++)
if (patterns[i].mask[j / 8] & BIT(j % 8))
bitmask[j] = 0xff;
old_pattern.mask = bitmask;
if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
if (patterns[i].pkt_offset < ETH_HLEN) {
ath10k_wow_convert_8023_to_80211(&new_pattern,
&old_pattern);
} else {
new_pattern = old_pattern;
new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
}
}
if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
return -EINVAL;
ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
pattern_id,
new_pattern.pattern,
new_pattern.mask,
new_pattern.pattern_len,
new_pattern.pkt_offset);
if (ret) {
ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
pattern_id,
arvif->vdev_id, ret);
return ret;
}
pattern_id++;
__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
}
for (i = 0; i < WOW_EVENT_MAX; i++) {
if (!test_bit(i, &wow_mask))
continue;
ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
if (ret) {
ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
wow_wakeup_event(i), arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath10k_wow_set_wakeups(struct ath10k *ar,
struct cfg80211_wowlan *wowlan)
{
struct ath10k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
if (ret) {
ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath10k_vif_wow_clean_nlo(struct ath10k_vif *arvif)
{
int ret = 0;
struct ath10k *ar = arvif->ar;
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_STA:
if (ar->nlo_enabled) {
struct wmi_pno_scan_req *pno;
pno = kzalloc(sizeof(*pno), GFP_KERNEL);
if (!pno)
return -ENOMEM;
pno->enable = 0;
ar->nlo_enabled = false;
ret = ath10k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
kfree(pno);
}
break;
default:
break;
}
return ret;
}
static int ath10k_wow_nlo_cleanup(struct ath10k *ar)
{
struct ath10k_vif *arvif;
int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath10k_vif_wow_clean_nlo(arvif);
if (ret) {
ath10k_warn(ar, "failed to clean nlo settings on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath10k_wow_enable(struct ath10k *ar)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
reinit_completion(&ar->target_suspend);
ret = ath10k_wmi_wow_enable(ar);
if (ret) {
ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
return ret;
}
ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
if (ret == 0) {
ath10k_warn(ar, "timed out while waiting for suspend completion\n");
return -ETIMEDOUT;
}
return 0;
}
static int ath10k_wow_wakeup(struct ath10k *ar)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
reinit_completion(&ar->wow.wakeup_completed);
ret = ath10k_wmi_wow_host_wakeup_ind(ar);
if (ret) {
ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
ret);
return ret;
}
ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
if (ret == 0) {
ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
return -ETIMEDOUT;
}
return 0;
}
int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
struct ath10k *ar = hw->priv;
int ret;
mutex_lock(&ar->conf_mutex);
if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
ar->running_fw->fw_file.fw_features))) {
ret = 1;
goto exit;
}
ret = ath10k_wow_cleanup(ar);
if (ret) {
ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
ret);
goto exit;
}
ret = ath10k_wow_set_wakeups(ar, wowlan);
if (ret) {
ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
ret);
goto cleanup;
}
ath10k_mac_wait_tx_complete(ar);
ret = ath10k_wow_enable(ar);
if (ret) {
ath10k_warn(ar, "failed to start wow: %d\n", ret);
goto cleanup;
}
ret = ath10k_hif_suspend(ar);
if (ret) {
ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
goto wakeup;
}
goto exit;
wakeup:
ath10k_wow_wakeup(ar);
cleanup:
ath10k_wow_cleanup(ar);
exit:
mutex_unlock(&ar->conf_mutex);
return ret ? 1 : 0;
}
void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
{
struct ath10k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
ar->running_fw->fw_file.fw_features)) {
device_set_wakeup_enable(ar->dev, enabled);
}
mutex_unlock(&ar->conf_mutex);
}
int ath10k_wow_op_resume(struct ieee80211_hw *hw)
{
struct ath10k *ar = hw->priv;
int ret;
mutex_lock(&ar->conf_mutex);
if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
ar->running_fw->fw_file.fw_features))) {
ret = 1;
goto exit;
}
ret = ath10k_hif_resume(ar);
if (ret) {
ath10k_warn(ar, "failed to resume hif: %d\n", ret);
goto exit;
}
ret = ath10k_wow_wakeup(ar);
if (ret)
ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
ret = ath10k_wow_nlo_cleanup(ar);
if (ret)
ath10k_warn(ar, "failed to cleanup nlo: %d\n", ret);
exit:
if (ret) {
switch (ar->state) {
case ATH10K_STATE_ON:
ar->state = ATH10K_STATE_RESTARTING;
ret = 1;
break;
case ATH10K_STATE_OFF:
case ATH10K_STATE_RESTARTING:
case ATH10K_STATE_RESTARTED:
case ATH10K_STATE_UTF:
case ATH10K_STATE_WEDGED:
ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
ar->state);
ret = -EIO;
break;
}
}
mutex_unlock(&ar->conf_mutex);
return ret;
}
int ath10k_wow_init(struct ath10k *ar)
{
if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
ar->running_fw->fw_file.fw_features))
return 0;
if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
return -EINVAL;
ar->wow.wowlan_support = ath10k_wowlan_support;
if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
}
if (test_bit(WMI_SERVICE_NLO, ar->wmi.svc_map)) {
ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
}
ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
device_set_wakeup_capable(ar->dev, true);
return 0;
}

View File

@ -0,0 +1,30 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2015,2017 Qualcomm Atheros, Inc.
*/
#ifndef _WOW_H_
#define _WOW_H_
struct ath10k_wow {
u32 max_num_patterns;
struct completion wakeup_completed;
struct wiphy_wowlan_support wowlan_support;
};
#ifdef CONFIG_PM
int ath10k_wow_init(struct ath10k *ar);
int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
int ath10k_wow_op_resume(struct ieee80211_hw *hw);
void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled);
#else
static inline int ath10k_wow_init(struct ath10k *ar)
{
return 0;
}
#endif /* CONFIG_PM */
#endif /* _WOW_H_ */

View File

@ -0,0 +1,50 @@
# $FreeBSD$
DEVATH10KDIR= ${SRCTOP}/sys/contrib/dev/athk/ath10k
.PATH: ${DEVATH10KDIR}
WITH_CONFIG_FWLOG= 1
KMOD= if_ath10k
SRCS+= core.c debug.c mac.c ce.c hw.c bmi.c
SRCS+= htc.c htt.c htt_rx.c htt_tx.c
SRCS+= txrx.c wmi.c wmi-tlv.c
SRCS+= p2p.c swap.c
SRCS+= pci.c
# Other
SRCS+= ${LINUXKPI_GENSRCS}
SRCS+= opt_wlan.h opt_inet6.h opt_inet.h opt_acpi.h
.if defined(WITH_CONFIG_FWLOG) && ${WITH_CONFIG_FWLOG} > 0
SRCS+= fwlog.c
CFLAGS+= -DCONFIG_FWLOG=${WITH_CONFIG_FWLOG}
.endif
CFLAGS+= -DKBUILD_MODNAME='"ath10k"'
CFLAGS+= -I${DEVATH10KDIR}
CFLAGS+= -I${DEVATH10KDIR}/..
CFLAGS+= ${LINUXKPI_INCLUDES}
# Helpful after fresh imports.
#CFLAGS+= -ferror-limit=0
CFLAGS+= -DCONFIG_ATH10K_DEBUG
#CFLAGS+= -DCONFIG_ATH10K_AHB
#CFLAGS+= -DCONFIG_ATH10K_DEBUGFS
#CFLAGS+= -DCONFIG_ATH10K_DFS_CERTIFIED
#CFLAGS+= -DCONFIG_ATH10K_SPECTRAL
#CFLAGS+= -DCONFIG_ATH10K_TRACING
#CFLAGS+= -DCONFIG_DEV_COREDUMP
#CFLAGS+= -DCONFIG_MAC80211_DEBUGFS
#CFLAGS+= -DCONFIG_MAC80211_MESH
#CFLAGS+= -DCONFIG_NL80211_TESTMODE
#CFLAGS+= -DCONFIG_PM
#CFLAGS+= -DCONFIG_PM_SLEEP
#CFLAGS+= -DCONFIG_THERMAL
.include <bsd.kmod.mk>