Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config ATH11K
tristate "Qualcomm 802.11ax chipset support"
depends on MAC80211 && HAS_DMA
select ATH_COMMON
select QCOM_QMI_HELPERS
help
This module adds support for Qualcomm Technologies 802.11ax family of
chipsets.
If you choose to build a module, it'll be called ath11k.
config ATH11K_AHB
tristate "Atheros ath11k AHB support"
depends on ATH11K
depends on REMOTEPROC
help
This module adds support for AHB bus
config ATH11K_PCI
tristate "Atheros ath11k PCI support"
depends on ATH11K && PCI
select MHI_BUS
select QRTR
select QRTR_MHI
select PCI_PWRCTRL_PWRSEQ if HAVE_PWRCTRL
help
This module adds support for PCIE bus
config ATH11K_DEBUG
bool "QCA ath11k debugging"
depends on ATH11K
help
Enables debug support
If unsure, say Y to make it easier to debug problems.
config ATH11K_DEBUGFS
bool "QCA ath11k debugfs support"
depends on ATH11K && DEBUG_FS && MAC80211_DEBUGFS
help
Enable ath11k debugfs support
If unsure, say Y to make it easier to debug problems.
config ATH11K_TRACING
bool "ath11k tracing support"
depends on ATH11K && EVENT_TRACING
help
Select this to use ath11k tracing infrastructure.
config ATH11K_SPECTRAL
bool "QCA ath11k spectral scan support"
depends on ATH11K_DEBUGFS
depends on RELAY
help
Enable ath11k spectral scan support
Say Y to enable access to the FFT/spectral data via debugfs.
config ATH11K_CFR
bool "ath11k channel frequency response support"
depends on ATH11K_DEBUGFS
depends on RELAY
help
Enable ath11k channel frequency response dump support.
This option exposes debugfs nodes that will allow the user
to enable, disable, and dump data.
Say Y to enable CFR data dump collection via debugfs.
@@ -0,0 +1,40 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_ATH11K) += ath11k.o
ath11k-y += core.o \
hal.o \
hal_tx.o \
hal_rx.o \
wmi.o \
mac.o \
reg.o \
htc.o \
qmi.o \
dp.o \
dp_tx.o \
dp_rx.o \
debug.o \
ce.o \
peer.o \
dbring.o \
hw.o \
pcic.o \
fw.o \
p2p.o
ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath11k-$(CONFIG_ATH11K_TRACING) += trace.o
ath11k-$(CONFIG_THERMAL) += thermal.o
ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o
ath11k-$(CONFIG_PM) += wow.o
ath11k-$(CONFIG_DEV_COREDUMP) += coredump.o
ath11k-$(CONFIG_ATH11K_CFR) += cfr.o
obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o
ath11k_ahb-y += ahb.o
obj-$(CONFIG_ATH11K_PCI) += ath11k_pci.o
ath11k_pci-y += mhi.o pci.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_AHB_H
#define ATH11K_AHB_H
#include "core.h"
#define ATH11K_AHB_RECOVERY_TIMEOUT (3 * HZ)
#define ATH11K_AHB_SMP2P_SMEM_MSG GENMASK(15, 0)
#define ATH11K_AHB_SMP2P_SMEM_SEQ_NO GENMASK(31, 16)
#define ATH11K_AHB_SMP2P_SMEM_VALUE_MASK 0xFFFFFFFF
enum ath11k_ahb_smp2p_msg_id {
ATH11K_AHB_POWER_SAVE_ENTER = 1,
ATH11K_AHB_POWER_SAVE_EXIT,
};
struct ath11k_base;
struct ath11k_ahb {
struct rproc *tgt_rproc;
struct {
struct device *dev;
struct iommu_domain *iommu_domain;
dma_addr_t msa_paddr;
u32 msa_size;
dma_addr_t ce_paddr;
u32 ce_size;
bool use_tz;
} fw;
struct {
unsigned short seq_no;
unsigned int smem_bit;
struct qcom_smem_state *smem_state;
} smp2p_info;
};
static inline struct ath11k_ahb *ath11k_ahb_priv(struct ath11k_base *ab)
{
return (struct ath11k_ahb *)ab->drv_priv;
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,211 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_CE_H
#define ATH11K_CE_H
#define CE_COUNT_MAX 12
/* Byte swap data words */
#define CE_ATTR_BYTE_SWAP_DATA 2
/* no interrupt on copy completion */
#define CE_ATTR_DIS_INTR 8
/* Host software's Copy Engine configuration. */
#ifdef __BIG_ENDIAN
#define CE_ATTR_FLAGS CE_ATTR_BYTE_SWAP_DATA
#else
#define CE_ATTR_FLAGS 0
#endif
/* Threshold to poll for tx completion in case of Interrupt disabled CE's */
#define ATH11K_CE_USAGE_THRESHOLD 32
void ath11k_ce_byte_swap(void *mem, u32 len);
/*
* 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 */
#define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */
/* CE address/mask */
#define CE_HOST_IE_ADDRESS 0x00A1803C
#define CE_HOST_IE_2_ADDRESS 0x00A18040
#define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS
/* CE IE registers are different for IPQ5018 */
#define CE_HOST_IPQ5018_IE_ADDRESS 0x0841804C
#define CE_HOST_IPQ5018_IE_2_ADDRESS 0x08418050
#define CE_HOST_IPQ5018_IE_3_ADDRESS CE_HOST_IPQ5018_IE_ADDRESS
#define CE_HOST_IE_3_SHIFT 0xC
#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
#define ATH11K_CE_RX_POST_RETRY_JIFFIES 50
struct ath11k_base;
/*
* Establish a mapping between a service/direction and a pipe.
* Configuration information for a Copy Engine pipe and services.
* Passed from Host to Target through QMI message and must be in
* little endian format.
*/
struct service_to_pipe {
__le32 service_id;
__le32 pipedir;
__le32 pipenum;
};
/*
* Configuration information for a Copy Engine pipe.
* Passed from Host to Target through QMI message 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;
};
struct ce_ie_addr {
u32 ie1_reg_addr;
u32 ie2_reg_addr;
u32 ie3_reg_addr;
};
struct ce_remap {
u32 base;
u32 size;
};
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 (*recv_cb)(struct ath11k_base *, struct sk_buff *);
void (*send_cb)(struct ath11k_base *, struct sk_buff *);
};
#define CE_DESC_RING_ALIGN 8
struct ath11k_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;
/* 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;
/* HAL ring id */
u32 hal_ring_id;
/* keep last */
struct sk_buff *skb[];
};
struct ath11k_ce_pipe {
struct ath11k_base *ab;
u16 pipe_num;
unsigned int attr_flags;
unsigned int buf_sz;
unsigned int rx_buf_needed;
void (*send_cb)(struct ath11k_base *, struct sk_buff *);
void (*recv_cb)(struct ath11k_base *, struct sk_buff *);
struct tasklet_struct intr_tq;
struct ath11k_ce_ring *src_ring;
struct ath11k_ce_ring *dest_ring;
struct ath11k_ce_ring *status_ring;
u64 timestamp;
};
struct ath11k_ce {
struct ath11k_ce_pipe ce_pipe[CE_COUNT_MAX];
/* Protects rings of all ce pipes */
spinlock_t ce_lock;
struct ath11k_hp_update_timer hp_timer[CE_COUNT_MAX];
};
extern const struct ce_attr ath11k_host_ce_config_ipq8074[];
extern const struct ce_attr ath11k_host_ce_config_qca6390[];
extern const struct ce_attr ath11k_host_ce_config_qcn9074[];
void ath11k_ce_cleanup_pipes(struct ath11k_base *ab);
void ath11k_ce_rx_replenish_retry(struct timer_list *t);
void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id);
int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id,
u16 transfer_id);
void ath11k_ce_rx_post_buf(struct ath11k_base *ab);
int ath11k_ce_init_pipes(struct ath11k_base *ab);
int ath11k_ce_alloc_pipes(struct ath11k_base *ab);
void ath11k_ce_free_pipes(struct ath11k_base *ab);
int ath11k_ce_get_attr_flags(struct ath11k_base *ab, int ce_id);
void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id);
void ath11k_ce_get_shadow_config(struct ath11k_base *ab,
u32 **shadow_cfg, u32 *shadow_cfg_len);
void ath11k_ce_stop_shadow_timers(struct ath11k_base *ab);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,308 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_CFR_H
#define ATH11K_CFR_H
#include "dbring.h"
#include "wmi.h"
#define ATH11K_CFR_NUM_RESP_PER_EVENT 1
#define ATH11K_CFR_EVENT_TIMEOUT_MS 1
#define ATH11K_CFR_NUM_RING_ENTRIES 1
#define ATH11K_MAX_CFR_ENABLED_CLIENTS 10
#define CFR_MAX_LUT_ENTRIES 136
#define HOST_MAX_CHAINS 8
enum ath11k_cfr_correlate_event_type {
ATH11K_CORRELATE_DBR_EVENT,
ATH11K_CORRELATE_TX_EVENT,
};
struct ath11k_sta;
struct ath11k_per_peer_cfr_capture;
#define ATH11K_CFR_START_MAGIC 0xDEADBEAF
#define ATH11K_CFR_END_MAGIC 0xBEAFDEAD
#define VENDOR_QCA 0x8cfdf0
#define PLATFORM_TYPE_ARM 2
enum ath11k_cfr_meta_version {
ATH11K_CFR_META_VERSION_NONE,
ATH11K_CFR_META_VERSION_1,
ATH11K_CFR_META_VERSION_2,
ATH11K_CFR_META_VERSION_3,
ATH11K_CFR_META_VERSION_4,
ATH11K_CFR_META_VERSION_MAX = 0xFF,
};
enum ath11k_cfr_data_version {
ATH11K_CFR_DATA_VERSION_NONE,
ATH11K_CFR_DATA_VERSION_1,
ATH11K_CFR_DATA_VERSION_MAX = 0xFF,
};
enum ath11k_cfr_capture_ack_mode {
ATH11K_CFR_CAPTURE_LEGACY_ACK,
ATH11K_CFR_CAPTURE_DUP_LEGACY_ACK,
ATH11K_CFR_CAPTURE_HT_ACK,
ATH11K_CFR_CAPTURE_VHT_ACK,
/*Always keep this at last*/
ATH11K_CFR_CAPTURE_INVALID_ACK
};
enum ath11k_cfr_correlate_status {
ATH11K_CORRELATE_STATUS_RELEASE,
ATH11K_CORRELATE_STATUS_HOLD,
ATH11K_CORRELATE_STATUS_ERR,
};
enum ath11k_cfr_preamble_type {
ATH11K_CFR_PREAMBLE_TYPE_LEGACY,
ATH11K_CFR_PREAMBLE_TYPE_HT,
ATH11K_CFR_PREAMBLE_TYPE_VHT,
};
struct ath11k_cfr_peer_tx_param {
u32 capture_method;
u32 vdev_id;
u8 peer_mac_addr[ETH_ALEN];
u32 primary_20mhz_chan;
u32 bandwidth;
u32 phy_mode;
u32 band_center_freq1;
u32 band_center_freq2;
u32 spatial_streams;
u32 correlation_info_1;
u32 correlation_info_2;
u32 status;
u32 timestamp_us;
u32 counter;
u32 chain_rssi[WMI_MAX_CHAINS];
u16 chain_phase[WMI_MAX_CHAINS];
u32 cfo_measurement;
u8 agc_gain[HOST_MAX_CHAINS];
u32 rx_start_ts;
};
struct cfr_metadata {
u8 peer_addr[ETH_ALEN];
u8 status;
u8 capture_bw;
u8 channel_bw;
u8 phy_mode;
u16 prim20_chan;
u16 center_freq1;
u16 center_freq2;
u8 capture_mode;
u8 capture_type;
u8 sts_count;
u8 num_rx_chain;
u32 timestamp;
u32 length;
u32 chain_rssi[HOST_MAX_CHAINS];
u16 chain_phase[HOST_MAX_CHAINS];
u32 cfo_measurement;
u8 agc_gain[HOST_MAX_CHAINS];
u32 rx_start_ts;
} __packed;
struct ath11k_csi_cfr_header {
u32 start_magic_num;
u32 vendorid;
u8 cfr_metadata_version;
u8 cfr_data_version;
u8 chip_type;
u8 platform_type;
u32 cfr_metadata_len;
struct cfr_metadata meta_data;
} __packed;
#define TONES_IN_20MHZ 256
#define TONES_IN_40MHZ 512
#define TONES_IN_80MHZ 1024
#define TONES_IN_160MHZ 2048 /* 160 MHz isn't supported yet */
#define TONES_INVALID 0
#define CFIR_DMA_HDR_INFO0_TAG GENMASK(7, 0)
#define CFIR_DMA_HDR_INFO0_LEN GENMASK(13, 8)
#define CFIR_DMA_HDR_INFO1_UPLOAD_DONE GENMASK(0, 0)
#define CFIR_DMA_HDR_INFO1_CAPTURE_TYPE GENMASK(3, 1)
#define CFIR_DMA_HDR_INFO1_PREAMBLE_TYPE GENMASK(5, 4)
#define CFIR_DMA_HDR_INFO1_NSS GENMASK(8, 6)
#define CFIR_DMA_HDR_INFO1_NUM_CHAINS GENMASK(11, 9)
#define CFIR_DMA_HDR_INFO1_UPLOAD_PKT_BW GENMASK(14, 12)
#define CFIR_DMA_HDR_INFO1_SW_PEER_ID_VALID GENMASK(15, 15)
struct ath11k_cfr_dma_hdr {
u16 info0;
u16 info1;
u16 sw_peer_id;
u16 phy_ppdu_id;
};
struct ath11k_look_up_table {
bool dbr_recv;
bool tx_recv;
u8 *data;
u32 data_len;
u16 dbr_ppdu_id;
u16 tx_ppdu_id;
dma_addr_t dbr_address;
struct ath11k_csi_cfr_header header;
struct ath11k_cfr_dma_hdr hdr;
u64 txrx_tstamp;
u64 dbr_tstamp;
u32 header_length;
u32 payload_length;
struct ath11k_dbring_element *buff;
};
struct cfr_unassoc_pool_entry {
u8 peer_mac[ETH_ALEN];
u32 period;
bool is_valid;
};
struct ath11k_cfr {
struct ath11k_dbring rx_ring;
/* Protects cfr data */
spinlock_t lock;
/* Protect for lut entries */
spinlock_t lut_lock;
struct ath11k_look_up_table *lut;
struct dentry *enable_cfr;
struct dentry *cfr_unassoc;
struct rchan *rfs_cfr_capture;
u8 cfr_enabled_peer_cnt;
u32 lut_num;
u64 tx_evt_cnt;
u64 dbr_evt_cnt;
u64 release_cnt;
u64 tx_peer_status_cfr_fail;
u64 tx_evt_status_cfr_fail;
u64 tx_dbr_lookup_fail;
u64 last_success_tstamp;
u64 flush_dbr_cnt;
u64 clear_txrx_event;
u64 cfr_dma_aborts;
bool enabled;
enum wmi_phy_mode phymode;
struct cfr_unassoc_pool_entry unassoc_pool[ATH11K_MAX_CFR_ENABLED_CLIENTS];
};
enum ath11k_cfr_capture_method {
ATH11K_CFR_CAPTURE_METHOD_NULL_FRAME,
ATH11K_CFR_CAPTURE_METHOD_NULL_FRAME_WITH_PHASE,
ATH11K_CFR_CAPTURE_METHOD_PROBE_RESP,
ATH11K_CFR_CAPTURE_METHOD_MAX,
};
enum ath11k_cfr_capture_bw {
ATH11K_CFR_CAPTURE_BW_20,
ATH11K_CFR_CAPTURE_BW_40,
ATH11K_CFR_CAPTURE_BW_80,
ATH11K_CFR_CAPTURE_BW_MAX,
};
#ifdef CONFIG_ATH11K_CFR
int ath11k_cfr_init(struct ath11k_base *ab);
void ath11k_cfr_deinit(struct ath11k_base *ab);
void ath11k_cfr_lut_update_paddr(struct ath11k *ar, dma_addr_t paddr,
u32 buf_id);
void ath11k_cfr_decrement_peer_count(struct ath11k *ar,
struct ath11k_sta *arsta);
void ath11k_cfr_update_unassoc_pool_entry(struct ath11k *ar,
const u8 *peer_mac);
bool ath11k_cfr_peer_is_in_cfr_unassoc_pool(struct ath11k *ar,
const u8 *peer_mac);
void ath11k_cfr_update_unassoc_pool(struct ath11k *ar,
struct ath11k_per_peer_cfr_capture *params,
u8 *peer_mac);
int ath11k_cfr_send_peer_cfr_capture_cmd(struct ath11k *ar,
struct ath11k_sta *arsta,
struct ath11k_per_peer_cfr_capture *params,
const u8 *peer_mac);
struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar);
void ath11k_cfr_release_lut_entry(struct ath11k_look_up_table *lut);
int ath11k_process_cfr_capture_event(struct ath11k_base *ab,
struct ath11k_cfr_peer_tx_param *params);
void ath11k_cfr_update_phymode(struct ath11k *ar, enum wmi_phy_mode phymode);
#else
static inline void ath11k_cfr_update_phymode(struct ath11k *ar,
enum wmi_phy_mode phymode)
{
}
static inline int ath11k_cfr_init(struct ath11k_base *ab)
{
return 0;
}
static inline void ath11k_cfr_deinit(struct ath11k_base *ab)
{
}
static inline void ath11k_cfr_lut_update_paddr(struct ath11k *ar,
dma_addr_t paddr, u32 buf_id)
{
}
static inline void ath11k_cfr_decrement_peer_count(struct ath11k *ar,
struct ath11k_sta *arsta)
{
}
static inline void ath11k_cfr_update_unassoc_pool_entry(struct ath11k *ar,
const u8 *peer_mac)
{
}
static inline bool
ath11k_cfr_peer_is_in_cfr_unassoc_pool(struct ath11k *ar, const u8 *peer_mac)
{
return false;
}
static inline void
ath11k_cfr_update_unassoc_pool(struct ath11k *ar,
struct ath11k_per_peer_cfr_capture *params,
u8 *peer_mac)
{
}
static inline int
ath11k_cfr_send_peer_cfr_capture_cmd(struct ath11k *ar,
struct ath11k_sta *arsta,
struct ath11k_per_peer_cfr_capture *params,
const u8 *peer_mac)
{
return 0;
}
static inline void ath11k_cfr_release_lut_entry(struct ath11k_look_up_table *lut)
{
}
static inline
struct ath11k_dbring *ath11k_cfr_get_dbring(struct ath11k *ar)
{
return NULL;
}
static inline
int ath11k_process_cfr_capture_event(struct ath11k_base *ab,
struct ath11k_cfr_peer_tx_param *params)
{
return 0;
}
#endif /* CONFIG_ATH11K_CFR */
#endif /* ATH11K_CFR_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/devcoredump.h>
#include <linux/export.h>
#include "hif.h"
#include "coredump.h"
#include "debug.h"
enum
ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type)
{
enum ath11k_fw_crash_dump_type dump_type;
switch (type) {
case HOST_DDR_REGION_TYPE:
dump_type = FW_CRASH_DUMP_REMOTE_MEM_DATA;
break;
case M3_DUMP_REGION_TYPE:
dump_type = FW_CRASH_DUMP_M3_DUMP;
break;
case PAGEABLE_MEM_REGION_TYPE:
dump_type = FW_CRASH_DUMP_PAGEABLE_DATA;
break;
case BDF_MEM_REGION_TYPE:
case CALDB_MEM_REGION_TYPE:
dump_type = FW_CRASH_DUMP_NONE;
break;
default:
dump_type = FW_CRASH_DUMP_TYPE_MAX;
break;
}
return dump_type;
}
EXPORT_SYMBOL(ath11k_coredump_get_dump_type);
void ath11k_coredump_upload(struct work_struct *work)
{
struct ath11k_base *ab = container_of(work, struct ath11k_base, dump_work);
ath11k_info(ab, "Uploading coredump\n");
/* dev_coredumpv() takes ownership of the buffer */
dev_coredumpv(ab->dev, ab->dump_data, ab->ath11k_coredump_len, GFP_KERNEL);
ab->dump_data = NULL;
}
void ath11k_coredump_collect(struct ath11k_base *ab)
{
ath11k_hif_coredump_download(ab);
}
@@ -0,0 +1,79 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_COREDUMP_H_
#define _ATH11K_COREDUMP_H_
#define ATH11K_FW_CRASH_DUMP_V2 2
enum ath11k_fw_crash_dump_type {
FW_CRASH_DUMP_PAGING_DATA,
FW_CRASH_DUMP_RDDM_DATA,
FW_CRASH_DUMP_REMOTE_MEM_DATA,
FW_CRASH_DUMP_PAGEABLE_DATA,
FW_CRASH_DUMP_M3_DUMP,
FW_CRASH_DUMP_NONE,
/* keep last */
FW_CRASH_DUMP_TYPE_MAX,
};
#define COREDUMP_TLV_HDR_SIZE 8
struct ath11k_tlv_dump_data {
/* see ath11k_fw_crash_dump_type above */
__le32 type;
/* in bytes */
__le32 tlv_len;
/* pad to 32-bit boundaries as needed */
u8 tlv_data[];
} __packed;
struct ath11k_dump_file_data {
/* "ATH11K-FW-DUMP" */
char df_magic[16];
/* total dump len in bytes */
__le32 len;
/* file dump version */
__le32 version;
/* pci device id */
__le32 chip_id;
/* qrtr instance id */
__le32 qrtr_id;
/* pci domain id */
__le32 bus_id;
guid_t guid;
/* time-of-day stamp */
__le64 tv_sec;
/* time-of-day stamp, nano-seconds */
__le64 tv_nsec;
/* room for growth w/out changing binary format */
u8 unused[128];
u8 data[];
} __packed;
#ifdef CONFIG_DEV_COREDUMP
enum ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type);
void ath11k_coredump_upload(struct work_struct *work);
void ath11k_coredump_collect(struct ath11k_base *ab);
#else
static inline enum
ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type)
{
return FW_CRASH_DUMP_TYPE_MAX;
}
static inline void ath11k_coredump_upload(struct work_struct *work)
{
}
static inline void ath11k_coredump_collect(struct ath11k_base *ab)
{
}
#endif
#endif
@@ -0,0 +1,427 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "core.h"
#include "debug.h"
#define ATH11K_DB_MAGIC_VALUE 0xdeadbeaf
int ath11k_dbring_validate_buffer(struct ath11k *ar, void *buffer, u32 size)
{
u32 *temp;
int idx;
size = size >> 2;
for (idx = 0, temp = buffer; idx < size; idx++, temp++) {
if (*temp == ATH11K_DB_MAGIC_VALUE)
return -EINVAL;
}
return 0;
}
static void ath11k_dbring_fill_magic_value(struct ath11k *ar,
void *buffer, u32 size)
{
/* memset32 function fills buffer payload with the ATH11K_DB_MAGIC_VALUE
* and the variable size is expected to be the number of u32 values
* to be stored, not the number of bytes.
*/
size = size / sizeof(u32);
memset32(buffer, ATH11K_DB_MAGIC_VALUE, size);
}
int ath11k_dbring_bufs_replenish(struct ath11k *ar,
struct ath11k_dbring *ring,
struct ath11k_dbring_element *buff,
enum wmi_direct_buffer_module id)
{
struct ath11k_base *ab = ar->ab;
struct hal_srng *srng;
dma_addr_t paddr;
void *ptr_aligned, *ptr_unaligned, *desc;
int ret;
int buf_id;
u32 cookie;
srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
lockdep_assert_held(&srng->lock);
ath11k_hal_srng_access_begin(ab, srng);
ptr_unaligned = buff->payload;
ptr_aligned = PTR_ALIGN(ptr_unaligned, ring->buf_align);
ath11k_dbring_fill_magic_value(ar, ptr_aligned, ring->buf_sz);
paddr = dma_map_single(ab->dev, ptr_aligned, ring->buf_sz,
DMA_FROM_DEVICE);
ret = dma_mapping_error(ab->dev, paddr);
if (ret)
goto err;
spin_lock_bh(&ring->idr_lock);
buf_id = idr_alloc(&ring->bufs_idr, buff, 0, ring->bufs_max, GFP_ATOMIC);
spin_unlock_bh(&ring->idr_lock);
if (buf_id < 0) {
ret = -ENOBUFS;
goto err_dma_unmap;
}
desc = ath11k_hal_srng_src_get_next_entry(ab, srng);
if (!desc) {
ret = -ENOENT;
goto err_idr_remove;
}
if (id == WMI_DIRECT_BUF_CFR)
ath11k_cfr_lut_update_paddr(ar, paddr, buf_id);
buff->paddr = paddr;
cookie = FIELD_PREP(DP_RXDMA_BUF_COOKIE_PDEV_ID, ar->pdev_idx) |
FIELD_PREP(DP_RXDMA_BUF_COOKIE_BUF_ID, buf_id);
ath11k_hal_rx_buf_addr_info_set(desc, paddr, cookie, 0);
ath11k_debugfs_add_dbring_entry(ar, id, ATH11K_DBG_DBR_EVENT_REPLENISH, srng);
ath11k_hal_srng_access_end(ab, srng);
return 0;
err_idr_remove:
spin_lock_bh(&ring->idr_lock);
idr_remove(&ring->bufs_idr, buf_id);
spin_unlock_bh(&ring->idr_lock);
err_dma_unmap:
dma_unmap_single(ab->dev, paddr, ring->buf_sz,
DMA_FROM_DEVICE);
err:
ath11k_hal_srng_access_end(ab, srng);
return ret;
}
static int ath11k_dbring_fill_bufs(struct ath11k *ar,
struct ath11k_dbring *ring,
enum wmi_direct_buffer_module id)
{
struct ath11k_dbring_element *buff;
struct hal_srng *srng;
int num_remain, req_entries, num_free;
u32 align;
int size, ret;
srng = &ar->ab->hal.srng_list[ring->refill_srng.ring_id];
spin_lock_bh(&srng->lock);
num_free = ath11k_hal_srng_src_num_free(ar->ab, srng, true);
req_entries = min(num_free, ring->bufs_max);
num_remain = req_entries;
align = ring->buf_align;
size = ring->buf_sz + align - 1;
while (num_remain > 0) {
buff = kzalloc_obj(*buff, GFP_ATOMIC);
if (!buff)
break;
buff->payload = kzalloc(size, GFP_ATOMIC);
if (!buff->payload) {
kfree(buff);
break;
}
ret = ath11k_dbring_bufs_replenish(ar, ring, buff, id);
if (ret) {
ath11k_warn(ar->ab, "failed to replenish db ring num_remain %d req_ent %d\n",
num_remain, req_entries);
kfree(buff->payload);
kfree(buff);
break;
}
num_remain--;
}
spin_unlock_bh(&srng->lock);
return num_remain;
}
int ath11k_dbring_wmi_cfg_setup(struct ath11k *ar,
struct ath11k_dbring *ring,
enum wmi_direct_buffer_module id)
{
struct ath11k_wmi_pdev_dma_ring_cfg_req_cmd param = {};
int ret, i;
if (id >= WMI_DIRECT_BUF_MAX)
return -EINVAL;
param.module_id = id;
param.base_paddr_lo = lower_32_bits(ring->refill_srng.paddr);
param.base_paddr_hi = upper_32_bits(ring->refill_srng.paddr);
param.head_idx_paddr_lo = lower_32_bits(ring->hp_addr);
param.head_idx_paddr_hi = upper_32_bits(ring->hp_addr);
param.tail_idx_paddr_lo = lower_32_bits(ring->tp_addr);
param.tail_idx_paddr_hi = upper_32_bits(ring->tp_addr);
param.num_elems = ring->bufs_max;
param.buf_size = ring->buf_sz;
param.num_resp_per_event = ring->num_resp_per_event;
param.event_timeout_ms = ring->event_timeout_ms;
/* For single pdev, 2GHz and 5GHz use one DBR. */
if (ar->ab->hw_params.single_pdev_only) {
for (i = 0; i < ar->ab->target_pdev_count; i++) {
param.pdev_id = ar->ab->target_pdev_ids[i].pdev_id;
ret = ath11k_wmi_pdev_dma_ring_cfg(ar, &param);
if (ret) {
ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
return ret;
}
}
} else {
param.pdev_id = DP_SW2HW_MACID(ring->pdev_id);
ret = ath11k_wmi_pdev_dma_ring_cfg(ar, &param);
if (ret) {
ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
return ret;
}
}
return 0;
}
int ath11k_dbring_set_cfg(struct ath11k *ar, struct ath11k_dbring *ring,
u32 num_resp_per_event, u32 event_timeout_ms,
int (*handler)(struct ath11k *,
struct ath11k_dbring_data *))
{
if (WARN_ON(!ring))
return -EINVAL;
ring->num_resp_per_event = num_resp_per_event;
ring->event_timeout_ms = event_timeout_ms;
ring->handler = handler;
return 0;
}
int ath11k_dbring_buf_setup(struct ath11k *ar,
struct ath11k_dbring *ring,
struct ath11k_dbring_cap *db_cap)
{
struct ath11k_base *ab = ar->ab;
struct hal_srng *srng;
int ret;
srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
ring->bufs_max = ring->refill_srng.size /
ath11k_hal_srng_get_entrysize(ab, HAL_RXDMA_DIR_BUF);
ring->buf_sz = db_cap->min_buf_sz;
ring->buf_align = db_cap->min_buf_align;
ring->pdev_id = db_cap->pdev_id;
ring->hp_addr = ath11k_hal_srng_get_hp_addr(ar->ab, srng);
ring->tp_addr = ath11k_hal_srng_get_tp_addr(ar->ab, srng);
ret = ath11k_dbring_fill_bufs(ar, ring, db_cap->id);
return ret;
}
int ath11k_dbring_srng_setup(struct ath11k *ar, struct ath11k_dbring *ring,
int ring_num, int num_entries)
{
int ret;
ret = ath11k_dp_srng_setup(ar->ab, &ring->refill_srng, HAL_RXDMA_DIR_BUF,
ring_num, ar->pdev_idx, num_entries);
if (ret < 0) {
ath11k_warn(ar->ab, "failed to setup srng: %d ring_id %d\n",
ret, ring_num);
goto err;
}
return 0;
err:
ath11k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
return ret;
}
int ath11k_dbring_get_cap(struct ath11k_base *ab,
u8 pdev_idx,
enum wmi_direct_buffer_module id,
struct ath11k_dbring_cap *db_cap)
{
int i;
if (!ab->num_db_cap || !ab->db_caps)
return -ENOENT;
if (id >= WMI_DIRECT_BUF_MAX)
return -EINVAL;
for (i = 0; i < ab->num_db_cap; i++) {
if (pdev_idx == ab->db_caps[i].pdev_id &&
id == ab->db_caps[i].id) {
*db_cap = ab->db_caps[i];
return 0;
}
}
return -ENOENT;
}
int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
struct ath11k_dbring_buf_release_event *ev)
{
struct ath11k_dbring *ring;
struct hal_srng *srng;
struct ath11k *ar;
struct ath11k_dbring_element *buff;
struct ath11k_dbring_data handler_data;
struct ath11k_buffer_addr desc;
u8 *vaddr_unalign;
u32 num_entry, num_buff_reaped;
u8 pdev_idx, rbm, module_id;
u32 cookie;
int buf_id;
int size;
dma_addr_t paddr;
int ret = 0;
int status;
pdev_idx = ev->fixed.pdev_id;
module_id = ev->fixed.module_id;
if (ab->hw_params.single_pdev_only &&
pdev_idx < ab->target_pdev_count)
pdev_idx = 0;
if (pdev_idx >= ab->num_radios) {
ath11k_warn(ab, "Invalid pdev id %d\n", pdev_idx);
return -EINVAL;
}
if (ev->fixed.num_buf_release_entry !=
ev->fixed.num_meta_data_entry) {
ath11k_warn(ab, "Buffer entry %d mismatch meta entry %d\n",
ev->fixed.num_buf_release_entry,
ev->fixed.num_meta_data_entry);
return -EINVAL;
}
ar = ab->pdevs[pdev_idx].ar;
rcu_read_lock();
if (!rcu_dereference(ab->pdevs_active[pdev_idx])) {
ret = -EINVAL;
goto rcu_unlock;
}
switch (ev->fixed.module_id) {
case WMI_DIRECT_BUF_SPECTRAL:
ring = ath11k_spectral_get_dbring(ar);
break;
case WMI_DIRECT_BUF_CFR:
ring = ath11k_cfr_get_dbring(ar);
break;
default:
ring = NULL;
ath11k_warn(ab, "Recv dma buffer release ev on unsupp module %d\n",
ev->fixed.module_id);
break;
}
if (!ring) {
ret = -EINVAL;
goto rcu_unlock;
}
srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
num_entry = ev->fixed.num_buf_release_entry;
size = ring->buf_sz + ring->buf_align - 1;
num_buff_reaped = 0;
spin_lock_bh(&srng->lock);
while (num_buff_reaped < num_entry) {
desc.info0 = ev->buf_entry[num_buff_reaped].paddr_lo;
desc.info1 = ev->buf_entry[num_buff_reaped].paddr_hi;
handler_data.meta = ev->meta_data[num_buff_reaped];
num_buff_reaped++;
ath11k_hal_rx_buf_addr_info_get(&desc, &paddr, &cookie, &rbm);
buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie);
spin_lock_bh(&ring->idr_lock);
buff = idr_find(&ring->bufs_idr, buf_id);
if (!buff) {
spin_unlock_bh(&ring->idr_lock);
continue;
}
idr_remove(&ring->bufs_idr, buf_id);
spin_unlock_bh(&ring->idr_lock);
dma_unmap_single(ab->dev, buff->paddr, ring->buf_sz,
DMA_FROM_DEVICE);
ath11k_debugfs_add_dbring_entry(ar, module_id,
ATH11K_DBG_DBR_EVENT_RX, srng);
if (ring->handler) {
vaddr_unalign = buff->payload;
handler_data.data = PTR_ALIGN(vaddr_unalign,
ring->buf_align);
handler_data.data_sz = ring->buf_sz;
handler_data.buff = buff;
handler_data.buf_id = buf_id;
status = ring->handler(ar, &handler_data);
if (status == ATH11K_CORRELATE_STATUS_HOLD)
continue;
}
buff->paddr = 0;
memset(buff->payload, 0, size);
ath11k_dbring_bufs_replenish(ar, ring, buff, module_id);
}
spin_unlock_bh(&srng->lock);
rcu_unlock:
rcu_read_unlock();
return ret;
}
void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring)
{
ath11k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
}
void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring)
{
struct ath11k_dbring_element *buff;
int buf_id;
spin_lock_bh(&ring->idr_lock);
idr_for_each_entry(&ring->bufs_idr, buff, buf_id) {
idr_remove(&ring->bufs_idr, buf_id);
dma_unmap_single(ar->ab->dev, buff->paddr,
ring->buf_sz, DMA_FROM_DEVICE);
kfree(buff->payload);
kfree(buff);
}
idr_destroy(&ring->bufs_idr);
spin_unlock_bh(&ring->idr_lock);
}
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_DBRING_H
#define ATH11K_DBRING_H
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/spinlock.h>
#include "dp.h"
struct ath11k_dbring_element {
dma_addr_t paddr;
u8 *payload;
};
struct ath11k_dbring_data {
void *data;
u32 data_sz;
struct wmi_dma_buf_release_meta_data meta;
struct ath11k_dbring_element *buff;
u32 buf_id;
};
struct ath11k_dbring_buf_release_event {
struct ath11k_wmi_dma_buf_release_fixed_param fixed;
struct wmi_dma_buf_release_entry *buf_entry;
struct wmi_dma_buf_release_meta_data *meta_data;
u32 num_buf_entry;
u32 num_meta;
};
struct ath11k_dbring_cap {
u32 pdev_id;
enum wmi_direct_buffer_module id;
u32 min_elem;
u32 min_buf_sz;
u32 min_buf_align;
};
struct ath11k_dbring {
struct dp_srng refill_srng;
struct idr bufs_idr;
/* Protects bufs_idr */
spinlock_t idr_lock;
dma_addr_t tp_addr;
dma_addr_t hp_addr;
int bufs_max;
u32 pdev_id;
u32 buf_sz;
u32 buf_align;
u32 num_resp_per_event;
u32 event_timeout_ms;
int (*handler)(struct ath11k *, struct ath11k_dbring_data *);
};
int ath11k_dbring_set_cfg(struct ath11k *ar,
struct ath11k_dbring *ring,
u32 num_resp_per_event,
u32 event_timeout_ms,
int (*handler)(struct ath11k *,
struct ath11k_dbring_data *));
int ath11k_dbring_bufs_replenish(struct ath11k *ar,
struct ath11k_dbring *ring,
struct ath11k_dbring_element *buff,
enum wmi_direct_buffer_module id);
int ath11k_dbring_wmi_cfg_setup(struct ath11k *ar,
struct ath11k_dbring *ring,
enum wmi_direct_buffer_module id);
int ath11k_dbring_buf_setup(struct ath11k *ar,
struct ath11k_dbring *ring,
struct ath11k_dbring_cap *db_cap);
int ath11k_dbring_srng_setup(struct ath11k *ar, struct ath11k_dbring *ring,
int ring_num, int num_entries);
int ath11k_dbring_buffer_release_event(struct ath11k_base *ab,
struct ath11k_dbring_buf_release_event *ev);
int ath11k_dbring_get_cap(struct ath11k_base *ab,
u8 pdev_idx,
enum wmi_direct_buffer_module id,
struct ath11k_dbring_cap *db_cap);
void ath11k_dbring_srng_cleanup(struct ath11k *ar, struct ath11k_dbring *ring);
void ath11k_dbring_buf_cleanup(struct ath11k *ar, struct ath11k_dbring *ring);
int ath11k_dbring_validate_buffer(struct ath11k *ar, void *data, u32 size);
#endif /* ATH11K_DBRING_H */
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/export.h>
#include <linux/vmalloc.h>
#include "core.h"
#include "debug.h"
void ath11k_info(struct ath11k_base *ab, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
dev_info(ab->dev, "%pV", &vaf);
trace_ath11k_log_info(ab, &vaf);
va_end(args);
}
EXPORT_SYMBOL(ath11k_info);
void ath11k_err(struct ath11k_base *ab, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
dev_err(ab->dev, "%pV", &vaf);
trace_ath11k_log_err(ab, &vaf);
va_end(args);
}
EXPORT_SYMBOL(ath11k_err);
void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
dev_warn_ratelimited(ab->dev, "%pV", &vaf);
trace_ath11k_log_warn(ab, &vaf);
va_end(args);
}
EXPORT_SYMBOL(ath11k_warn);
#ifdef CONFIG_ATH11K_DEBUG
void __ath11k_dbg(struct ath11k_base *ab, enum ath11k_debug_mask mask,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
if (ath11k_debug_mask & mask)
dev_printk(KERN_DEBUG, ab->dev, "%s %pV", ath11k_dbg_str(mask), &vaf);
trace_ath11k_log_dbg(ab, mask, &vaf);
va_end(args);
}
EXPORT_SYMBOL(__ath11k_dbg);
void ath11k_dbg_dump(struct ath11k_base *ab,
enum ath11k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len)
{
char linebuf[256];
size_t linebuflen;
const void *ptr;
if (ath11k_debug_mask & mask) {
if (msg)
__ath11k_dbg(ab, mask, "%s\n", msg);
for (ptr = buf; (ptr - buf) < len; ptr += 16) {
linebuflen = 0;
linebuflen += scnprintf(linebuf + linebuflen,
sizeof(linebuf) - linebuflen,
"%s%08x: ",
(prefix ? prefix : ""),
(unsigned int)(ptr - buf));
hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,
linebuf + linebuflen,
sizeof(linebuf) - linebuflen, true);
dev_printk(KERN_DEBUG, ab->dev, "%s\n", linebuf);
}
}
/* tracing code doesn't like null strings */
trace_ath11k_log_dbg_dump(ab, msg ? msg : "", prefix ? prefix : "",
buf, len);
}
EXPORT_SYMBOL(ath11k_dbg_dump);
#endif /* CONFIG_ATH11K_DEBUG */
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_DEBUG_H_
#define _ATH11K_DEBUG_H_
#include "trace.h"
#include "debugfs.h"
enum ath11k_debug_mask {
ATH11K_DBG_AHB = 0x00000001,
ATH11K_DBG_WMI = 0x00000002,
ATH11K_DBG_HTC = 0x00000004,
ATH11K_DBG_DP_HTT = 0x00000008,
ATH11K_DBG_MAC = 0x00000010,
ATH11K_DBG_BOOT = 0x00000020,
ATH11K_DBG_QMI = 0x00000040,
ATH11K_DBG_DATA = 0x00000080,
ATH11K_DBG_MGMT = 0x00000100,
ATH11K_DBG_REG = 0x00000200,
ATH11K_DBG_TESTMODE = 0x00000400,
ATH11K_DBG_HAL = 0x00000800,
ATH11K_DBG_PCI = 0x00001000,
ATH11K_DBG_DP_TX = 0x00002000,
ATH11K_DBG_DP_RX = 0x00004000,
ATH11K_DBG_CE = 0x00008000,
ATH11K_DBG_CFR = 0x00010000,
ATH11K_DBG_CFR_DUMP = 0x00020000,
};
static inline const char *ath11k_dbg_str(enum ath11k_debug_mask mask)
{
switch (mask) {
case ATH11K_DBG_AHB:
return "ahb";
case ATH11K_DBG_WMI:
return "wmi";
case ATH11K_DBG_HTC:
return "htc";
case ATH11K_DBG_DP_HTT:
return "dp_htt";
case ATH11K_DBG_MAC:
return "mac";
case ATH11K_DBG_BOOT:
return "boot";
case ATH11K_DBG_QMI:
return "qmi";
case ATH11K_DBG_DATA:
return "data";
case ATH11K_DBG_MGMT:
return "mgmt";
case ATH11K_DBG_REG:
return "reg";
case ATH11K_DBG_TESTMODE:
return "testmode";
case ATH11K_DBG_HAL:
return "hal";
case ATH11K_DBG_PCI:
return "pci";
case ATH11K_DBG_DP_TX:
return "dp_tx";
case ATH11K_DBG_DP_RX:
return "dp_rx";
case ATH11K_DBG_CE:
return "ce";
case ATH11K_DBG_CFR:
return "cfr";
case ATH11K_DBG_CFR_DUMP:
return "cfr_dump";
/* no default handler to allow compiler to check that the
* enum is fully handled
*/
}
return "<?>";
}
__printf(2, 3) void ath11k_info(struct ath11k_base *ab, const char *fmt, ...);
__printf(2, 3) void ath11k_err(struct ath11k_base *ab, const char *fmt, ...);
__printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...);
extern unsigned int ath11k_debug_mask;
#ifdef CONFIG_ATH11K_DEBUG
__printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab,
enum ath11k_debug_mask mask,
const char *fmt, ...);
void ath11k_dbg_dump(struct ath11k_base *ab,
enum ath11k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len);
#else /* CONFIG_ATH11K_DEBUG */
static inline int __ath11k_dbg(struct ath11k_base *ab,
enum ath11k_debug_mask dbg_mask,
const char *fmt, ...)
{
return 0;
}
static inline void ath11k_dbg_dump(struct ath11k_base *ab,
enum ath11k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len)
{
}
#endif /* CONFIG_ATH11K_DEBUG */
#define ath11k_dbg(ar, dbg_mask, fmt, ...) \
do { \
if ((ath11k_debug_mask & dbg_mask) || \
trace_ath11k_log_dbg_enabled()) \
__ath11k_dbg(ar, dbg_mask, fmt, ##__VA_ARGS__); \
} while (0)
#endif /* _ATH11K_DEBUG_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,391 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_DEBUGFS_H_
#define _ATH11K_DEBUGFS_H_
#include "hal_tx.h"
#define ATH11K_TX_POWER_MAX_VAL 70
#define ATH11K_TX_POWER_MIN_VAL 0
/* htt_dbg_ext_stats_type */
enum ath11k_dbg_htt_ext_stats_type {
ATH11K_DBG_HTT_EXT_STATS_RESET = 0,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TX = 1,
ATH11K_DBG_HTT_EXT_STATS_PDEV_RX = 2,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_HWQ = 3,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4,
ATH11K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TQM = 6,
ATH11K_DBG_HTT_EXT_STATS_TQM_CMDQ = 7,
ATH11K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9,
ATH11K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10,
ATH11K_DBG_HTT_EXT_STATS_PEER_INFO = 11,
ATH11K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12,
ATH11K_DBG_HTT_EXT_STATS_TX_MU_HWQ = 13,
ATH11K_DBG_HTT_EXT_STATS_RING_IF_INFO = 14,
ATH11K_DBG_HTT_EXT_STATS_SRNG_INFO = 15,
ATH11K_DBG_HTT_EXT_STATS_SFM_INFO = 16,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17,
ATH11K_DBG_HTT_EXT_STATS_ACTIVE_PEERS_LIST = 18,
ATH11K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19,
ATH11K_DBG_HTT_EXT_STATS_TWT_SESSIONS = 20,
ATH11K_DBG_HTT_EXT_STATS_REO_RESOURCE_STATS = 21,
ATH11K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22,
ATH11K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23,
ATH11K_DBG_HTT_EXT_STATS_RING_BACKPRESSURE_STATS = 24,
ATH11K_DBG_HTT_EXT_STATS_PEER_CTRL_PATH_TXRX_STATS = 29,
ATH11K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF_STATS = 31,
ATH11K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32,
ATH11K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37,
/* keep this last */
ATH11K_DBG_HTT_NUM_EXT_STATS,
};
#define ATH11K_DEBUG_DBR_ENTRIES_MAX 512
enum ath11k_dbg_dbr_event {
ATH11K_DBG_DBR_EVENT_INVALID,
ATH11K_DBG_DBR_EVENT_RX,
ATH11K_DBG_DBR_EVENT_REPLENISH,
ATH11K_DBG_DBR_EVENT_MAX,
};
struct ath11k_dbg_dbr_entry {
u32 hp;
u32 tp;
u64 timestamp;
enum ath11k_dbg_dbr_event event;
};
struct ath11k_dbg_dbr_data {
/* protects ath11k_db_ring_debug data */
spinlock_t lock;
struct ath11k_dbg_dbr_entry *entries;
u32 dbr_debug_idx;
u32 num_ring_debug_entries;
};
struct ath11k_debug_dbr {
struct ath11k_dbg_dbr_data dbr_dbg_data;
struct dentry *dbr_debugfs;
bool dbr_debug_enabled;
};
struct debug_htt_stats_req {
bool done;
u8 pdev_id;
u8 type;
u8 peer_addr[ETH_ALEN];
struct completion cmpln;
u32 buf_len;
u8 buf[];
};
struct ath_pktlog_hdr {
u16 flags;
u16 missed_cnt;
u16 log_type;
u16 size;
u32 timestamp;
u32 type_specific_data;
u8 payload[];
};
#define ATH11K_HTT_PEER_STATS_RESET BIT(16)
#define ATH11K_HTT_STATS_BUF_SIZE (1024 * 512)
#define ATH11K_FW_STATS_BUF_SIZE (1024 * 1024)
enum ath11k_pktlog_filter {
ATH11K_PKTLOG_RX = 0x000000001,
ATH11K_PKTLOG_TX = 0x000000002,
ATH11K_PKTLOG_RCFIND = 0x000000004,
ATH11K_PKTLOG_RCUPDATE = 0x000000008,
ATH11K_PKTLOG_EVENT_SMART_ANT = 0x000000020,
ATH11K_PKTLOG_EVENT_SW = 0x000000040,
ATH11K_PKTLOG_ANY = 0x00000006f,
};
enum ath11k_pktlog_mode {
ATH11K_PKTLOG_MODE_LITE = 1,
ATH11K_PKTLOG_MODE_FULL = 2,
};
enum ath11k_pktlog_enum {
ATH11K_PKTLOG_TYPE_INVALID = 0,
ATH11K_PKTLOG_TYPE_TX_CTRL = 1,
ATH11K_PKTLOG_TYPE_TX_STAT = 2,
ATH11K_PKTLOG_TYPE_TX_MSDU_ID = 3,
ATH11K_PKTLOG_TYPE_RX_STAT = 5,
ATH11K_PKTLOG_TYPE_RC_FIND = 6,
ATH11K_PKTLOG_TYPE_RC_UPDATE = 7,
ATH11K_PKTLOG_TYPE_TX_VIRT_ADDR = 8,
ATH11K_PKTLOG_TYPE_RX_CBF = 10,
ATH11K_PKTLOG_TYPE_RX_STATBUF = 22,
ATH11K_PKTLOG_TYPE_PPDU_STATS = 23,
ATH11K_PKTLOG_TYPE_LITE_RX = 24,
};
enum ath11k_dbg_aggr_mode {
ATH11K_DBG_AGGR_MODE_AUTO,
ATH11K_DBG_AGGR_MODE_MANUAL,
ATH11K_DBG_AGGR_MODE_MAX,
};
enum fw_dbglog_wlan_module_id {
WLAN_MODULE_ID_MIN = 0,
WLAN_MODULE_INF = WLAN_MODULE_ID_MIN,
WLAN_MODULE_WMI,
WLAN_MODULE_STA_PWRSAVE,
WLAN_MODULE_WHAL,
WLAN_MODULE_COEX,
WLAN_MODULE_ROAM,
WLAN_MODULE_RESMGR_CHAN_MANAGER,
WLAN_MODULE_RESMGR,
WLAN_MODULE_VDEV_MGR,
WLAN_MODULE_SCAN,
WLAN_MODULE_RATECTRL,
WLAN_MODULE_AP_PWRSAVE,
WLAN_MODULE_BLOCKACK,
WLAN_MODULE_MGMT_TXRX,
WLAN_MODULE_DATA_TXRX,
WLAN_MODULE_HTT,
WLAN_MODULE_HOST,
WLAN_MODULE_BEACON,
WLAN_MODULE_OFFLOAD,
WLAN_MODULE_WAL,
WLAN_WAL_MODULE_DE,
WLAN_MODULE_PCIELP,
WLAN_MODULE_RTT,
WLAN_MODULE_RESOURCE,
WLAN_MODULE_DCS,
WLAN_MODULE_CACHEMGR,
WLAN_MODULE_ANI,
WLAN_MODULE_P2P,
WLAN_MODULE_CSA,
WLAN_MODULE_NLO,
WLAN_MODULE_CHATTER,
WLAN_MODULE_WOW,
WLAN_MODULE_WAL_VDEV,
WLAN_MODULE_WAL_PDEV,
WLAN_MODULE_TEST,
WLAN_MODULE_STA_SMPS,
WLAN_MODULE_SWBMISS,
WLAN_MODULE_WMMAC,
WLAN_MODULE_TDLS,
WLAN_MODULE_HB,
WLAN_MODULE_TXBF,
WLAN_MODULE_BATCH_SCAN,
WLAN_MODULE_THERMAL_MGR,
WLAN_MODULE_PHYERR_DFS,
WLAN_MODULE_RMC,
WLAN_MODULE_STATS,
WLAN_MODULE_NAN,
WLAN_MODULE_IBSS_PWRSAVE,
WLAN_MODULE_HIF_UART,
WLAN_MODULE_LPI,
WLAN_MODULE_EXTSCAN,
WLAN_MODULE_UNIT_TEST,
WLAN_MODULE_MLME,
WLAN_MODULE_SUPPL,
WLAN_MODULE_ERE,
WLAN_MODULE_OCB,
WLAN_MODULE_RSSI_MONITOR,
WLAN_MODULE_WPM,
WLAN_MODULE_CSS,
WLAN_MODULE_PPS,
WLAN_MODULE_SCAN_CH_PREDICT,
WLAN_MODULE_MAWC,
WLAN_MODULE_CMC_QMIC,
WLAN_MODULE_EGAP,
WLAN_MODULE_NAN20,
WLAN_MODULE_QBOOST,
WLAN_MODULE_P2P_LISTEN_OFFLOAD,
WLAN_MODULE_HALPHY,
WLAN_WAL_MODULE_ENQ,
WLAN_MODULE_GNSS,
WLAN_MODULE_WAL_MEM,
WLAN_MODULE_SCHED_ALGO,
WLAN_MODULE_TX,
WLAN_MODULE_RX,
WLAN_MODULE_WLM,
WLAN_MODULE_RU_ALLOCATOR,
WLAN_MODULE_11K_OFFLOAD,
WLAN_MODULE_STA_TWT,
WLAN_MODULE_AP_TWT,
WLAN_MODULE_UL_OFDMA,
WLAN_MODULE_HPCS_PULSE,
WLAN_MODULE_DTF,
WLAN_MODULE_QUIET_IE,
WLAN_MODULE_SHMEM_MGR,
WLAN_MODULE_CFIR,
WLAN_MODULE_CODE_COVER,
WLAN_MODULE_SHO,
WLAN_MODULE_MLO_MGR,
WLAN_MODULE_PEER_INIT,
WLAN_MODULE_STA_MLO_PS,
WLAN_MODULE_ID_MAX,
WLAN_MODULE_ID_INVALID = WLAN_MODULE_ID_MAX,
};
enum fw_dbglog_log_level {
ATH11K_FW_DBGLOG_ML = 0,
ATH11K_FW_DBGLOG_VERBOSE = 0,
ATH11K_FW_DBGLOG_INFO,
ATH11K_FW_DBGLOG_INFO_LVL_1,
ATH11K_FW_DBGLOG_INFO_LVL_2,
ATH11K_FW_DBGLOG_WARN,
ATH11K_FW_DBGLOG_ERR,
ATH11K_FW_DBGLOG_LVL_MAX
};
struct ath11k_fw_dbglog {
enum wmi_debug_log_param param;
union {
struct {
/* log_level values are given in enum fw_dbglog_log_level */
u16 log_level;
/* module_id values are given in enum fw_dbglog_wlan_module_id */
u16 module_id;
};
/* value is either log_level&module_id/vdev_id/vdev_id_bitmap/log_level
* according to param
*/
u32 value;
};
};
#ifdef CONFIG_ATH11K_DEBUGFS
int ath11k_debugfs_soc_create(struct ath11k_base *ab);
void ath11k_debugfs_soc_destroy(struct ath11k_base *ab);
int ath11k_debugfs_pdev_create(struct ath11k_base *ab);
void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab);
int ath11k_debugfs_register(struct ath11k *ar);
void ath11k_debugfs_unregister(struct ath11k *ar);
void ath11k_debugfs_fw_stats_process(struct ath11k *ar, struct ath11k_fw_stats *stats);
void ath11k_debugfs_fw_stats_init(struct ath11k *ar);
static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar)
{
return (ar->debug.pktlog_mode == ATH11K_PKTLOG_MODE_LITE);
}
static inline bool ath11k_debugfs_is_pktlog_rx_stats_enabled(struct ath11k *ar)
{
return (!ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode);
}
static inline bool ath11k_debugfs_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr)
{
return (ar->debug.pktlog_peer_valid && ar->debug.pktlog_mode &&
ether_addr_equal(addr, ar->debug.pktlog_peer_addr));
}
static inline int ath11k_debugfs_is_extd_tx_stats_enabled(struct ath11k *ar)
{
return ar->debug.extd_tx_stats;
}
static inline int ath11k_debugfs_is_extd_rx_stats_enabled(struct ath11k *ar)
{
return ar->debug.extd_rx_stats;
}
static inline int ath11k_debugfs_rx_filter(struct ath11k *ar)
{
return ar->debug.rx_filter;
}
void ath11k_debugfs_op_vif_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
void ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
enum wmi_direct_buffer_module id,
enum ath11k_dbg_dbr_event event,
struct hal_srng *srng);
#else
static inline int ath11k_debugfs_soc_create(struct ath11k_base *ab)
{
return 0;
}
static inline void ath11k_debugfs_soc_destroy(struct ath11k_base *ab)
{
}
static inline int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
{
return 0;
}
static inline void ath11k_debugfs_pdev_destroy(struct ath11k_base *ab)
{
}
static inline int ath11k_debugfs_register(struct ath11k *ar)
{
return 0;
}
static inline void ath11k_debugfs_unregister(struct ath11k *ar)
{
}
static inline void ath11k_debugfs_fw_stats_process(struct ath11k *ar,
struct ath11k_fw_stats *stats)
{
}
static inline void ath11k_debugfs_fw_stats_init(struct ath11k *ar)
{
}
static inline int ath11k_debugfs_is_extd_tx_stats_enabled(struct ath11k *ar)
{
return 0;
}
static inline int ath11k_debugfs_is_extd_rx_stats_enabled(struct ath11k *ar)
{
return 0;
}
static inline bool ath11k_debugfs_is_pktlog_lite_mode_enabled(struct ath11k *ar)
{
return false;
}
static inline bool ath11k_debugfs_is_pktlog_rx_stats_enabled(struct ath11k *ar)
{
return false;
}
static inline bool ath11k_debugfs_is_pktlog_peer_valid(struct ath11k *ar, u8 *addr)
{
return false;
}
static inline int ath11k_debugfs_rx_filter(struct ath11k *ar)
{
return 0;
}
static inline void
ath11k_debugfs_add_dbring_entry(struct ath11k *ar,
enum wmi_direct_buffer_module id,
enum ath11k_dbg_dbr_event event,
struct hal_srng *srng)
{
}
#endif /* CONFIG_ATH11K_DEBUGFS*/
#endif /* _ATH11K_DEBUGFS_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
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_DEBUGFS_STA_H_
#define _ATH11K_DEBUGFS_STA_H_
#include <net/mac80211.h>
#include "core.h"
#include "hal_tx.h"
#ifdef CONFIG_ATH11K_DEBUGFS
void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
void ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta,
struct ath11k_per_peer_tx_stats *peer_stats,
u8 legacy_rate_idx);
void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar,
struct hal_tx_status *ts);
#else /* CONFIG_ATH11K_DEBUGFS */
#define ath11k_debugfs_sta_op_add NULL
static inline void
ath11k_debugfs_sta_add_tx_stats(struct ath11k_sta *arsta,
struct ath11k_per_peer_tx_stats *peer_stats,
u8 legacy_rate_idx)
{
}
static inline void ath11k_debugfs_sta_update_txcompl(struct ath11k *ar,
struct hal_tx_status *ts)
{
}
#endif /* CONFIG_ATH11K_DEBUGFS */
#endif /* _ATH11K_DEBUGFS_STA_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
@@ -0,0 +1,101 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_DP_RX_H
#define ATH11K_DP_RX_H
#include "core.h"
#include "rx_desc.h"
#include "debug.h"
#define DP_MAX_NWIFI_HDR_LEN 30
#define DP_RX_MPDU_ERR_FCS BIT(0)
#define DP_RX_MPDU_ERR_DECRYPT BIT(1)
#define DP_RX_MPDU_ERR_TKIP_MIC BIT(2)
#define DP_RX_MPDU_ERR_AMSDU_ERR BIT(3)
#define DP_RX_MPDU_ERR_OVERFLOW BIT(4)
#define DP_RX_MPDU_ERR_MSDU_LEN BIT(5)
#define DP_RX_MPDU_ERR_MPDU_LEN BIT(6)
#define DP_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7)
enum dp_rx_decap_type {
DP_RX_DECAP_TYPE_RAW,
DP_RX_DECAP_TYPE_NATIVE_WIFI,
DP_RX_DECAP_TYPE_ETHERNET2_DIX,
DP_RX_DECAP_TYPE_8023,
};
struct ath11k_dp_amsdu_subframe_hdr {
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
__be16 len;
} __packed;
struct ath11k_dp_rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctrl;
u8 snap_oui[3];
__be16 snap_type;
} __packed;
int ath11k_dp_rx_ampdu_start(struct ath11k *ar,
struct ieee80211_ampdu_params *params);
int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
struct ieee80211_ampdu_params *params);
int ath11k_dp_peer_rx_pn_replay_config(struct ath11k_vif *arvif,
const u8 *peer_addr,
enum set_key_cmd key_cmd,
struct ieee80211_key_conf *key);
void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer);
void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer);
void ath11k_peer_rx_tid_delete(struct ath11k *ar,
struct ath11k_peer *peer, u8 tid);
int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id,
u8 tid, u32 ba_win_sz, u16 ssn,
enum hal_pn_type pn_type);
void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab,
struct sk_buff *skb);
int ath11k_dp_pdev_reo_setup(struct ath11k_base *ab);
void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab);
int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int pdev_idx);
void ath11k_dp_rx_pdev_free(struct ath11k_base *ab, int pdev_idx);
void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab);
void ath11k_dp_process_reo_status(struct ath11k_base *ab);
int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget);
int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab,
struct napi_struct *napi, int budget);
int ath11k_dp_process_rx_err(struct ath11k_base *ab, struct napi_struct *napi,
int budget);
int ath11k_dp_process_rx(struct ath11k_base *ab, int mac_id,
struct napi_struct *napi,
int budget);
int ath11k_dp_rxbufs_replenish(struct ath11k_base *ab, int mac_id,
struct dp_rxdma_ring *rx_ring,
int req_entries,
enum hal_rx_buf_return_buf_manager mgr);
int ath11k_dp_htt_tlv_iter(struct ath11k_base *ab, const void *ptr, size_t len,
int (*iter)(struct ath11k_base *ar, u16 tag, u16 len,
const void *ptr, void *data),
void *data);
int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id,
struct napi_struct *napi, int budget);
int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id,
struct napi_struct *napi, int budget);
int ath11k_dp_rx_mon_status_bufs_replenish(struct ath11k_base *ab, int mac_id,
struct dp_rxdma_ring *rx_ring,
int req_entries,
enum hal_rx_buf_return_buf_manager mgr);
int ath11k_dp_rx_pdev_mon_detach(struct ath11k *ar);
int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar);
int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id);
int ath11k_dp_rx_pktlog_start(struct ath11k_base *ab);
int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer);
int ath11k_dp_rx_crypto_mic_len(struct ath11k *ar, enum hal_encrypt_type enctype);
#endif /* ATH11K_DP_RX_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_DP_TX_H
#define ATH11K_DP_TX_H
#include "core.h"
#include "hal_tx.h"
struct ath11k_dp_htt_wbm_tx_status {
u32 msdu_id;
bool acked;
s8 ack_rssi;
u16 peer_id;
};
void ath11k_dp_tx_update_txcompl(struct ath11k *ar, struct hal_tx_status *ts);
int ath11k_dp_tx_htt_h2t_ver_req_msg(struct ath11k_base *ab);
int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif,
struct ath11k_sta *arsta, struct sk_buff *skb);
void ath11k_dp_tx_completion_handler(struct ath11k_base *ab, int ring_id);
int ath11k_dp_tx_send_reo_cmd(struct ath11k_base *ab, struct dp_rx_tid *rx_tid,
enum hal_reo_cmd_type type,
struct ath11k_hal_reo_cmd *cmd,
void (*func)(struct ath11k_dp *, void *,
enum hal_reo_cmd_status));
int ath11k_dp_tx_htt_h2t_ppdu_stats_req(struct ath11k *ar, u32 mask);
int
ath11k_dp_tx_htt_h2t_ext_stats_req(struct ath11k *ar, u8 type,
struct htt_ext_stats_cfg_params *cfg_params,
u64 cookie);
int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset);
int ath11k_dp_tx_htt_rx_filter_setup(struct ath11k_base *ab, u32 ring_id,
int mac_id, enum hal_ring_type ring_type,
int rx_buf_size,
struct htt_rx_ring_tlv_filter *tlv_filter);
int ath11k_dp_tx_htt_rx_full_mon_setup(struct ath11k_base *ab, int mac_id,
bool config);
#endif
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/export.h>
#include "core.h"
#include "debug.h"
static int ath11k_fw_request_firmware_api_n(struct ath11k_base *ab,
const char *name)
{
size_t magic_len, len, ie_len;
int ie_id, i, index, bit, ret;
struct ath11k_fw_ie *hdr;
const u8 *data;
__le32 *timestamp;
ab->fw.fw = ath11k_core_firmware_request(ab, name);
if (IS_ERR(ab->fw.fw)) {
ret = PTR_ERR(ab->fw.fw);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "failed to load %s: %d\n", name, ret);
ab->fw.fw = NULL;
return ret;
}
data = ab->fw.fw->data;
len = ab->fw.fw->size;
/* magic also includes the null byte, check that as well */
magic_len = strlen(ATH11K_FIRMWARE_MAGIC) + 1;
if (len < magic_len) {
ath11k_err(ab, "firmware image too small to contain magic: %zu\n",
len);
ret = -EINVAL;
goto err;
}
if (memcmp(data, ATH11K_FIRMWARE_MAGIC, magic_len) != 0) {
ath11k_err(ab, "Invalid firmware magic\n");
ret = -EINVAL;
goto err;
}
/* jump over the padding */
magic_len = ALIGN(magic_len, 4);
/* make sure there's space for padding */
if (magic_len > len) {
ath11k_err(ab, "No space for padding after magic\n");
ret = -EINVAL;
goto err;
}
len -= magic_len;
data += magic_len;
/* loop elements */
while (len > sizeof(struct ath11k_fw_ie)) {
hdr = (struct ath11k_fw_ie *)data;
ie_id = le32_to_cpu(hdr->id);
ie_len = le32_to_cpu(hdr->len);
len -= sizeof(*hdr);
data += sizeof(*hdr);
if (len < ie_len) {
ath11k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n",
ie_id, len, ie_len);
ret = -EINVAL;
goto err;
}
switch (ie_id) {
case ATH11K_FW_IE_TIMESTAMP:
if (ie_len != sizeof(u32))
break;
timestamp = (__le32 *)data;
ath11k_dbg(ab, ATH11K_DBG_BOOT, "found fw timestamp %d\n",
le32_to_cpup(timestamp));
break;
case ATH11K_FW_IE_FEATURES:
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"found firmware features ie (%zd B)\n",
ie_len);
for (i = 0; i < ATH11K_FW_FEATURE_COUNT; i++) {
index = i / 8;
bit = i % 8;
if (index == ie_len)
break;
if (data[index] & (1 << bit))
__set_bit(i, ab->fw.fw_features);
}
ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "features", "",
ab->fw.fw_features,
sizeof(ab->fw.fw_features));
break;
case ATH11K_FW_IE_AMSS_IMAGE:
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"found fw image ie (%zd B)\n",
ie_len);
ab->fw.amss_data = data;
ab->fw.amss_len = ie_len;
break;
case ATH11K_FW_IE_M3_IMAGE:
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"found m3 image ie (%zd B)\n",
ie_len);
ab->fw.m3_data = data;
ab->fw.m3_len = ie_len;
break;
default:
ath11k_warn(ab, "Unknown FW IE: %u\n", ie_id);
break;
}
/* jump over the padding */
ie_len = ALIGN(ie_len, 4);
/* make sure there's space for padding */
if (ie_len > len)
break;
len -= ie_len;
data += ie_len;
}
return 0;
err:
release_firmware(ab->fw.fw);
ab->fw.fw = NULL;
return ret;
}
int ath11k_fw_pre_init(struct ath11k_base *ab)
{
int ret;
ret = ath11k_fw_request_firmware_api_n(ab, ATH11K_FW_API2_FILE);
if (ret == 0) {
ab->fw.api_version = 2;
goto out;
}
ab->fw.api_version = 1;
out:
ath11k_dbg(ab, ATH11K_DBG_BOOT, "using fw api %d\n",
ab->fw.api_version);
return 0;
}
void ath11k_fw_destroy(struct ath11k_base *ab)
{
release_firmware(ab->fw.fw);
}
EXPORT_SYMBOL(ath11k_fw_destroy);
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_FW_H
#define ATH11K_FW_H
#define ATH11K_FW_API2_FILE "firmware-2.bin"
#define ATH11K_FIRMWARE_MAGIC "QCOM-ATH11K-FW"
enum ath11k_fw_ie_type {
ATH11K_FW_IE_TIMESTAMP = 0,
ATH11K_FW_IE_FEATURES = 1,
ATH11K_FW_IE_AMSS_IMAGE = 2,
ATH11K_FW_IE_M3_IMAGE = 3,
};
enum ath11k_fw_features {
/* keep last */
ATH11K_FW_FEATURE_COUNT,
};
int ath11k_fw_pre_init(struct ath11k_base *ab);
void ath11k_fw_destroy(struct ath11k_base *ab);
#endif /* ATH11K_FW_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,978 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_HAL_H
#define ATH11K_HAL_H
#include "hal_desc.h"
#include "rx_desc.h"
struct ath11k_base;
#define HAL_LINK_DESC_SIZE (32 << 2)
#define HAL_LINK_DESC_ALIGN 128
#define HAL_NUM_MPDUS_PER_LINK_DESC 6
#define HAL_NUM_TX_MSDUS_PER_LINK_DESC 7
#define HAL_NUM_RX_MSDUS_PER_LINK_DESC 6
#define HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC 12
#define HAL_MAX_AVAIL_BLK_RES 3
#define HAL_RING_BASE_ALIGN 8
#define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704
/* TODO: Check with hw team on the supported scatter buf size */
#define HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE 8
#define HAL_WBM_IDLE_SCATTER_BUF_SIZE (HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX - \
HAL_WBM_IDLE_SCATTER_NEXT_PTR_SIZE)
#define HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX 48
#define HAL_DSCP_TID_TBL_SIZE 24
/* calculate the register address from bar0 of shadow register x */
#define HAL_SHADOW_BASE_ADDR(ab) ab->hw_params.regs->hal_shadow_base_addr
#define HAL_SHADOW_NUM_REGS 36
#define HAL_HP_OFFSET_IN_REG_START 1
#define HAL_OFFSET_FROM_HP_TO_TP 4
#define HAL_SHADOW_REG(ab, x) (HAL_SHADOW_BASE_ADDR(ab) + (4 * (x)))
/* WCSS Relative address */
#define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000
#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000
#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000
#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) \
((ab)->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg)
#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) \
((ab)->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg)
#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) \
((ab)->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg)
#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) \
((ab)->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg)
#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000
#define HAL_CE_WFSS_CE_REG_BASE 0x01b80000
#define HAL_WLAON_REG_BASE 0x01f80000
/* SW2TCL(x) R0 ring configuration address */
#define HAL_TCL1_RING_CMN_CTRL_REG 0x00000014
#define HAL_TCL1_RING_DSCP_TID_MAP 0x0000002c
#define HAL_TCL1_RING_BASE_LSB(ab) ab->hw_params.regs->hal_tcl1_ring_base_lsb
#define HAL_TCL1_RING_BASE_MSB(ab) ab->hw_params.regs->hal_tcl1_ring_base_msb
#define HAL_TCL1_RING_ID(ab) ab->hw_params.regs->hal_tcl1_ring_id
#define HAL_TCL1_RING_MISC(ab) ab->hw_params.regs->hal_tcl1_ring_misc
#define HAL_TCL1_RING_TP_ADDR_LSB(ab) \
ab->hw_params.regs->hal_tcl1_ring_tp_addr_lsb
#define HAL_TCL1_RING_TP_ADDR_MSB(ab) \
ab->hw_params.regs->hal_tcl1_ring_tp_addr_msb
#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) \
ab->hw_params.regs->hal_tcl1_ring_consumer_int_setup_ix0
#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) \
ab->hw_params.regs->hal_tcl1_ring_consumer_int_setup_ix1
#define HAL_TCL1_RING_MSI1_BASE_LSB(ab) \
ab->hw_params.regs->hal_tcl1_ring_msi1_base_lsb
#define HAL_TCL1_RING_MSI1_BASE_MSB(ab) \
ab->hw_params.regs->hal_tcl1_ring_msi1_base_msb
#define HAL_TCL1_RING_MSI1_DATA(ab) \
ab->hw_params.regs->hal_tcl1_ring_msi1_data
#define HAL_TCL2_RING_BASE_LSB(ab) ab->hw_params.regs->hal_tcl2_ring_base_lsb
#define HAL_TCL_RING_BASE_LSB(ab) ab->hw_params.regs->hal_tcl_ring_base_lsb
#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) \
(HAL_TCL1_RING_MSI1_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) \
(HAL_TCL1_RING_MSI1_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) \
(HAL_TCL1_RING_MSI1_DATA(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_BASE_MSB_OFFSET(ab) \
(HAL_TCL1_RING_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_ID_OFFSET(ab) \
(HAL_TCL1_RING_ID(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) \
(HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) \
(HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) \
(HAL_TCL1_RING_TP_ADDR_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) \
(HAL_TCL1_RING_TP_ADDR_MSB(ab) - HAL_TCL1_RING_BASE_LSB(ab))
#define HAL_TCL1_RING_MISC_OFFSET(ab) \
(HAL_TCL1_RING_MISC(ab) - HAL_TCL1_RING_BASE_LSB(ab))
/* SW2TCL(x) R2 ring pointers (head/tail) address */
#define HAL_TCL1_RING_HP 0x00002000
#define HAL_TCL1_RING_TP 0x00002004
#define HAL_TCL2_RING_HP 0x00002008
#define HAL_TCL_RING_HP 0x00002018
#define HAL_TCL1_RING_TP_OFFSET \
(HAL_TCL1_RING_TP - HAL_TCL1_RING_HP)
/* TCL STATUS ring address */
#define HAL_TCL_STATUS_RING_BASE_LSB(ab) \
ab->hw_params.regs->hal_tcl_status_ring_base_lsb
#define HAL_TCL_STATUS_RING_HP 0x00002030
/* REO2SW(x) R0 ring configuration address */
#define HAL_REO1_GEN_ENABLE 0x00000000
#define HAL_REO1_DEST_RING_CTRL_IX_0 0x00000004
#define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008
#define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c
#define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010
#define HAL_REO1_MISC_CTL(ab) ab->hw_params.regs->hal_reo1_misc_ctl
#define HAL_REO1_RING_BASE_LSB(ab) ab->hw_params.regs->hal_reo1_ring_base_lsb
#define HAL_REO1_RING_BASE_MSB(ab) ab->hw_params.regs->hal_reo1_ring_base_msb
#define HAL_REO1_RING_ID(ab) ab->hw_params.regs->hal_reo1_ring_id
#define HAL_REO1_RING_MISC(ab) ab->hw_params.regs->hal_reo1_ring_misc
#define HAL_REO1_RING_HP_ADDR_LSB(ab) \
ab->hw_params.regs->hal_reo1_ring_hp_addr_lsb
#define HAL_REO1_RING_HP_ADDR_MSB(ab) \
ab->hw_params.regs->hal_reo1_ring_hp_addr_msb
#define HAL_REO1_RING_PRODUCER_INT_SETUP(ab) \
ab->hw_params.regs->hal_reo1_ring_producer_int_setup
#define HAL_REO1_RING_MSI1_BASE_LSB(ab) \
ab->hw_params.regs->hal_reo1_ring_msi1_base_lsb
#define HAL_REO1_RING_MSI1_BASE_MSB(ab) \
ab->hw_params.regs->hal_reo1_ring_msi1_base_msb
#define HAL_REO1_RING_MSI1_DATA(ab) \
ab->hw_params.regs->hal_reo1_ring_msi1_data
#define HAL_REO2_RING_BASE_LSB(ab) ab->hw_params.regs->hal_reo2_ring_base_lsb
#define HAL_REO1_AGING_THRESH_IX_0(ab) \
ab->hw_params.regs->hal_reo1_aging_thresh_ix_0
#define HAL_REO1_AGING_THRESH_IX_1(ab) \
ab->hw_params.regs->hal_reo1_aging_thresh_ix_1
#define HAL_REO1_AGING_THRESH_IX_2(ab) \
ab->hw_params.regs->hal_reo1_aging_thresh_ix_2
#define HAL_REO1_AGING_THRESH_IX_3(ab) \
ab->hw_params.regs->hal_reo1_aging_thresh_ix_3
#define HAL_REO1_RING_MSI1_BASE_LSB_OFFSET(ab) \
(HAL_REO1_RING_MSI1_BASE_LSB(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_MSI1_BASE_MSB_OFFSET(ab) \
(HAL_REO1_RING_MSI1_BASE_MSB(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_MSI1_DATA_OFFSET(ab) \
(HAL_REO1_RING_MSI1_DATA(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_BASE_MSB_OFFSET(ab) \
(HAL_REO1_RING_BASE_MSB(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_ID_OFFSET(ab) (HAL_REO1_RING_ID(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET(ab) \
(HAL_REO1_RING_PRODUCER_INT_SETUP(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_HP_ADDR_LSB_OFFSET(ab) \
(HAL_REO1_RING_HP_ADDR_LSB(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_HP_ADDR_MSB_OFFSET(ab) \
(HAL_REO1_RING_HP_ADDR_MSB(ab) - HAL_REO1_RING_BASE_LSB(ab))
#define HAL_REO1_RING_MISC_OFFSET(ab) \
(HAL_REO1_RING_MISC(ab) - HAL_REO1_RING_BASE_LSB(ab))
/* REO2SW(x) R2 ring pointers (head/tail) address */
#define HAL_REO1_RING_HP(ab) ab->hw_params.regs->hal_reo1_ring_hp
#define HAL_REO1_RING_TP(ab) ab->hw_params.regs->hal_reo1_ring_tp
#define HAL_REO2_RING_HP(ab) ab->hw_params.regs->hal_reo2_ring_hp
#define HAL_REO1_RING_TP_OFFSET(ab) (HAL_REO1_RING_TP(ab) - HAL_REO1_RING_HP(ab))
/* REO2TCL R0 ring configuration address */
#define HAL_REO_TCL_RING_BASE_LSB(ab) \
ab->hw_params.regs->hal_reo_tcl_ring_base_lsb
/* REO2TCL R2 ring pointer (head/tail) address */
#define HAL_REO_TCL_RING_HP(ab) ab->hw_params.regs->hal_reo_tcl_ring_hp
/* REO CMD R0 address */
#define HAL_REO_CMD_RING_BASE_LSB(ab) \
ab->hw_params.regs->hal_reo_cmd_ring_base_lsb
/* REO CMD R2 address */
#define HAL_REO_CMD_HP(ab) ab->hw_params.regs->hal_reo_cmd_ring_hp
/* SW2REO R0 address */
#define HAL_SW2REO_RING_BASE_LSB(ab) \
ab->hw_params.regs->hal_sw2reo_ring_base_lsb
/* SW2REO R2 address */
#define HAL_SW2REO_RING_HP(ab) ab->hw_params.regs->hal_sw2reo_ring_hp
/* CE ring R0 address */
#define HAL_CE_DST_RING_BASE_LSB 0x00000000
#define HAL_CE_DST_STATUS_RING_BASE_LSB 0x00000058
#define HAL_CE_DST_RING_CTRL 0x000000b0
/* CE ring R2 address */
#define HAL_CE_DST_RING_HP 0x00000400
#define HAL_CE_DST_STATUS_RING_HP 0x00000408
/* REO status address */
#define HAL_REO_STATUS_RING_BASE_LSB(ab) \
ab->hw_params.regs->hal_reo_status_ring_base_lsb
#define HAL_REO_STATUS_HP(ab) ab->hw_params.regs->hal_reo_status_hp
/* WBM Idle R0 address */
#define HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab) \
((ab)->hw_params.regs->hal_wbm_idle_link_ring_base_lsb)
#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR(ab) \
((ab)->hw_params.regs->hal_wbm_idle_link_ring_misc)
#define HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR 0x00000048
#define HAL_WBM_R0_IDLE_LIST_SIZE_ADDR 0x0000004c
#define HAL_WBM_SCATTERED_RING_BASE_LSB 0x00000058
#define HAL_WBM_SCATTERED_RING_BASE_MSB 0x0000005c
#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX0 0x00000068
#define HAL_WBM_SCATTERED_DESC_PTR_HEAD_INFO_IX1 0x0000006c
#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX0 0x00000078
#define HAL_WBM_SCATTERED_DESC_PTR_TAIL_INFO_IX1 0x0000007c
#define HAL_WBM_SCATTERED_DESC_PTR_HP_ADDR 0x00000084
/* WBM Idle R2 address */
#define HAL_WBM_IDLE_LINK_RING_HP 0x000030b0
/* SW2WBM R0 release address */
#define HAL_WBM_RELEASE_RING_BASE_LSB(ab) \
((ab)->hw_params.regs->hal_wbm_release_ring_base_lsb)
/* SW2WBM R2 release address */
#define HAL_WBM_RELEASE_RING_HP 0x00003018
/* WBM2SW R0 release address */
#define HAL_WBM0_RELEASE_RING_BASE_LSB(ab) \
((ab)->hw_params.regs->hal_wbm0_release_ring_base_lsb)
#define HAL_WBM1_RELEASE_RING_BASE_LSB(ab) \
((ab)->hw_params.regs->hal_wbm1_release_ring_base_lsb)
/* WBM2SW R2 release address */
#define HAL_WBM0_RELEASE_RING_HP 0x000030c0
#define HAL_WBM1_RELEASE_RING_HP 0x000030c8
/* TCL ring field mask and offset */
#define HAL_TCL1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8)
#define HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0)
#define HAL_TCL1_RING_ID_ENTRY_SIZE GENMASK(7, 0)
#define HAL_TCL1_RING_MISC_MSI_LOOPCNT_DISABLE BIT(1)
#define HAL_TCL1_RING_MISC_MSI_SWAP BIT(3)
#define HAL_TCL1_RING_MISC_HOST_FW_SWAP BIT(4)
#define HAL_TCL1_RING_MISC_DATA_TLV_SWAP BIT(5)
#define HAL_TCL1_RING_MISC_SRNG_ENABLE BIT(6)
#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_INTR_TMR_THOLD GENMASK(31, 16)
#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_BATCH_COUNTER_THOLD GENMASK(14, 0)
#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_LOW_THOLD GENMASK(15, 0)
#define HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8)
#define HAL_TCL1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0)
#define HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN BIT(17)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP GENMASK(31, 0)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP0 GENMASK(2, 0)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP1 GENMASK(5, 3)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP2 GENMASK(8, 6)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP3 GENMASK(11, 9)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP4 GENMASK(14, 12)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP5 GENMASK(17, 15)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP6 GENMASK(20, 18)
#define HAL_TCL1_RING_FIELD_DSCP_TID_MAP7 GENMASK(23, 21)
/* REO ring field mask and offset */
#define HAL_REO1_RING_BASE_MSB_RING_SIZE GENMASK(27, 8)
#define HAL_REO1_RING_BASE_MSB_RING_BASE_ADDR_MSB GENMASK(7, 0)
#define HAL_REO1_RING_ID_RING_ID GENMASK(15, 8)
#define HAL_REO1_RING_ID_ENTRY_SIZE GENMASK(7, 0)
#define HAL_REO1_RING_MISC_MSI_SWAP BIT(3)
#define HAL_REO1_RING_MISC_HOST_FW_SWAP BIT(4)
#define HAL_REO1_RING_MISC_DATA_TLV_SWAP BIT(5)
#define HAL_REO1_RING_MISC_SRNG_ENABLE BIT(6)
#define HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD GENMASK(31, 16)
#define HAL_REO1_RING_PRDR_INT_SETUP_BATCH_COUNTER_THOLD GENMASK(14, 0)
#define HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE BIT(8)
#define HAL_REO1_RING_MSI1_BASE_MSB_ADDR GENMASK(7, 0)
#define HAL_REO1_GEN_ENABLE_FRAG_DST_RING GENMASK(25, 23)
#define HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE BIT(2)
#define HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE BIT(3)
#define HAL_REO1_MISC_CTL_FRAGMENT_DST_RING GENMASK(20, 17)
/* CE ring bit field mask and shift */
#define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0)
#define HAL_ADDR_LSB_REG_MASK 0xffffffff
#define HAL_ADDR_MSB_REG_SHIFT 32
/* WBM ring bit field mask and shift */
#define HAL_WBM_LINK_DESC_IDLE_LIST_MODE BIT(1)
#define HAL_WBM_SCATTER_BUFFER_SIZE GENMASK(10, 2)
#define HAL_WBM_SCATTER_RING_SIZE_OF_IDLE_LINK_DESC_LIST GENMASK(31, 16)
#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_39_32 GENMASK(7, 0)
#define HAL_WBM_SCATTERED_DESC_MSB_BASE_ADDR_MATCH_TAG GENMASK(31, 8)
#define HAL_WBM_SCATTERED_DESC_HEAD_P_OFFSET_IX1 GENMASK(20, 8)
#define HAL_WBM_SCATTERED_DESC_TAIL_P_OFFSET_IX1 GENMASK(20, 8)
#define BASE_ADDR_MATCH_TAG_VAL 0x5
#define HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE 0x000fffff
#define HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE 0x000fffff
#define HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_REO_CMD_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE 0x000fffff
#define HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE 0x000fffff
#define HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_CE_SRC_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_CE_DST_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_CE_DST_STATUS_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_WBM_IDLE_LINK_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_SW2WBM_RELEASE_RING_BASE_MSB_RING_SIZE 0x0000ffff
#define HAL_WBM2SW_RELEASE_RING_BASE_MSB_RING_SIZE 0x000fffff
#define HAL_RXDMA_RING_MAX_SIZE 0x0000ffff
/* IPQ5018 ce registers */
#define HAL_IPQ5018_CE_WFSS_REG_BASE 0x08400000
#define HAL_IPQ5018_CE_SIZE 0x200000
/* Add any other errors here and return them in
* ath11k_hal_rx_desc_get_err().
*/
enum hal_srng_ring_id {
HAL_SRNG_RING_ID_REO2SW1 = 0,
HAL_SRNG_RING_ID_REO2SW2,
HAL_SRNG_RING_ID_REO2SW3,
HAL_SRNG_RING_ID_REO2SW4,
HAL_SRNG_RING_ID_REO2TCL,
HAL_SRNG_RING_ID_SW2REO,
HAL_SRNG_RING_ID_REO_CMD = 8,
HAL_SRNG_RING_ID_REO_STATUS,
HAL_SRNG_RING_ID_SW2TCL1 = 16,
HAL_SRNG_RING_ID_SW2TCL2,
HAL_SRNG_RING_ID_SW2TCL3,
HAL_SRNG_RING_ID_SW2TCL4,
HAL_SRNG_RING_ID_SW2TCL_CMD = 24,
HAL_SRNG_RING_ID_TCL_STATUS,
HAL_SRNG_RING_ID_CE0_SRC = 32,
HAL_SRNG_RING_ID_CE1_SRC,
HAL_SRNG_RING_ID_CE2_SRC,
HAL_SRNG_RING_ID_CE3_SRC,
HAL_SRNG_RING_ID_CE4_SRC,
HAL_SRNG_RING_ID_CE5_SRC,
HAL_SRNG_RING_ID_CE6_SRC,
HAL_SRNG_RING_ID_CE7_SRC,
HAL_SRNG_RING_ID_CE8_SRC,
HAL_SRNG_RING_ID_CE9_SRC,
HAL_SRNG_RING_ID_CE10_SRC,
HAL_SRNG_RING_ID_CE11_SRC,
HAL_SRNG_RING_ID_CE0_DST = 56,
HAL_SRNG_RING_ID_CE1_DST,
HAL_SRNG_RING_ID_CE2_DST,
HAL_SRNG_RING_ID_CE3_DST,
HAL_SRNG_RING_ID_CE4_DST,
HAL_SRNG_RING_ID_CE5_DST,
HAL_SRNG_RING_ID_CE6_DST,
HAL_SRNG_RING_ID_CE7_DST,
HAL_SRNG_RING_ID_CE8_DST,
HAL_SRNG_RING_ID_CE9_DST,
HAL_SRNG_RING_ID_CE10_DST,
HAL_SRNG_RING_ID_CE11_DST,
HAL_SRNG_RING_ID_CE0_DST_STATUS = 80,
HAL_SRNG_RING_ID_CE1_DST_STATUS,
HAL_SRNG_RING_ID_CE2_DST_STATUS,
HAL_SRNG_RING_ID_CE3_DST_STATUS,
HAL_SRNG_RING_ID_CE4_DST_STATUS,
HAL_SRNG_RING_ID_CE5_DST_STATUS,
HAL_SRNG_RING_ID_CE6_DST_STATUS,
HAL_SRNG_RING_ID_CE7_DST_STATUS,
HAL_SRNG_RING_ID_CE8_DST_STATUS,
HAL_SRNG_RING_ID_CE9_DST_STATUS,
HAL_SRNG_RING_ID_CE10_DST_STATUS,
HAL_SRNG_RING_ID_CE11_DST_STATUS,
HAL_SRNG_RING_ID_WBM_IDLE_LINK = 104,
HAL_SRNG_RING_ID_WBM_SW_RELEASE,
HAL_SRNG_RING_ID_WBM2SW0_RELEASE,
HAL_SRNG_RING_ID_WBM2SW1_RELEASE,
HAL_SRNG_RING_ID_WBM2SW2_RELEASE,
HAL_SRNG_RING_ID_WBM2SW3_RELEASE,
HAL_SRNG_RING_ID_WBM2SW4_RELEASE,
HAL_SRNG_RING_ID_UMAC_ID_END = 127,
HAL_SRNG_RING_ID_LMAC1_ID_START,
HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF = HAL_SRNG_RING_ID_LMAC1_ID_START,
HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF,
HAL_SRNG_RING_ID_WMAC1_SW2RXDMA2_BUF,
HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_STATBUF,
HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF,
HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0,
HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1,
HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_DESC,
HAL_SRNG_RING_ID_RXDMA_DIR_BUF,
HAL_SRNG_RING_ID_LMAC1_ID_END = 143
};
/* SRNG registers are split into two groups R0 and R2 */
#define HAL_SRNG_REG_GRP_R0 0
#define HAL_SRNG_REG_GRP_R2 1
#define HAL_SRNG_NUM_REG_GRP 2
#define HAL_SRNG_NUM_LMACS 3
#define HAL_SRNG_REO_EXCEPTION HAL_SRNG_RING_ID_REO2SW1
#define HAL_SRNG_RINGS_PER_LMAC (HAL_SRNG_RING_ID_LMAC1_ID_END - \
HAL_SRNG_RING_ID_LMAC1_ID_START)
#define HAL_SRNG_NUM_LMAC_RINGS (HAL_SRNG_NUM_LMACS * HAL_SRNG_RINGS_PER_LMAC)
#define HAL_SRNG_RING_ID_MAX (HAL_SRNG_RING_ID_UMAC_ID_END + \
HAL_SRNG_NUM_LMAC_RINGS)
enum hal_ring_type {
HAL_REO_DST,
HAL_REO_EXCEPTION,
HAL_REO_REINJECT,
HAL_REO_CMD,
HAL_REO_STATUS,
HAL_TCL_DATA,
HAL_TCL_CMD,
HAL_TCL_STATUS,
HAL_CE_SRC,
HAL_CE_DST,
HAL_CE_DST_STATUS,
HAL_WBM_IDLE_LINK,
HAL_SW2WBM_RELEASE,
HAL_WBM2SW_RELEASE,
HAL_RXDMA_BUF,
HAL_RXDMA_DST,
HAL_RXDMA_MONITOR_BUF,
HAL_RXDMA_MONITOR_STATUS,
HAL_RXDMA_MONITOR_DST,
HAL_RXDMA_MONITOR_DESC,
HAL_RXDMA_DIR_BUF,
HAL_MAX_RING_TYPES,
};
#define HAL_RX_MAX_BA_WINDOW 256
#define HAL_DEFAULT_REO_TIMEOUT_USEC (40 * 1000)
/**
* enum hal_reo_cmd_type: Enum for REO command type
* @HAL_REO_CMD_GET_QUEUE_STATS: Get REO queue status/stats
* @HAL_REO_CMD_FLUSH_QUEUE: Flush all frames in REO queue
* @HAL_REO_CMD_FLUSH_CACHE: Flush descriptor entries in the cache
* @HAL_REO_CMD_UNBLOCK_CACHE: Unblock a descriptor's address that was blocked
* earlier with a 'REO_FLUSH_CACHE' command
* @HAL_REO_CMD_FLUSH_TIMEOUT_LIST: Flush buffers/descriptors from timeout list
* @HAL_REO_CMD_UPDATE_RX_QUEUE: Update REO queue settings
*/
enum hal_reo_cmd_type {
HAL_REO_CMD_GET_QUEUE_STATS = 0,
HAL_REO_CMD_FLUSH_QUEUE = 1,
HAL_REO_CMD_FLUSH_CACHE = 2,
HAL_REO_CMD_UNBLOCK_CACHE = 3,
HAL_REO_CMD_FLUSH_TIMEOUT_LIST = 4,
HAL_REO_CMD_UPDATE_RX_QUEUE = 5,
};
/**
* enum hal_reo_cmd_status: Enum for execution status of REO command
* @HAL_REO_CMD_SUCCESS: Command has successfully executed
* @HAL_REO_CMD_BLOCKED: Command could not be executed as the queue
* or cache was blocked
* @HAL_REO_CMD_FAILED: Command execution failed, could be due to
* invalid queue desc
* @HAL_REO_CMD_RESOURCE_BLOCKED:
* @HAL_REO_CMD_DRAIN:
*/
enum hal_reo_cmd_status {
HAL_REO_CMD_SUCCESS = 0,
HAL_REO_CMD_BLOCKED = 1,
HAL_REO_CMD_FAILED = 2,
HAL_REO_CMD_RESOURCE_BLOCKED = 3,
HAL_REO_CMD_DRAIN = 0xff,
};
struct hal_wbm_idle_scatter_list {
dma_addr_t paddr;
struct hal_wbm_link_desc *vaddr;
};
struct hal_srng_params {
dma_addr_t ring_base_paddr;
u32 *ring_base_vaddr;
int num_entries;
u32 intr_batch_cntr_thres_entries;
u32 intr_timer_thres_us;
u32 flags;
u32 max_buffer_len;
u32 low_threshold;
dma_addr_t msi_addr;
u32 msi_data;
/* Add more params as needed */
};
enum hal_srng_dir {
HAL_SRNG_DIR_SRC,
HAL_SRNG_DIR_DST
};
/* srng flags */
#define HAL_SRNG_FLAGS_MSI_SWAP 0x00000008
#define HAL_SRNG_FLAGS_RING_PTR_SWAP 0x00000010
#define HAL_SRNG_FLAGS_DATA_TLV_SWAP 0x00000020
#define HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN 0x00010000
#define HAL_SRNG_FLAGS_MSI_INTR 0x00020000
#define HAL_SRNG_FLAGS_CACHED 0x20000000
#define HAL_SRNG_FLAGS_LMAC_RING 0x80000000
#define HAL_SRNG_FLAGS_REMAP_CE_RING 0x10000000
#define HAL_SRNG_TLV_HDR_TAG GENMASK(9, 1)
#define HAL_SRNG_TLV_HDR_LEN GENMASK(25, 10)
/* Common SRNG ring structure for source and destination rings */
struct hal_srng {
/* Unique SRNG ring ID */
u8 ring_id;
/* Ring initialization done */
u8 initialized;
/* Interrupt/MSI value assigned to this ring */
int irq;
/* Physical base address of the ring */
dma_addr_t ring_base_paddr;
/* Virtual base address of the ring */
u32 *ring_base_vaddr;
/* Number of entries in ring */
u32 num_entries;
/* Ring size */
u32 ring_size;
/* Ring size mask */
u32 ring_size_mask;
/* Size of ring entry */
u32 entry_size;
/* Interrupt timer threshold - in micro seconds */
u32 intr_timer_thres_us;
/* Interrupt batch counter threshold - in number of ring entries */
u32 intr_batch_cntr_thres_entries;
/* MSI Address */
dma_addr_t msi_addr;
/* MSI data */
u32 msi_data;
/* Misc flags */
u32 flags;
/* Lock for serializing ring index updates */
spinlock_t lock;
/* Start offset of SRNG register groups for this ring
* TBD: See if this is required - register address can be derived
* from ring ID
*/
u32 hwreg_base[HAL_SRNG_NUM_REG_GRP];
u64 timestamp;
/* Source or Destination ring */
enum hal_srng_dir ring_dir;
union {
struct {
/* SW tail pointer */
u32 tp;
/* Shadow head pointer location to be updated by HW */
volatile u32 *hp_addr;
/* Cached head pointer */
u32 cached_hp;
/* Tail pointer location to be updated by SW - This
* will be a register address and need not be
* accessed through SW structure
*/
u32 *tp_addr;
/* Current SW loop cnt */
u32 loop_cnt;
/* max transfer size */
u16 max_buffer_length;
/* head pointer at access end */
u32 last_hp;
} dst_ring;
struct {
/* SW head pointer */
u32 hp;
/* SW reap head pointer */
u32 reap_hp;
/* Shadow tail pointer location to be updated by HW */
u32 *tp_addr;
/* Cached tail pointer */
u32 cached_tp;
/* Head pointer location to be updated by SW - This
* will be a register address and need not be accessed
* through SW structure
*/
u32 *hp_addr;
/* Low threshold - in number of ring entries */
u32 low_threshold;
/* tail pointer at access end */
u32 last_tp;
} src_ring;
} u;
};
/* Interrupt mitigation - Batch threshold in terms of number of frames */
#define HAL_SRNG_INT_BATCH_THRESHOLD_TX 256
#define HAL_SRNG_INT_BATCH_THRESHOLD_RX 128
#define HAL_SRNG_INT_BATCH_THRESHOLD_OTHER 1
/* Interrupt mitigation - timer threshold in us */
#define HAL_SRNG_INT_TIMER_THRESHOLD_TX 1000
#define HAL_SRNG_INT_TIMER_THRESHOLD_RX 500
#define HAL_SRNG_INT_TIMER_THRESHOLD_OTHER 256
/* HW SRNG configuration table */
struct hal_srng_config {
int start_ring_id;
u16 max_rings;
u16 entry_size;
u32 reg_start[HAL_SRNG_NUM_REG_GRP];
u16 reg_size[HAL_SRNG_NUM_REG_GRP];
u8 lmac_ring;
enum hal_srng_dir ring_dir;
u32 max_size;
};
/**
* enum hal_rx_buf_return_buf_manager - manager for returned rx buffers
*
* @HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST: Buffer returned to WBM idle buffer list
* @HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST: Descriptor returned to WBM idle
* descriptor list.
* @HAL_RX_BUF_RBM_FW_BM: Buffer returned to FW
* @HAL_RX_BUF_RBM_SW0_BM: For Tx completion -- returned to host
* @HAL_RX_BUF_RBM_SW1_BM: For Tx completion -- returned to host
* @HAL_RX_BUF_RBM_SW2_BM: For Tx completion -- returned to host
* @HAL_RX_BUF_RBM_SW3_BM: For Rx release -- returned to host
* @HAL_RX_BUF_RBM_SW4_BM: For Tx completion -- returned to host
*/
enum hal_rx_buf_return_buf_manager {
HAL_RX_BUF_RBM_WBM_IDLE_BUF_LIST,
HAL_RX_BUF_RBM_WBM_IDLE_DESC_LIST,
HAL_RX_BUF_RBM_FW_BM,
HAL_RX_BUF_RBM_SW0_BM,
HAL_RX_BUF_RBM_SW1_BM,
HAL_RX_BUF_RBM_SW2_BM,
HAL_RX_BUF_RBM_SW3_BM,
HAL_RX_BUF_RBM_SW4_BM,
};
#define HAL_SRNG_DESC_LOOP_CNT 0xf0000000
#define HAL_REO_CMD_FLG_NEED_STATUS BIT(0)
#define HAL_REO_CMD_FLG_STATS_CLEAR BIT(1)
#define HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER BIT(2)
#define HAL_REO_CMD_FLG_FLUSH_RELEASE_BLOCKING BIT(3)
#define HAL_REO_CMD_FLG_FLUSH_NO_INVAL BIT(4)
#define HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS BIT(5)
#define HAL_REO_CMD_FLG_FLUSH_ALL BIT(6)
#define HAL_REO_CMD_FLG_UNBLK_RESOURCE BIT(7)
#define HAL_REO_CMD_FLG_UNBLK_CACHE BIT(8)
/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO0_UPD_* fields */
#define HAL_REO_CMD_UPD0_RX_QUEUE_NUM BIT(8)
#define HAL_REO_CMD_UPD0_VLD BIT(9)
#define HAL_REO_CMD_UPD0_ALDC BIT(10)
#define HAL_REO_CMD_UPD0_DIS_DUP_DETECTION BIT(11)
#define HAL_REO_CMD_UPD0_SOFT_REORDER_EN BIT(12)
#define HAL_REO_CMD_UPD0_AC BIT(13)
#define HAL_REO_CMD_UPD0_BAR BIT(14)
#define HAL_REO_CMD_UPD0_RETRY BIT(15)
#define HAL_REO_CMD_UPD0_CHECK_2K_MODE BIT(16)
#define HAL_REO_CMD_UPD0_OOR_MODE BIT(17)
#define HAL_REO_CMD_UPD0_BA_WINDOW_SIZE BIT(18)
#define HAL_REO_CMD_UPD0_PN_CHECK BIT(19)
#define HAL_REO_CMD_UPD0_EVEN_PN BIT(20)
#define HAL_REO_CMD_UPD0_UNEVEN_PN BIT(21)
#define HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE BIT(22)
#define HAL_REO_CMD_UPD0_PN_SIZE BIT(23)
#define HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG BIT(24)
#define HAL_REO_CMD_UPD0_SVLD BIT(25)
#define HAL_REO_CMD_UPD0_SSN BIT(26)
#define HAL_REO_CMD_UPD0_SEQ_2K_ERR BIT(27)
#define HAL_REO_CMD_UPD0_PN_ERR BIT(28)
#define HAL_REO_CMD_UPD0_PN_VALID BIT(29)
#define HAL_REO_CMD_UPD0_PN BIT(30)
/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO1_* fields */
#define HAL_REO_CMD_UPD1_VLD BIT(16)
#define HAL_REO_CMD_UPD1_ALDC GENMASK(18, 17)
#define HAL_REO_CMD_UPD1_DIS_DUP_DETECTION BIT(19)
#define HAL_REO_CMD_UPD1_SOFT_REORDER_EN BIT(20)
#define HAL_REO_CMD_UPD1_AC GENMASK(22, 21)
#define HAL_REO_CMD_UPD1_BAR BIT(23)
#define HAL_REO_CMD_UPD1_RETRY BIT(24)
#define HAL_REO_CMD_UPD1_CHECK_2K_MODE BIT(25)
#define HAL_REO_CMD_UPD1_OOR_MODE BIT(26)
#define HAL_REO_CMD_UPD1_PN_CHECK BIT(27)
#define HAL_REO_CMD_UPD1_EVEN_PN BIT(28)
#define HAL_REO_CMD_UPD1_UNEVEN_PN BIT(29)
#define HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE BIT(30)
#define HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG BIT(31)
/* Should be matching with HAL_REO_UPD_RX_QUEUE_INFO2_* fields */
#define HAL_REO_CMD_UPD2_SVLD BIT(10)
#define HAL_REO_CMD_UPD2_SSN GENMASK(22, 11)
#define HAL_REO_CMD_UPD2_SEQ_2K_ERR BIT(23)
#define HAL_REO_CMD_UPD2_PN_ERR BIT(24)
#define HAL_REO_DEST_RING_CTRL_HASH_RING_MAP GENMASK(31, 8)
struct ath11k_hal_reo_cmd {
u32 addr_lo;
u32 flag;
u32 upd0;
u32 upd1;
u32 upd2;
u32 pn[4];
u16 rx_queue_num;
u16 min_rel;
u16 min_fwd;
u8 addr_hi;
u8 ac_list;
u8 blocking_idx;
u16 ba_window_size;
u8 pn_size;
};
enum hal_pn_type {
HAL_PN_TYPE_NONE,
HAL_PN_TYPE_WPA,
HAL_PN_TYPE_WAPI_EVEN,
HAL_PN_TYPE_WAPI_UNEVEN,
};
enum hal_ce_desc {
HAL_CE_DESC_SRC,
HAL_CE_DESC_DST,
HAL_CE_DESC_DST_STATUS,
};
#define HAL_HASH_ROUTING_RING_TCL 0
#define HAL_HASH_ROUTING_RING_SW1 1
#define HAL_HASH_ROUTING_RING_SW2 2
#define HAL_HASH_ROUTING_RING_SW3 3
#define HAL_HASH_ROUTING_RING_SW4 4
#define HAL_HASH_ROUTING_RING_REL 5
#define HAL_HASH_ROUTING_RING_FW 6
struct hal_reo_status_header {
u16 cmd_num;
enum hal_reo_cmd_status cmd_status;
u16 cmd_exe_time;
u32 timestamp;
};
struct hal_reo_status_queue_stats {
u16 ssn;
u16 curr_idx;
u32 pn[4];
u32 last_rx_queue_ts;
u32 last_rx_dequeue_ts;
u32 rx_bitmap[8]; /* Bitmap from 0-255 */
u32 curr_mpdu_cnt;
u32 curr_msdu_cnt;
u16 fwd_due_to_bar_cnt;
u16 dup_cnt;
u32 frames_in_order_cnt;
u32 num_mpdu_processed_cnt;
u32 num_msdu_processed_cnt;
u32 total_num_processed_byte_cnt;
u32 late_rx_mpdu_cnt;
u32 reorder_hole_cnt;
u8 timeout_cnt;
u8 bar_rx_cnt;
u8 num_window_2k_jump_cnt;
};
struct hal_reo_status_flush_queue {
bool err_detected;
};
enum hal_reo_status_flush_cache_err_code {
HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_SUCCESS,
HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_IN_USE,
HAL_REO_STATUS_FLUSH_CACHE_ERR_CODE_NOT_FOUND,
};
struct hal_reo_status_flush_cache {
bool err_detected;
enum hal_reo_status_flush_cache_err_code err_code;
bool cache_controller_flush_status_hit;
u8 cache_controller_flush_status_desc_type;
u8 cache_controller_flush_status_client_id;
u8 cache_controller_flush_status_err;
u8 cache_controller_flush_status_cnt;
};
enum hal_reo_status_unblock_cache_type {
HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE,
HAL_REO_STATUS_UNBLOCK_ENTIRE_CACHE_USAGE,
};
struct hal_reo_status_unblock_cache {
bool err_detected;
enum hal_reo_status_unblock_cache_type unblock_type;
};
struct hal_reo_status_flush_timeout_list {
bool err_detected;
bool list_empty;
u16 release_desc_cnt;
u16 fwd_buf_cnt;
};
enum hal_reo_threshold_idx {
HAL_REO_THRESHOLD_IDX_DESC_COUNTER0,
HAL_REO_THRESHOLD_IDX_DESC_COUNTER1,
HAL_REO_THRESHOLD_IDX_DESC_COUNTER2,
HAL_REO_THRESHOLD_IDX_DESC_COUNTER_SUM,
};
struct hal_reo_status_desc_thresh_reached {
enum hal_reo_threshold_idx threshold_idx;
u32 link_desc_counter0;
u32 link_desc_counter1;
u32 link_desc_counter2;
u32 link_desc_counter_sum;
};
struct hal_reo_status {
struct hal_reo_status_header uniform_hdr;
u8 loop_cnt;
union {
struct hal_reo_status_queue_stats queue_stats;
struct hal_reo_status_flush_queue flush_queue;
struct hal_reo_status_flush_cache flush_cache;
struct hal_reo_status_unblock_cache unblock_cache;
struct hal_reo_status_flush_timeout_list timeout_list;
struct hal_reo_status_desc_thresh_reached desc_thresh_reached;
} u;
};
/* HAL context to be used to access SRNG APIs (currently used by data path
* and transport (CE) modules)
*/
struct ath11k_hal {
/* HAL internal state for all SRNG rings.
*/
struct hal_srng srng_list[HAL_SRNG_RING_ID_MAX];
/* SRNG configuration table */
struct hal_srng_config *srng_config;
/* Remote pointer memory for HW/FW updates */
struct {
u32 *vaddr;
dma_addr_t paddr;
} rdp;
/* Shared memory for ring pointer updates from host to FW */
struct {
u32 *vaddr;
dma_addr_t paddr;
} wrp;
/* Available REO blocking resources bitmap */
u8 avail_blk_resource;
u8 current_blk_index;
/* shadow register configuration */
u32 shadow_reg_addr[HAL_SHADOW_NUM_REGS];
int num_shadow_reg_configured;
struct lock_class_key srng_key[HAL_SRNG_RING_ID_MAX];
};
u32 ath11k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid);
void ath11k_hal_reo_qdesc_setup(void *vaddr, int tid, u32 ba_window_size,
u32 start_seq, enum hal_pn_type type);
void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab,
struct hal_srng *srng);
void ath11k_hal_setup_link_idle_list(struct ath11k_base *ab,
struct hal_wbm_idle_scatter_list *sbuf,
u32 nsbufs, u32 tot_link_desc,
u32 end_offset);
dma_addr_t ath11k_hal_srng_get_tp_addr(struct ath11k_base *ab,
struct hal_srng *srng);
dma_addr_t ath11k_hal_srng_get_hp_addr(struct ath11k_base *ab,
struct hal_srng *srng);
void ath11k_hal_set_link_desc_addr(struct hal_wbm_link_desc *desc, u32 cookie,
dma_addr_t paddr);
u32 ath11k_hal_ce_get_desc_size(enum hal_ce_desc type);
void ath11k_hal_ce_src_set_desc(void *buf, dma_addr_t paddr, u32 len, u32 id,
u8 byte_swap_data);
void ath11k_hal_ce_dst_set_desc(void *buf, dma_addr_t paddr);
u32 ath11k_hal_ce_dst_status_get_length(void *buf);
int ath11k_hal_srng_get_entrysize(struct ath11k_base *ab, u32 ring_type);
int ath11k_hal_srng_get_max_entries(struct ath11k_base *ab, u32 ring_type);
void ath11k_hal_srng_get_params(struct ath11k_base *ab, struct hal_srng *srng,
struct hal_srng_params *params);
u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab,
struct hal_srng *srng);
u32 *ath11k_hal_srng_dst_peek(struct ath11k_base *ab, struct hal_srng *srng);
int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng,
bool sync_hw_ptr);
u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng);
u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab,
struct hal_srng *srng);
u32 *ath11k_hal_srng_src_get_next_reaped(struct ath11k_base *ab,
struct hal_srng *srng);
u32 *ath11k_hal_srng_src_reap_next(struct ath11k_base *ab,
struct hal_srng *srng);
u32 *ath11k_hal_srng_src_get_next_entry(struct ath11k_base *ab,
struct hal_srng *srng);
int ath11k_hal_srng_src_num_free(struct ath11k_base *ab, struct hal_srng *srng,
bool sync_hw_ptr);
void ath11k_hal_srng_access_begin(struct ath11k_base *ab,
struct hal_srng *srng);
void ath11k_hal_srng_access_end(struct ath11k_base *ab, struct hal_srng *srng);
int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type,
int ring_num, int mac_id,
struct hal_srng_params *params);
int ath11k_hal_srng_init(struct ath11k_base *ath11k);
void ath11k_hal_srng_deinit(struct ath11k_base *ath11k);
void ath11k_hal_srng_clear(struct ath11k_base *ab);
void ath11k_hal_dump_srng_stats(struct ath11k_base *ab);
void ath11k_hal_srng_get_shadow_config(struct ath11k_base *ab,
u32 **cfg, u32 *len);
int ath11k_hal_srng_update_shadow_config(struct ath11k_base *ab,
enum hal_ring_type ring_type,
int ring_num);
void ath11k_hal_srng_shadow_config(struct ath11k_base *ab);
void ath11k_hal_srng_shadow_update_hp_tp(struct ath11k_base *ab,
struct hal_srng *srng);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,520 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_HAL_RX_H
#define ATH11K_HAL_RX_H
struct hal_rx_wbm_rel_info {
u32 cookie;
enum hal_wbm_rel_src_module err_rel_src;
enum hal_reo_dest_ring_push_reason push_reason;
u32 err_code;
bool first_msdu;
bool last_msdu;
};
#define HAL_INVALID_PEERID 0xffff
#define VHT_SIG_SU_NSS_MASK 0x7
#define HAL_RX_MAX_MCS 12
#define HAL_RX_MAX_NSS 8
struct hal_rx_mon_status_tlv_hdr {
u32 hdr;
u8 value[];
};
enum hal_rx_su_mu_coding {
HAL_RX_SU_MU_CODING_BCC,
HAL_RX_SU_MU_CODING_LDPC,
HAL_RX_SU_MU_CODING_MAX,
};
enum hal_rx_gi {
HAL_RX_GI_0_8_US,
HAL_RX_GI_0_4_US,
HAL_RX_GI_1_6_US,
HAL_RX_GI_3_2_US,
HAL_RX_GI_MAX,
};
enum hal_rx_bw {
HAL_RX_BW_20MHZ,
HAL_RX_BW_40MHZ,
HAL_RX_BW_80MHZ,
HAL_RX_BW_160MHZ,
HAL_RX_BW_MAX,
};
enum hal_rx_preamble {
HAL_RX_PREAMBLE_11A,
HAL_RX_PREAMBLE_11B,
HAL_RX_PREAMBLE_11N,
HAL_RX_PREAMBLE_11AC,
HAL_RX_PREAMBLE_11AX,
HAL_RX_PREAMBLE_MAX,
};
enum hal_rx_reception_type {
HAL_RX_RECEPTION_TYPE_SU,
HAL_RX_RECEPTION_TYPE_MU_MIMO,
HAL_RX_RECEPTION_TYPE_MU_OFDMA,
HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO,
HAL_RX_RECEPTION_TYPE_MAX,
};
#define HAL_RX_FCS_LEN 4
enum hal_rx_mon_status {
HAL_RX_MON_STATUS_PPDU_NOT_DONE,
HAL_RX_MON_STATUS_PPDU_DONE,
HAL_RX_MON_STATUS_BUF_DONE,
};
struct hal_rx_user_status {
u32 mcs:4,
nss:3,
ofdma_info_valid:1,
dl_ofdma_ru_start_index:7,
dl_ofdma_ru_width:7,
dl_ofdma_ru_size:8;
u32 ul_ofdma_user_v0_word0;
u32 ul_ofdma_user_v0_word1;
u32 ast_index;
u32 tid;
u16 tcp_msdu_count;
u16 udp_msdu_count;
u16 other_msdu_count;
u16 frame_control;
u8 frame_control_info_valid;
u8 data_sequence_control_info_valid;
u16 first_data_seq_ctrl;
u32 preamble_type;
u16 ht_flags;
u16 vht_flags;
u16 he_flags;
u8 rs_flags;
u32 mpdu_cnt_fcs_ok;
u32 mpdu_cnt_fcs_err;
u32 mpdu_fcs_ok_bitmap[8];
u32 mpdu_ok_byte_count;
u32 mpdu_err_byte_count;
};
#define HAL_TLV_STATUS_PPDU_NOT_DONE HAL_RX_MON_STATUS_PPDU_NOT_DONE
#define HAL_TLV_STATUS_PPDU_DONE HAL_RX_MON_STATUS_PPDU_DONE
#define HAL_TLV_STATUS_BUF_DONE HAL_RX_MON_STATUS_BUF_DONE
struct hal_sw_mon_ring_entries {
dma_addr_t mon_dst_paddr;
dma_addr_t mon_status_paddr;
u32 mon_dst_sw_cookie;
u32 mon_status_sw_cookie;
void *dst_buf_addr_info;
void *status_buf_addr_info;
u16 ppdu_id;
u8 status_buf_count;
u8 msdu_cnt;
bool end_of_ppdu;
bool drop_ppdu;
};
struct hal_rx_mon_ppdu_info {
u32 ppdu_id;
u32 ppdu_ts;
u32 num_mpdu_fcs_ok;
u32 num_mpdu_fcs_err;
u32 preamble_type;
u16 chan_num;
u16 tcp_msdu_count;
u16 tcp_ack_msdu_count;
u16 udp_msdu_count;
u16 other_msdu_count;
u16 peer_id;
u8 rate;
u8 mcs;
u8 nss;
u8 bw;
u8 vht_flag_values1;
u8 vht_flag_values2;
u8 vht_flag_values3[4];
u8 vht_flag_values4;
u8 vht_flag_values5;
u16 vht_flag_values6;
u8 is_stbc;
u8 gi;
u8 ldpc;
u8 beamformed;
u8 rssi_comb;
u8 rssi_chain_pri20[HAL_RX_MAX_NSS];
u16 tid;
u16 ht_flags;
u16 vht_flags;
u16 he_flags;
u16 he_mu_flags;
u8 dcm;
u8 ru_alloc;
u8 reception_type;
u64 tsft;
u64 rx_duration;
u16 frame_control;
u32 ast_index;
u8 rs_fcs_err;
u8 rs_flags;
u8 cck_flag;
u8 ofdm_flag;
u8 ulofdma_flag;
u8 frame_control_info_valid;
u16 he_per_user_1;
u16 he_per_user_2;
u8 he_per_user_position;
u8 he_per_user_known;
u16 he_flags1;
u16 he_flags2;
u8 he_RU[4];
u16 he_data1;
u16 he_data2;
u16 he_data3;
u16 he_data4;
u16 he_data5;
u16 he_data6;
u32 ppdu_len;
u32 prev_ppdu_id;
u32 device_id;
u16 first_data_seq_ctrl;
u8 monitor_direct_used;
u8 data_sequence_control_info_valid;
u8 ltf_size;
u8 rxpcu_filter_pass;
char rssi_chain[8][8];
struct hal_rx_user_status userstats;
};
#define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0)
struct hal_rx_ppdu_start {
__le32 info0;
__le32 chan_num;
__le32 ppdu_start_ts;
} __packed;
#define HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR GENMASK(25, 16)
#define HAL_RX_PPDU_END_USER_STATS_INFO1_MPDU_CNT_FCS_OK GENMASK(8, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO1_FC_VALID BIT(9)
#define HAL_RX_PPDU_END_USER_STATS_INFO1_QOS_CTRL_VALID BIT(10)
#define HAL_RX_PPDU_END_USER_STATS_INFO1_HT_CTRL_VALID BIT(11)
#define HAL_RX_PPDU_END_USER_STATS_INFO1_PKT_TYPE GENMASK(23, 20)
#define HAL_RX_PPDU_END_USER_STATS_INFO2_AST_INDEX GENMASK(15, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO2_FRAME_CTRL GENMASK(31, 16)
#define HAL_RX_PPDU_END_USER_STATS_INFO3_QOS_CTRL GENMASK(31, 16)
#define HAL_RX_PPDU_END_USER_STATS_INFO4_UDP_MSDU_CNT GENMASK(15, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO4_TCP_MSDU_CNT GENMASK(31, 16)
#define HAL_RX_PPDU_END_USER_STATS_INFO5_OTHER_MSDU_CNT GENMASK(15, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO5_TCP_ACK_MSDU_CNT GENMASK(31, 16)
#define HAL_RX_PPDU_END_USER_STATS_INFO7_TID_BITMAP GENMASK(15, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO7_TID_EOSP_BITMAP GENMASK(31, 16)
#define HAL_RX_PPDU_END_USER_STATS_INFO8_MPDU_OK_BYTE_COUNT GENMASK(24, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO9_MPDU_ERR_BYTE_COUNT GENMASK(24, 0)
struct hal_rx_ppdu_end_user_stats {
__le32 rsvd0[2];
__le32 info0;
__le32 info1;
__le32 info2;
__le32 info3;
__le32 ht_ctrl;
__le32 rsvd1[2];
__le32 info4;
__le32 info5;
__le32 info6;
__le32 info7;
__le32 rsvd2[4];
__le32 info8;
__le32 rsvd3;
__le32 info9;
__le32 rsvd4[2];
__le32 info10;
} __packed;
struct hal_rx_ppdu_end_user_stats_ext {
u32 info0;
u32 info1;
u32 info2;
u32 info3;
u32 info4;
u32 info5;
u32 info6;
} __packed;
#define HAL_RX_HT_SIG_INFO_INFO0_MCS GENMASK(6, 0)
#define HAL_RX_HT_SIG_INFO_INFO0_BW BIT(7)
#define HAL_RX_HT_SIG_INFO_INFO1_STBC GENMASK(5, 4)
#define HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING BIT(6)
#define HAL_RX_HT_SIG_INFO_INFO1_GI BIT(7)
struct hal_rx_ht_sig_info {
__le32 info0;
__le32 info1;
} __packed;
#define HAL_RX_LSIG_B_INFO_INFO0_RATE GENMASK(3, 0)
#define HAL_RX_LSIG_B_INFO_INFO0_LEN GENMASK(15, 4)
struct hal_rx_lsig_b_info {
__le32 info0;
} __packed;
#define HAL_RX_LSIG_A_INFO_INFO0_RATE GENMASK(3, 0)
#define HAL_RX_LSIG_A_INFO_INFO0_LEN GENMASK(16, 5)
#define HAL_RX_LSIG_A_INFO_INFO0_PKT_TYPE GENMASK(27, 24)
struct hal_rx_lsig_a_info {
__le32 info0;
} __packed;
#define HAL_RX_VHT_SIG_A_INFO_INFO0_BW GENMASK(1, 0)
#define HAL_RX_VHT_SIG_A_INFO_INFO0_STBC BIT(3)
#define HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID GENMASK(9, 4)
#define HAL_RX_VHT_SIG_A_INFO_INFO0_NSTS GENMASK(21, 10)
#define HAL_RX_VHT_SIG_A_INFO_INFO1_GI_SETTING GENMASK(1, 0)
#define HAL_RX_VHT_SIG_A_INFO_INFO1_SU_MU_CODING BIT(2)
#define HAL_RX_VHT_SIG_A_INFO_INFO1_MCS GENMASK(7, 4)
#define HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED BIT(8)
struct hal_rx_vht_sig_a_info {
__le32 info0;
__le32 info1;
} __packed;
enum hal_rx_vht_sig_a_gi_setting {
HAL_RX_VHT_SIG_A_NORMAL_GI = 0,
HAL_RX_VHT_SIG_A_SHORT_GI = 1,
HAL_RX_VHT_SIG_A_SHORT_GI_AMBIGUITY = 3,
};
#define HAL_RX_SU_MU_CODING_LDPC 0x01
#define HE_GI_0_8 0
#define HE_GI_0_4 1
#define HE_GI_1_6 2
#define HE_GI_3_2 3
#define HE_LTF_1_X 0
#define HE_LTF_2_X 1
#define HE_LTF_4_X 2
#define HE_LTF_UNKNOWN 3
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_MCS GENMASK(6, 3)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM BIT(7)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_TRANSMIT_BW GENMASK(20, 19)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_CP_LTF_SIZE GENMASK(22, 21)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS GENMASK(25, 23)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_BSS_COLOR GENMASK(13, 8)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_SPATIAL_REUSE GENMASK(18, 15)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_FORMAT_IND BIT(0)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_BEAM_CHANGE BIT(1)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO0_DL_UL_FLAG BIT(2)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXOP_DURATION GENMASK(6, 0)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_CODING BIT(7)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_LDPC_EXTRA BIT(8)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC BIT(9)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF BIT(10)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_FACTOR GENMASK(12, 11)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_PKT_EXT_PE_DISAM BIT(13)
#define HAL_RX_HE_SIG_A_SU_INFO_INFO1_DOPPLER_IND BIT(15)
struct hal_rx_he_sig_a_su_info {
__le32 info0;
__le32 info1;
} __packed;
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_UL_FLAG BIT(1)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_MCS_OF_SIGB GENMASK(3, 1)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DCM_OF_SIGB BIT(4)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_BSS_COLOR GENMASK(10, 5)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_SPATIAL_REUSE GENMASK(14, 11)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_TRANSMIT_BW GENMASK(17, 15)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_NUM_SIGB_SYMB GENMASK(21, 18)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_COMP_MODE_SIGB BIT(22)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_CP_LTF_SIZE GENMASK(24, 23)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO0_DOPPLER_INDICATION BIT(25)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXOP_DURATION GENMASK(6, 0)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_CODING BIT(7)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_NUM_LTF_SYMB GENMASK(10, 8)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_LDPC_EXTRA BIT(11)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_STBC BIT(12)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_TXBF BIT(10)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_FACTOR GENMASK(14, 13)
#define HAL_RX_HE_SIG_A_MU_DL_INFO_INFO1_PKT_EXT_PE_DISAM BIT(15)
struct hal_rx_he_sig_a_mu_dl_info {
__le32 info0;
__le32 info1;
} __packed;
#define HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION GENMASK(7, 0)
struct hal_rx_he_sig_b1_mu_info {
__le32 info0;
} __packed;
#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_ID GENMASK(10, 0)
#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_MCS GENMASK(18, 15)
#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_CODING BIT(20)
#define HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS GENMASK(31, 29)
struct hal_rx_he_sig_b2_mu_info {
__le32 info0;
} __packed;
#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_ID GENMASK(10, 0)
#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS GENMASK(13, 11)
#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF BIT(19)
#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_MCS GENMASK(18, 15)
#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_DCM BIT(19)
#define HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_CODING BIT(20)
struct hal_rx_he_sig_b2_ofdma_info {
__le32 info0;
} __packed;
#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RSSI_COMB GENMASK(15, 8)
#define HAL_RX_PHYRX_RSSI_PREAMBLE_PRI20 GENMASK(7, 0)
struct hal_rx_phyrx_chain_rssi {
__le32 rssi_2040;
__le32 rssi_80;
} __packed;
struct hal_rx_phyrx_rssi_legacy_info {
__le32 rsvd[3];
struct hal_rx_phyrx_chain_rssi pre_rssi[HAL_RX_MAX_NSS];
struct hal_rx_phyrx_chain_rssi preamble[HAL_RX_MAX_NSS];
__le32 info0;
} __packed;
#define HAL_RX_MPDU_INFO_INFO0_PEERID GENMASK(31, 16)
#define HAL_RX_MPDU_INFO_INFO0_PEERID_WCN6855 GENMASK(15, 0)
#define HAL_RX_MPDU_INFO_INFO1_MPDU_LEN GENMASK(13, 0)
struct hal_rx_mpdu_info_ipq8074 {
__le32 rsvd0;
__le32 info0;
__le32 rsvd1[11];
__le32 info1;
__le32 rsvd2[9];
} __packed;
struct hal_rx_mpdu_info_qcn9074 {
__le32 rsvd0[10];
__le32 info0;
__le32 rsvd1[2];
__le32 info1;
__le32 rsvd2[9];
} __packed;
struct hal_rx_mpdu_info_wcn6855 {
__le32 rsvd0[8];
__le32 info0;
__le32 rsvd1[14];
} __packed;
struct hal_rx_mpdu_info {
union {
struct hal_rx_mpdu_info_ipq8074 ipq8074;
struct hal_rx_mpdu_info_qcn9074 qcn9074;
struct hal_rx_mpdu_info_wcn6855 wcn6855;
} u;
} __packed;
#define HAL_RX_PPDU_END_DURATION GENMASK(23, 0)
struct hal_rx_ppdu_end_duration {
__le32 rsvd0[9];
__le32 info0;
__le32 rsvd1[4];
} __packed;
struct hal_rx_rxpcu_classification_overview {
u32 rsvd0;
} __packed;
struct hal_rx_msdu_desc_info {
u32 msdu_flags;
u16 msdu_len; /* 14 bits for length */
};
#define HAL_RX_NUM_MSDU_DESC 6
struct hal_rx_msdu_list {
struct hal_rx_msdu_desc_info msdu_info[HAL_RX_NUM_MSDU_DESC];
u32 sw_cookie[HAL_RX_NUM_MSDU_DESC];
u8 rbm[HAL_RX_NUM_MSDU_DESC];
};
void ath11k_hal_reo_status_queue_stats(struct ath11k_base *ab, u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_flush_queue_status(struct ath11k_base *ab, u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_flush_cache_status(struct ath11k_base *ab, u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_unblk_cache_status(struct ath11k_base *ab, u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_flush_timeout_list_status(struct ath11k_base *ab,
u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_desc_thresh_reached_status(struct ath11k_base *ab,
u32 *reo_desc,
struct hal_reo_status *status);
void ath11k_hal_reo_update_rx_reo_queue_status(struct ath11k_base *ab,
u32 *reo_desc,
struct hal_reo_status *status);
int ath11k_hal_reo_process_status(u8 *reo_desc, u8 *status);
void ath11k_hal_rx_msdu_link_info_get(void *link_desc, u32 *num_msdus,
u32 *msdu_cookies,
enum hal_rx_buf_return_buf_manager *rbm);
void ath11k_hal_rx_msdu_link_desc_set(struct ath11k_base *ab, void *desc,
void *link_desc,
enum hal_wbm_rel_bm_act action);
void ath11k_hal_rx_buf_addr_info_set(void *desc, dma_addr_t paddr,
u32 cookie, u8 manager);
void ath11k_hal_rx_buf_addr_info_get(void *desc, dma_addr_t *paddr,
u32 *cookie, u8 *rbm);
int ath11k_hal_desc_reo_parse_err(struct ath11k_base *ab, u32 *rx_desc,
dma_addr_t *paddr, u32 *desc_bank);
int ath11k_hal_wbm_desc_parse_err(struct ath11k_base *ab, void *desc,
struct hal_rx_wbm_rel_info *rel_info);
void ath11k_hal_rx_reo_ent_paddr_get(struct ath11k_base *ab, void *desc,
dma_addr_t *paddr, u32 *desc_bank);
void ath11k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc,
dma_addr_t *paddr, u32 *sw_cookie,
void **pp_buf_addr_info, u8 *rbm,
u32 *msdu_cnt);
void
ath11k_hal_rx_sw_mon_ring_buf_paddr_get(void *rx_desc,
struct hal_sw_mon_ring_entries *sw_mon_ent);
enum hal_rx_mon_status
ath11k_hal_rx_parse_mon_status(struct ath11k_base *ab,
struct hal_rx_mon_ppdu_info *ppdu_info,
struct sk_buff *skb);
#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0 0xDDBEEF
#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1 0xADBEEF
#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2 0xBDBEEF
#define REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3 0xCDBEEF
#endif
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "hal_desc.h"
#include "hal.h"
#include "hal_tx.h"
#include "hif.h"
#define DSCP_TID_MAP_TBL_ENTRY_SIZE 64
/* dscp_tid_map - Default DSCP-TID mapping
*
* DSCP TID
* 000000 0
* 001000 1
* 010000 2
* 011000 3
* 100000 4
* 101000 5
* 110000 6
* 111000 7
*/
static const u8 dscp_tid_map[DSCP_TID_MAP_TBL_ENTRY_SIZE] = {
0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7,
};
void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd,
struct hal_tx_info *ti)
{
struct hal_tcl_data_cmd *tcl_cmd = cmd;
tcl_cmd->buf_addr_info.info0 =
FIELD_PREP(BUFFER_ADDR_INFO0_ADDR, ti->paddr);
tcl_cmd->buf_addr_info.info1 =
FIELD_PREP(BUFFER_ADDR_INFO1_ADDR,
((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT));
tcl_cmd->buf_addr_info.info1 |=
FIELD_PREP(BUFFER_ADDR_INFO1_RET_BUF_MGR, ti->rbm_id) |
FIELD_PREP(BUFFER_ADDR_INFO1_SW_COOKIE, ti->desc_id);
tcl_cmd->info0 =
FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_DESC_TYPE, ti->type) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCAP_TYPE, ti->encap_type) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ENCRYPT_TYPE,
ti->encrypt_type) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_SEARCH_TYPE,
ti->search_type) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_ADDR_EN,
ti->addr_search_flags) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO0_CMD_NUM,
ti->meta_data_flags);
tcl_cmd->info1 = ti->flags0 |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_DATA_LEN, ti->data_len) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO1_PKT_OFFSET, ti->pkt_offset);
tcl_cmd->info2 = ti->flags1 |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_TID, ti->tid) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO2_LMAC_ID, ti->lmac_id);
tcl_cmd->info3 = FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_DSCP_TID_TABLE_IDX,
ti->dscp_tid_tbl_idx) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_SEARCH_INDEX,
ti->bss_ast_idx) |
FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_CACHE_SET_NUM,
ti->bss_ast_hash);
tcl_cmd->info4 = 0;
if (ti->enable_mesh)
ab->hw_params.hw_ops->tx_mesh_enable(ab, tcl_cmd);
}
void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id)
{
u32 ctrl_reg_val;
u32 addr;
u8 hw_map_val[HAL_DSCP_TID_TBL_SIZE];
int i;
u32 value;
int cnt = 0;
ctrl_reg_val = ath11k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG +
HAL_TCL1_RING_CMN_CTRL_REG);
/* Enable read/write access */
ctrl_reg_val |= HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN;
ath11k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG +
HAL_TCL1_RING_CMN_CTRL_REG, ctrl_reg_val);
addr = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_DSCP_TID_MAP +
(4 * id * (HAL_DSCP_TID_TBL_SIZE / 4));
/* Configure each DSCP-TID mapping in three bits there by configure
* three bytes in an iteration.
*/
for (i = 0; i < DSCP_TID_MAP_TBL_ENTRY_SIZE; i += 8) {
value = FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP0,
dscp_tid_map[i]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP1,
dscp_tid_map[i + 1]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP2,
dscp_tid_map[i + 2]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP3,
dscp_tid_map[i + 3]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP4,
dscp_tid_map[i + 4]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP5,
dscp_tid_map[i + 5]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP6,
dscp_tid_map[i + 6]) |
FIELD_PREP(HAL_TCL1_RING_FIELD_DSCP_TID_MAP7,
dscp_tid_map[i + 7]);
memcpy(&hw_map_val[cnt], (u8 *)&value, 3);
cnt += 3;
}
for (i = 0; i < HAL_DSCP_TID_TBL_SIZE; i += 4) {
ath11k_hif_write32(ab, addr, *(u32 *)&hw_map_val[i]);
addr += 4;
}
/* Disable read/write access */
ctrl_reg_val = ath11k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG +
HAL_TCL1_RING_CMN_CTRL_REG);
ctrl_reg_val &= ~HAL_TCL1_RING_CMN_CTRL_DSCP_TID_MAP_PROG_EN;
ath11k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG +
HAL_TCL1_RING_CMN_CTRL_REG,
ctrl_reg_val);
}
void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, struct hal_srng *srng)
{
struct hal_srng_params params;
struct hal_tlv_hdr *tlv;
int i, entry_size;
u8 *desc;
memset(&params, 0, sizeof(params));
entry_size = ath11k_hal_srng_get_entrysize(ab, HAL_TCL_DATA);
ath11k_hal_srng_get_params(ab, srng, &params);
desc = (u8 *)params.ring_base_vaddr;
for (i = 0; i < params.num_entries; i++) {
tlv = (struct hal_tlv_hdr *)desc;
tlv->tl = FIELD_PREP(HAL_TLV_HDR_TAG, HAL_TCL_DATA_CMD) |
FIELD_PREP(HAL_TLV_HDR_LEN,
sizeof(struct hal_tcl_data_cmd));
desc += entry_size;
}
}
@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_HAL_TX_H
#define ATH11K_HAL_TX_H
#include "hal_desc.h"
#include "core.h"
#define HAL_TX_ADDRX_EN 1
#define HAL_TX_ADDRY_EN 2
#define HAL_TX_ADDR_SEARCH_DEFAULT 0
#define HAL_TX_ADDR_SEARCH_INDEX 1
struct hal_tx_info {
u16 meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */
u8 ring_id;
u32 desc_id;
enum hal_tcl_desc_type type;
enum hal_tcl_encap_type encap_type;
dma_addr_t paddr;
u32 data_len;
u32 pkt_offset;
enum hal_encrypt_type encrypt_type;
u32 flags0; /* %HAL_TCL_DATA_CMD_INFO1_ */
u32 flags1; /* %HAL_TCL_DATA_CMD_INFO2_ */
u16 addr_search_flags; /* %HAL_TCL_DATA_CMD_INFO0_ADDR(X/Y)_ */
u16 bss_ast_hash;
u16 bss_ast_idx;
u8 tid;
u8 search_type; /* %HAL_TX_ADDR_SEARCH_ */
u8 lmac_id;
u8 dscp_tid_tbl_idx;
bool enable_mesh;
u8 rbm_id;
};
/* TODO: Check if the actual desc macros can be used instead */
#define HAL_TX_STATUS_FLAGS_FIRST_MSDU BIT(0)
#define HAL_TX_STATUS_FLAGS_LAST_MSDU BIT(1)
#define HAL_TX_STATUS_FLAGS_MSDU_IN_AMSDU BIT(2)
#define HAL_TX_STATUS_FLAGS_RATE_STATS_VALID BIT(3)
#define HAL_TX_STATUS_FLAGS_RATE_LDPC BIT(4)
#define HAL_TX_STATUS_FLAGS_RATE_STBC BIT(5)
#define HAL_TX_STATUS_FLAGS_OFDMA BIT(6)
#define HAL_TX_STATUS_DESC_LEN sizeof(struct hal_wbm_release_ring)
/* Tx status parsed from srng desc */
struct hal_tx_status {
enum hal_wbm_rel_src_module buf_rel_source;
enum hal_wbm_tqm_rel_reason status;
s8 ack_rssi;
u32 flags; /* %HAL_TX_STATUS_FLAGS_ */
u32 ppdu_id;
u8 try_cnt;
u8 tid;
u16 peer_id;
u32 rate_stats;
};
void ath11k_hal_tx_cmd_desc_setup(struct ath11k_base *ab, void *cmd,
struct hal_tx_info *ti);
void ath11k_hal_tx_set_dscp_tid_map(struct ath11k_base *ab, int id);
int ath11k_hal_reo_cmd_send(struct ath11k_base *ab, struct hal_srng *srng,
enum hal_reo_cmd_type type,
struct ath11k_hal_reo_cmd *cmd);
void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab,
struct hal_srng *srng);
#endif
@@ -0,0 +1,162 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _HIF_H_
#define _HIF_H_
#include "core.h"
struct ath11k_hif_ops {
u32 (*read32)(struct ath11k_base *ab, u32 address);
void (*write32)(struct ath11k_base *ab, u32 address, u32 data);
int (*read)(struct ath11k_base *ab, void *buf, u32 start, u32 end);
void (*irq_enable)(struct ath11k_base *ab);
void (*irq_disable)(struct ath11k_base *ab);
int (*start)(struct ath11k_base *ab);
void (*stop)(struct ath11k_base *ab);
int (*power_up)(struct ath11k_base *ab);
void (*power_down)(struct ath11k_base *ab, bool is_suspend);
int (*suspend)(struct ath11k_base *ab);
int (*resume)(struct ath11k_base *ab);
int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
int (*get_user_msi_vector)(struct ath11k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector);
void (*get_msi_address)(struct ath11k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi);
void (*ce_irq_enable)(struct ath11k_base *ab);
void (*ce_irq_disable)(struct ath11k_base *ab);
void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
void (*coredump_download)(struct ath11k_base *ab);
};
static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab)
{
if (ab->hif.ops->ce_irq_enable)
ab->hif.ops->ce_irq_enable(ab);
}
static inline void ath11k_hif_ce_irq_disable(struct ath11k_base *ab)
{
if (ab->hif.ops->ce_irq_disable)
ab->hif.ops->ce_irq_disable(ab);
}
static inline int ath11k_hif_start(struct ath11k_base *ab)
{
return ab->hif.ops->start(ab);
}
static inline void ath11k_hif_stop(struct ath11k_base *ab)
{
ab->hif.ops->stop(ab);
}
static inline void ath11k_hif_irq_enable(struct ath11k_base *ab)
{
ab->hif.ops->irq_enable(ab);
}
static inline void ath11k_hif_irq_disable(struct ath11k_base *ab)
{
ab->hif.ops->irq_disable(ab);
}
static inline int ath11k_hif_power_up(struct ath11k_base *ab)
{
if (!ab->hif.ops->power_up)
return -EOPNOTSUPP;
return ab->hif.ops->power_up(ab);
}
static inline void ath11k_hif_power_down(struct ath11k_base *ab, bool is_suspend)
{
if (!ab->hif.ops->power_down)
return;
ab->hif.ops->power_down(ab, is_suspend);
}
static inline int ath11k_hif_suspend(struct ath11k_base *ab)
{
if (ab->hif.ops->suspend)
return ab->hif.ops->suspend(ab);
return 0;
}
static inline int ath11k_hif_resume(struct ath11k_base *ab)
{
if (ab->hif.ops->resume)
return ab->hif.ops->resume(ab);
return 0;
}
static inline u32 ath11k_hif_read32(struct ath11k_base *ab, u32 address)
{
return ab->hif.ops->read32(ab, address);
}
static inline void ath11k_hif_write32(struct ath11k_base *ab, u32 address, u32 data)
{
ab->hif.ops->write32(ab, address, data);
}
static inline int ath11k_hif_read(struct ath11k_base *ab, void *buf,
u32 start, u32 end)
{
if (!ab->hif.ops->read)
return -EOPNOTSUPP;
return ab->hif.ops->read(ab, buf, start, end);
}
static inline int ath11k_hif_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe)
{
return ab->hif.ops->map_service_to_pipe(ab, service_id, ul_pipe, dl_pipe);
}
static inline int ath11k_get_user_msi_vector(struct ath11k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector)
{
if (!ab->hif.ops->get_user_msi_vector)
return -EOPNOTSUPP;
return ab->hif.ops->get_user_msi_vector(ab, user_name, num_vectors,
user_base_data,
base_vector);
}
static inline void ath11k_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi)
{
if (!ab->hif.ops->get_msi_address)
return;
ab->hif.ops->get_msi_address(ab, msi_addr_lo, msi_addr_hi);
}
static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id,
u32 *msi_data_idx)
{
if (ab->hif.ops->get_ce_msi_idx)
ab->hif.ops->get_ce_msi_idx(ab, ce_id, msi_data_idx);
else
*msi_data_idx = ce_id;
}
static inline void ath11k_hif_coredump_download(struct ath11k_base *ab)
{
if (ab->hif.ops->coredump_download)
ab->hif.ops->coredump_download(ab);
}
#endif /* _HIF_H_ */
@@ -0,0 +1,845 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
#include "debug.h"
#include "hif.h"
struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ab, int size)
{
struct sk_buff *skb;
skb = dev_alloc_skb(size + sizeof(struct ath11k_htc_hdr));
if (!skb)
return NULL;
skb_reserve(skb, sizeof(struct ath11k_htc_hdr));
/* FW/HTC requires 4-byte aligned streams */
if (!IS_ALIGNED((unsigned long)skb->data, 4))
ath11k_warn(ab, "Unaligned HTC tx skb\n");
return skb;
}
static void ath11k_htc_control_tx_complete(struct ath11k_base *ab,
struct sk_buff *skb)
{
kfree_skb(skb);
}
static struct sk_buff *ath11k_htc_build_tx_ctrl_skb(void *ab)
{
struct sk_buff *skb;
struct ath11k_skb_cb *skb_cb;
skb = dev_alloc_skb(ATH11K_HTC_CONTROL_BUFFER_SIZE);
if (!skb)
return NULL;
skb_reserve(skb, sizeof(struct ath11k_htc_hdr));
WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
skb_cb = ATH11K_SKB_CB(skb);
memset(skb_cb, 0, sizeof(*skb_cb));
return skb;
}
static void ath11k_htc_prepare_tx_skb(struct ath11k_htc_ep *ep,
struct sk_buff *skb)
{
struct ath11k_htc_hdr *hdr;
hdr = (struct ath11k_htc_hdr *)skb->data;
memset(hdr, 0, sizeof(*hdr));
hdr->htc_info = FIELD_PREP(HTC_HDR_ENDPOINTID, ep->eid) |
FIELD_PREP(HTC_HDR_PAYLOADLEN,
(skb->len - sizeof(*hdr)));
if (ep->tx_credit_flow_enabled)
hdr->htc_info |= FIELD_PREP(HTC_HDR_FLAGS,
ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE);
spin_lock_bh(&ep->htc->tx_lock);
hdr->ctrl_info = FIELD_PREP(HTC_HDR_CONTROLBYTES1, ep->seq_no++);
spin_unlock_bh(&ep->htc->tx_lock);
}
int ath11k_htc_send(struct ath11k_htc *htc,
enum ath11k_htc_ep_id eid,
struct sk_buff *skb)
{
struct ath11k_htc_ep *ep = &htc->endpoint[eid];
struct ath11k_skb_cb *skb_cb = ATH11K_SKB_CB(skb);
struct device *dev = htc->ab->dev;
struct ath11k_base *ab = htc->ab;
int credits = 0;
int ret;
bool credit_flow_enabled = (ab->hw_params.credit_flow &&
ep->tx_credit_flow_enabled);
if (eid >= ATH11K_HTC_EP_COUNT) {
ath11k_warn(ab, "Invalid endpoint id: %d\n", eid);
return -ENOENT;
}
skb_push(skb, sizeof(struct ath11k_htc_hdr));
if (credit_flow_enabled) {
credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
spin_lock_bh(&htc->tx_lock);
if (ep->tx_credits < credits) {
ath11k_dbg(ab, ATH11K_DBG_HTC,
"ep %d insufficient credits required %d total %d\n",
eid, credits, ep->tx_credits);
spin_unlock_bh(&htc->tx_lock);
ret = -EAGAIN;
goto err_pull;
}
ep->tx_credits -= credits;
ath11k_dbg(ab, ATH11K_DBG_HTC,
"ep %d credits consumed %d total %d\n",
eid, credits, ep->tx_credits);
spin_unlock_bh(&htc->tx_lock);
}
ath11k_htc_prepare_tx_skb(ep, skb);
skb_cb->eid = eid;
skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
ret = dma_mapping_error(dev, skb_cb->paddr);
if (ret) {
ret = -EIO;
goto err_credits;
}
ath11k_dbg(ab, ATH11K_DBG_HTC, "tx skb %p eid %d paddr %pad\n",
skb, skb_cb->eid, &skb_cb->paddr);
ret = ath11k_ce_send(htc->ab, skb, ep->ul_pipe_id, ep->eid);
if (ret)
goto err_unmap;
return 0;
err_unmap:
dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
err_credits:
if (credit_flow_enabled) {
spin_lock_bh(&htc->tx_lock);
ep->tx_credits += credits;
ath11k_dbg(ab, ATH11K_DBG_HTC,
"ep %d credits reverted %d total %d\n",
eid, credits, ep->tx_credits);
spin_unlock_bh(&htc->tx_lock);
if (ep->ep_ops.ep_tx_credits)
ep->ep_ops.ep_tx_credits(htc->ab);
}
err_pull:
skb_pull(skb, sizeof(struct ath11k_htc_hdr));
return ret;
}
static void
ath11k_htc_process_credit_report(struct ath11k_htc *htc,
const struct ath11k_htc_credit_report *report,
int len,
enum ath11k_htc_ep_id eid)
{
struct ath11k_base *ab = htc->ab;
struct ath11k_htc_ep *ep;
int i, n_reports;
if (len % sizeof(*report))
ath11k_warn(ab, "Uneven credit report len %d", len);
n_reports = len / sizeof(*report);
spin_lock_bh(&htc->tx_lock);
for (i = 0; i < n_reports; i++, report++) {
if (report->eid >= ATH11K_HTC_EP_COUNT)
break;
ep = &htc->endpoint[report->eid];
ep->tx_credits += report->credits;
ath11k_dbg(ab, ATH11K_DBG_HTC, "ep %d credits got %d total %d\n",
report->eid, report->credits, ep->tx_credits);
if (ep->ep_ops.ep_tx_credits) {
spin_unlock_bh(&htc->tx_lock);
ep->ep_ops.ep_tx_credits(htc->ab);
spin_lock_bh(&htc->tx_lock);
}
}
spin_unlock_bh(&htc->tx_lock);
}
static int ath11k_htc_process_trailer(struct ath11k_htc *htc,
u8 *buffer,
int length,
enum ath11k_htc_ep_id src_eid)
{
struct ath11k_base *ab = htc->ab;
int status = 0;
struct ath11k_htc_record *record;
size_t len;
while (length > 0) {
record = (struct ath11k_htc_record *)buffer;
if (length < sizeof(record->hdr)) {
status = -EINVAL;
break;
}
if (record->hdr.len > length) {
/* no room left in buffer for record */
ath11k_warn(ab, "Invalid record length: %d\n",
record->hdr.len);
status = -EINVAL;
break;
}
if (ab->hw_params.credit_flow) {
switch (record->hdr.id) {
case ATH11K_HTC_RECORD_CREDITS:
len = sizeof(struct ath11k_htc_credit_report);
if (record->hdr.len < len) {
ath11k_warn(ab, "Credit report too long\n");
status = -EINVAL;
break;
}
ath11k_htc_process_credit_report(htc,
record->credit_report,
record->hdr.len,
src_eid);
break;
default:
ath11k_warn(ab, "Unhandled record: id:%d length:%d\n",
record->hdr.id, record->hdr.len);
break;
}
}
if (status)
break;
/* multiple records may be present in a trailer */
buffer += sizeof(record->hdr) + record->hdr.len;
length -= sizeof(record->hdr) + record->hdr.len;
}
return status;
}
static void ath11k_htc_suspend_complete(struct ath11k_base *ab, bool ack)
{
ath11k_dbg(ab, ATH11K_DBG_BOOT, "suspend complete %d\n", ack);
if (ack)
set_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
else
clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
complete(&ab->htc_suspend);
}
void ath11k_htc_tx_completion_handler(struct ath11k_base *ab,
struct sk_buff *skb)
{
struct ath11k_htc *htc = &ab->htc;
struct ath11k_htc_ep *ep;
void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *);
u8 eid;
eid = ATH11K_SKB_CB(skb)->eid;
if (eid >= ATH11K_HTC_EP_COUNT) {
dev_kfree_skb_any(skb);
return;
}
ep = &htc->endpoint[eid];
spin_lock_bh(&htc->tx_lock);
ep_tx_complete = ep->ep_ops.ep_tx_complete;
spin_unlock_bh(&htc->tx_lock);
if (!ep_tx_complete) {
dev_kfree_skb_any(skb);
return;
}
ep_tx_complete(htc->ab, skb);
}
static void ath11k_htc_wakeup_from_suspend(struct ath11k_base *ab)
{
ath11k_dbg(ab, ATH11K_DBG_BOOT, "wakeup from suspend is received\n");
}
void ath11k_htc_rx_completion_handler(struct ath11k_base *ab,
struct sk_buff *skb)
{
int status = 0;
struct ath11k_htc *htc = &ab->htc;
struct ath11k_htc_hdr *hdr;
struct ath11k_htc_ep *ep;
u16 payload_len;
u32 message_id, trailer_len = 0;
size_t min_len;
u8 eid;
bool trailer_present;
hdr = (struct ath11k_htc_hdr *)skb->data;
skb_pull(skb, sizeof(*hdr));
eid = FIELD_GET(HTC_HDR_ENDPOINTID, hdr->htc_info);
if (eid >= ATH11K_HTC_EP_COUNT) {
ath11k_warn(ab, "HTC Rx: invalid eid %d\n", eid);
goto out;
}
ep = &htc->endpoint[eid];
payload_len = FIELD_GET(HTC_HDR_PAYLOADLEN, hdr->htc_info);
if (payload_len + sizeof(*hdr) > ATH11K_HTC_MAX_LEN) {
ath11k_warn(ab, "HTC rx frame too long, len: %zu\n",
payload_len + sizeof(*hdr));
goto out;
}
if (skb->len < payload_len) {
ath11k_warn(ab, "HTC Rx: insufficient length, got %d, expected %d\n",
skb->len, payload_len);
goto out;
}
/* get flags to check for trailer */
trailer_present = (FIELD_GET(HTC_HDR_FLAGS, hdr->htc_info)) &
ATH11K_HTC_FLAG_TRAILER_PRESENT;
ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p trailer_present %d\n",
eid, skb, trailer_present);
if (trailer_present) {
u8 *trailer;
trailer_len = FIELD_GET(HTC_HDR_CONTROLBYTES0, hdr->ctrl_info);
min_len = sizeof(struct ath11k_htc_record_hdr);
if ((trailer_len < min_len) ||
(trailer_len > payload_len)) {
ath11k_warn(ab, "Invalid trailer length: %d\n",
trailer_len);
goto out;
}
trailer = (u8 *)hdr;
trailer += sizeof(*hdr);
trailer += payload_len;
trailer -= trailer_len;
status = ath11k_htc_process_trailer(htc, trailer,
trailer_len, eid);
if (status)
goto out;
skb_trim(skb, skb->len - trailer_len);
}
if (trailer_len >= payload_len)
/* zero length packet with trailer data, just drop these */
goto out;
if (eid == ATH11K_HTC_EP_0) {
struct ath11k_htc_msg *msg = (struct ath11k_htc_msg *)skb->data;
message_id = FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id);
ath11k_dbg(ab, ATH11K_DBG_HTC, "rx ep %d skb %p message_id %d\n",
eid, skb, message_id);
switch (message_id) {
case ATH11K_HTC_MSG_READY_ID:
case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID:
/* handle HTC control message */
if (completion_done(&htc->ctl_resp)) {
/* this is a fatal error, target should not be
* sending unsolicited messages on the ep 0
*/
ath11k_warn(ab, "HTC rx ctrl still processing\n");
complete(&htc->ctl_resp);
goto out;
}
htc->control_resp_len =
min_t(int, skb->len,
ATH11K_HTC_MAX_CTRL_MSG_LEN);
memcpy(htc->control_resp_buffer, skb->data,
htc->control_resp_len);
complete(&htc->ctl_resp);
break;
case ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE:
ath11k_htc_suspend_complete(ab, true);
break;
case ATH11K_HTC_MSG_NACK_SUSPEND:
ath11k_htc_suspend_complete(ab, false);
break;
case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
ath11k_htc_wakeup_from_suspend(ab);
break;
default:
ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n",
FIELD_GET(HTC_MSG_MESSAGEID, msg->msg_svc_id));
break;
}
goto out;
}
ep->ep_ops.ep_rx_complete(ab, skb);
/* poll tx completion for interrupt disabled CE's */
ath11k_ce_poll_send_completed(ab, ep->ul_pipe_id);
/* skb is now owned by the rx completion handler */
skb = NULL;
out:
kfree_skb(skb);
}
static void ath11k_htc_control_rx_complete(struct ath11k_base *ab,
struct sk_buff *skb)
{
/* This is unexpected. FW is not supposed to send regular rx on this
* endpoint.
*/
ath11k_warn(ab, "unexpected htc rx\n");
kfree_skb(skb);
}
static const char *htc_service_name(enum ath11k_htc_svc_id id)
{
switch (id) {
case ATH11K_HTC_SVC_ID_RESERVED:
return "Reserved";
case ATH11K_HTC_SVC_ID_RSVD_CTRL:
return "Control";
case ATH11K_HTC_SVC_ID_WMI_CONTROL:
return "WMI";
case ATH11K_HTC_SVC_ID_WMI_DATA_BE:
return "DATA BE";
case ATH11K_HTC_SVC_ID_WMI_DATA_BK:
return "DATA BK";
case ATH11K_HTC_SVC_ID_WMI_DATA_VI:
return "DATA VI";
case ATH11K_HTC_SVC_ID_WMI_DATA_VO:
return "DATA VO";
case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1:
return "WMI MAC1";
case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2:
return "WMI MAC2";
case ATH11K_HTC_SVC_ID_NMI_CONTROL:
return "NMI Control";
case ATH11K_HTC_SVC_ID_NMI_DATA:
return "NMI Data";
case ATH11K_HTC_SVC_ID_HTT_DATA_MSG:
return "HTT Data";
case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS:
return "RAW";
case ATH11K_HTC_SVC_ID_IPA_TX:
return "IPA TX";
case ATH11K_HTC_SVC_ID_PKT_LOG:
return "PKT LOG";
}
return "Unknown";
}
static void ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc)
{
struct ath11k_htc_ep *ep;
int i;
for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) {
ep = &htc->endpoint[i];
ep->service_id = ATH11K_HTC_SVC_ID_UNUSED;
ep->max_ep_message_len = 0;
ep->max_tx_queue_depth = 0;
ep->eid = i;
ep->htc = htc;
ep->tx_credit_flow_enabled = true;
}
}
static u8 ath11k_htc_get_credit_allocation(struct ath11k_htc *htc,
u16 service_id)
{
u8 i, allocation = 0;
for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
if (htc->service_alloc_table[i].service_id == service_id) {
allocation =
htc->service_alloc_table[i].credit_allocation;
}
}
return allocation;
}
static int ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc)
{
struct ath11k_htc_svc_tx_credits *serv_entry;
static const u32 svc_id[] = {
ATH11K_HTC_SVC_ID_WMI_CONTROL,
ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1,
ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2,
};
int i, credits;
credits = htc->total_transmit_credits;
serv_entry = htc->service_alloc_table;
if ((htc->wmi_ep_count == 0) ||
(htc->wmi_ep_count > ARRAY_SIZE(svc_id)))
return -EINVAL;
/* Divide credits among number of endpoints for WMI */
credits = credits / htc->wmi_ep_count;
for (i = 0; i < htc->wmi_ep_count; i++) {
serv_entry[i].service_id = svc_id[i];
serv_entry[i].credit_allocation = credits;
}
return 0;
}
int ath11k_htc_wait_target(struct ath11k_htc *htc)
{
int i, status = 0;
struct ath11k_base *ab = htc->ab;
unsigned long time_left;
struct ath11k_htc_ready *ready;
u16 message_id;
u16 credit_count;
u16 credit_size;
time_left = wait_for_completion_timeout(&htc->ctl_resp,
ATH11K_HTC_WAIT_TIMEOUT_HZ);
if (!time_left) {
ath11k_warn(ab, "failed to receive control response completion, polling..\n");
for (i = 0; i < ab->hw_params.ce_count; i++)
ath11k_ce_per_engine_service(htc->ab, i);
time_left =
wait_for_completion_timeout(&htc->ctl_resp,
ATH11K_HTC_WAIT_TIMEOUT_HZ);
if (!time_left)
status = -ETIMEDOUT;
}
if (status < 0) {
ath11k_warn(ab, "ctl_resp never came in (%d)\n", status);
return status;
}
if (htc->control_resp_len < sizeof(*ready)) {
ath11k_warn(ab, "Invalid HTC ready msg len:%d\n",
htc->control_resp_len);
return -ECOMM;
}
ready = (struct ath11k_htc_ready *)htc->control_resp_buffer;
message_id = FIELD_GET(HTC_MSG_MESSAGEID, ready->id_credit_count);
credit_count = FIELD_GET(HTC_READY_MSG_CREDITCOUNT,
ready->id_credit_count);
credit_size = FIELD_GET(HTC_READY_MSG_CREDITSIZE, ready->size_ep);
if (message_id != ATH11K_HTC_MSG_READY_ID) {
ath11k_warn(ab, "Invalid HTC ready msg: 0x%x\n", message_id);
return -ECOMM;
}
htc->total_transmit_credits = credit_count;
htc->target_credit_size = credit_size;
ath11k_dbg(ab, ATH11K_DBG_HTC,
"target ready total_transmit_credits %d target_credit_size %d\n",
htc->total_transmit_credits, htc->target_credit_size);
if ((htc->total_transmit_credits == 0) ||
(htc->target_credit_size == 0)) {
ath11k_warn(ab, "Invalid credit size received\n");
return -ECOMM;
}
/* For QCA6390, wmi endpoint uses 1 credit to avoid
* back-to-back write.
*/
if (ab->hw_params.supports_shadow_regs)
htc->total_transmit_credits = 1;
ath11k_htc_setup_target_buffer_assignments(htc);
return 0;
}
int ath11k_htc_connect_service(struct ath11k_htc *htc,
struct ath11k_htc_svc_conn_req *conn_req,
struct ath11k_htc_svc_conn_resp *conn_resp)
{
struct ath11k_base *ab = htc->ab;
struct ath11k_htc_conn_svc *req_msg;
struct ath11k_htc_conn_svc_resp resp_msg_dummy;
struct ath11k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy;
enum ath11k_htc_ep_id assigned_eid = ATH11K_HTC_EP_COUNT;
struct ath11k_htc_ep *ep;
struct sk_buff *skb;
unsigned int max_msg_size = 0;
int length, status;
unsigned long time_left;
bool disable_credit_flow_ctrl = false;
u16 message_id, service_id, flags = 0;
u8 tx_alloc = 0;
/* special case for HTC pseudo control service */
if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) {
disable_credit_flow_ctrl = true;
assigned_eid = ATH11K_HTC_EP_0;
max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN;
memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
goto setup;
}
tx_alloc = ath11k_htc_get_credit_allocation(htc,
conn_req->service_id);
if (!tx_alloc)
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"htc service %s does not allocate target credits\n",
htc_service_name(conn_req->service_id));
skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);
if (!skb) {
ath11k_warn(ab, "Failed to allocate HTC packet\n");
return -ENOMEM;
}
length = sizeof(*req_msg);
skb_put(skb, length);
memset(skb->data, 0, length);
req_msg = (struct ath11k_htc_conn_svc *)skb->data;
req_msg->msg_svc_id = FIELD_PREP(HTC_MSG_MESSAGEID,
ATH11K_HTC_MSG_CONNECT_SERVICE_ID);
flags |= FIELD_PREP(ATH11K_HTC_CONN_FLAGS_RECV_ALLOC, tx_alloc);
/* Only enable credit flow control for WMI ctrl service */
if (!(conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL ||
conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 ||
conn_req->service_id == ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2)) {
flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
disable_credit_flow_ctrl = true;
}
if (!ab->hw_params.credit_flow) {
flags |= ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
disable_credit_flow_ctrl = true;
}
req_msg->flags_len = FIELD_PREP(HTC_SVC_MSG_CONNECTIONFLAGS, flags);
req_msg->msg_svc_id |= FIELD_PREP(HTC_SVC_MSG_SERVICE_ID,
conn_req->service_id);
reinit_completion(&htc->ctl_resp);
status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);
if (status) {
kfree_skb(skb);
return status;
}
/* wait for response */
time_left = wait_for_completion_timeout(&htc->ctl_resp,
ATH11K_HTC_CONN_SVC_TIMEOUT_HZ);
if (!time_left) {
ath11k_err(ab, "Service connect timeout\n");
return -ETIMEDOUT;
}
/* we controlled the buffer creation, it's aligned */
resp_msg = (struct ath11k_htc_conn_svc_resp *)htc->control_resp_buffer;
message_id = FIELD_GET(HTC_MSG_MESSAGEID, resp_msg->msg_svc_id);
service_id = FIELD_GET(HTC_SVC_RESP_MSG_SERVICEID,
resp_msg->msg_svc_id);
if ((message_id != ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
(htc->control_resp_len < sizeof(*resp_msg))) {
ath11k_err(ab, "Invalid resp message ID 0x%x", message_id);
return -EPROTO;
}
ath11k_dbg(ab, ATH11K_DBG_HTC,
"service %s connect response status 0x%lx assigned ep 0x%lx\n",
htc_service_name(service_id),
FIELD_GET(HTC_SVC_RESP_MSG_STATUS, resp_msg->flags_len),
FIELD_GET(HTC_SVC_RESP_MSG_ENDPOINTID, resp_msg->flags_len));
conn_resp->connect_resp_code = FIELD_GET(HTC_SVC_RESP_MSG_STATUS,
resp_msg->flags_len);
/* check response status */
if (conn_resp->connect_resp_code != ATH11K_HTC_CONN_SVC_STATUS_SUCCESS) {
ath11k_err(ab, "HTC Service %s connect request failed: 0x%x)\n",
htc_service_name(service_id),
conn_resp->connect_resp_code);
return -EPROTO;
}
assigned_eid = (enum ath11k_htc_ep_id)FIELD_GET(
HTC_SVC_RESP_MSG_ENDPOINTID,
resp_msg->flags_len);
max_msg_size = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
resp_msg->flags_len);
setup:
if (assigned_eid >= ATH11K_HTC_EP_COUNT)
return -EPROTO;
if (max_msg_size == 0)
return -EPROTO;
ep = &htc->endpoint[assigned_eid];
ep->eid = assigned_eid;
if (ep->service_id != ATH11K_HTC_SVC_ID_UNUSED)
return -EPROTO;
/* return assigned endpoint to caller */
conn_resp->eid = assigned_eid;
conn_resp->max_msg_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
resp_msg->flags_len);
/* setup the endpoint */
ep->service_id = conn_req->service_id;
ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
ep->max_ep_message_len = FIELD_GET(HTC_SVC_RESP_MSG_MAXMSGSIZE,
resp_msg->flags_len);
ep->tx_credits = tx_alloc;
/* copy all the callbacks */
ep->ep_ops = conn_req->ep_ops;
status = ath11k_hif_map_service_to_pipe(htc->ab,
ep->service_id,
&ep->ul_pipe_id,
&ep->dl_pipe_id);
if (status)
return status;
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"htc service '%s' ul pipe %d dl pipe %d eid %d ready\n",
htc_service_name(ep->service_id), ep->ul_pipe_id,
ep->dl_pipe_id, ep->eid);
if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) {
ep->tx_credit_flow_enabled = false;
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"htc service '%s' eid %d tx flow control disabled\n",
htc_service_name(ep->service_id), assigned_eid);
}
return status;
}
int ath11k_htc_start(struct ath11k_htc *htc)
{
struct sk_buff *skb;
int status = 0;
struct ath11k_base *ab = htc->ab;
struct ath11k_htc_setup_complete_extended *msg;
skb = ath11k_htc_build_tx_ctrl_skb(htc->ab);
if (!skb)
return -ENOMEM;
skb_put(skb, sizeof(*msg));
memset(skb->data, 0, skb->len);
msg = (struct ath11k_htc_setup_complete_extended *)skb->data;
msg->msg_id = FIELD_PREP(HTC_MSG_MESSAGEID,
ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID);
if (ab->hw_params.credit_flow)
ath11k_dbg(ab, ATH11K_DBG_HTC, "using tx credit flow control\n");
else
msg->flags |= ATH11K_GLOBAL_DISABLE_CREDIT_FLOW;
status = ath11k_htc_send(htc, ATH11K_HTC_EP_0, skb);
if (status) {
kfree_skb(skb);
return status;
}
return 0;
}
int ath11k_htc_init(struct ath11k_base *ab)
{
struct ath11k_htc *htc = &ab->htc;
struct ath11k_htc_svc_conn_req conn_req;
struct ath11k_htc_svc_conn_resp conn_resp;
int ret;
spin_lock_init(&htc->tx_lock);
ath11k_htc_reset_endpoint_states(htc);
htc->ab = ab;
switch (ab->wmi_ab.preferred_hw_mode) {
case WMI_HOST_HW_MODE_SINGLE:
htc->wmi_ep_count = 1;
break;
case WMI_HOST_HW_MODE_DBS:
case WMI_HOST_HW_MODE_DBS_OR_SBS:
htc->wmi_ep_count = 2;
break;
case WMI_HOST_HW_MODE_DBS_SBS:
htc->wmi_ep_count = 3;
break;
default:
htc->wmi_ep_count = ab->hw_params.max_radios;
break;
}
/* setup our pseudo HTC control endpoint connection */
memset(&conn_req, 0, sizeof(conn_req));
memset(&conn_resp, 0, sizeof(conn_resp));
conn_req.ep_ops.ep_tx_complete = ath11k_htc_control_tx_complete;
conn_req.ep_ops.ep_rx_complete = ath11k_htc_control_rx_complete;
conn_req.max_send_queue_depth = ATH11K_NUM_CONTROL_TX_BUFFERS;
conn_req.service_id = ATH11K_HTC_SVC_ID_RSVD_CTRL;
/* connect fake service */
ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp);
if (ret) {
ath11k_err(ab, "could not connect to htc service (%d)\n", ret);
return ret;
}
init_completion(&htc->ctl_resp);
return 0;
}
@@ -0,0 +1,298 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_HTC_H
#define ATH11K_HTC_H
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/bug.h>
#include <linux/skbuff.h>
#include <linux/timer.h>
struct ath11k_base;
#define HTC_HDR_ENDPOINTID GENMASK(7, 0)
#define HTC_HDR_FLAGS GENMASK(15, 8)
#define HTC_HDR_PAYLOADLEN GENMASK(31, 16)
#define HTC_HDR_CONTROLBYTES0 GENMASK(7, 0)
#define HTC_HDR_CONTROLBYTES1 GENMASK(15, 8)
#define HTC_HDR_RESERVED GENMASK(31, 16)
#define HTC_SVC_MSG_SERVICE_ID GENMASK(31, 16)
#define HTC_SVC_MSG_CONNECTIONFLAGS GENMASK(15, 0)
#define HTC_SVC_MSG_SERVICEMETALENGTH GENMASK(23, 16)
#define HTC_READY_MSG_CREDITCOUNT GENMASK(31, 16)
#define HTC_READY_MSG_CREDITSIZE GENMASK(15, 0)
#define HTC_READY_MSG_MAXENDPOINTS GENMASK(23, 16)
#define HTC_READY_EX_MSG_HTCVERSION GENMASK(7, 0)
#define HTC_READY_EX_MSG_MAXMSGSPERHTCBUNDLE GENMASK(15, 8)
#define HTC_SVC_RESP_MSG_SERVICEID GENMASK(31, 16)
#define HTC_SVC_RESP_MSG_STATUS GENMASK(7, 0)
#define HTC_SVC_RESP_MSG_ENDPOINTID GENMASK(15, 8)
#define HTC_SVC_RESP_MSG_MAXMSGSIZE GENMASK(31, 16)
#define HTC_SVC_RESP_MSG_SERVICEMETALENGTH GENMASK(7, 0)
#define HTC_MSG_MESSAGEID GENMASK(15, 0)
#define HTC_SETUP_COMPLETE_EX_MSG_SETUPFLAGS GENMASK(31, 0)
#define HTC_SETUP_COMPLETE_EX_MSG_MAXMSGSPERBUNDLEDRECV GENMASK(7, 0)
#define HTC_SETUP_COMPLETE_EX_MSG_RSVD0 GENMASK(15, 8)
#define HTC_SETUP_COMPLETE_EX_MSG_RSVD1 GENMASK(23, 16)
#define HTC_SETUP_COMPLETE_EX_MSG_RSVD2 GENMASK(31, 24)
enum ath11k_htc_tx_flags {
ATH11K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
ATH11K_HTC_FLAG_SEND_BUNDLE = 0x02
};
enum ath11k_htc_rx_flags {
ATH11K_HTC_FLAG_TRAILER_PRESENT = 0x02,
ATH11K_HTC_FLAG_BUNDLE_MASK = 0xF0
};
struct ath11k_htc_hdr {
u32 htc_info;
u32 ctrl_info;
} __packed __aligned(4);
enum ath11k_htc_msg_id {
ATH11K_HTC_MSG_READY_ID = 1,
ATH11K_HTC_MSG_CONNECT_SERVICE_ID = 2,
ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
ATH11K_HTC_MSG_SETUP_COMPLETE_ID = 4,
ATH11K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5,
ATH11K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6,
ATH11K_HTC_MSG_NACK_SUSPEND = 7,
ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID = 8,
};
enum ath11k_htc_version {
ATH11K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
ATH11K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
};
#define ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK GENMASK(1, 0)
#define ATH11K_HTC_CONN_FLAGS_RECV_ALLOC GENMASK(15, 8)
enum ath11k_htc_conn_flags {
ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0,
ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1,
ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2,
ATH11K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3,
ATH11K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 0x4,
ATH11K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 0x8,
};
enum ath11k_htc_conn_svc_status {
ATH11K_HTC_CONN_SVC_STATUS_SUCCESS = 0,
ATH11K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1,
ATH11K_HTC_CONN_SVC_STATUS_FAILED = 2,
ATH11K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
ATH11K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4
};
struct ath11k_htc_ready {
u32 id_credit_count;
u32 size_ep;
} __packed;
struct ath11k_htc_ready_extended {
struct ath11k_htc_ready base;
u32 ver_bundle;
} __packed;
struct ath11k_htc_conn_svc {
u32 msg_svc_id;
u32 flags_len;
} __packed;
struct ath11k_htc_conn_svc_resp {
u32 msg_svc_id;
u32 flags_len;
u32 svc_meta_pad;
} __packed;
#define ATH11K_GLOBAL_DISABLE_CREDIT_FLOW BIT(1)
struct ath11k_htc_setup_complete_extended {
u32 msg_id;
u32 flags;
u32 max_msgs_per_bundled_recv;
} __packed;
struct ath11k_htc_msg {
u32 msg_svc_id;
u32 flags_len;
} __packed __aligned(4);
enum ath11k_htc_record_id {
ATH11K_HTC_RECORD_NULL = 0,
ATH11K_HTC_RECORD_CREDITS = 1
};
struct ath11k_htc_record_hdr {
u8 id; /* @enum ath11k_htc_record_id */
u8 len;
u8 pad0;
u8 pad1;
} __packed;
struct ath11k_htc_credit_report {
u8 eid; /* @enum ath11k_htc_ep_id */
u8 credits;
u8 pad0;
u8 pad1;
} __packed;
struct ath11k_htc_record {
struct ath11k_htc_record_hdr hdr;
struct ath11k_htc_credit_report credit_report[];
} __packed __aligned(4);
enum ath11k_htc_svc_gid {
ATH11K_HTC_SVC_GRP_RSVD = 0,
ATH11K_HTC_SVC_GRP_WMI = 1,
ATH11K_HTC_SVC_GRP_NMI = 2,
ATH11K_HTC_SVC_GRP_HTT = 3,
ATH11K_HTC_SVC_GRP_CFG = 4,
ATH11K_HTC_SVC_GRP_IPA = 5,
ATH11K_HTC_SVC_GRP_PKTLOG = 6,
ATH11K_HTC_SVC_GRP_TEST = 254,
ATH11K_HTC_SVC_GRP_LAST = 255,
};
#define SVC(group, idx) \
(int)(((int)(group) << 8) | (int)(idx))
enum ath11k_htc_svc_id {
/* NOTE: service ID of 0x0000 is reserved and should never be used */
ATH11K_HTC_SVC_ID_RESERVED = 0x0000,
ATH11K_HTC_SVC_ID_UNUSED = ATH11K_HTC_SVC_ID_RESERVED,
ATH11K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH11K_HTC_SVC_GRP_RSVD, 1),
ATH11K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH11K_HTC_SVC_GRP_WMI, 0),
ATH11K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH11K_HTC_SVC_GRP_WMI, 1),
ATH11K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH11K_HTC_SVC_GRP_WMI, 2),
ATH11K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH11K_HTC_SVC_GRP_WMI, 3),
ATH11K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH11K_HTC_SVC_GRP_WMI, 4),
ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1 = SVC(ATH11K_HTC_SVC_GRP_WMI, 5),
ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2 = SVC(ATH11K_HTC_SVC_GRP_WMI, 6),
ATH11K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH11K_HTC_SVC_GRP_NMI, 0),
ATH11K_HTC_SVC_ID_NMI_DATA = SVC(ATH11K_HTC_SVC_GRP_NMI, 1),
ATH11K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH11K_HTC_SVC_GRP_HTT, 0),
/* raw stream service (i.e. flash, tcmd, calibration apps) */
ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH11K_HTC_SVC_GRP_TEST, 0),
ATH11K_HTC_SVC_ID_IPA_TX = SVC(ATH11K_HTC_SVC_GRP_IPA, 0),
ATH11K_HTC_SVC_ID_PKT_LOG = SVC(ATH11K_HTC_SVC_GRP_PKTLOG, 0),
};
#undef SVC
enum ath11k_htc_ep_id {
ATH11K_HTC_EP_UNUSED = -1,
ATH11K_HTC_EP_0 = 0,
ATH11K_HTC_EP_1 = 1,
ATH11K_HTC_EP_2,
ATH11K_HTC_EP_3,
ATH11K_HTC_EP_4,
ATH11K_HTC_EP_5,
ATH11K_HTC_EP_6,
ATH11K_HTC_EP_7,
ATH11K_HTC_EP_8,
ATH11K_HTC_EP_COUNT,
};
struct ath11k_htc_ep_ops {
void (*ep_tx_complete)(struct ath11k_base *, struct sk_buff *);
void (*ep_rx_complete)(struct ath11k_base *, struct sk_buff *);
void (*ep_tx_credits)(struct ath11k_base *);
};
/* service connection information */
struct ath11k_htc_svc_conn_req {
u16 service_id;
struct ath11k_htc_ep_ops ep_ops;
int max_send_queue_depth;
};
/* service connection response information */
struct ath11k_htc_svc_conn_resp {
u8 buffer_len;
u8 actual_len;
enum ath11k_htc_ep_id eid;
unsigned int max_msg_len;
u8 connect_resp_code;
};
#define ATH11K_NUM_CONTROL_TX_BUFFERS 2
#define ATH11K_HTC_MAX_LEN 4096
#define ATH11K_HTC_MAX_CTRL_MSG_LEN 256
#define ATH11K_HTC_WAIT_TIMEOUT_HZ (1 * HZ)
#define ATH11K_HTC_CONTROL_BUFFER_SIZE (ATH11K_HTC_MAX_CTRL_MSG_LEN + \
sizeof(struct ath11k_htc_hdr))
#define ATH11K_HTC_CONN_SVC_TIMEOUT_HZ (1 * HZ)
#define ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES 8
struct ath11k_htc_ep {
struct ath11k_htc *htc;
enum ath11k_htc_ep_id eid;
enum ath11k_htc_svc_id service_id;
struct ath11k_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;
bool tx_credit_flow_enabled;
};
struct ath11k_htc_svc_tx_credits {
u16 service_id;
u8 credit_allocation;
};
struct ath11k_htc {
struct ath11k_base *ab;
struct ath11k_htc_ep endpoint[ATH11K_HTC_EP_COUNT];
/* protects endpoints */
spinlock_t tx_lock;
u8 control_resp_buffer[ATH11K_HTC_MAX_CTRL_MSG_LEN];
int control_resp_len;
struct completion ctl_resp;
int total_transmit_credits;
struct ath11k_htc_svc_tx_credits
service_alloc_table[ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES];
int target_credit_size;
u8 wmi_ep_count;
};
int ath11k_htc_init(struct ath11k_base *ar);
int ath11k_htc_wait_target(struct ath11k_htc *htc);
int ath11k_htc_start(struct ath11k_htc *htc);
int ath11k_htc_connect_service(struct ath11k_htc *htc,
struct ath11k_htc_svc_conn_req *conn_req,
struct ath11k_htc_svc_conn_resp *conn_resp);
int ath11k_htc_send(struct ath11k_htc *htc, enum ath11k_htc_ep_id eid,
struct sk_buff *packet);
struct sk_buff *ath11k_htc_alloc_skb(struct ath11k_base *ar, int size);
void ath11k_htc_rx_completion_handler(struct ath11k_base *ar,
struct sk_buff *skb);
void ath11k_htc_tx_completion_handler(struct ath11k_base *ab,
struct sk_buff *skb);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,443 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_HW_H
#define ATH11K_HW_H
#include "hal.h"
#include "wmi.h"
/* Target configuration defines */
/* Num VDEVS per radio */
#define TARGET_NUM_VDEVS(ab) (ab->hw_params.num_vdevs)
#define TARGET_NUM_PEERS_PDEV(ab) (ab->hw_params.num_peers + TARGET_NUM_VDEVS(ab))
/* Num of peers for Single Radio mode */
#define TARGET_NUM_PEERS_SINGLE(ab) (TARGET_NUM_PEERS_PDEV(ab))
/* Num of peers for DBS */
#define TARGET_NUM_PEERS_DBS(ab) (2 * TARGET_NUM_PEERS_PDEV(ab))
/* Num of peers for DBS_SBS */
#define TARGET_NUM_PEERS_DBS_SBS(ab) (3 * TARGET_NUM_PEERS_PDEV(ab))
/* Max num of stations (per radio) */
#define TARGET_NUM_STATIONS(ab) (ab->hw_params.num_peers)
#define TARGET_NUM_PEERS(ab, x) TARGET_NUM_PEERS_##x(ab)
#define TARGET_NUM_PEER_KEYS 2
#define TARGET_NUM_TIDS(ab, x) (2 * TARGET_NUM_PEERS(ab, x) + \
4 * TARGET_NUM_VDEVS(ab) + 8)
#define TARGET_AST_SKID_LIMIT 16
#define TARGET_NUM_OFFLD_PEERS 4
#define TARGET_NUM_OFFLD_REORDER_BUFFS 4
#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4))
#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(4))
#define TARGET_RX_TIMEOUT_LO_PRI 100
#define TARGET_RX_TIMEOUT_HI_PRI 40
#define TARGET_DECAP_MODE_RAW 0
#define TARGET_DECAP_MODE_NATIVE_WIFI 1
#define TARGET_DECAP_MODE_ETH 2
#define TARGET_SCAN_MAX_PENDING_REQS 4
#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3
#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3
#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES 8
#define TARGET_GTK_OFFLOAD_MAX_VDEV 3
#define TARGET_NUM_MCAST_GROUPS 12
#define TARGET_NUM_MCAST_TABLE_ELEMS 64
#define TARGET_MCAST2UCAST_MODE 2
#define TARGET_TX_DBG_LOG_SIZE 1024
#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1
#define TARGET_VOW_CONFIG 0
#define TARGET_NUM_MSDU_DESC (2500)
#define TARGET_MAX_FRAG_ENTRIES 6
#define TARGET_MAX_BCN_OFFLD 16
#define TARGET_NUM_WDS_ENTRIES 32
#define TARGET_DMA_BURST_SIZE 1
#define TARGET_RX_BATCHMODE 1
#define TARGET_EMA_MAX_PROFILE_PERIOD 8
#define ATH11K_HW_MAX_QUEUES 4
#define ATH11K_QUEUE_LEN 4096
#define ATH11k_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4
#define ATH11K_FW_DIR "ath11k"
#define ATH11K_BOARD_MAGIC "QCA-ATH11K-BOARD"
#define ATH11K_BOARD_API2_FILE "board-2.bin"
#define ATH11K_DEFAULT_BOARD_FILE "board.bin"
#define ATH11K_DEFAULT_CAL_FILE "caldata.bin"
#define ATH11K_AMSS_FILE "amss.bin"
#define ATH11K_M3_FILE "m3.bin"
#define ATH11K_REGDB_FILE_NAME "regdb.bin"
#define ATH11K_CE_OFFSET(ab) (ab->mem_ce - ab->mem)
enum ath11k_hw_rate_cck {
ATH11K_HW_RATE_CCK_LP_11M = 0,
ATH11K_HW_RATE_CCK_LP_5_5M,
ATH11K_HW_RATE_CCK_LP_2M,
ATH11K_HW_RATE_CCK_LP_1M,
ATH11K_HW_RATE_CCK_SP_11M,
ATH11K_HW_RATE_CCK_SP_5_5M,
ATH11K_HW_RATE_CCK_SP_2M,
};
enum ath11k_hw_rate_ofdm {
ATH11K_HW_RATE_OFDM_48M = 0,
ATH11K_HW_RATE_OFDM_24M,
ATH11K_HW_RATE_OFDM_12M,
ATH11K_HW_RATE_OFDM_6M,
ATH11K_HW_RATE_OFDM_54M,
ATH11K_HW_RATE_OFDM_36M,
ATH11K_HW_RATE_OFDM_18M,
ATH11K_HW_RATE_OFDM_9M,
};
enum ath11k_bus {
ATH11K_BUS_AHB,
ATH11K_BUS_PCI,
};
#define ATH11K_EXT_IRQ_GRP_NUM_MAX 11
struct hal_rx_desc;
struct hal_tcl_data_cmd;
struct ath11k_hw_ring_mask {
u8 tx[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_mon_status[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 rx[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_err[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_wbm_rel[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 reo_status[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 rxdma2host[ATH11K_EXT_IRQ_GRP_NUM_MAX];
u8 host2rxdma[ATH11K_EXT_IRQ_GRP_NUM_MAX];
};
struct ath11k_hw_tcl2wbm_rbm_map {
u8 tcl_ring_num;
u8 wbm_ring_num;
u8 rbm_id;
};
struct ath11k_hw_hal_params {
enum hal_rx_buf_return_buf_manager rx_buf_rbm;
const struct ath11k_hw_tcl2wbm_rbm_map *tcl2wbm_rbm_map;
size_t num_tx_rings;
};
struct ath11k_hw_params {
const char *name;
u16 hw_rev;
u8 max_radios;
u32 bdf_addr;
struct {
const char *dir;
size_t board_size;
size_t cal_offset;
} fw;
const struct ath11k_hw_ops *hw_ops;
const struct ath11k_hw_ring_mask *ring_mask;
bool internal_sleep_clock;
const struct ath11k_hw_regs *regs;
u32 qmi_service_ins_id;
const struct ce_attr *host_ce_config;
u32 ce_count;
const struct ce_pipe_config *target_ce_config;
u32 target_ce_count;
const struct service_to_pipe *svc_to_ce_map;
u32 svc_to_ce_map_len;
const struct ce_ie_addr *ce_ie_addr;
const struct ce_remap *ce_remap;
bool single_pdev_only;
bool rxdma1_enable;
int num_rxdma_per_pdev;
bool rx_mac_buf_ring;
bool vdev_start_delay;
bool htt_peer_map_v2;
struct {
u8 fft_sz;
u8 fft_pad_sz;
u8 summary_pad_sz;
u8 fft_hdr_len;
u16 max_fft_bins;
bool fragment_160mhz;
} spectral;
u16 interface_modes;
bool supports_monitor;
bool full_monitor_mode;
bool supports_shadow_regs;
bool idle_ps;
bool supports_sta_ps;
bool coldboot_cal_mm;
bool coldboot_cal_ftm;
bool cbcal_restart_fw;
int fw_mem_mode;
u32 num_vdevs;
u32 num_peers;
bool supports_suspend;
u32 hal_desc_sz;
bool supports_regdb;
bool fix_l1ss;
bool credit_flow;
const struct ath11k_hw_hal_params *hal_params;
bool supports_dynamic_smps_6ghz;
bool alloc_cacheable_memory;
bool supports_rssi_stats;
bool fw_wmi_diag_event;
bool current_cc_support;
bool dbr_debug_support;
bool global_reset;
const struct cfg80211_sar_capa *bios_sar_capa;
bool m3_fw_support;
bool fixed_bdf_addr;
bool fixed_mem_region;
bool static_window_map;
bool hybrid_bus_type;
bool fixed_fw_mem;
bool support_off_channel_tx;
bool supports_multi_bssid;
struct {
u32 start;
u32 end;
} sram_dump;
bool tcl_ring_retry;
u32 tx_ring_size;
bool smp2p_wow_exit;
bool support_fw_mac_sequence;
bool support_dual_stations;
bool pdev_suspend;
bool cfr_support;
u32 cfr_num_stream_bufs;
u32 cfr_stream_buf_size;
};
struct ath11k_hw_ops {
u8 (*get_hw_mac_from_pdev_id)(int pdev_id);
void (*wmi_init_config)(struct ath11k_base *ab,
struct target_resource_config *config);
int (*mac_id_to_pdev_id)(struct ath11k_hw_params *hw, int mac_id);
int (*mac_id_to_srng_id)(struct ath11k_hw_params *hw, int mac_id);
void (*tx_mesh_enable)(struct ath11k_base *ab,
struct hal_tcl_data_cmd *tcl_cmd);
bool (*rx_desc_get_first_msdu)(struct hal_rx_desc *desc);
bool (*rx_desc_get_last_msdu)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_l3_pad_bytes)(struct hal_rx_desc *desc);
u8 *(*rx_desc_get_hdr_status)(struct hal_rx_desc *desc);
bool (*rx_desc_encrypt_valid)(struct hal_rx_desc *desc);
u32 (*rx_desc_get_encrypt_type)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_decap_type)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_mesh_ctl)(struct hal_rx_desc *desc);
bool (*rx_desc_get_ldpc_support)(struct hal_rx_desc *desc);
bool (*rx_desc_get_mpdu_seq_ctl_vld)(struct hal_rx_desc *desc);
bool (*rx_desc_get_mpdu_fc_valid)(struct hal_rx_desc *desc);
u16 (*rx_desc_get_mpdu_start_seq_no)(struct hal_rx_desc *desc);
u16 (*rx_desc_get_msdu_len)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_msdu_sgi)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_msdu_rate_mcs)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_msdu_rx_bw)(struct hal_rx_desc *desc);
u32 (*rx_desc_get_msdu_freq)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_msdu_pkt_type)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_msdu_nss)(struct hal_rx_desc *desc);
u8 (*rx_desc_get_mpdu_tid)(struct hal_rx_desc *desc);
u16 (*rx_desc_get_mpdu_peer_id)(struct hal_rx_desc *desc);
void (*rx_desc_copy_attn_end_tlv)(struct hal_rx_desc *fdesc,
struct hal_rx_desc *ldesc);
u32 (*rx_desc_get_mpdu_start_tag)(struct hal_rx_desc *desc);
u32 (*rx_desc_get_mpdu_ppdu_id)(struct hal_rx_desc *desc);
void (*rx_desc_set_msdu_len)(struct hal_rx_desc *desc, u16 len);
struct rx_attention *(*rx_desc_get_attention)(struct hal_rx_desc *desc);
u8 *(*rx_desc_get_msdu_payload)(struct hal_rx_desc *desc);
void (*reo_setup)(struct ath11k_base *ab);
u16 (*mpdu_info_get_peerid)(struct hal_rx_mpdu_info *mpdu_info);
bool (*rx_desc_mac_addr2_valid)(struct hal_rx_desc *desc);
u8* (*rx_desc_mpdu_start_addr2)(struct hal_rx_desc *desc);
u32 (*get_ring_selector)(struct sk_buff *skb);
};
extern const struct ath11k_hw_ops ipq8074_ops;
extern const struct ath11k_hw_ops ipq6018_ops;
extern const struct ath11k_hw_ops qca6390_ops;
extern const struct ath11k_hw_ops qcn9074_ops;
extern const struct ath11k_hw_ops wcn6855_ops;
extern const struct ath11k_hw_ops wcn6750_ops;
extern const struct ath11k_hw_ops ipq5018_ops;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074;
extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750;
extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074;
extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018;
extern const struct ce_remap ath11k_ce_remap_ipq5018;
extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq5018;
extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074;
extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_qca6390;
extern const struct ath11k_hw_hal_params ath11k_hw_hal_params_wcn6750;
static inline
int ath11k_hw_get_mac_from_pdev_id(struct ath11k_hw_params *hw,
int pdev_idx)
{
if (hw->hw_ops->get_hw_mac_from_pdev_id)
return hw->hw_ops->get_hw_mac_from_pdev_id(pdev_idx);
return 0;
}
static inline int ath11k_hw_mac_id_to_pdev_id(struct ath11k_hw_params *hw,
int mac_id)
{
if (hw->hw_ops->mac_id_to_pdev_id)
return hw->hw_ops->mac_id_to_pdev_id(hw, mac_id);
return 0;
}
static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw,
int mac_id)
{
if (hw->hw_ops->mac_id_to_srng_id)
return hw->hw_ops->mac_id_to_srng_id(hw, mac_id);
return 0;
}
struct ath11k_fw_ie {
__le32 id;
__le32 len;
u8 data[];
};
enum ath11k_bd_ie_board_type {
ATH11K_BD_IE_BOARD_NAME = 0,
ATH11K_BD_IE_BOARD_DATA = 1,
};
enum ath11k_bd_ie_regdb_type {
ATH11K_BD_IE_REGDB_NAME = 0,
ATH11K_BD_IE_REGDB_DATA = 1,
};
enum ath11k_bd_ie_type {
/* contains sub IEs of enum ath11k_bd_ie_board_type */
ATH11K_BD_IE_BOARD = 0,
/* contains sub IEs of enum ath11k_bd_ie_regdb_type */
ATH11K_BD_IE_REGDB = 1,
};
struct ath11k_hw_regs {
u32 hal_tcl1_ring_base_lsb;
u32 hal_tcl1_ring_base_msb;
u32 hal_tcl1_ring_id;
u32 hal_tcl1_ring_misc;
u32 hal_tcl1_ring_tp_addr_lsb;
u32 hal_tcl1_ring_tp_addr_msb;
u32 hal_tcl1_ring_consumer_int_setup_ix0;
u32 hal_tcl1_ring_consumer_int_setup_ix1;
u32 hal_tcl1_ring_msi1_base_lsb;
u32 hal_tcl1_ring_msi1_base_msb;
u32 hal_tcl1_ring_msi1_data;
u32 hal_tcl2_ring_base_lsb;
u32 hal_tcl_ring_base_lsb;
u32 hal_tcl_status_ring_base_lsb;
u32 hal_reo1_ring_base_lsb;
u32 hal_reo1_ring_base_msb;
u32 hal_reo1_ring_id;
u32 hal_reo1_ring_misc;
u32 hal_reo1_ring_hp_addr_lsb;
u32 hal_reo1_ring_hp_addr_msb;
u32 hal_reo1_ring_producer_int_setup;
u32 hal_reo1_ring_msi1_base_lsb;
u32 hal_reo1_ring_msi1_base_msb;
u32 hal_reo1_ring_msi1_data;
u32 hal_reo2_ring_base_lsb;
u32 hal_reo1_aging_thresh_ix_0;
u32 hal_reo1_aging_thresh_ix_1;
u32 hal_reo1_aging_thresh_ix_2;
u32 hal_reo1_aging_thresh_ix_3;
u32 hal_reo1_ring_hp;
u32 hal_reo1_ring_tp;
u32 hal_reo2_ring_hp;
u32 hal_reo_tcl_ring_base_lsb;
u32 hal_reo_tcl_ring_hp;
u32 hal_reo_status_ring_base_lsb;
u32 hal_reo_status_hp;
u32 hal_reo_cmd_ring_base_lsb;
u32 hal_reo_cmd_ring_hp;
u32 hal_sw2reo_ring_base_lsb;
u32 hal_sw2reo_ring_hp;
u32 hal_seq_wcss_umac_ce0_src_reg;
u32 hal_seq_wcss_umac_ce0_dst_reg;
u32 hal_seq_wcss_umac_ce1_src_reg;
u32 hal_seq_wcss_umac_ce1_dst_reg;
u32 hal_wbm_idle_link_ring_base_lsb;
u32 hal_wbm_idle_link_ring_misc;
u32 hal_wbm_release_ring_base_lsb;
u32 hal_wbm0_release_ring_base_lsb;
u32 hal_wbm1_release_ring_base_lsb;
u32 pcie_qserdes_sysclk_en_sel;
u32 pcie_pcs_osc_dtct_config_base;
u32 hal_shadow_base_addr;
u32 hal_reo1_misc_ctl;
};
extern const struct ath11k_hw_regs ipq8074_regs;
extern const struct ath11k_hw_regs qca6390_regs;
extern const struct ath11k_hw_regs qcn9074_regs;
extern const struct ath11k_hw_regs wcn6855_regs;
extern const struct ath11k_hw_regs wcn6750_regs;
extern const struct ath11k_hw_regs ipq5018_regs;
static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)
{
switch (type) {
case ATH11K_BD_IE_BOARD:
return "board data";
case ATH11K_BD_IE_REGDB:
return "regdb data";
}
return "unknown";
}
extern const struct cfg80211_sar_capa ath11k_hw_sar_capa_wcn6855;
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,184 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_MAC_H
#define ATH11K_MAC_H
#include <net/mac80211.h>
#include <net/cfg80211.h>
#include "wmi.h"
struct ath11k;
struct ath11k_base;
struct ath11k_generic_iter {
struct ath11k *ar;
int ret;
};
/* number of failed packets (20 packets with 16 sw reties each) */
#define ATH11K_KICKOUT_THRESHOLD (20 * 16)
/* Use insanely high numbers to make sure that the firmware implementation
* won't start, we have the same functionality already in hostapd. Unit
* is seconds.
*/
#define ATH11K_KEEPALIVE_MIN_IDLE 3747
#define ATH11K_KEEPALIVE_MAX_IDLE 3895
#define ATH11K_KEEPALIVE_MAX_UNRESPONSIVE 3900
#define WMI_HOST_RC_DS_FLAG 0x01
#define WMI_HOST_RC_CW40_FLAG 0x02
#define WMI_HOST_RC_SGI_FLAG 0x04
#define WMI_HOST_RC_HT_FLAG 0x08
#define WMI_HOST_RC_RTSCTS_FLAG 0x10
#define WMI_HOST_RC_TX_STBC_FLAG 0x20
#define WMI_HOST_RC_RX_STBC_FLAG 0xC0
#define WMI_HOST_RC_RX_STBC_FLAG_S 6
#define WMI_HOST_RC_WEP_TKIP_FLAG 0x100
#define WMI_HOST_RC_TS_FLAG 0x200
#define WMI_HOST_RC_UAPSD_FLAG 0x400
#define WMI_HT_CAP_ENABLED 0x0001
#define WMI_HT_CAP_HT20_SGI 0x0002
#define WMI_HT_CAP_DYNAMIC_SMPS 0x0004
#define WMI_HT_CAP_TX_STBC 0x0008
#define WMI_HT_CAP_TX_STBC_MASK_SHIFT 3
#define WMI_HT_CAP_RX_STBC 0x0030
#define WMI_HT_CAP_RX_STBC_MASK_SHIFT 4
#define WMI_HT_CAP_LDPC 0x0040
#define WMI_HT_CAP_L_SIG_TXOP_PROT 0x0080
#define WMI_HT_CAP_MPDU_DENSITY 0x0700
#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8
#define WMI_HT_CAP_HT40_SGI 0x0800
#define WMI_HT_CAP_RX_LDPC 0x1000
#define WMI_HT_CAP_TX_LDPC 0x2000
#define WMI_HT_CAP_IBF_BFER 0x4000
/* These macros should be used when we wish to advertise STBC support for
* only 1SS or 2SS or 3SS.
*/
#define WMI_HT_CAP_RX_STBC_1SS 0x0010
#define WMI_HT_CAP_RX_STBC_2SS 0x0020
#define WMI_HT_CAP_RX_STBC_3SS 0x0030
#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED | \
WMI_HT_CAP_HT20_SGI | \
WMI_HT_CAP_HT40_SGI | \
WMI_HT_CAP_TX_STBC | \
WMI_HT_CAP_RX_STBC | \
WMI_HT_CAP_LDPC)
#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK 0x00000003
#define WMI_VHT_CAP_RX_LDPC 0x00000010
#define WMI_VHT_CAP_SGI_80MHZ 0x00000020
#define WMI_VHT_CAP_SGI_160MHZ 0x00000040
#define WMI_VHT_CAP_TX_STBC 0x00000080
#define WMI_VHT_CAP_RX_STBC_MASK 0x00000300
#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT 8
#define WMI_VHT_CAP_SU_BFER 0x00000800
#define WMI_VHT_CAP_SU_BFEE 0x00001000
#define WMI_VHT_CAP_MAX_CS_ANT_MASK 0x0000E000
#define WMI_VHT_CAP_MAX_CS_ANT_MASK_SHIFT 13
#define WMI_VHT_CAP_MAX_SND_DIM_MASK 0x00070000
#define WMI_VHT_CAP_MAX_SND_DIM_MASK_SHIFT 16
#define WMI_VHT_CAP_MU_BFER 0x00080000
#define WMI_VHT_CAP_MU_BFEE 0x00100000
#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP 0x03800000
#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIT 23
#define WMI_VHT_CAP_RX_FIXED_ANT 0x10000000
#define WMI_VHT_CAP_TX_FIXED_ANT 0x20000000
#define WMI_VHT_CAP_MAX_MPDU_LEN_11454 0x00000002
/* These macros should be used when we wish to advertise STBC support for
* only 1SS or 2SS or 3SS.
*/
#define WMI_VHT_CAP_RX_STBC_1SS 0x00000100
#define WMI_VHT_CAP_RX_STBC_2SS 0x00000200
#define WMI_VHT_CAP_RX_STBC_3SS 0x00000300
#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454 | \
WMI_VHT_CAP_SGI_80MHZ | \
WMI_VHT_CAP_TX_STBC | \
WMI_VHT_CAP_RX_STBC_MASK | \
WMI_VHT_CAP_RX_LDPC | \
WMI_VHT_CAP_MAX_AMPDU_LEN_EXP | \
WMI_VHT_CAP_RX_FIXED_ANT | \
WMI_VHT_CAP_TX_FIXED_ANT)
/* FIXME: should these be in ieee80211.h? */
#define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16)
#define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24)
#define WMI_MAX_SPATIAL_STREAM 3
#define ATH11K_CHAN_WIDTH_NUM 8
#define ATH11K_BW_NSS_MAP_ENABLE BIT(31)
#define ATH11K_PEER_RX_NSS_160MHZ GENMASK(2, 0)
#define ATH11K_PEER_RX_NSS_80_80MHZ GENMASK(5, 3)
#define ATH11K_OBSS_PD_MAX_THRESHOLD -82
#define ATH11K_OBSS_PD_NON_SRG_MAX_THRESHOLD -62
#define ATH11K_OBSS_PD_THRESHOLD_IN_DBM BIT(29)
#define ATH11K_OBSS_PD_SRG_EN BIT(30)
#define ATH11K_OBSS_PD_NON_SRG_EN BIT(31)
extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default;
#define ATH11K_SCAN_11D_INTERVAL 600000
#define ATH11K_11D_INVALID_VDEV_ID 0xFFFF
void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id);
void ath11k_mac_11d_scan_stop(struct ath11k *ar);
void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab);
void ath11k_mac_destroy(struct ath11k_base *ab);
void ath11k_mac_unregister(struct ath11k_base *ab);
int ath11k_mac_register(struct ath11k_base *ab);
int ath11k_mac_allocate(struct ath11k_base *ab);
int ath11k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx,
u16 *rate);
u8 ath11k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
u32 bitrate);
u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
u8 hw_rate, bool cck);
void __ath11k_mac_scan_finish(struct ath11k *ar);
void ath11k_mac_scan_finish(struct ath11k *ar);
struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id);
struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab,
u32 vdev_id);
u8 ath11k_mac_get_target_pdev_id(struct ath11k *ar);
u8 ath11k_mac_get_target_pdev_id_from_vif(struct ath11k_vif *arvif);
struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab);
struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id);
struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id);
void ath11k_mac_drain_tx(struct ath11k *ar);
void ath11k_mac_peer_cleanup_all(struct ath11k *ar);
int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
u8 ath11k_mac_bw_to_mac80211_bw(u8 bw);
enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi);
enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy);
enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones);
enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw);
enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher);
void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb);
void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id);
void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif);
int ath11k_mac_wait_tx_complete(struct ath11k *ar);
int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
enum wmi_sta_keepalive_method method,
u32 interval);
void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *ctx);
int ath11k_mac_fw_stats_request(struct ath11k *ar,
struct stats_request_params *req_param);
#endif
@@ -0,0 +1,504 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/msi.h>
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/ioport.h>
#include "core.h"
#include "debug.h"
#include "mhi.h"
#include "pci.h"
#include "pcic.h"
#define MHI_TIMEOUT_DEFAULT_MS 20000
#define RDDM_DUMP_SIZE 0x420000
#define MHI_CB_INVALID 0xff
static const struct mhi_channel_config ath11k_mhi_channels_qca6390[] = {
{
.num = 20,
.name = "IPCR",
.num_elements = 64,
.event_ring = 1,
.dir = DMA_TO_DEVICE,
.ee_mask = 0x4,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
},
{
.num = 21,
.name = "IPCR",
.num_elements = 64,
.event_ring = 1,
.dir = DMA_FROM_DEVICE,
.ee_mask = 0x4,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
},
};
static struct mhi_event_config ath11k_mhi_events_qca6390[] = {
{
.num_elements = 32,
.irq_moderation_ms = 0,
.irq = 1,
.mode = MHI_DB_BRST_DISABLE,
.data_type = MHI_ER_CTRL,
.hardware_event = false,
.client_managed = false,
.offload_channel = false,
},
{
.num_elements = 256,
.irq_moderation_ms = 1,
.irq = 2,
.mode = MHI_DB_BRST_DISABLE,
.priority = 1,
.hardware_event = false,
.client_managed = false,
.offload_channel = false,
},
};
static const struct mhi_controller_config ath11k_mhi_config_qca6390 = {
.max_channels = 128,
.timeout_ms = 2000,
.use_bounce_buf = false,
.buf_len = 8192,
.num_channels = ARRAY_SIZE(ath11k_mhi_channels_qca6390),
.ch_cfg = ath11k_mhi_channels_qca6390,
.num_events = ARRAY_SIZE(ath11k_mhi_events_qca6390),
.event_cfg = ath11k_mhi_events_qca6390,
};
static const struct mhi_channel_config ath11k_mhi_channels_qcn9074[] = {
{
.num = 20,
.name = "IPCR",
.num_elements = 32,
.event_ring = 1,
.dir = DMA_TO_DEVICE,
.ee_mask = 0x14,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
},
{
.num = 21,
.name = "IPCR",
.num_elements = 32,
.event_ring = 1,
.dir = DMA_FROM_DEVICE,
.ee_mask = 0x14,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
},
};
static struct mhi_event_config ath11k_mhi_events_qcn9074[] = {
{
.num_elements = 32,
.irq_moderation_ms = 0,
.irq = 1,
.data_type = MHI_ER_CTRL,
.mode = MHI_DB_BRST_DISABLE,
.hardware_event = false,
.client_managed = false,
.offload_channel = false,
},
{
.num_elements = 256,
.irq_moderation_ms = 1,
.irq = 2,
.mode = MHI_DB_BRST_DISABLE,
.priority = 1,
.hardware_event = false,
.client_managed = false,
.offload_channel = false,
},
};
static const struct mhi_controller_config ath11k_mhi_config_qcn9074 = {
.max_channels = 30,
.timeout_ms = 10000,
.use_bounce_buf = false,
.buf_len = 0,
.num_channels = ARRAY_SIZE(ath11k_mhi_channels_qcn9074),
.ch_cfg = ath11k_mhi_channels_qcn9074,
.num_events = ARRAY_SIZE(ath11k_mhi_events_qcn9074),
.event_cfg = ath11k_mhi_events_qcn9074,
};
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab)
{
u32 val;
val = ath11k_pcic_read32(ab, MHISTATUS);
ath11k_dbg(ab, ATH11K_DBG_PCI, "mhistatus 0x%x\n", val);
/* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set
* and thus need to set MHICTRL_RESET to clear SYSERR.
*/
ath11k_pcic_write32(ab, MHICTRL, MHICTRL_RESET_MASK);
mdelay(10);
}
static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab)
{
ath11k_pcic_write32(ab, PCIE_TXVECDB, 0);
}
static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab)
{
ath11k_pcic_write32(ab, PCIE_TXVECSTATUS, 0);
}
static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab)
{
ath11k_pcic_write32(ab, PCIE_RXVECDB, 0);
}
static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab)
{
ath11k_pcic_write32(ab, PCIE_RXVECSTATUS, 0);
}
void ath11k_mhi_clear_vector(struct ath11k_base *ab)
{
ath11k_mhi_reset_txvecdb(ab);
ath11k_mhi_reset_txvecstatus(ab);
ath11k_mhi_reset_rxvecdb(ab);
ath11k_mhi_reset_rxvecstatus(ab);
}
static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
{
struct ath11k_base *ab = ab_pci->ab;
u32 user_base_data, base_vector;
int ret, num_vectors, i;
int *irq;
unsigned int msi_data;
ret = ath11k_pcic_get_user_msi_assignment(ab, "MHI", &num_vectors,
&user_base_data, &base_vector);
if (ret)
return ret;
ath11k_dbg(ab, ATH11K_DBG_PCI, "num_vectors %d base_vector %d\n",
num_vectors, base_vector);
irq = kzalloc_objs(int, num_vectors);
if (!irq)
return -ENOMEM;
for (i = 0; i < num_vectors; i++) {
msi_data = base_vector;
if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
msi_data += i;
irq[i] = ath11k_pci_get_msi_irq(ab, msi_data);
}
ab_pci->mhi_ctrl->irq = irq;
ab_pci->mhi_ctrl->nr_irqs = num_vectors;
return 0;
}
static int ath11k_mhi_op_runtime_get(struct mhi_controller *mhi_cntrl)
{
return 0;
}
static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl)
{
}
static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason)
{
switch (reason) {
case MHI_CB_IDLE:
return "MHI_CB_IDLE";
case MHI_CB_PENDING_DATA:
return "MHI_CB_PENDING_DATA";
case MHI_CB_LPM_ENTER:
return "MHI_CB_LPM_ENTER";
case MHI_CB_LPM_EXIT:
return "MHI_CB_LPM_EXIT";
case MHI_CB_EE_RDDM:
return "MHI_CB_EE_RDDM";
case MHI_CB_EE_MISSION_MODE:
return "MHI_CB_EE_MISSION_MODE";
case MHI_CB_SYS_ERROR:
return "MHI_CB_SYS_ERROR";
case MHI_CB_FATAL_ERROR:
return "MHI_CB_FATAL_ERROR";
case MHI_CB_BW_REQ:
return "MHI_CB_BW_REQ";
default:
return "UNKNOWN";
}
};
static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
enum mhi_callback cb)
{
struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "notify status reason %s\n",
ath11k_mhi_op_callback_to_str(cb));
switch (cb) {
case MHI_CB_SYS_ERROR:
ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");
break;
case MHI_CB_EE_RDDM:
ath11k_warn(ab, "firmware crashed: MHI_CB_EE_RDDM\n");
if (ab_pci->mhi_pre_cb == MHI_CB_EE_RDDM) {
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"do not queue again for consecutive RDDM event\n");
break;
}
spin_lock_bh(&ab->base_lock);
if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
queue_work(ab->workqueue_aux, &ab->reset_work);
spin_unlock_bh(&ab->base_lock);
break;
default:
break;
}
ab_pci->mhi_pre_cb = cb;
}
static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *addr,
u32 *out)
{
*out = readl(addr);
return 0;
}
static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
void __iomem *addr,
u32 val)
{
writel(val, addr);
}
static int ath11k_mhi_read_addr_from_dt(struct mhi_controller *mhi_ctrl)
{
struct device_node *np;
struct resource res;
int ret;
np = of_find_node_by_type(NULL, "memory");
if (!np)
return -ENOENT;
ret = of_address_to_resource(np, 0, &res);
of_node_put(np);
if (ret)
return ret;
mhi_ctrl->iova_start = res.start + 0x1000000;
mhi_ctrl->iova_stop = res.end;
return 0;
}
int ath11k_mhi_register(struct ath11k_pci *ab_pci)
{
struct ath11k_base *ab = ab_pci->ab;
struct mhi_controller *mhi_ctrl;
const struct mhi_controller_config *ath11k_mhi_config;
int ret;
mhi_ctrl = mhi_alloc_controller();
if (!mhi_ctrl)
return -ENOMEM;
ab_pci->mhi_ctrl = mhi_ctrl;
mhi_ctrl->cntrl_dev = ab->dev;
mhi_ctrl->regs = ab->mem;
mhi_ctrl->reg_len = ab->mem_len;
if (ab->fw.amss_data && ab->fw.amss_len > 0) {
/* use MHI firmware file from firmware-N.bin */
mhi_ctrl->fw_data = ab->fw.amss_data;
mhi_ctrl->fw_sz = ab->fw.amss_len;
} else {
/* use the old separate mhi.bin MHI firmware file */
ath11k_core_create_firmware_path(ab, ATH11K_AMSS_FILE,
ab_pci->amss_path,
sizeof(ab_pci->amss_path));
mhi_ctrl->fw_image = ab_pci->amss_path;
}
ret = ath11k_mhi_get_msi(ab_pci);
if (ret) {
ath11k_err(ab, "failed to get msi for mhi\n");
goto free_controller;
}
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING;
if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) {
ret = ath11k_mhi_read_addr_from_dt(mhi_ctrl);
if (ret < 0)
goto free_controller;
} else {
mhi_ctrl->iova_start = 0;
mhi_ctrl->iova_stop = ab_pci->dma_mask;
}
mhi_ctrl->rddm_size = RDDM_DUMP_SIZE;
mhi_ctrl->sbl_size = SZ_512K;
mhi_ctrl->seg_len = SZ_512K;
mhi_ctrl->fbc_download = true;
mhi_ctrl->runtime_get = ath11k_mhi_op_runtime_get;
mhi_ctrl->runtime_put = ath11k_mhi_op_runtime_put;
mhi_ctrl->status_cb = ath11k_mhi_op_status_cb;
mhi_ctrl->read_reg = ath11k_mhi_op_read_reg;
mhi_ctrl->write_reg = ath11k_mhi_op_write_reg;
switch (ab->hw_rev) {
case ATH11K_HW_QCN9074_HW10:
ath11k_mhi_config = &ath11k_mhi_config_qcn9074;
break;
case ATH11K_HW_QCA6390_HW20:
case ATH11K_HW_WCN6855_HW20:
case ATH11K_HW_WCN6855_HW21:
case ATH11K_HW_QCA2066_HW21:
case ATH11K_HW_QCA6698AQ_HW21:
ath11k_mhi_config = &ath11k_mhi_config_qca6390;
break;
default:
ath11k_err(ab, "failed assign mhi_config for unknown hw rev %d\n",
ab->hw_rev);
ret = -EINVAL;
goto free_controller;
}
ab_pci->mhi_pre_cb = MHI_CB_INVALID;
ret = mhi_register_controller(mhi_ctrl, ath11k_mhi_config);
if (ret) {
ath11k_err(ab, "failed to register to mhi bus, err = %d\n", ret);
goto free_controller;
}
return 0;
free_controller:
mhi_free_controller(mhi_ctrl);
ab_pci->mhi_ctrl = NULL;
return ret;
}
void ath11k_mhi_unregister(struct ath11k_pci *ab_pci)
{
struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl;
mhi_unregister_controller(mhi_ctrl);
kfree(mhi_ctrl->irq);
mhi_free_controller(mhi_ctrl);
}
int ath11k_mhi_start(struct ath11k_pci *ab_pci)
{
struct ath11k_base *ab = ab_pci->ab;
int ret;
ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS;
ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl);
if (ret) {
ath11k_warn(ab, "failed to prepare mhi: %d", ret);
return ret;
}
ret = mhi_sync_power_up(ab_pci->mhi_ctrl);
if (ret) {
ath11k_warn(ab, "failed to power up mhi: %d", ret);
return ret;
}
return 0;
}
void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend)
{
/* During suspend we need to use mhi_power_down_keep_dev()
* workaround, otherwise ath11k_core_resume() will timeout
* during resume.
*/
if (is_suspend)
mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true);
else
mhi_power_down(ab_pci->mhi_ctrl, true);
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
}
int ath11k_mhi_suspend(struct ath11k_pci *ab_pci)
{
struct ath11k_base *ab = ab_pci->ab;
int ret;
ret = mhi_pm_suspend(ab_pci->mhi_ctrl);
if (ret) {
ath11k_warn(ab, "failed to suspend mhi: %d", ret);
return ret;
}
return 0;
}
int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
{
struct ath11k_base *ab = ab_pci->ab;
int ret;
/* Do force MHI resume as some devices like QCA6390, WCN6855
* are not in M3 state but they are functional. So just ignore
* the MHI state while resuming.
*/
ret = mhi_pm_resume_force(ab_pci->mhi_ctrl);
if (ret) {
ath11k_warn(ab, "failed to resume mhi: %d", ret);
return ret;
}
return 0;
}
void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic)
{
mhi_download_rddm_image(mhi_ctrl, in_panic);
}
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_MHI_H
#define _ATH11K_MHI_H
#include "pci.h"
#define PCIE_TXVECDB 0x360
#define PCIE_TXVECSTATUS 0x368
#define PCIE_RXVECDB 0x394
#define PCIE_RXVECSTATUS 0x39C
#define MHISTATUS 0x48
#define MHICTRL 0x38
#define MHICTRL_RESET_MASK 0x2
int ath11k_mhi_start(struct ath11k_pci *ar_pci);
void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend);
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
void ath11k_mhi_clear_vector(struct ath11k_base *ab);
int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic);
#endif
@@ -0,0 +1,149 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "core.h"
#include "wmi.h"
#include "mac.h"
#include "p2p.h"
static void ath11k_p2p_noa_ie_fill(u8 *data, size_t len,
const struct ath11k_wmi_p2p_noa_info *noa)
{
struct ieee80211_p2p_noa_attr *noa_attr;
u8 noa_descriptors, ctwindow;
bool oppps;
__le16 *noa_attr_len;
u16 attr_len;
int i;
ctwindow = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_CTWIN_TU);
oppps = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS);
noa_descriptors = u32_get_bits(noa->noa_attr,
WMI_P2P_NOA_INFO_DESC_NUM);
/* 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 = u32_get_bits(noa->noa_attr,
WMI_P2P_NOA_INFO_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 = noa->descriptors[i].type_count;
noa_attr->desc[i].duration =
cpu_to_le32(noa->descriptors[i].duration);
noa_attr->desc[i].interval =
cpu_to_le32(noa->descriptors[i].interval);
noa_attr->desc[i].start_time =
cpu_to_le32(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
ath11k_p2p_noa_ie_len_compute(const struct ath11k_wmi_p2p_noa_info *noa)
{
size_t len = 0;
u8 noa_descriptors = u32_get_bits(noa->noa_attr,
WMI_P2P_NOA_INFO_DESC_NUM);
if (!(noa_descriptors) &&
!(u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS)))
return 0;
len += 1 + 1 + 4; /* EID + len + OUI */
len += 1 + 2; /* noa attr + attr len */
len += 1 + 1; /* index + oppps_ctwindow */
len += noa_descriptors *
sizeof(struct ieee80211_p2p_noa_desc);
return len;
}
static void ath11k_p2p_noa_ie_assign(struct ath11k_vif *arvif, void *ie,
size_t len)
{
struct ath11k *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 __ath11k_p2p_noa_update(struct ath11k_vif *arvif,
const struct ath11k_wmi_p2p_noa_info *noa)
{
struct ath11k *ar = arvif->ar;
void *ie;
size_t len;
lockdep_assert_held(&ar->data_lock);
ath11k_p2p_noa_ie_assign(arvif, NULL, 0);
len = ath11k_p2p_noa_ie_len_compute(noa);
if (!len)
return;
ie = kmalloc(len, GFP_ATOMIC);
if (!ie)
return;
ath11k_p2p_noa_ie_fill(ie, len, noa);
ath11k_p2p_noa_ie_assign(arvif, ie, len); }
void ath11k_p2p_noa_update(struct ath11k_vif *arvif,
const struct ath11k_wmi_p2p_noa_info *noa)
{
struct ath11k *ar = arvif->ar;
spin_lock_bh(&ar->data_lock);
__ath11k_p2p_noa_update(arvif, noa);
spin_unlock_bh(&ar->data_lock);
}
static void ath11k_p2p_noa_update_vdev_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ath11k_p2p_noa_arg *arg = data;
if (arvif->vdev_id != arg->vdev_id)
return;
ath11k_p2p_noa_update(arvif, arg->noa);
}
void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id,
const struct ath11k_wmi_p2p_noa_info *noa)
{
struct ath11k_p2p_noa_arg arg = {
.vdev_id = vdev_id,
.noa = noa,
};
ieee80211_iterate_active_interfaces_atomic(ar->hw,
IEEE80211_IFACE_ITER_NORMAL,
ath11k_p2p_noa_update_vdev_iter,
&arg);
}
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_P2P_H
#define ATH11K_P2P_H
#include "wmi.h"
struct ath11k_wmi_p2p_noa_info;
struct ath11k_p2p_noa_arg {
u32 vdev_id;
const struct ath11k_wmi_p2p_noa_info *noa;
};
void ath11k_p2p_noa_update(struct ath11k_vif *arvif,
const struct ath11k_wmi_p2p_noa_info *noa);
void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id,
const struct ath11k_wmi_p2p_noa_info *noa);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,85 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_PCI_H
#define _ATH11K_PCI_H
#include <linux/mhi.h>
#include "core.h"
#define PCIE_SOC_GLOBAL_RESET 0x3008
#define PCIE_SOC_GLOBAL_RESET_V 1
#define WLAON_WARM_SW_ENTRY 0x1f80504
#define WLAON_SOC_RESET_CAUSE_REG 0x01f8060c
#define PCIE_Q6_COOKIE_ADDR 0x01f80500
#define PCIE_Q6_COOKIE_DATA 0xc0000000
/* register to wake the UMAC from power collapse */
#define PCIE_SCRATCH_0_SOC_PCIE_REG 0x4040
/* register used for handshake mechanism to validate UMAC is awake */
#define PCIE_SOC_WAKE_PCIE_LOCAL_REG 0x3004
#define PCIE_PCIE_PARF_LTSSM 0x1e081b0
#define PARM_LTSSM_VALUE 0x111
#define GCC_GCC_PCIE_HOT_RST 0x1e402bc
#define GCC_GCC_PCIE_HOT_RST_VAL 0x10
#define PCIE_PCIE_INT_ALL_CLEAR 0x1e08228
#define PCIE_SMLH_REQ_RST_LINK_DOWN 0x2
#define PCIE_INT_CLEAR_ALL 0xffffffff
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(ab) \
((ab)->hw_params.regs->pcie_qserdes_sysclk_en_sel)
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL 0x10
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK 0xffffffff
#define PCIE_PCS_OSC_DTCT_CONFIG1_REG(ab) \
((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base)
#define PCIE_PCS_OSC_DTCT_CONFIG1_VAL 0x02
#define PCIE_PCS_OSC_DTCT_CONFIG2_REG(ab) \
((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0x4)
#define PCIE_PCS_OSC_DTCT_CONFIG2_VAL 0x52
#define PCIE_PCS_OSC_DTCT_CONFIG4_REG(ab) \
((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0xc)
#define PCIE_PCS_OSC_DTCT_CONFIG4_VAL 0xff
#define PCIE_PCS_OSC_DTCT_CONFIG_MSK 0x000000ff
#define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c
#define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4
enum ath11k_pci_flags {
ATH11K_PCI_ASPM_RESTORE,
};
struct ath11k_pci {
struct pci_dev *pdev;
struct ath11k_base *ab;
u16 dev_id;
char amss_path[100];
struct mhi_controller *mhi_ctrl;
const struct ath11k_msi_config *msi_config;
enum mhi_callback mhi_pre_cb;
u32 register_window;
/* protects register_window above */
spinlock_t window_lock;
/* enum ath11k_pci_flags */
unsigned long flags;
u16 link_ctl;
u64 dma_mask;
};
static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
{
return (struct ath11k_pci *)ab->drv_priv;
}
int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector);
#endif
@@ -0,0 +1,864 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/export.h>
#include "core.h"
#include "pcic.h"
#include "debug.h"
static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {
"bhi",
"mhi-er0",
"mhi-er1",
"ce0",
"ce1",
"ce2",
"ce3",
"ce4",
"ce5",
"ce6",
"ce7",
"ce8",
"ce9",
"ce10",
"ce11",
"host2wbm-desc-feed",
"host2reo-re-injection",
"host2reo-command",
"host2rxdma-monitor-ring3",
"host2rxdma-monitor-ring2",
"host2rxdma-monitor-ring1",
"reo2ost-exception",
"wbm2host-rx-release",
"reo2host-status",
"reo2host-destination-ring4",
"reo2host-destination-ring3",
"reo2host-destination-ring2",
"reo2host-destination-ring1",
"rxdma2host-monitor-destination-mac3",
"rxdma2host-monitor-destination-mac2",
"rxdma2host-monitor-destination-mac1",
"ppdu-end-interrupts-mac3",
"ppdu-end-interrupts-mac2",
"ppdu-end-interrupts-mac1",
"rxdma2host-monitor-status-ring-mac3",
"rxdma2host-monitor-status-ring-mac2",
"rxdma2host-monitor-status-ring-mac1",
"host2rxdma-host-buf-ring-mac3",
"host2rxdma-host-buf-ring-mac2",
"host2rxdma-host-buf-ring-mac1",
"rxdma2host-destination-ring-mac3",
"rxdma2host-destination-ring-mac2",
"rxdma2host-destination-ring-mac1",
"host2tcl-input-ring4",
"host2tcl-input-ring3",
"host2tcl-input-ring2",
"host2tcl-input-ring1",
"wbm2host-tx-completions-ring3",
"wbm2host-tx-completions-ring2",
"wbm2host-tx-completions-ring1",
"tcl2host-status-ring",
};
static const struct ath11k_msi_config ath11k_msi_config[] = {
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_QCA6390_HW20,
},
{
.total_vectors = 16,
.total_users = 3,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 5, .base_vector = 3 },
{ .name = "DP", .num_vectors = 8, .base_vector = 8 },
},
.hw_rev = ATH11K_HW_QCN9074_HW10,
},
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_WCN6855_HW20,
},
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_WCN6855_HW21,
},
{
.total_vectors = 28,
.total_users = 2,
.users = (struct ath11k_msi_user[]) {
{ .name = "CE", .num_vectors = 10, .base_vector = 0 },
{ .name = "DP", .num_vectors = 18, .base_vector = 10 },
},
.hw_rev = ATH11K_HW_WCN6750_HW10,
},
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_QCA2066_HW21,
},
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_QCA6698AQ_HW21,
},
};
int ath11k_pcic_init_msi_config(struct ath11k_base *ab)
{
const struct ath11k_msi_config *msi_config;
int i;
for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) {
msi_config = &ath11k_msi_config[i];
if (msi_config->hw_rev == ab->hw_rev)
break;
}
if (i == ARRAY_SIZE(ath11k_msi_config)) {
ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n",
ab->hw_rev);
return -EINVAL;
}
ab->pci.msi.config = msi_config;
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_init_msi_config);
static void __ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)
{
if (offset < ATH11K_PCI_WINDOW_START)
iowrite32(value, ab->mem + offset);
else
ab->pci.ops->window_write32(ab, offset, value);
}
void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)
{
int ret = 0;
bool wakeup_required;
/* for offset beyond BAR + 4K - 32, may
* need to wakeup the device to access.
*/
wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
if (wakeup_required && ab->pci.ops->wakeup)
ret = ab->pci.ops->wakeup(ab);
__ath11k_pcic_write32(ab, offset, value);
if (wakeup_required && !ret && ab->pci.ops->release)
ab->pci.ops->release(ab);
}
EXPORT_SYMBOL(ath11k_pcic_write32);
static u32 __ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)
{
u32 val;
if (offset < ATH11K_PCI_WINDOW_START)
val = ioread32(ab->mem + offset);
else
val = ab->pci.ops->window_read32(ab, offset);
return val;
}
u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)
{
int ret = 0;
u32 val;
bool wakeup_required;
/* for offset beyond BAR + 4K - 32, may
* need to wakeup the device to access.
*/
wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
if (wakeup_required && ab->pci.ops->wakeup)
ret = ab->pci.ops->wakeup(ab);
val = __ath11k_pcic_read32(ab, offset);
if (wakeup_required && !ret && ab->pci.ops->release)
ab->pci.ops->release(ab);
return val;
}
EXPORT_SYMBOL(ath11k_pcic_read32);
int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end)
{
int ret = 0;
bool wakeup_required;
u32 *data = buf;
u32 i;
/* for offset beyond BAR + 4K - 32, may
* need to wakeup the device to access.
*/
wakeup_required = test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
end >= ATH11K_PCI_ACCESS_ALWAYS_OFF;
if (wakeup_required && ab->pci.ops->wakeup) {
ret = ab->pci.ops->wakeup(ab);
if (ret) {
ath11k_warn(ab,
"wakeup failed, data may be invalid: %d",
ret);
/* Even though wakeup() failed, continue processing rather
* than returning because some parts of the data may still
* be valid and useful in some cases, e.g. could give us
* some clues on firmware crash.
* Mislead due to invalid data could be avoided because we
* are aware of the wakeup failure.
*/
}
}
for (i = start; i < end + 1; i += 4)
*data++ = __ath11k_pcic_read32(ab, i);
if (wakeup_required && ab->pci.ops->release)
ab->pci.ops->release(ab);
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_read);
void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi)
{
*msi_addr_lo = ab->pci.msi.addr_lo;
*msi_addr_hi = ab->pci.msi.addr_hi;
}
EXPORT_SYMBOL(ath11k_pcic_get_msi_address);
int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector)
{
const struct ath11k_msi_config *msi_config = ab->pci.msi.config;
int idx;
for (idx = 0; idx < msi_config->total_users; idx++) {
if (strcmp(user_name, msi_config->users[idx].name) == 0) {
*num_vectors = msi_config->users[idx].num_vectors;
*base_vector = msi_config->users[idx].base_vector;
*user_base_data = *base_vector + ab->pci.msi.ep_base_data;
ath11k_dbg(ab, ATH11K_DBG_PCI,
"msi assignment %s num_vectors %d user_base_data %u base_vector %u\n",
user_name, *num_vectors, *user_base_data,
*base_vector);
return 0;
}
}
ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);
return -EINVAL;
}
EXPORT_SYMBOL(ath11k_pcic_get_user_msi_assignment);
void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx)
{
u32 i, msi_data_idx;
for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
if (ce_id == i)
break;
msi_data_idx++;
}
*msi_idx = msi_data_idx;
}
EXPORT_SYMBOL(ath11k_pcic_get_ce_msi_idx);
static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)
{
int i, j;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
for (j = 0; j < irq_grp->num_irq; j++)
free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
netif_napi_del(&irq_grp->napi);
free_netdev(irq_grp->napi_ndev);
}
}
void ath11k_pcic_free_irq(struct ath11k_base *ab)
{
int i, irq_idx;
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
}
ath11k_pcic_free_ext_irq(ab);
}
EXPORT_SYMBOL(ath11k_pcic_free_irq);
static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
{
u32 irq_idx;
/* In case of one MSI vector, we handle irq enable/disable in a
* uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
enable_irq(ab->irq_num[irq_idx]);
}
static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)
{
u32 irq_idx;
/* In case of one MSI vector, we handle irq enable/disable in a
* uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
disable_irq_nosync(ab->irq_num[irq_idx]);
}
static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab)
{
int i;
clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
ath11k_pcic_ce_irq_disable(ab, i);
}
}
static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab)
{
int i;
int irq_idx;
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
synchronize_irq(ab->irq_num[irq_idx]);
}
}
static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t)
{
struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);
int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
enable_irq(ce_pipe->ab->irq_num[irq_idx]);
}
static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg)
{
struct ath11k_ce_pipe *ce_pipe = arg;
struct ath11k_base *ab = ce_pipe->ab;
int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))
return IRQ_HANDLED;
/* last interrupt received for this CE */
ce_pipe->timestamp = jiffies;
disable_irq_nosync(ab->irq_num[irq_idx]);
tasklet_schedule(&ce_pipe->intr_tq);
return IRQ_HANDLED;
}
static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
{
struct ath11k_base *ab = irq_grp->ab;
int i;
/* In case of one MSI vector, we handle irq enable/disable
* in a uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
for (i = 0; i < irq_grp->num_irq; i++)
disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
}
static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)
{
int i;
clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
ath11k_pcic_ext_grp_disable(irq_grp);
if (irq_grp->napi_enabled) {
napi_synchronize(&irq_grp->napi);
napi_disable(&irq_grp->napi);
irq_grp->napi_enabled = false;
}
}
}
static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
{
struct ath11k_base *ab = irq_grp->ab;
int i;
/* In case of one MSI vector, we handle irq enable/disable in a
* uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
for (i = 0; i < irq_grp->num_irq; i++)
enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
}
void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
{
int i;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
if (!irq_grp->napi_enabled) {
napi_enable(&irq_grp->napi);
irq_grp->napi_enabled = true;
}
ath11k_pcic_ext_grp_enable(irq_grp);
}
set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
}
EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable);
static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab)
{
int i, j, irq_idx;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
for (j = 0; j < irq_grp->num_irq; j++) {
irq_idx = irq_grp->irqs[j];
synchronize_irq(ab->irq_num[irq_idx]);
}
}
}
void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)
{
__ath11k_pcic_ext_irq_disable(ab);
ath11k_pcic_sync_ext_irqs(ab);
}
EXPORT_SYMBOL(ath11k_pcic_ext_irq_disable);
static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget)
{
struct ath11k_ext_irq_grp *irq_grp = container_of(napi,
struct ath11k_ext_irq_grp,
napi);
struct ath11k_base *ab = irq_grp->ab;
int work_done;
int i;
work_done = ath11k_dp_service_srng(ab, irq_grp, budget);
if (work_done < budget) {
napi_complete_done(napi, work_done);
for (i = 0; i < irq_grp->num_irq; i++)
enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
}
if (work_done > budget)
work_done = budget;
return work_done;
}
static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg)
{
struct ath11k_ext_irq_grp *irq_grp = arg;
struct ath11k_base *ab = irq_grp->ab;
int i;
if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))
return IRQ_HANDLED;
ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq %d\n", irq);
/* last interrupt received for this group */
irq_grp->timestamp = jiffies;
for (i = 0; i < irq_grp->num_irq; i++)
disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
napi_schedule(&irq_grp->napi);
return IRQ_HANDLED;
}
static int
ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)
{
return ab->pci.ops->get_msi_irq(ab, vector);
}
static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
{
int i, j, n, ret, num_vectors = 0;
u32 user_base_data = 0, base_vector = 0;
struct ath11k_ext_irq_grp *irq_grp;
unsigned long irq_flags;
ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors,
&user_base_data,
&base_vector);
if (ret < 0)
return ret;
irq_flags = IRQF_SHARED;
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
irq_flags |= IRQF_NOBALANCING;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
irq_grp = &ab->ext_irq_grp[i];
u32 num_irq = 0;
irq_grp->ab = ab;
irq_grp->grp_id = i;
irq_grp->napi_ndev = alloc_netdev_dummy(0);
if (!irq_grp->napi_ndev) {
ret = -ENOMEM;
goto fail_allocate;
}
netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
ath11k_pcic_ext_grp_napi_poll);
if (ab->hw_params.ring_mask->tx[i] ||
ab->hw_params.ring_mask->rx[i] ||
ab->hw_params.ring_mask->rx_err[i] ||
ab->hw_params.ring_mask->rx_wbm_rel[i] ||
ab->hw_params.ring_mask->reo_status[i] ||
ab->hw_params.ring_mask->rxdma2host[i] ||
ab->hw_params.ring_mask->host2rxdma[i] ||
ab->hw_params.ring_mask->rx_mon_status[i]) {
num_irq = 1;
}
irq_grp->num_irq = num_irq;
irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;
for (j = 0; j < irq_grp->num_irq; j++) {
int irq_idx = irq_grp->irqs[j];
int vector = (i % num_vectors) + base_vector;
int irq = ath11k_pcic_get_msi_irq(ab, vector);
if (irq < 0) {
ret = irq;
goto fail_irq;
}
ab->irq_num[irq_idx] = irq;
ath11k_dbg(ab, ATH11K_DBG_PCI,
"irq %d group %d\n", irq, i);
irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,
irq_flags, "DP_EXT_IRQ", irq_grp);
if (ret) {
ath11k_err(ab, "failed request irq %d: %d\n",
vector, ret);
for (n = 0; n <= i; n++) {
irq_grp = &ab->ext_irq_grp[n];
free_netdev(irq_grp->napi_ndev);
}
return ret;
}
}
ath11k_pcic_ext_grp_disable(irq_grp);
}
return 0;
fail_irq:
/* i ->napi_ndev was properly allocated. Free it also */
i += 1;
fail_allocate:
for (n = 0; n < i; n++) {
irq_grp = &ab->ext_irq_grp[n];
free_netdev(irq_grp->napi_ndev);
}
return ret;
}
int ath11k_pcic_config_irq(struct ath11k_base *ab)
{
struct ath11k_ce_pipe *ce_pipe;
u32 msi_data_start;
u32 msi_data_count, msi_data_idx;
u32 msi_irq_start;
unsigned int msi_data;
int irq, i, ret, irq_idx;
unsigned long irq_flags;
ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count,
&msi_data_start, &msi_irq_start);
if (ret)
return ret;
irq_flags = IRQF_SHARED;
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
irq_flags |= IRQF_NOBALANCING;
/* Configure CE irqs */
for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;
irq = ath11k_pcic_get_msi_irq(ab, msi_data);
if (irq < 0)
return irq;
ce_pipe = &ab->ce.ce_pipe[i];
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);
ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,
irq_flags, irq_name[irq_idx], ce_pipe);
if (ret) {
ath11k_err(ab, "failed to request irq %d: %d\n",
irq_idx, ret);
return ret;
}
ab->irq_num[irq_idx] = irq;
msi_data_idx++;
ath11k_pcic_ce_irq_disable(ab, i);
}
ret = ath11k_pcic_ext_irq_config(ab);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_config_irq);
void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab)
{
int i;
set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
ath11k_pcic_ce_irq_enable(ab, i);
}
}
EXPORT_SYMBOL(ath11k_pcic_ce_irqs_enable);
static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab)
{
int i;
for (i = 0; i < ab->hw_params.ce_count; i++) {
struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
tasklet_kill(&ce_pipe->intr_tq);
}
}
void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab)
{
ath11k_pcic_ce_irqs_disable(ab);
ath11k_pcic_sync_ce_irqs(ab);
ath11k_pcic_kill_tasklets(ab);
}
EXPORT_SYMBOL(ath11k_pcic_ce_irq_disable_sync);
void ath11k_pcic_stop(struct ath11k_base *ab)
{
ath11k_pcic_ce_irq_disable_sync(ab);
ath11k_ce_cleanup_pipes(ab);
}
EXPORT_SYMBOL(ath11k_pcic_stop);
int ath11k_pcic_start(struct ath11k_base *ab)
{
set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
ath11k_pcic_ce_irqs_enable(ab);
ath11k_ce_rx_post_buf(ab);
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_start);
int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe)
{
const struct service_to_pipe *entry;
bool ul_set = false, dl_set = false;
int i;
for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {
entry = &ab->hw_params.svc_to_ce_map[i];
if (__le32_to_cpu(entry->service_id) != service_id)
continue;
switch (__le32_to_cpu(entry->pipedir)) {
case PIPEDIR_NONE:
break;
case PIPEDIR_IN:
WARN_ON(dl_set);
*dl_pipe = __le32_to_cpu(entry->pipenum);
dl_set = true;
break;
case PIPEDIR_OUT:
WARN_ON(ul_set);
*ul_pipe = __le32_to_cpu(entry->pipenum);
ul_set = true;
break;
case PIPEDIR_INOUT:
WARN_ON(dl_set);
WARN_ON(ul_set);
*dl_pipe = __le32_to_cpu(entry->pipenum);
*ul_pipe = __le32_to_cpu(entry->pipenum);
dl_set = true;
ul_set = true;
break;
}
}
if (WARN_ON(!ul_set || !dl_set))
return -ENOENT;
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_map_service_to_pipe);
int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,
const struct ath11k_pci_ops *pci_ops)
{
if (!pci_ops)
return 0;
/* Return error if mandatory pci_ops callbacks are missing */
if (!pci_ops->get_msi_irq || !pci_ops->window_write32 ||
!pci_ops->window_read32)
return -EINVAL;
ab->pci.ops = pci_ops;
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_register_pci_ops);
void ath11k_pci_enable_ce_irqs_except_wake_irq(struct ath11k_base *ab)
{
int i;
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||
i == ATH11K_PCI_CE_WAKE_IRQ)
continue;
ath11k_pcic_ce_irq_enable(ab, i);
}
}
EXPORT_SYMBOL(ath11k_pci_enable_ce_irqs_except_wake_irq);
void ath11k_pci_disable_ce_irqs_except_wake_irq(struct ath11k_base *ab)
{
int i;
int irq_idx;
struct ath11k_ce_pipe *ce_pipe;
for (i = 0; i < ab->hw_params.ce_count; i++) {
ce_pipe = &ab->ce.ce_pipe[i];
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR ||
i == ATH11K_PCI_CE_WAKE_IRQ)
continue;
disable_irq_nosync(ab->irq_num[irq_idx]);
synchronize_irq(ab->irq_num[irq_idx]);
tasklet_kill(&ce_pipe->intr_tq);
}
}
EXPORT_SYMBOL(ath11k_pci_disable_ce_irqs_except_wake_irq);
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_PCI_CMN_H
#define _ATH11K_PCI_CMN_H
#include "core.h"
#define ATH11K_PCI_IRQ_CE0_OFFSET 3
#define ATH11K_PCI_IRQ_DP_OFFSET 14
#define ATH11K_PCI_CE_WAKE_IRQ 2
#define ATH11K_PCI_WINDOW_ENABLE_BIT 0x40000000
#define ATH11K_PCI_WINDOW_REG_ADDRESS 0x310c
#define ATH11K_PCI_WINDOW_VALUE_MASK GENMASK(24, 19)
#define ATH11K_PCI_WINDOW_START 0x80000
#define ATH11K_PCI_WINDOW_RANGE_MASK GENMASK(18, 0)
/* BAR0 + 4k is always accessible, and no
* need to force wakeup.
* 4K - 32 = 0xFE0
*/
#define ATH11K_PCI_ACCESS_ALWAYS_OFF 0xFE0
int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector);
void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value);
u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset);
void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi);
void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
void ath11k_pcic_free_irq(struct ath11k_base *ab);
int ath11k_pcic_config_irq(struct ath11k_base *ab);
void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab);
void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab);
void ath11k_pcic_stop(struct ath11k_base *ab);
int ath11k_pcic_start(struct ath11k_base *ab);
int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab);
void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab);
int ath11k_pcic_init_msi_config(struct ath11k_base *ab);
int ath11k_pcic_register_pci_ops(struct ath11k_base *ab,
const struct ath11k_pci_ops *pci_ops);
int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end);
void ath11k_pci_enable_ce_irqs_except_wake_irq(struct ath11k_base *ab);
void ath11k_pci_disable_ce_irqs_except_wake_irq(struct ath11k_base *ab);
#endif
@@ -0,0 +1,670 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "core.h"
#include "peer.h"
#include "debug.h"
static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab,
int peer_id)
{
struct ath11k_peer *peer;
lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) {
if (peer->peer_id != peer_id)
continue;
return peer;
}
return NULL;
}
struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,
const u8 *addr)
{
struct ath11k_peer *peer;
lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) {
if (peer->vdev_id != vdev_id)
continue;
if (!ether_addr_equal(peer->addr, addr))
continue;
return peer;
}
return NULL;
}
struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab,
const u8 *addr)
{
struct ath11k_peer *peer;
lockdep_assert_held(&ab->base_lock);
if (!ab->rhead_peer_addr)
return NULL;
peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr,
ab->rhash_peer_addr_param);
return peer;
}
struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,
int peer_id)
{
struct ath11k_peer *peer;
lockdep_assert_held(&ab->base_lock);
if (!ab->rhead_peer_id)
return NULL;
peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id,
ab->rhash_peer_id_param);
return peer;
}
struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
int vdev_id)
{
struct ath11k_peer *peer;
spin_lock_bh(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) {
if (vdev_id == peer->vdev_id) {
spin_unlock_bh(&ab->base_lock);
return peer;
}
}
spin_unlock_bh(&ab->base_lock);
return NULL;
}
void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)
{
struct ath11k_peer *peer;
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_list_by_id(ab, peer_id);
if (!peer) {
ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n",
peer_id);
goto exit;
}
ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, peer_id);
list_del(&peer->list);
kfree(peer);
wake_up(&ab->peer_mapping_wq);
exit:
spin_unlock_bh(&ab->base_lock);
}
void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id,
u8 *mac_addr, u16 ast_hash, u16 hw_peer_id)
{
struct ath11k_peer *peer;
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find(ab, vdev_id, mac_addr);
if (!peer) {
peer = kzalloc_obj(*peer, GFP_ATOMIC);
if (!peer)
goto exit;
peer->vdev_id = vdev_id;
peer->peer_id = peer_id;
peer->ast_hash = ast_hash;
peer->hw_peer_id = hw_peer_id;
ether_addr_copy(peer->addr, mac_addr);
list_add(&peer->list, &ab->peers);
wake_up(&ab->peer_mapping_wq);
}
ath11k_dbg(ab, ATH11K_DBG_DP_HTT, "peer map vdev %d peer %pM id %d\n",
vdev_id, mac_addr, peer_id);
exit:
spin_unlock_bh(&ab->base_lock);
}
static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id,
const u8 *addr, bool expect_mapped)
{
int ret;
ret = wait_event_timeout(ab->peer_mapping_wq, ({
bool mapped;
spin_lock_bh(&ab->base_lock);
mapped = !!ath11k_peer_find(ab, vdev_id, addr);
spin_unlock_bh(&ab->base_lock);
(mapped == expect_mapped ||
test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags));
}), 3 * HZ);
if (ret <= 0)
return -ETIMEDOUT;
return 0;
}
static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab,
struct rhashtable *rtbl,
struct rhash_head *rhead,
struct rhashtable_params *params,
void *key)
{
struct ath11k_peer *tmp;
lockdep_assert_held(&ab->tbl_mtx_lock);
tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params);
if (!tmp)
return 0;
else if (IS_ERR(tmp))
return PTR_ERR(tmp);
else
return -EEXIST;
}
static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab,
struct rhashtable *rtbl,
struct rhash_head *rhead,
struct rhashtable_params *params)
{
int ret;
lockdep_assert_held(&ab->tbl_mtx_lock);
ret = rhashtable_remove_fast(rtbl, rhead, *params);
if (ret && ret != -ENOENT)
return ret;
return 0;
}
static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer)
{
int ret;
lockdep_assert_held(&ab->base_lock);
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_id || !ab->rhead_peer_addr)
return -EPERM;
ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id,
&ab->rhash_peer_id_param, &peer->peer_id);
if (ret) {
ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n",
peer->addr, peer->peer_id, ret);
return ret;
}
ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr,
&ab->rhash_peer_addr_param, &peer->addr);
if (ret) {
ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n",
peer->addr, peer->peer_id, ret);
goto err_clean;
}
return 0;
err_clean:
ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,
&ab->rhash_peer_id_param);
return ret;
}
void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)
{
struct ath11k_peer *peer, *tmp;
struct ath11k_base *ab = ar->ab;
lockdep_assert_held(&ar->conf_mutex);
mutex_lock(&ab->tbl_mtx_lock);
spin_lock_bh(&ab->base_lock);
list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
if (peer->vdev_id != vdev_id)
continue;
ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id);
ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
}
spin_unlock_bh(&ab->base_lock);
mutex_unlock(&ab->tbl_mtx_lock);
}
static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr)
{
return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, false);
}
int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
const u8 *addr)
{
int ret;
unsigned long time_left;
ret = ath11k_wait_for_peer_deleted(ar, vdev_id, addr);
if (ret) {
ath11k_warn(ar->ab, "failed wait for peer deleted");
return ret;
}
time_left = wait_for_completion_timeout(&ar->peer_delete_done,
3 * HZ);
if (time_left == 0) {
ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n");
return -ETIMEDOUT;
}
return 0;
}
static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr)
{
int ret;
struct ath11k_peer *peer;
struct ath11k_base *ab = ar->ab;
lockdep_assert_held(&ar->conf_mutex);
mutex_lock(&ab->tbl_mtx_lock);
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_addr(ab, addr);
/* Check if the found peer is what we want to remove.
* While the sta is transitioning to another band we may
* have 2 peer with the same addr assigned to different
* vdev_id. Make sure we are deleting the correct peer.
*/
if (peer && peer->vdev_id == vdev_id)
ath11k_peer_rhash_delete(ab, peer);
/* Fallback to peer list search if the correct peer can't be found.
* Skip the deletion of the peer from the rhash since it has already
* been deleted in peer add.
*/
if (!peer)
peer = ath11k_peer_find(ab, vdev_id, addr);
if (!peer) {
spin_unlock_bh(&ab->base_lock);
mutex_unlock(&ab->tbl_mtx_lock);
ath11k_warn(ab,
"failed to find peer vdev_id %d addr %pM in delete\n",
vdev_id, addr);
return -EINVAL;
}
spin_unlock_bh(&ab->base_lock);
mutex_unlock(&ab->tbl_mtx_lock);
reinit_completion(&ar->peer_delete_done);
ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);
if (ret) {
ath11k_warn(ab,
"failed to delete peer vdev_id %d addr %pM ret %d\n",
vdev_id, addr, ret);
return ret;
}
ret = ath11k_wait_for_peer_delete_done(ar, vdev_id, addr);
if (ret)
return ret;
return 0;
}
int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
ret = __ath11k_peer_delete(ar, vdev_id, addr);
if (ret)
return ret;
ar->num_peers--;
return 0;
}
static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8 *addr)
{
return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true);
}
int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
struct ieee80211_sta *sta, struct peer_create_params *param)
{
struct ath11k_peer *peer;
struct ath11k_sta *arsta;
int ret, fbret;
lockdep_assert_held(&ar->conf_mutex);
if (ar->num_peers > (ar->max_num_peers - 1)) {
ath11k_warn(ar->ab,
"failed to create peer due to insufficient peer entry resource in firmware\n");
return -ENOBUFS;
}
mutex_lock(&ar->ab->tbl_mtx_lock);
spin_lock_bh(&ar->ab->base_lock);
peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr);
if (peer) {
if (peer->vdev_id == param->vdev_id) {
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
return -EINVAL;
}
/* Assume sta is transitioning to another band.
* Remove here the peer from rhash.
*/
ath11k_peer_rhash_delete(ar->ab, peer);
}
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
ret = ath11k_wmi_send_peer_create_cmd(ar, param);
if (ret) {
ath11k_warn(ar->ab,
"failed to send peer create vdev_id %d ret %d\n",
param->vdev_id, ret);
return ret;
}
ret = ath11k_wait_for_peer_created(ar, param->vdev_id,
param->peer_addr);
if (ret)
return ret;
mutex_lock(&ar->ab->tbl_mtx_lock);
spin_lock_bh(&ar->ab->base_lock);
peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr);
if (!peer) {
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",
param->peer_addr, param->vdev_id);
ret = -ENOENT;
goto cleanup;
}
ret = ath11k_peer_rhash_add(ar->ab, peer);
if (ret) {
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
goto cleanup;
}
peer->pdev_idx = ar->pdev_idx;
peer->sta = sta;
if (arvif->vif->type == NL80211_IFTYPE_STATION) {
arvif->ast_hash = peer->ast_hash;
arvif->ast_idx = peer->hw_peer_id;
}
peer->sec_type = HAL_ENCRYPT_TYPE_OPEN;
peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN;
if (sta) {
arsta = ath11k_sta_to_arsta(sta);
arsta->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 0) |
FIELD_PREP(HTT_TCL_META_DATA_PEER_ID,
peer->peer_id);
/* set HTT extension valid bit to 0 by default */
arsta->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT;
}
ar->num_peers++;
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
return 0;
cleanup:
fbret = __ath11k_peer_delete(ar, param->vdev_id, param->peer_addr);
if (fbret)
ath11k_warn(ar->ab, "failed peer %pM delete vdev_id %d fallback ret %d\n",
param->peer_addr, param->vdev_id, fbret);
return ret;
}
int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer)
{
int ret;
lockdep_assert_held(&ab->base_lock);
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_id || !ab->rhead_peer_addr)
return -EPERM;
ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr,
&ab->rhash_peer_addr_param);
if (ret) {
ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n",
peer->addr, peer->peer_id, ret);
return ret;
}
ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,
&ab->rhash_peer_id_param);
if (ret) {
ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n",
peer->addr, peer->peer_id, ret);
return ret;
}
return 0;
}
static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab)
{
struct rhashtable_params *param;
struct rhashtable *rhash_id_tbl;
int ret;
size_t size;
lockdep_assert_held(&ab->tbl_mtx_lock);
if (ab->rhead_peer_id)
return 0;
size = sizeof(*ab->rhead_peer_id);
rhash_id_tbl = kzalloc(size, GFP_KERNEL);
if (!rhash_id_tbl) {
ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n",
size);
return -ENOMEM;
}
param = &ab->rhash_peer_id_param;
param->key_offset = offsetof(struct ath11k_peer, peer_id);
param->head_offset = offsetof(struct ath11k_peer, rhash_id);
param->key_len = sizeof_field(struct ath11k_peer, peer_id);
param->automatic_shrinking = true;
param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);
ret = rhashtable_init(rhash_id_tbl, param);
if (ret) {
ath11k_warn(ab, "failed to init peer id rhash table %d\n", ret);
goto err_free;
}
spin_lock_bh(&ab->base_lock);
if (!ab->rhead_peer_id) {
ab->rhead_peer_id = rhash_id_tbl;
} else {
spin_unlock_bh(&ab->base_lock);
goto cleanup_tbl;
}
spin_unlock_bh(&ab->base_lock);
return 0;
cleanup_tbl:
rhashtable_destroy(rhash_id_tbl);
err_free:
kfree(rhash_id_tbl);
return ret;
}
static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab)
{
struct rhashtable_params *param;
struct rhashtable *rhash_addr_tbl;
int ret;
size_t size;
lockdep_assert_held(&ab->tbl_mtx_lock);
if (ab->rhead_peer_addr)
return 0;
size = sizeof(*ab->rhead_peer_addr);
rhash_addr_tbl = kzalloc(size, GFP_KERNEL);
if (!rhash_addr_tbl) {
ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n",
size);
return -ENOMEM;
}
param = &ab->rhash_peer_addr_param;
param->key_offset = offsetof(struct ath11k_peer, addr);
param->head_offset = offsetof(struct ath11k_peer, rhash_addr);
param->key_len = sizeof_field(struct ath11k_peer, addr);
param->automatic_shrinking = true;
param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);
ret = rhashtable_init(rhash_addr_tbl, param);
if (ret) {
ath11k_warn(ab, "failed to init peer addr rhash table %d\n", ret);
goto err_free;
}
spin_lock_bh(&ab->base_lock);
if (!ab->rhead_peer_addr) {
ab->rhead_peer_addr = rhash_addr_tbl;
} else {
spin_unlock_bh(&ab->base_lock);
goto cleanup_tbl;
}
spin_unlock_bh(&ab->base_lock);
return 0;
cleanup_tbl:
rhashtable_destroy(rhash_addr_tbl);
err_free:
kfree(rhash_addr_tbl);
return ret;
}
static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab)
{
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_id)
return;
rhashtable_destroy(ab->rhead_peer_id);
kfree(ab->rhead_peer_id);
ab->rhead_peer_id = NULL;
}
static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab)
{
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_addr)
return;
rhashtable_destroy(ab->rhead_peer_addr);
kfree(ab->rhead_peer_addr);
ab->rhead_peer_addr = NULL;
}
int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab)
{
int ret;
mutex_lock(&ab->tbl_mtx_lock);
ret = ath11k_peer_rhash_id_tbl_init(ab);
if (ret)
goto out;
ret = ath11k_peer_rhash_addr_tbl_init(ab);
if (ret)
goto cleanup_tbl;
mutex_unlock(&ab->tbl_mtx_lock);
return 0;
cleanup_tbl:
ath11k_peer_rhash_id_tbl_destroy(ab);
out:
mutex_unlock(&ab->tbl_mtx_lock);
return ret;
}
void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab)
{
mutex_lock(&ab->tbl_mtx_lock);
ath11k_peer_rhash_addr_tbl_destroy(ab);
ath11k_peer_rhash_id_tbl_destroy(ab);
mutex_unlock(&ab->tbl_mtx_lock);
}
@@ -0,0 +1,59 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_PEER_H
#define ATH11K_PEER_H
struct ath11k_peer {
struct list_head list;
struct ieee80211_sta *sta;
int vdev_id;
u8 addr[ETH_ALEN];
int peer_id;
u16 ast_hash;
u8 pdev_idx;
u16 hw_peer_id;
/* protected by ab->data_lock */
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];
/* peer id based rhashtable list pointer */
struct rhash_head rhash_id;
/* peer addr based rhashtable list pointer */
struct rhash_head rhash_addr;
/* Info used in MMIC verification of
* RX fragments
*/
u8 mcast_keyidx;
u8 ucast_keyidx;
u16 sec_type;
u16 sec_type_grp;
bool is_authorized;
bool dp_setup_done;
};
void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id);
void ath11k_peer_map_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id,
u8 *mac_addr, u16 ast_hash, u16 hw_peer_id);
struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,
const u8 *addr);
struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab,
const u8 *addr);
struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id);
void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id);
int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr);
int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
struct ieee80211_sta *sta, struct peer_create_params *param);
int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
const u8 *addr);
struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
int vdev_id);
int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab);
void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab);
int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer);
#endif /* _PEER_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,525 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_QMI_H
#define ATH11K_QMI_H
#include <linux/mutex.h>
#include <linux/soc/qcom/qmi.h>
#define ATH11K_HOST_VERSION_STRING "WIN"
#define ATH11K_QMI_WLANFW_TIMEOUT_MS 10000
#define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64
#define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000
#define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128
#define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074 0x07
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750 0x03
#define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32
#define ATH11K_QMI_RESP_LEN_MAX 8192
#define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52
#define ATH11K_QMI_CALDB_SIZE 0x480000
#define ATH11K_QMI_BDF_EXT_STR_LENGTH 0x20
#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT 5
#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035
#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037
#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x003E
#define QMI_WLFW_FW_READY_IND_V01 0x0021
#define QMI_WLFW_FW_INIT_DONE_IND_V01 0x0038
#define QMI_WLANFW_MAX_DATA_SIZE_V01 6144
#define ATH11K_FIRMWARE_MODE_OFF 4
#define ATH11K_COLD_BOOT_FW_RESET_DELAY (60 * HZ)
#define ATH11K_QMI_DEVICE_BAR_SIZE 0x200000
struct ath11k_base;
enum ath11k_qmi_file_type {
ATH11K_QMI_FILE_TYPE_BDF_GOLDEN,
ATH11K_QMI_FILE_TYPE_CALDATA = 2,
ATH11K_QMI_FILE_TYPE_EEPROM,
ATH11K_QMI_MAX_FILE_TYPE,
};
enum ath11k_qmi_bdf_type {
ATH11K_QMI_BDF_TYPE_BIN = 0,
ATH11K_QMI_BDF_TYPE_ELF = 1,
ATH11K_QMI_BDF_TYPE_REGDB = 4,
};
enum ath11k_qmi_event_type {
ATH11K_QMI_EVENT_SERVER_ARRIVE,
ATH11K_QMI_EVENT_SERVER_EXIT,
ATH11K_QMI_EVENT_REQUEST_MEM,
ATH11K_QMI_EVENT_FW_MEM_READY,
ATH11K_QMI_EVENT_FW_READY,
ATH11K_QMI_EVENT_COLD_BOOT_CAL_START,
ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE,
ATH11K_QMI_EVENT_REGISTER_DRIVER,
ATH11K_QMI_EVENT_UNREGISTER_DRIVER,
ATH11K_QMI_EVENT_RECOVERY,
ATH11K_QMI_EVENT_FORCE_FW_ASSERT,
ATH11K_QMI_EVENT_POWER_UP,
ATH11K_QMI_EVENT_POWER_DOWN,
ATH11K_QMI_EVENT_FW_INIT_DONE,
ATH11K_QMI_EVENT_MAX,
};
struct ath11k_qmi_driver_event {
struct list_head list;
enum ath11k_qmi_event_type type;
void *data;
};
struct ath11k_qmi_ce_cfg {
const struct ce_pipe_config *tgt_ce;
int tgt_ce_len;
const struct service_to_pipe *svc_to_ce_map;
int svc_to_ce_map_len;
const u8 *shadow_reg;
int shadow_reg_len;
u32 *shadow_reg_v2;
int shadow_reg_v2_len;
};
struct ath11k_qmi_event_msg {
struct list_head list;
enum ath11k_qmi_event_type type;
};
struct target_mem_chunk {
u32 size;
u32 type;
u32 prev_size;
u32 prev_type;
dma_addr_t paddr;
union {
u32 *vaddr;
void __iomem *iaddr;
void *anyaddr;
};
};
struct target_info {
u32 chip_id;
u32 chip_family;
u32 board_id;
u32 soc_id;
u32 fw_version;
u32 eeprom_caldata;
char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1];
char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1];
char bdf_ext[ATH11K_QMI_BDF_EXT_STR_LENGTH];
};
struct m3_mem_region {
u32 size;
dma_addr_t paddr;
void *vaddr;
};
struct ath11k_qmi {
struct ath11k_base *ab;
struct qmi_handle handle;
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 */
struct ath11k_qmi_ce_cfg ce_cfg;
struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
u32 mem_seg_count;
u32 target_mem_mode;
bool target_mem_delayed;
u8 cal_done;
struct target_info target;
struct m3_mem_region m3_mem;
unsigned int service_ins_id;
wait_queue_head_t cold_boot_waitq;
};
#define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 261
#define QMI_WLANFW_HOST_CAP_REQ_V01 0x0034
#define QMI_WLANFW_HOST_CAP_RESP_MSG_V01_MAX_LEN 7
#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034
#define QMI_WLFW_MAX_NUM_GPIO_V01 32
#define QMI_IPQ8074_FW_MEM_MODE 0xFF
#define HOST_DDR_REGION_TYPE 0x1
#define BDF_MEM_REGION_TYPE 0x2
#define M3_DUMP_REGION_TYPE 0x3
#define CALDB_MEM_REGION_TYPE 0x4
#define PAGEABLE_MEM_REGION_TYPE 0x9
struct qmi_wlanfw_host_cap_req_msg_v01 {
u8 num_clients_valid;
u32 num_clients;
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;
};
struct qmi_wlanfw_host_cap_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define QMI_WLANFW_IND_REGISTER_REQ_MSG_V01_MAX_LEN 54
#define QMI_WLANFW_IND_REGISTER_REQ_V01 0x0020
#define QMI_WLANFW_IND_REGISTER_RESP_MSG_V01_MAX_LEN 18
#define QMI_WLANFW_IND_REGISTER_RESP_V01 0x0020
#define QMI_WLANFW_CLIENT_ID 0x4b4e454c
struct qmi_wlanfw_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 fw_mem_ready_enable_valid;
u8 fw_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;
u8 cal_done_enable_valid;
u8 cal_done_enable;
};
struct qmi_wlanfw_ind_register_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 fw_status_valid;
u64 fw_status;
};
#define QMI_WLANFW_REQUEST_MEM_IND_MSG_V01_MAX_LEN 1824
#define QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN 888
#define QMI_WLANFW_RESPOND_MEM_RESP_MSG_V01_MAX_LEN 7
#define QMI_WLANFW_REQUEST_MEM_IND_V01 0x0035
#define QMI_WLANFW_RESPOND_MEM_REQ_V01 0x0036
#define QMI_WLANFW_RESPOND_MEM_RESP_V01 0x0036
#define QMI_WLANFW_MAX_NUM_MEM_CFG_V01 2
struct qmi_wlanfw_mem_cfg_s_v01 {
u64 offset;
u32 size;
u8 secure_flag;
};
enum qmi_wlanfw_mem_type_enum_v01 {
WLANFW_MEM_TYPE_ENUM_MIN_VAL_V01 = INT_MIN,
QMI_WLANFW_MEM_TYPE_MSA_V01 = 0,
QMI_WLANFW_MEM_TYPE_DDR_V01 = 1,
QMI_WLANFW_MEM_BDF_V01 = 2,
QMI_WLANFW_MEM_M3_V01 = 3,
QMI_WLANFW_MEM_CAL_V01 = 4,
QMI_WLANFW_MEM_DPD_V01 = 5,
WLANFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
};
struct qmi_wlanfw_mem_seg_s_v01 {
u32 size;
enum qmi_wlanfw_mem_type_enum_v01 type;
u32 mem_cfg_len;
struct qmi_wlanfw_mem_cfg_s_v01 mem_cfg[QMI_WLANFW_MAX_NUM_MEM_CFG_V01];
};
struct qmi_wlanfw_request_mem_ind_msg_v01 {
u32 mem_seg_len;
struct qmi_wlanfw_mem_seg_s_v01 mem_seg[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
};
struct qmi_wlanfw_mem_seg_resp_s_v01 {
u64 addr;
u32 size;
enum qmi_wlanfw_mem_type_enum_v01 type;
u8 restore;
};
struct qmi_wlanfw_respond_mem_req_msg_v01 {
u32 mem_seg_len;
struct qmi_wlanfw_mem_seg_resp_s_v01 mem_seg[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
};
struct qmi_wlanfw_respond_mem_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
struct qmi_wlanfw_fw_mem_ready_ind_msg_v01 {
char placeholder;
};
struct qmi_wlanfw_fw_ready_ind_msg_v01 {
char placeholder;
};
struct qmi_wlanfw_fw_cold_cal_done_ind_msg_v01 {
char placeholder;
};
struct qmi_wlfw_fw_init_done_ind_msg_v01 {
char placeholder;
};
#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0
#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 235
#define QMI_WLANFW_CAP_REQ_V01 0x0024
#define QMI_WLANFW_CAP_RESP_V01 0x0024
#define QMI_WLANFW_DEVICE_INFO_REQ_V01 0x004C
#define QMI_WLANFW_DEVICE_INFO_REQ_MSG_V01_MAX_LEN 0
enum qmi_wlanfw_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,
};
struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01 {
__le32 pipe_num;
__le32 pipe_dir;
__le32 nentries;
__le32 nbytes_max;
__le32 flags;
};
struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01 {
__le32 service_id;
__le32 pipe_dir;
__le32 pipe_num;
};
struct qmi_wlanfw_shadow_reg_cfg_s_v01 {
u16 id;
u16 offset;
};
struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01 {
u32 addr;
};
struct qmi_wlanfw_memory_region_info_s_v01 {
u64 region_addr;
u32 size;
u8 secure_flag;
};
struct qmi_wlanfw_rf_chip_info_s_v01 {
u32 chip_id;
u32 chip_family;
};
struct qmi_wlanfw_rf_board_info_s_v01 {
u32 board_id;
};
struct qmi_wlanfw_soc_info_s_v01 {
u32 soc_id;
};
struct qmi_wlanfw_fw_version_info_s_v01 {
u32 fw_version;
char fw_build_timestamp[ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1];
};
enum qmi_wlanfw_cal_temp_id_enum_v01 {
QMI_WLANFW_CAL_TEMP_IDX_0_V01 = 0,
QMI_WLANFW_CAL_TEMP_IDX_1_V01 = 1,
QMI_WLANFW_CAL_TEMP_IDX_2_V01 = 2,
QMI_WLANFW_CAL_TEMP_IDX_3_V01 = 3,
QMI_WLANFW_CAL_TEMP_IDX_4_V01 = 4,
QMI_WLANFW_CAL_TEMP_ID_MAX_V01 = 0xFF,
};
struct qmi_wlanfw_cap_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u8 chip_info_valid;
struct qmi_wlanfw_rf_chip_info_s_v01 chip_info;
u8 board_info_valid;
struct qmi_wlanfw_rf_board_info_s_v01 board_info;
u8 soc_info_valid;
struct qmi_wlanfw_soc_info_s_v01 soc_info;
u8 fw_version_info_valid;
struct qmi_wlanfw_fw_version_info_s_v01 fw_version_info;
u8 fw_build_id_valid;
char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1];
u8 num_macs_valid;
u8 num_macs;
u8 voltage_mv_valid;
u32 voltage_mv;
u8 time_freq_hz_valid;
u32 time_freq_hz;
u8 otp_version_valid;
u32 otp_version;
u8 eeprom_read_timeout_valid;
u32 eeprom_read_timeout;
};
struct qmi_wlanfw_cap_req_msg_v01 {
char placeholder;
};
struct qmi_wlanfw_device_info_req_msg_v01 {
char placeholder;
};
struct qmi_wlanfw_device_info_resp_msg_v01 {
struct qmi_response_type_v01 resp;
u64 bar_addr;
u32 bar_size;
u8 bar_addr_valid;
u8 bar_size_valid;
};
#define QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6182
#define QMI_WLANFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7
#define QMI_WLANFW_BDF_DOWNLOAD_RESP_V01 0x0025
#define QMI_WLANFW_BDF_DOWNLOAD_REQ_V01 0x0025
/* TODO: Need to check with MCL and FW team that data can be pointer and
* can be last element in structure
*/
struct qmi_wlanfw_bdf_download_req_msg_v01 {
u8 valid;
u8 file_id_valid;
enum qmi_wlanfw_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_WLANFW_MAX_DATA_SIZE_V01];
u8 end_valid;
u8 end;
u8 bdf_type_valid;
u8 bdf_type;
};
struct qmi_wlanfw_bdf_download_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define QMI_WLANFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
#define QMI_WLANFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7
#define QMI_WLANFW_M3_INFO_RESP_V01 0x003C
#define QMI_WLANFW_M3_INFO_REQ_V01 0x003C
struct qmi_wlanfw_m3_info_req_msg_v01 {
u64 addr;
u32 size;
};
struct qmi_wlanfw_m3_info_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define QMI_WLANFW_WLAN_MODE_REQ_MSG_V01_MAX_LEN 11
#define QMI_WLANFW_WLAN_MODE_RESP_MSG_V01_MAX_LEN 7
#define QMI_WLANFW_WLAN_CFG_REQ_MSG_V01_MAX_LEN 803
#define QMI_WLANFW_WLAN_CFG_RESP_MSG_V01_MAX_LEN 7
#define QMI_WLANFW_WLAN_INI_REQ_MSG_V01_MAX_LEN 4
#define QMI_WLANFW_WLAN_MODE_REQ_V01 0x0022
#define QMI_WLANFW_WLAN_MODE_RESP_V01 0x0022
#define QMI_WLANFW_WLAN_CFG_REQ_V01 0x0023
#define QMI_WLANFW_WLAN_CFG_RESP_V01 0x0023
#define QMI_WLANFW_WLAN_INI_REQ_V01 0x002F
#define QMI_WLANFW_MAX_STR_LEN_V01 16
#define QMI_WLANFW_MAX_NUM_CE_V01 12
#define QMI_WLANFW_MAX_NUM_SVC_V01 24
#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V01 24
#define QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01 36
struct qmi_wlanfw_wlan_mode_req_msg_v01 {
u32 mode;
u8 hw_debug_valid;
u8 hw_debug;
};
struct qmi_wlanfw_wlan_mode_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
struct qmi_wlanfw_wlan_cfg_req_msg_v01 {
u8 host_version_valid;
char host_version[QMI_WLANFW_MAX_STR_LEN_V01 + 1];
u8 tgt_cfg_valid;
u32 tgt_cfg_len;
struct qmi_wlanfw_ce_tgt_pipe_cfg_s_v01
tgt_cfg[QMI_WLANFW_MAX_NUM_CE_V01];
u8 svc_cfg_valid;
u32 svc_cfg_len;
struct qmi_wlanfw_ce_svc_pipe_cfg_s_v01
svc_cfg[QMI_WLANFW_MAX_NUM_SVC_V01];
u8 shadow_reg_valid;
u32 shadow_reg_len;
struct qmi_wlanfw_shadow_reg_cfg_s_v01
shadow_reg[QMI_WLANFW_MAX_NUM_SHADOW_REG_V01];
u8 shadow_reg_v2_valid;
u32 shadow_reg_v2_len;
struct qmi_wlanfw_shadow_reg_v2_cfg_s_v01
shadow_reg_v2[QMI_WLANFW_MAX_NUM_SHADOW_REG_V2_V01];
};
struct qmi_wlanfw_wlan_cfg_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
struct qmi_wlanfw_wlan_ini_req_msg_v01 {
/* Must be set to true if enablefwlog is being passed */
u8 enablefwlog_valid;
u8 enablefwlog;
};
struct qmi_wlanfw_wlan_ini_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
int ath11k_qmi_firmware_start(struct ath11k_base *ab,
u32 mode);
void ath11k_qmi_firmware_stop(struct ath11k_base *ab);
void ath11k_qmi_deinit_service(struct ath11k_base *ab);
int ath11k_qmi_init_service(struct ath11k_base *ab);
void ath11k_qmi_free_resource(struct ath11k_base *ab);
int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_REG_H
#define ATH11K_REG_H
#include <linux/kernel.h>
#include <net/regulatory.h>
struct ath11k_base;
struct ath11k;
/* DFS regdomains supported by Firmware */
enum ath11k_dfs_region {
ATH11K_DFS_REG_UNSET,
ATH11K_DFS_REG_FCC,
ATH11K_DFS_REG_ETSI,
ATH11K_DFS_REG_MKK,
ATH11K_DFS_REG_CN,
ATH11K_DFS_REG_KR,
ATH11K_DFS_REG_MKK_N,
ATH11K_DFS_REG_UNDEF,
};
/* Phy bitmaps */
#define ATH11K_REG_PHY_BITMAP_NO11AX BIT(5)
/* ATH11K Regulatory API's */
void ath11k_reg_init(struct ath11k *ar);
void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
void ath11k_reg_free(struct ath11k_base *ab);
void ath11k_regd_update_work(struct work_struct *work);
void ath11k_regd_update_chan_list_work(struct work_struct *work);
struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab,
struct cur_regulatory_info *reg_info, bool intersect,
enum wmi_vdev_type vdev_type,
enum ieee80211_ap_reg_power power_type);
int ath11k_regd_update(struct ath11k *ar);
int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait);
enum wmi_reg_6ghz_ap_type
ath11k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type);
int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
struct cur_regulatory_info *reg_info,
enum ieee80211_ap_reg_power power_type);
int ath11k_reg_set_cc(struct ath11k *ar);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_SPECTRAL_H
#define ATH11K_SPECTRAL_H
#include "../spectral_common.h"
#include "dbring.h"
/* enum ath11k_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 ath11k_spectral_mode {
ATH11K_SPECTRAL_DISABLED = 0,
ATH11K_SPECTRAL_BACKGROUND,
ATH11K_SPECTRAL_MANUAL,
};
struct ath11k_spectral {
struct ath11k_dbring rx_ring;
/* Protects enabled */
spinlock_t lock;
struct rchan *rfs_scan; /* relay(fs) channel for spectral scan */
struct dentry *scan_ctl;
struct dentry *scan_count;
struct dentry *scan_bins;
enum ath11k_spectral_mode mode;
u16 count;
u8 fft_size;
bool enabled;
bool is_primary;
};
#ifdef CONFIG_ATH11K_SPECTRAL
int ath11k_spectral_init(struct ath11k_base *ab);
void ath11k_spectral_deinit(struct ath11k_base *ab);
int ath11k_spectral_vif_stop(struct ath11k_vif *arvif);
void ath11k_spectral_reset_buffer(struct ath11k *ar);
enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar);
struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar);
#else
static inline int ath11k_spectral_init(struct ath11k_base *ab)
{
return 0;
}
static inline void ath11k_spectral_deinit(struct ath11k_base *ab)
{
}
static inline int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
{
return 0;
}
static inline void ath11k_spectral_reset_buffer(struct ath11k *ar)
{
}
static inline
enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
{
return ATH11K_SPECTRAL_DISABLED;
}
static inline
struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
{
return NULL;
}
#endif /* CONFIG_ATH11K_SPECTRAL */
#endif /* ATH11K_SPECTRAL_H */
@@ -0,0 +1,503 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "testmode.h"
#include <net/netlink.h>
#include "debug.h"
#include "wmi.h"
#include "hw.h"
#include "core.h"
#include "../testmode_i.h"
#define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
#define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
static const struct nla_policy ath11k_tm_policy[ATH_TM_ATTR_MAX + 1] = {
[ATH_TM_ATTR_CMD] = { .type = NLA_U32 },
[ATH_TM_ATTR_DATA] = { .type = NLA_BINARY,
.len = ATH_TM_DATA_MAX_LEN },
[ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 },
[ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 },
[ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
};
static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab)
{
struct ath11k_pdev *pdev;
struct ath11k *ar = NULL;
int i;
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
if (ar && ar->state == ATH11K_STATE_FTM)
break;
}
return ar;
}
/* This function handles unsegmented events. Data in various events are aggregated
* in application layer, this event is unsegmented from host perspective.
*/
static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id,
struct sk_buff *skb)
{
struct sk_buff *nl_skb;
struct ath11k *ar;
ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
"event wmi cmd_id %d skb length %d\n",
cmd_id, skb->len);
ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
ar = ath11k_tm_get_ar(ab);
if (!ar) {
ath11k_warn(ab, "testmode event not handled due to invalid pdev\n");
return;
}
spin_lock_bh(&ar->data_lock);
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
2 * nla_total_size(sizeof(u32)) +
nla_total_size(skb->len),
GFP_ATOMIC);
if (!nl_skb) {
ath11k_warn(ab,
"failed to allocate skb for unsegmented testmode wmi event\n");
goto out;
}
if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) ||
nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) {
ath11k_warn(ab, "failed to populate testmode unsegmented event\n");
kfree_skb(nl_skb);
goto out;
}
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
spin_unlock_bh(&ar->data_lock);
return;
out:
spin_unlock_bh(&ar->data_lock);
ath11k_warn(ab, "Failed to send testmode event to higher layers\n");
}
/* This function handles segmented events. Data of various events received
* from firmware is aggregated and sent to application layer
*/
static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id,
const struct wmi_ftm_event_msg *ftm_msg,
u16 length)
{
struct sk_buff *nl_skb;
int ret = 0;
struct ath11k *ar;
u8 const *buf_pos;
u16 datalen;
u8 total_segments, current_seq;
u32 data_pos;
u32 pdev_id;
ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
"event wmi cmd_id %d ftm event msg %p datalen %d\n",
cmd_id, ftm_msg, length);
ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length);
pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id);
if (pdev_id >= ab->num_radios) {
ath11k_warn(ab, "testmode event not handled due to invalid pdev id: %d\n",
pdev_id);
return -EINVAL;
}
ar = ab->pdevs[pdev_id].ar;
if (!ar) {
ath11k_warn(ab, "testmode event not handled due to absence of pdev\n");
return -ENODEV;
}
current_seq = FIELD_GET(ATH11K_FTM_SEGHDR_CURRENT_SEQ,
ftm_msg->seg_hdr.segmentinfo);
total_segments = FIELD_GET(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS,
ftm_msg->seg_hdr.segmentinfo);
datalen = length - (sizeof(struct wmi_ftm_seg_hdr));
buf_pos = ftm_msg->data;
spin_lock_bh(&ar->data_lock);
if (current_seq == 0) {
ab->testmode.expected_seq = 0;
ab->testmode.data_pos = 0;
}
data_pos = ab->testmode.data_pos;
if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
ath11k_warn(ab, "Invalid ftm event length at %d: %d\n",
data_pos, datalen);
ret = -EINVAL;
goto out;
}
memcpy(&ab->testmode.eventdata[data_pos], buf_pos, datalen);
data_pos += datalen;
if (++ab->testmode.expected_seq != total_segments) {
ab->testmode.data_pos = data_pos;
ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
"partial data received current_seq %d total_seg %d\n",
current_seq, total_segments);
goto out;
}
ath11k_dbg(ab, ATH11K_DBG_TESTMODE,
"total data length pos %d len %d\n",
data_pos, ftm_msg->seg_hdr.len);
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
2 * nla_total_size(sizeof(u32)) +
nla_total_size(data_pos),
GFP_ATOMIC);
if (!nl_skb) {
ath11k_warn(ab,
"failed to allocate skb for segmented testmode wmi event\n");
ret = -ENOMEM;
goto out;
}
if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD,
ATH_TM_CMD_WMI_FTM) ||
nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) ||
nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos,
&ab->testmode.eventdata[0])) {
ath11k_warn(ab, "failed to populate segmented testmode event");
kfree_skb(nl_skb);
ret = -ENOBUFS;
goto out;
}
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
out:
spin_unlock_bh(&ar->data_lock);
return ret;
}
static void ath11k_tm_wmi_event_segmented(struct ath11k_base *ab, u32 cmd_id,
struct sk_buff *skb)
{
const void **tb;
const struct wmi_ftm_event_msg *ev;
u16 length;
int ret;
tb = ath11k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret);
return;
}
ev = tb[WMI_TAG_ARRAY_BYTE];
if (!ev) {
ath11k_warn(ab, "failed to fetch ftm msg\n");
kfree(tb);
return;
}
length = skb->len - TLV_HDR_SIZE;
ret = ath11k_tm_process_event(ab, cmd_id, ev, length);
if (ret)
ath11k_warn(ab, "Failed to process ftm event\n");
kfree(tb);
}
void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id, struct sk_buff *skb)
{
if (test_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags))
ath11k_tm_wmi_event_segmented(ab, cmd_id, skb);
else
ath11k_tm_wmi_event_unsegmented(ab, cmd_id, skb);
}
static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[])
{
struct sk_buff *skb;
int ret;
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
"cmd get version_major %d version_minor %d\n",
ATH_TESTMODE_VERSION_MAJOR,
ATH_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, ATH_TM_ATTR_VERSION_MAJOR,
ATH_TESTMODE_VERSION_MAJOR);
if (ret) {
kfree_skb(skb);
return ret;
}
ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR,
ATH_TESTMODE_VERSION_MINOR);
if (ret) {
kfree_skb(skb);
return ret;
}
return cfg80211_testmode_reply(skb);
}
static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[])
{
int ret;
mutex_lock(&ar->conf_mutex);
if (ar->state == ATH11K_STATE_FTM) {
ret = -EALREADY;
goto err;
}
/* start utf only when the driver is not in use */
if (ar->state != ATH11K_STATE_OFF) {
ret = -EBUSY;
goto err;
}
ar->ab->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH,
GFP_KERNEL);
if (!ar->ab->testmode.eventdata) {
ret = -ENOMEM;
goto err;
}
ar->state = ATH11K_STATE_FTM;
ar->ftm_msgref = 0;
mutex_unlock(&ar->conf_mutex);
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "cmd start\n");
return 0;
err:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[],
struct ieee80211_vif *vif)
{
struct ath11k_pdev_wmi *wmi = ar->wmi;
struct sk_buff *skb;
struct ath11k_vif *arvif;
u32 cmd_id, buf_len;
int ret, tag;
void *buf;
u32 *ptr;
mutex_lock(&ar->conf_mutex);
if (!tb[ATH_TM_ATTR_DATA]) {
ret = -EINVAL;
goto out;
}
if (!tb[ATH_TM_ATTR_WMI_CMDID]) {
ret = -EINVAL;
goto out;
}
buf = nla_data(tb[ATH_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
if (!buf_len) {
ath11k_warn(ar->ab, "No data present in testmode wmi command\n");
ret = -EINVAL;
goto out;
}
cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]);
/* Make sure that the buffer length is long enough to
* hold TLV and pdev/vdev id.
*/
if (buf_len < sizeof(struct wmi_tlv) + sizeof(u32)) {
ret = -EINVAL;
goto out;
}
ptr = buf;
tag = FIELD_GET(WMI_TLV_TAG, *ptr);
/* pdev/vdev id start after TLV header */
ptr++;
if (tag == WMI_TAG_PDEV_SET_PARAM_CMD)
*ptr = ar->pdev->pdev_id;
if (ar->ab->fw_mode != ATH11K_FIRMWARE_MODE_FTM &&
(tag == WMI_TAG_VDEV_SET_PARAM_CMD || tag == WMI_TAG_UNIT_TEST_CMD)) {
if (vif) {
arvif = ath11k_vif_to_arvif(vif);
*ptr = arvif->vdev_id;
} else {
ret = -EINVAL;
goto out;
}
}
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
"cmd wmi cmd_id %d buf length %d\n",
cmd_id, buf_len);
ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, buf_len);
if (!skb) {
ret = -ENOMEM;
goto out;
}
memcpy(skb->data, buf, buf_len);
ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
if (ret) {
dev_kfree_skb(skb);
ath11k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n",
ret);
goto out;
}
ret = 0;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[])
{
struct ath11k_pdev_wmi *wmi = ar->wmi;
struct ath11k_base *ab = ar->ab;
struct sk_buff *skb;
u32 cmd_id, buf_len, hdr_info;
int ret;
void *buf;
u8 segnumber = 0, seginfo;
u16 chunk_len, total_bytes, num_segments;
u8 *bufpos;
struct wmi_ftm_cmd *ftm_cmd;
set_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags);
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH11K_STATE_FTM) {
ret = -ENETDOWN;
goto out;
}
if (!tb[ATH_TM_ATTR_DATA]) {
ret = -EINVAL;
goto out;
}
buf = nla_data(tb[ATH_TM_ATTR_DATA]);
buf_len = nla_len(tb[ATH_TM_ATTR_DATA]);
cmd_id = WMI_PDEV_UTF_CMDID;
ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE,
"cmd wmi ftm cmd_id %d buffer length %d\n",
cmd_id, buf_len);
ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len);
bufpos = buf;
total_bytes = buf_len;
num_segments = total_bytes / MAX_WMI_UTF_LEN;
if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
num_segments++;
while (buf_len) {
chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len +
sizeof(struct wmi_ftm_cmd)));
if (!skb) {
ret = -ENOMEM;
goto out;
}
ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, (chunk_len +
sizeof(struct wmi_ftm_seg_hdr)));
ftm_cmd->tlv_header = hdr_info;
ftm_cmd->seg_hdr.len = total_bytes;
ftm_cmd->seg_hdr.msgref = ar->ftm_msgref;
seginfo = FIELD_PREP(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
FIELD_PREP(ATH11K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
ftm_cmd->seg_hdr.segmentinfo = seginfo;
segnumber++;
memcpy(&ftm_cmd->data, bufpos, chunk_len);
ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
if (ret) {
ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret);
dev_kfree_skb(skb);
goto out;
}
buf_len -= chunk_len;
bufpos += chunk_len;
}
ar->ftm_msgref++;
ret = 0;
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct ath11k *ar = hw->priv;
struct nlattr *tb[ATH_TM_ATTR_MAX + 1];
int ret;
ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath11k_tm_policy,
NULL);
if (ret)
return ret;
if (!tb[ATH_TM_ATTR_CMD])
return -EINVAL;
switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) {
case ATH_TM_CMD_GET_VERSION:
return ath11k_tm_cmd_get_version(ar, tb);
case ATH_TM_CMD_WMI:
return ath11k_tm_cmd_wmi(ar, tb, vif);
case ATH_TM_CMD_TESTMODE_START:
return ath11k_tm_cmd_testmode_start(ar, tb);
case ATH_TM_CMD_WMI_FTM:
return ath11k_tm_cmd_wmi_ftm(ar, tb);
default:
return -EOPNOTSUPP;
}
}
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "core.h"
#ifdef CONFIG_NL80211_TESTMODE
void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id, struct sk_buff *skb);
int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
#else
static inline void ath11k_tm_wmi_event(struct ath11k_base *ab, u32 cmd_id,
struct sk_buff *skb)
{
}
static inline int ath11k_tm_cmd(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
void *data, int len)
{
return 0;
}
#endif
@@ -0,0 +1,230 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#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"
static int
ath11k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
*state = ATH11K_THERMAL_THROTTLE_MAX;
return 0;
}
static int
ath11k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct ath11k *ar = cdev->devdata;
mutex_lock(&ar->conf_mutex);
*state = ar->thermal.throttle_state;
mutex_unlock(&ar->conf_mutex);
return 0;
}
static int
ath11k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
unsigned long throttle_state)
{
struct ath11k *ar = cdev->devdata;
int ret;
if (throttle_state > ATH11K_THERMAL_THROTTLE_MAX) {
ath11k_warn(ar->ab, "throttle state %ld is exceeding the limit %d\n",
throttle_state, ATH11K_THERMAL_THROTTLE_MAX);
return -EINVAL;
}
mutex_lock(&ar->conf_mutex);
ret = ath11k_thermal_set_throttling(ar, throttle_state);
if (ret == 0)
ar->thermal.throttle_state = throttle_state;
mutex_unlock(&ar->conf_mutex);
return ret;
}
static const struct thermal_cooling_device_ops ath11k_thermal_ops = {
.get_max_state = ath11k_thermal_get_max_throttle_state,
.get_cur_state = ath11k_thermal_get_cur_throttle_state,
.set_cur_state = ath11k_thermal_set_cur_throttle_state,
};
static ssize_t ath11k_thermal_show_temp(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ath11k *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 != ATH11K_STATE_ON) {
ret = -ENETDOWN;
goto out;
}
reinit_completion(&ar->thermal.wmi_sync);
ret = ath11k_wmi_send_pdev_temperature_cmd(ar);
if (ret) {
ath11k_warn(ar->ab, "failed to read temperature %d\n", ret);
goto out;
}
if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags)) {
ret = -ESHUTDOWN;
goto out;
}
time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
ATH11K_THERMAL_SYNC_TIMEOUT_HZ);
if (!time_left) {
ath11k_warn(ar->ab, "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 Celsius */
ret = sysfs_emit(buf, "%d\n", temperature * 1000);
out:
mutex_unlock(&ar->conf_mutex);
return ret;
}
void ath11k_thermal_event_temperature(struct ath11k *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, ath11k_thermal_show_temp,
NULL, 0);
static struct attribute *ath11k_hwmon_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
ATTRIBUTE_GROUPS(ath11k_hwmon);
int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state)
{
struct ath11k_base *ab = ar->ab;
struct thermal_mitigation_params param;
int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
if (ar->state != ATH11K_STATE_ON)
return 0;
memset(&param, 0, sizeof(param));
param.pdev_id = ar->pdev->pdev_id;
param.enable = throttle_state ? 1 : 0;
param.dc = ATH11K_THERMAL_DEFAULT_DUTY_CYCLE;
param.dc_per_event = 0xFFFFFFFF;
param.levelconf[0].tmplwm = ATH11K_THERMAL_TEMP_LOW_MARK;
param.levelconf[0].tmphwm = ATH11K_THERMAL_TEMP_HIGH_MARK;
param.levelconf[0].dcoffpercent = throttle_state;
param.levelconf[0].priority = 0; /* disable all data tx queues */
ret = ath11k_wmi_send_thermal_mitigation_param_cmd(ar, &param);
if (ret) {
ath11k_warn(ab, "failed to send thermal mitigation duty cycle %u ret %d\n",
throttle_state, ret);
}
return ret;
}
int ath11k_thermal_register(struct ath11k_base *ab)
{
struct thermal_cooling_device *cdev;
struct device *hwmon_dev;
struct ath11k *ar;
struct ath11k_pdev *pdev;
int i, ret;
if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
return 0;
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
if (!ar)
continue;
cdev = thermal_cooling_device_register("ath11k_thermal", ar,
&ath11k_thermal_ops);
if (IS_ERR(cdev)) {
ath11k_err(ab, "failed to setup thermal device result: %ld\n",
PTR_ERR(cdev));
ret = -EINVAL;
goto err_thermal_destroy;
}
ar->thermal.cdev = cdev;
ret = sysfs_create_link(&ar->hw->wiphy->dev.kobj, &cdev->device.kobj,
"cooling_device");
if (ret) {
ath11k_err(ab, "failed to create cooling device symlink\n");
goto err_thermal_destroy;
}
if (!IS_REACHABLE(CONFIG_HWMON))
return 0;
hwmon_dev = devm_hwmon_device_register_with_groups(&ar->hw->wiphy->dev,
"ath11k_hwmon", ar,
ath11k_hwmon_groups);
if (IS_ERR(hwmon_dev)) {
ath11k_err(ar->ab, "failed to register hwmon device: %ld\n",
PTR_ERR(hwmon_dev));
ret = -EINVAL;
goto err_thermal_destroy;
}
}
return 0;
err_thermal_destroy:
ath11k_thermal_unregister(ab);
return ret;
}
void ath11k_thermal_unregister(struct ath11k_base *ab)
{
struct ath11k *ar;
struct ath11k_pdev *pdev;
int i;
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
if (!ar)
continue;
sysfs_remove_link(&ar->hw->wiphy->dev.kobj, "cooling_device");
thermal_cooling_device_unregister(ar->thermal.cdev);
}
}
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_THERMAL_
#define _ATH11K_THERMAL_
#define ATH11K_THERMAL_TEMP_LOW_MARK -100
#define ATH11K_THERMAL_TEMP_HIGH_MARK 150
#define ATH11K_THERMAL_THROTTLE_MAX 100
#define ATH11K_THERMAL_DEFAULT_DUTY_CYCLE 100
#define ATH11K_HWMON_NAME_LEN 15
#define ATH11K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
struct ath11k_thermal {
struct thermal_cooling_device *cdev;
struct completion wmi_sync;
/* protected by conf_mutex */
u32 throttle_state;
/* temperature value in Celsius degree
* protected by data_lock
*/
int temperature;
};
#if IS_REACHABLE(CONFIG_THERMAL)
int ath11k_thermal_register(struct ath11k_base *ab);
void ath11k_thermal_unregister(struct ath11k_base *ab);
int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state);
void ath11k_thermal_event_temperature(struct ath11k *ar, int temperature);
#else
static inline int ath11k_thermal_register(struct ath11k_base *ab)
{
return 0;
}
static inline void ath11k_thermal_unregister(struct ath11k_base *ab)
{
}
static inline int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state)
{
return 0;
}
static inline void ath11k_thermal_event_temperature(struct ath11k *ar,
int temperature)
{
}
#endif
#endif /* _ATH11K_THERMAL_ */
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/export.h>
#include <linux/module.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
EXPORT_SYMBOL(__tracepoint_ath11k_log_dbg);
@@ -0,0 +1,346 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
#include <linux/tracepoint.h>
#include "core.h"
#define _TRACE_H_
/* create empty functions when tracing is disabled */
#if !defined(CONFIG_ATH11K_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_ATH11K_TRACING || __CHECKER__ */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ath11k
#define ATH11K_MSG_MAX 400
TRACE_EVENT(ath11k_htt_pktlog,
TP_PROTO(struct ath11k *ar, const void *buf, u16 buf_len,
u32 pktlog_checksum),
TP_ARGS(ar, buf, buf_len, pktlog_checksum),
TP_STRUCT__entry(
__string(device, dev_name(ar->ab->dev))
__string(driver, dev_driver_string(ar->ab->dev))
__field(u16, buf_len)
__field(u32, pktlog_checksum)
__dynamic_array(u8, pktlog, buf_len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__entry->buf_len = buf_len;
__entry->pktlog_checksum = pktlog_checksum;
memcpy(__get_dynamic_array(pktlog), buf, buf_len);
),
TP_printk(
"%s %s size %u pktlog_checksum %d",
__get_str(driver),
__get_str(device),
__entry->buf_len,
__entry->pktlog_checksum
)
);
TRACE_EVENT(ath11k_htt_ppdu_stats,
TP_PROTO(struct ath11k *ar, const void *data, size_t len),
TP_ARGS(ar, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ar->ab->dev))
__string(driver, dev_driver_string(ar->ab->dev))
__field(u16, len)
__dynamic_array(u8, ppdu, len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__entry->len = len;
memcpy(__get_dynamic_array(ppdu), data, len);
),
TP_printk(
"%s %s ppdu len %d",
__get_str(driver),
__get_str(device),
__entry->len
)
);
TRACE_EVENT(ath11k_htt_rxdesc,
TP_PROTO(struct ath11k *ar, const void *data, size_t log_type, size_t len),
TP_ARGS(ar, data, log_type, len),
TP_STRUCT__entry(
__string(device, dev_name(ar->ab->dev))
__string(driver, dev_driver_string(ar->ab->dev))
__field(u16, len)
__field(u16, log_type)
__dynamic_array(u8, rxdesc, len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__entry->len = len;
__entry->log_type = log_type;
memcpy(__get_dynamic_array(rxdesc), data, len);
),
TP_printk(
"%s %s rxdesc len %d type %d",
__get_str(driver),
__get_str(device),
__entry->len,
__entry->log_type
)
);
DECLARE_EVENT_CLASS(ath11k_log_event,
TP_PROTO(struct ath11k_base *ab, struct va_format *vaf),
TP_ARGS(ab, vaf),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__vstring(msg, vaf->fmt, vaf->va)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__assign_vstr(msg, vaf->fmt, vaf->va);
),
TP_printk(
"%s %s %s",
__get_str(driver),
__get_str(device),
__get_str(msg)
)
);
DEFINE_EVENT(ath11k_log_event, ath11k_log_err,
TP_PROTO(struct ath11k_base *ab, struct va_format *vaf),
TP_ARGS(ab, vaf)
);
DEFINE_EVENT(ath11k_log_event, ath11k_log_warn,
TP_PROTO(struct ath11k_base *ab, struct va_format *vaf),
TP_ARGS(ab, vaf)
);
DEFINE_EVENT(ath11k_log_event, ath11k_log_info,
TP_PROTO(struct ath11k_base *ab, struct va_format *vaf),
TP_ARGS(ab, vaf)
);
TRACE_EVENT(ath11k_wmi_cmd,
TP_PROTO(struct ath11k_base *ab, int id, const void *buf, size_t buf_len),
TP_ARGS(ab, id, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__field(unsigned int, id)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__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(ath11k_wmi_event,
TP_PROTO(struct ath11k_base *ab, int id, const void *buf, size_t buf_len),
TP_ARGS(ab, id, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__field(unsigned int, id)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__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(ath11k_log_dbg,
TP_PROTO(struct ath11k_base *ab, unsigned int level, struct va_format *vaf),
TP_ARGS(ab, level, vaf),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__field(unsigned int, level)
__dynamic_array(char, msg, ATH11K_MSG_MAX)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__entry->level = level;
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
ATH11K_MSG_MAX, vaf->fmt,
*vaf->va) >= ATH11K_MSG_MAX);
),
TP_printk(
"%s %s %s",
__get_str(driver),
__get_str(device),
__get_str(msg)
)
);
TRACE_EVENT(ath11k_log_dbg_dump,
TP_PROTO(struct ath11k_base *ab, const char *msg, const char *prefix,
const void *buf, size_t buf_len),
TP_ARGS(ab, msg, prefix, buf, buf_len),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__string(msg, msg)
__string(prefix, prefix)
__field(size_t, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__assign_str(msg);
__assign_str(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(ath11k_wmi_diag,
TP_PROTO(struct ath11k_base *ab, const void *data, size_t len),
TP_ARGS(ab, data, len),
TP_STRUCT__entry(
__string(device, dev_name(ab->dev))
__string(driver, dev_driver_string(ab->dev))
__field(u16, len)
__dynamic_array(u8, data, len)
),
TP_fast_assign(
__assign_str(device);
__assign_str(driver);
__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
)
);
TRACE_EVENT(ath11k_ps_timekeeper,
TP_PROTO(struct ath11k *ar, const void *peer_addr,
u32 peer_ps_timestamp, u8 peer_ps_state),
TP_ARGS(ar, peer_addr, peer_ps_timestamp, peer_ps_state),
TP_STRUCT__entry(__string(device, dev_name(ar->ab->dev))
__string(driver, dev_driver_string(ar->ab->dev))
__dynamic_array(u8, peer_addr, ETH_ALEN)
__field(u8, peer_ps_state)
__field(u32, peer_ps_timestamp)
),
TP_fast_assign(__assign_str(device);
__assign_str(driver);
memcpy(__get_dynamic_array(peer_addr), peer_addr,
ETH_ALEN);
__entry->peer_ps_state = peer_ps_state;
__entry->peer_ps_timestamp = peer_ps_timestamp;
),
TP_printk("%s %s %u %u",
__get_str(driver),
__get_str(device),
__entry->peer_ps_state,
__entry->peer_ps_timestamp
)
);
#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>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,883 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/delay.h>
#include "mac.h"
#include <net/mac80211.h>
#include "core.h"
#include "hif.h"
#include "debug.h"
#include "wmi.h"
#include "wow.h"
#include "dp_rx.h"
static const struct wiphy_wowlan_support ath11k_wowlan_support = {
.flags = WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE,
.pattern_min_len = WOW_MIN_PATTERN_SIZE,
.pattern_max_len = WOW_MAX_PATTERN_SIZE,
.max_pkt_offset = WOW_MAX_PKT_OFFSET,
};
int ath11k_wow_enable(struct ath11k_base *ab)
{
struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
int i, ret;
clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) {
reinit_completion(&ab->htc_suspend);
ret = ath11k_wmi_wow_enable(ar);
if (ret) {
ath11k_warn(ab, "failed to issue wow enable: %d\n", ret);
return ret;
}
ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ);
if (ret == 0) {
ath11k_warn(ab,
"timed out while waiting for htc suspend completion\n");
return -ETIMEDOUT;
}
if (test_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags))
/* success, suspend complete received */
return 0;
ath11k_warn(ab, "htc suspend not complete, retrying (try %d)\n",
i);
msleep(ATH11K_WOW_RETRY_WAIT_MS);
}
ath11k_warn(ab, "htc suspend not complete, failing after %d tries\n", i);
return -ETIMEDOUT;
}
int ath11k_wow_wakeup(struct ath11k_base *ab)
{
struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
int ret;
/* In the case of WCN6750, WoW wakeup is done
* by sending SMP2P power save exit message
* to the target processor.
*/
if (ab->hw_params.smp2p_wow_exit)
return 0;
reinit_completion(&ab->wow.wakeup_completed);
ret = ath11k_wmi_wow_host_wakeup_ind(ar);
if (ret) {
ath11k_warn(ab, "failed to send wow wakeup indication: %d\n",
ret);
return ret;
}
ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ);
if (ret == 0) {
ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n");
return -ETIMEDOUT;
}
return 0;
}
static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif)
{
struct ath11k *ar = arvif->ar;
int i, ret;
for (i = 0; i < WOW_EVENT_MAX; i++) {
ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
if (ret) {
ath11k_warn(ar->ab, "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 = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
if (ret) {
ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n",
i, arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath11k_wow_cleanup(struct ath11k *ar)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_wow_vif_cleanup(arvif);
if (ret) {
ath11k_warn(ar->ab, "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... |
* +--+------------+----+-----------+---------------+-----------+
*/
/* clang stack usage explodes if this is inlined */
static noinline_for_stack
void ath11k_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] = {};
u8 bytemask[WOW_MAX_PATTERN_SIZE] = {};
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);
int i;
/* convert bitmask to bytemask */
for (i = 0; i < old->pattern_len; i++)
if (old->mask[i / 8] & BIT(i % 8))
bytemask[i] = 0xff;
memcpy(hdr_8023_pattern + old->pkt_offset,
old->pattern, ETH_HLEN - old->pkt_offset);
memcpy(hdr_8023_bit_mask + old->pkt_offset,
bytemask, 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));
/* Compute 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;
/* Compute 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,
bytemask + ETH_HLEN - old->pkt_offset,
total_len - ETH_HLEN);
new->pattern_len += total_len - ETH_HLEN;
}
}
static int ath11k_wmi_pno_check_and_convert(struct ath11k *ar, u32 vdev_id,
struct cfg80211_sched_scan_request *nd_config,
struct wmi_pno_scan_req *pno)
{
int i, j;
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 = 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 (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,
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 {
ath11k_warn(ar->ab, "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 0;
}
static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif,
struct cfg80211_wowlan *wowlan)
{
int ret, i;
unsigned long wow_mask = 0;
struct ath11k *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_obj(*pno);
if (!pno)
return -ENOMEM;
ar->nlo_enabled = true;
ret = ath11k_wmi_pno_check_and_convert(ar, arvif->vdev_id,
wowlan->nd_config, pno);
if (!ret) {
ath11k_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 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
struct cfg80211_pkt_pattern new_pattern = {};
new_pattern.pattern = ath_pattern;
new_pattern.mask = ath_bitmask;
if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
continue;
if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
ATH11K_HW_TXRX_NATIVE_WIFI) {
if (patterns[i].pkt_offset < ETH_HLEN) {
ath11k_wow_convert_8023_to_80211(&new_pattern,
&patterns[i]);
} else {
int j;
new_pattern = patterns[i];
new_pattern.mask = ath_bitmask;
/* convert bitmask to bytemask */
for (j = 0; j < patterns[i].pattern_len; j++)
if (patterns[i].mask[j / 8] & BIT(j % 8))
ath_bitmask[j] = 0xff;
new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
}
}
if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
return -EINVAL;
ret = ath11k_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) {
ath11k_warn(ar->ab, "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 = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
if (ret) {
ath11k_warn(ar->ab, "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 ath11k_wow_set_wakeups(struct ath11k *ar,
struct cfg80211_wowlan *wowlan)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_vif_wow_set_wakeups(arvif, wowlan);
if (ret) {
ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath11k_vif_wow_clean_nlo(struct ath11k_vif *arvif)
{
int ret = 0;
struct ath11k *ar = arvif->ar;
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_STA:
if (ar->nlo_enabled) {
struct wmi_pno_scan_req *pno;
pno = kzalloc_obj(*pno);
if (!pno)
return -ENOMEM;
pno->enable = 0;
ar->nlo_enabled = false;
ret = ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
kfree(pno);
}
break;
default:
break;
}
return ret;
}
static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_vif_wow_clean_nlo(arvif);
if (ret) {
ath11k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath11k_wow_set_hw_filter(struct ath11k *ar)
{
struct ath11k_vif *arvif;
u32 bitmap;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC |
WMI_HW_DATA_FILTER_DROP_NON_ARP_BC;
ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id,
bitmap,
true);
if (ret) {
ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath11k_wow_clear_hw_filter(struct ath11k *ar)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false);
if (ret) {
ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath11k_wow_arp_ns_offload(struct ath11k *ar, bool enable)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
continue;
ret = ath11k_wmi_arp_ns_offload(ar, arvif, enable);
if (ret) {
ath11k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n",
arvif->vdev_id, enable, ret);
return ret;
}
}
return 0;
}
static int ath11k_gtk_rekey_offload(struct ath11k *ar, bool enable)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
!arvif->is_up ||
!arvif->rekey_data.enable_offload)
continue;
/* get rekey info before disable rekey offload */
if (!enable) {
ret = ath11k_wmi_gtk_rekey_getinfo(ar, arvif);
if (ret) {
ath11k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n",
arvif->vdev_id, ret);
return ret;
}
}
ret = ath11k_wmi_gtk_rekey_offload(ar, arvif, enable);
if (ret) {
ath11k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n",
arvif->vdev_id, enable, ret);
return ret;
}
}
return 0;
}
static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable)
{
int ret;
ret = ath11k_wow_arp_ns_offload(ar, enable);
if (ret) {
ath11k_warn(ar->ab, "failed to offload ARP and NS %d %d\n",
enable, ret);
return ret;
}
ret = ath11k_gtk_rekey_offload(ar, enable);
if (ret) {
ath11k_warn(ar->ab, "failed to offload gtk rekey %d %d\n",
enable, ret);
return ret;
}
return 0;
}
static int ath11k_wow_set_keepalive(struct ath11k *ar,
enum wmi_sta_keepalive_method method,
u32 interval)
{
struct ath11k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
ret = ath11k_mac_vif_set_keepalive(arvif, method, interval);
if (ret)
return ret;
}
return 0;
}
int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
struct ath11k *ar = hw->priv;
int ret;
ret = ath11k_mac_wait_tx_complete(ar);
if (ret) {
ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret);
return ret;
}
mutex_lock(&ar->conf_mutex);
ret = ath11k_dp_rx_pktlog_stop(ar->ab, true);
if (ret) {
ath11k_warn(ar->ab,
"failed to stop dp rx (and timer) pktlog during wow suspend: %d\n",
ret);
goto exit;
}
ret = ath11k_wow_cleanup(ar);
if (ret) {
ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n",
ret);
goto exit;
}
ret = ath11k_wow_set_wakeups(ar, wowlan);
if (ret) {
ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n",
ret);
goto cleanup;
}
ret = ath11k_wow_protocol_offload(ar, true);
if (ret) {
ath11k_warn(ar->ab, "failed to set wow protocol offload events: %d\n",
ret);
goto cleanup;
}
ret = ath11k_wow_set_hw_filter(ar);
if (ret) {
ath11k_warn(ar->ab, "failed to set hw filter: %d\n",
ret);
goto cleanup;
}
ret = ath11k_wow_set_keepalive(ar,
WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
WMI_STA_KEEPALIVE_INTERVAL_DEFAULT);
if (ret) {
ath11k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret);
goto cleanup;
}
ret = ath11k_wow_enable(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
goto cleanup;
}
ret = ath11k_dp_rx_pktlog_stop(ar->ab, false);
if (ret) {
ath11k_warn(ar->ab,
"failed to stop dp rx pktlog during wow suspend: %d\n",
ret);
goto cleanup;
}
ath11k_ce_stop_shadow_timers(ar->ab);
ath11k_dp_stop_shadow_timers(ar->ab);
ath11k_hif_irq_disable(ar->ab);
ath11k_hif_ce_irq_disable(ar->ab);
ret = ath11k_hif_suspend(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret);
goto wakeup;
}
goto exit;
wakeup:
ath11k_wow_wakeup(ar->ab);
cleanup:
ath11k_wow_cleanup(ar);
exit:
mutex_unlock(&ar->conf_mutex);
return ret ? 1 : 0;
}
void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
{
struct ath11k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
device_set_wakeup_enable(ar->ab->dev, enabled);
mutex_unlock(&ar->conf_mutex);
}
int ath11k_wow_op_resume(struct ieee80211_hw *hw)
{
struct ath11k *ar = hw->priv;
int ret;
mutex_lock(&ar->conf_mutex);
ret = ath11k_hif_resume(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret);
goto exit;
}
ath11k_hif_ce_irq_enable(ar->ab);
ath11k_hif_irq_enable(ar->ab);
ret = ath11k_dp_rx_pktlog_start(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret);
goto exit;
}
ret = ath11k_wow_wakeup(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret);
goto exit;
}
ret = ath11k_wow_nlo_cleanup(ar);
if (ret) {
ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret);
goto exit;
}
ret = ath11k_wow_clear_hw_filter(ar);
if (ret) {
ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
goto exit;
}
ret = ath11k_wow_protocol_offload(ar, false);
if (ret) {
ath11k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n",
ret);
goto exit;
}
ret = ath11k_wow_set_keepalive(ar,
WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
WMI_STA_KEEPALIVE_INTERVAL_DISABLE);
if (ret) {
ath11k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret);
goto exit;
}
exit:
if (ret) {
switch (ar->state) {
case ATH11K_STATE_ON:
ar->state = ATH11K_STATE_RESTARTING;
ret = 1;
break;
case ATH11K_STATE_OFF:
case ATH11K_STATE_RESTARTING:
case ATH11K_STATE_RESTARTED:
case ATH11K_STATE_WEDGED:
case ATH11K_STATE_FTM:
ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
ar->state);
ret = -EIO;
break;
}
}
mutex_unlock(&ar->conf_mutex);
return ret;
}
int ath11k_wow_init(struct ath11k *ar)
{
if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))
return 0;
ar->wow.wowlan_support = ath11k_wowlan_support;
if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
ATH11K_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_TLV_SERVICE_NLO, ar->wmi->wmi_ab->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.max_num_patterns = ATH11K_WOW_PATTERNS;
ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
device_set_wakeup_capable(ar->ab->dev, true);
return 0;
}
@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _WOW_H_
#define _WOW_H_
struct ath11k_wow {
u32 max_num_patterns;
struct completion wakeup_completed;
struct wiphy_wowlan_support wowlan_support;
};
struct rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctrl;
u8 snap_oui[3];
__be16 snap_type;
} __packed;
#define ATH11K_WOW_RETRY_NUM 3
#define ATH11K_WOW_RETRY_WAIT_MS 200
#define ATH11K_WOW_PATTERNS 22
#ifdef CONFIG_PM
int ath11k_wow_init(struct ath11k *ar);
int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
int ath11k_wow_op_resume(struct ieee80211_hw *hw);
void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled);
int ath11k_wow_enable(struct ath11k_base *ab);
int ath11k_wow_wakeup(struct ath11k_base *ab);
#else
static inline int ath11k_wow_init(struct ath11k *ar)
{
return 0;
}
static inline int ath11k_wow_enable(struct ath11k_base *ab)
{
return 0;
}
static inline int ath11k_wow_wakeup(struct ath11k_base *ab)
{
return 0;
}
#endif /* CONFIG_PM */
#endif /* _WOW_H_ */