wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices

ath12k is a new mac80211 driver for Qualcomm Wi-Fi 7 devices, first
supporting QCN9274 and WCN7850 PCI devices.  QCN9274 supports both AP
and station; WCN7850 supports only station mode. Monitor mode is not
(yet) supported. Only PCI bus devices are supported.

ath12k is forked from an earlier version of ath11k. It was simpler to
have a "clean start" for the new generation and not try to share the
code with ath11k. This makes maintenance easier and avoids major
changes in ath11k, which would have significantly increased the risk
of regressions in existing setups.

ath12k uses le32 and cpu_to_le32() macros to handle endian
conversions, instead of using the firmware byte swap feature utilized
by ath11k. There is only one kernel module, named ath12k.ko.

Currently ath12k only supports HE mode (IEEE 802.11ax) or older, but
work is ongoing to add EHT mode (IEEE 802.11be) support.

The size of the driver is ~41 kLOC and 45 files. To make the review
easier, this initial version of ath12k does not support Device Tree,
debugfs or any other extra features. Those will be added later, after
ath12k is accepted to upstream.

The driver is build tested by Intel's kernel test robot with both GCC
and Clang. Sparse reports no warnings. The driver is mostly free of
checkpatch warnings, albeit few of the warnings are omitted on
purpose, list of them here:

https://github.com/qca/qca-swiss-army-knife/blob/master/tools/scripts/ath12k/ath12k-check#L52

The driver has had multiple authors who are listed in alphabetical
order below.

Co-developed-by: Balamurugan Selvarajan <quic_bselvara@quicinc.com>
Signed-off-by: Balamurugan Selvarajan <quic_bselvara@quicinc.com>
Co-developed-by: Baochen Qiang <quic_bqiang@quicinc.com>
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Co-developed-by: Bhagavathi Perumal S <quic_bperumal@quicinc.com>
Signed-off-by: Bhagavathi Perumal S <quic_bperumal@quicinc.com>
Co-developed-by: Carl Huang <quic_cjhuang@quicinc.com>
Signed-off-by: Carl Huang <quic_cjhuang@quicinc.com>
Co-developed-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Co-developed-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
Co-developed-by: P Praneesh <quic_ppranees@quicinc.com>
Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
Co-developed-by: Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com>
Signed-off-by: Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com>
Co-developed-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com>
Signed-off-by: Ramya Gnanasekar <quic_rgnanase@quicinc.com>
Co-developed-by: Sriram R <quic_srirrama@quicinc.com>
Signed-off-by: Sriram R <quic_srirrama@quicinc.com>
Co-developed-by: Vasanthakumar Thiagarajan <quic_vthiagar@quicinc.com>
Signed-off-by: Vasanthakumar Thiagarajan <quic_vthiagar@quicinc.com>
Co-developed-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
This commit is contained in:
Kalle Valo 2022-11-28 17:09:53 +02:00
parent eceb024ee3
commit d889913205
50 changed files with 53316 additions and 0 deletions

View file

@ -16986,6 +16986,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
F: Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml
F: drivers/net/wireless/ath/ath11k/
QUALCOMM ATH12K WIRELESS DRIVER
M: Kalle Valo <kvalo@kernel.org>
L: ath12k@lists.infradead.org
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
F: drivers/net/wireless/ath/ath12k/
QUALCOMM ATHEROS ATH9K WIRELESS DRIVER
M: Toke Høiland-Jørgensen <toke@toke.dk>
L: linux-wireless@vger.kernel.org

View file

@ -63,5 +63,6 @@ source "drivers/net/wireless/ath/wil6210/Kconfig"
source "drivers/net/wireless/ath/ath10k/Kconfig"
source "drivers/net/wireless/ath/wcn36xx/Kconfig"
source "drivers/net/wireless/ath/ath11k/Kconfig"
source "drivers/net/wireless/ath/ath12k/Kconfig"
endif

View file

@ -8,6 +8,7 @@ obj-$(CONFIG_WIL6210) += wil6210/
obj-$(CONFIG_ATH10K) += ath10k/
obj-$(CONFIG_WCN36XX) += wcn36xx/
obj-$(CONFIG_ATH11K) += ath11k/
obj-$(CONFIG_ATH12K) += ath12k/
obj-$(CONFIG_ATH_COMMON) += ath.o

View file

@ -0,0 +1,34 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config ATH12K
tristate "Qualcomm Technologies Wi-Fi 7 support (ath12k)"
depends on MAC80211 && HAS_DMA && PCI
depends on CRYPTO_MICHAEL_MIC
select QCOM_QMI_HELPERS
select MHI_BUS
select QRTR
select QRTR_MHI
help
Enable support for Qualcomm Technologies Wi-Fi 7 (IEEE
802.11be) family of chipsets, for example WCN7850 and
QCN9274.
If you choose to build a module, it'll be called ath12k.
config ATH12K_DEBUG
bool "ath12k debugging"
depends on ATH12K
help
Enable debug support, for example debug messages which must
be enabled separately using the debug_mask module parameter.
If unsure, say Y to make it easier to debug problems. But if
you want optimal performance choose N.
config ATH12K_TRACING
bool "ath12k tracing support"
depends on ATH12K && EVENT_TRACING
help
Enable ath12k tracing infrastructure.
If unsure, say Y to make it easier to debug problems. But if
you want optimal performance choose N.

View file

@ -0,0 +1,27 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_ATH12K) += ath12k.o
ath12k-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 \
mhi.o \
pci.o \
dp_mon.o
ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)

View file

@ -0,0 +1,964 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "dp_rx.h"
#include "debug.h"
#include "hif.h"
const struct ce_attr ath12k_host_ce_config_qcn9274[] = {
/* CE0: host->target HTC control and raw streams */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 16,
.src_sz_max = 2048,
.dest_nentries = 0,
},
/* CE1: target->host HTT + HTC control */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 512,
.recv_cb = ath12k_htc_rx_completion_handler,
},
/* CE2: target->host WMI */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 128,
.recv_cb = ath12k_htc_rx_completion_handler,
},
/* CE3: host->target WMI (mac0) */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 32,
.src_sz_max = 2048,
.dest_nentries = 0,
},
/* CE4: host->target HTT */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 2048,
.src_sz_max = 256,
.dest_nentries = 0,
},
/* CE5: target->host pktlog */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 512,
.recv_cb = ath12k_dp_htt_htc_t2h_msg_handler,
},
/* CE6: target autonomous hif_memcpy */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE7: host->target WMI (mac1) */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 32,
.src_sz_max = 2048,
.dest_nentries = 0,
},
/* CE8: target autonomous hif_memcpy */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE9: MHI */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE10: MHI */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE11: MHI */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE12: CV Prefetch */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE13: CV Prefetch */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE14: target->host dbg log */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 512,
.recv_cb = ath12k_htc_rx_completion_handler,
},
/* CE15: reserved for future use */
{
.flags = (CE_ATTR_FLAGS | CE_ATTR_DIS_INTR),
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
};
const struct ce_attr ath12k_host_ce_config_wcn7850[] = {
/* CE0: host->target HTC control and raw streams */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 16,
.src_sz_max = 2048,
.dest_nentries = 0,
},
/* CE1: target->host HTT + HTC control */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 512,
.recv_cb = ath12k_htc_rx_completion_handler,
},
/* CE2: target->host WMI */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 64,
.recv_cb = ath12k_htc_rx_completion_handler,
},
/* CE3: host->target WMI (mac0) */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 32,
.src_sz_max = 2048,
.dest_nentries = 0,
},
/* CE4: host->target HTT */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 2048,
.src_sz_max = 256,
.dest_nentries = 0,
},
/* CE5: target->host pktlog */
{
.flags = CE_ATTR_FLAGS,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE6: target autonomous hif_memcpy */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
/* CE7: host->target WMI (mac1) */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 2048,
.dest_nentries = 0,
},
/* CE8: target autonomous hif_memcpy */
{
.flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR,
.src_nentries = 0,
.src_sz_max = 0,
.dest_nentries = 0,
},
};
static int ath12k_ce_rx_buf_enqueue_pipe(struct ath12k_ce_pipe *pipe,
struct sk_buff *skb, dma_addr_t paddr)
{
struct ath12k_base *ab = pipe->ab;
struct ath12k_ce_ring *ring = pipe->dest_ring;
struct hal_srng *srng;
unsigned int write_index;
unsigned int nentries_mask = ring->nentries_mask;
struct hal_ce_srng_dest_desc *desc;
int ret;
lockdep_assert_held(&ab->ce.ce_lock);
write_index = ring->write_index;
srng = &ab->hal.srng_list[ring->hal_ring_id];
spin_lock_bh(&srng->lock);
ath12k_hal_srng_access_begin(ab, srng);
if (unlikely(ath12k_hal_srng_src_num_free(ab, srng, false) < 1)) {
ret = -ENOSPC;
goto exit;
}
desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
if (!desc) {
ret = -ENOSPC;
goto exit;
}
ath12k_hal_ce_dst_set_desc(desc, paddr);
ring->skb[write_index] = skb;
write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
ring->write_index = write_index;
pipe->rx_buf_needed--;
ret = 0;
exit:
ath12k_hal_srng_access_end(ab, srng);
spin_unlock_bh(&srng->lock);
return ret;
}
static int ath12k_ce_rx_post_pipe(struct ath12k_ce_pipe *pipe)
{
struct ath12k_base *ab = pipe->ab;
struct sk_buff *skb;
dma_addr_t paddr;
int ret = 0;
if (!(pipe->dest_ring || pipe->status_ring))
return 0;
spin_lock_bh(&ab->ce.ce_lock);
while (pipe->rx_buf_needed) {
skb = dev_alloc_skb(pipe->buf_sz);
if (!skb) {
ret = -ENOMEM;
goto exit;
}
WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
paddr = dma_map_single(ab->dev, skb->data,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(ab->dev, paddr))) {
ath12k_warn(ab, "failed to dma map ce rx buf\n");
dev_kfree_skb_any(skb);
ret = -EIO;
goto exit;
}
ATH12K_SKB_RXCB(skb)->paddr = paddr;
ret = ath12k_ce_rx_buf_enqueue_pipe(pipe, skb, paddr);
if (ret) {
ath12k_warn(ab, "failed to enqueue rx buf: %d\n", ret);
dma_unmap_single(ab->dev, paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
goto exit;
}
}
exit:
spin_unlock_bh(&ab->ce.ce_lock);
return ret;
}
static int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe,
struct sk_buff **skb, int *nbytes)
{
struct ath12k_base *ab = pipe->ab;
struct hal_ce_srng_dst_status_desc *desc;
struct hal_srng *srng;
unsigned int sw_index;
unsigned int nentries_mask;
int ret = 0;
spin_lock_bh(&ab->ce.ce_lock);
sw_index = pipe->dest_ring->sw_index;
nentries_mask = pipe->dest_ring->nentries_mask;
srng = &ab->hal.srng_list[pipe->status_ring->hal_ring_id];
spin_lock_bh(&srng->lock);
ath12k_hal_srng_access_begin(ab, srng);
desc = ath12k_hal_srng_dst_get_next_entry(ab, srng);
if (!desc) {
ret = -EIO;
goto err;
}
*nbytes = ath12k_hal_ce_dst_status_get_length(desc);
if (*nbytes == 0) {
ret = -EIO;
goto err;
}
*skb = pipe->dest_ring->skb[sw_index];
pipe->dest_ring->skb[sw_index] = NULL;
sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
pipe->dest_ring->sw_index = sw_index;
pipe->rx_buf_needed++;
err:
ath12k_hal_srng_access_end(ab, srng);
spin_unlock_bh(&srng->lock);
spin_unlock_bh(&ab->ce.ce_lock);
return ret;
}
static void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe)
{
struct ath12k_base *ab = pipe->ab;
struct sk_buff *skb;
struct sk_buff_head list;
unsigned int nbytes, max_nbytes;
int ret;
__skb_queue_head_init(&list);
while (ath12k_ce_completed_recv_next(pipe, &skb, &nbytes) == 0) {
max_nbytes = skb->len + skb_tailroom(skb);
dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr,
max_nbytes, DMA_FROM_DEVICE);
if (unlikely(max_nbytes < nbytes)) {
ath12k_warn(ab, "rxed more than expected (nbytes %d, max %d)",
nbytes, max_nbytes);
dev_kfree_skb_any(skb);
continue;
}
skb_put(skb, nbytes);
__skb_queue_tail(&list, skb);
}
while ((skb = __skb_dequeue(&list))) {
ath12k_dbg(ab, ATH12K_DBG_AHB, "rx ce pipe %d len %d\n",
pipe->pipe_num, skb->len);
pipe->recv_cb(ab, skb);
}
ret = ath12k_ce_rx_post_pipe(pipe);
if (ret && ret != -ENOSPC) {
ath12k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n",
pipe->pipe_num, ret);
mod_timer(&ab->rx_replenish_retry,
jiffies + ATH12K_CE_RX_POST_RETRY_JIFFIES);
}
}
static struct sk_buff *ath12k_ce_completed_send_next(struct ath12k_ce_pipe *pipe)
{
struct ath12k_base *ab = pipe->ab;
struct hal_ce_srng_src_desc *desc;
struct hal_srng *srng;
unsigned int sw_index;
unsigned int nentries_mask;
struct sk_buff *skb;
spin_lock_bh(&ab->ce.ce_lock);
sw_index = pipe->src_ring->sw_index;
nentries_mask = pipe->src_ring->nentries_mask;
srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id];
spin_lock_bh(&srng->lock);
ath12k_hal_srng_access_begin(ab, srng);
desc = ath12k_hal_srng_src_reap_next(ab, srng);
if (!desc) {
skb = ERR_PTR(-EIO);
goto err_unlock;
}
skb = pipe->src_ring->skb[sw_index];
pipe->src_ring->skb[sw_index] = NULL;
sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
pipe->src_ring->sw_index = sw_index;
err_unlock:
spin_unlock_bh(&srng->lock);
spin_unlock_bh(&ab->ce.ce_lock);
return skb;
}
static void ath12k_ce_send_done_cb(struct ath12k_ce_pipe *pipe)
{
struct ath12k_base *ab = pipe->ab;
struct sk_buff *skb;
while (!IS_ERR(skb = ath12k_ce_completed_send_next(pipe))) {
if (!skb)
continue;
dma_unmap_single(ab->dev, ATH12K_SKB_CB(skb)->paddr, skb->len,
DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
}
}
static void ath12k_ce_srng_msi_ring_params_setup(struct ath12k_base *ab, u32 ce_id,
struct hal_srng_params *ring_params)
{
u32 msi_data_start;
u32 msi_data_count, msi_data_idx;
u32 msi_irq_start;
u32 addr_lo;
u32 addr_hi;
int ret;
ret = ath12k_hif_get_user_msi_vector(ab, "CE",
&msi_data_count, &msi_data_start,
&msi_irq_start);
if (ret)
return;
ath12k_hif_get_msi_address(ab, &addr_lo, &addr_hi);
ath12k_hif_get_ce_msi_idx(ab, ce_id, &msi_data_idx);
ring_params->msi_addr = addr_lo;
ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32);
ring_params->msi_data = (msi_data_idx % msi_data_count) + msi_data_start;
ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR;
}
static int ath12k_ce_init_ring(struct ath12k_base *ab,
struct ath12k_ce_ring *ce_ring,
int ce_id, enum hal_ring_type type)
{
struct hal_srng_params params = { 0 };
int ret;
params.ring_base_paddr = ce_ring->base_addr_ce_space;
params.ring_base_vaddr = ce_ring->base_addr_owner_space;
params.num_entries = ce_ring->nentries;
if (!(CE_ATTR_DIS_INTR & ab->hw_params->host_ce_config[ce_id].flags))
ath12k_ce_srng_msi_ring_params_setup(ab, ce_id, &params);
switch (type) {
case HAL_CE_SRC:
if (!(CE_ATTR_DIS_INTR & ab->hw_params->host_ce_config[ce_id].flags))
params.intr_batch_cntr_thres_entries = 1;
break;
case HAL_CE_DST:
params.max_buffer_len = ab->hw_params->host_ce_config[ce_id].src_sz_max;
if (!(ab->hw_params->host_ce_config[ce_id].flags & CE_ATTR_DIS_INTR)) {
params.intr_timer_thres_us = 1024;
params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN;
params.low_threshold = ce_ring->nentries - 3;
}
break;
case HAL_CE_DST_STATUS:
if (!(ab->hw_params->host_ce_config[ce_id].flags & CE_ATTR_DIS_INTR)) {
params.intr_batch_cntr_thres_entries = 1;
params.intr_timer_thres_us = 0x1000;
}
break;
default:
ath12k_warn(ab, "Invalid CE ring type %d\n", type);
return -EINVAL;
}
/* TODO: Init other params needed by HAL to init the ring */
ret = ath12k_hal_srng_setup(ab, type, ce_id, 0, &params);
if (ret < 0) {
ath12k_warn(ab, "failed to setup srng: %d ring_id %d\n",
ret, ce_id);
return ret;
}
ce_ring->hal_ring_id = ret;
return 0;
}
static struct ath12k_ce_ring *
ath12k_ce_alloc_ring(struct ath12k_base *ab, int nentries, int desc_sz)
{
struct ath12k_ce_ring *ce_ring;
dma_addr_t base_addr;
ce_ring = kzalloc(struct_size(ce_ring, skb, nentries), GFP_KERNEL);
if (!ce_ring)
return ERR_PTR(-ENOMEM);
ce_ring->nentries = nentries;
ce_ring->nentries_mask = nentries - 1;
/* Legacy platforms that do not support cache
* coherent DMA are unsupported
*/
ce_ring->base_addr_owner_space_unaligned =
dma_alloc_coherent(ab->dev,
nentries * desc_sz + CE_DESC_RING_ALIGN,
&base_addr, GFP_KERNEL);
if (!ce_ring->base_addr_owner_space_unaligned) {
kfree(ce_ring);
return ERR_PTR(-ENOMEM);
}
ce_ring->base_addr_ce_space_unaligned = base_addr;
ce_ring->base_addr_owner_space =
PTR_ALIGN(ce_ring->base_addr_owner_space_unaligned,
CE_DESC_RING_ALIGN);
ce_ring->base_addr_ce_space = ALIGN(ce_ring->base_addr_ce_space_unaligned,
CE_DESC_RING_ALIGN);
return ce_ring;
}
static int ath12k_ce_alloc_pipe(struct ath12k_base *ab, int ce_id)
{
struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id];
const struct ce_attr *attr = &ab->hw_params->host_ce_config[ce_id];
struct ath12k_ce_ring *ring;
int nentries;
int desc_sz;
pipe->attr_flags = attr->flags;
if (attr->src_nentries) {
pipe->send_cb = ath12k_ce_send_done_cb;
nentries = roundup_pow_of_two(attr->src_nentries);
desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_SRC);
ring = ath12k_ce_alloc_ring(ab, nentries, desc_sz);
if (IS_ERR(ring))
return PTR_ERR(ring);
pipe->src_ring = ring;
}
if (attr->dest_nentries) {
pipe->recv_cb = attr->recv_cb;
nentries = roundup_pow_of_two(attr->dest_nentries);
desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST);
ring = ath12k_ce_alloc_ring(ab, nentries, desc_sz);
if (IS_ERR(ring))
return PTR_ERR(ring);
pipe->dest_ring = ring;
desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS);
ring = ath12k_ce_alloc_ring(ab, nentries, desc_sz);
if (IS_ERR(ring))
return PTR_ERR(ring);
pipe->status_ring = ring;
}
return 0;
}
void ath12k_ce_per_engine_service(struct ath12k_base *ab, u16 ce_id)
{
struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id];
if (pipe->send_cb)
pipe->send_cb(pipe);
if (pipe->recv_cb)
ath12k_ce_recv_process_cb(pipe);
}
void ath12k_ce_poll_send_completed(struct ath12k_base *ab, u8 pipe_id)
{
struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id];
if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb)
pipe->send_cb(pipe);
}
int ath12k_ce_send(struct ath12k_base *ab, struct sk_buff *skb, u8 pipe_id,
u16 transfer_id)
{
struct ath12k_ce_pipe *pipe = &ab->ce.ce_pipe[pipe_id];
struct hal_ce_srng_src_desc *desc;
struct hal_srng *srng;
unsigned int write_index, sw_index;
unsigned int nentries_mask;
int ret = 0;
u8 byte_swap_data = 0;
int num_used;
/* Check if some entries could be regained by handling tx completion if
* the CE has interrupts disabled and the used entries is more than the
* defined usage threshold.
*/
if (pipe->attr_flags & CE_ATTR_DIS_INTR) {
spin_lock_bh(&ab->ce.ce_lock);
write_index = pipe->src_ring->write_index;
sw_index = pipe->src_ring->sw_index;
if (write_index >= sw_index)
num_used = write_index - sw_index;
else
num_used = pipe->src_ring->nentries - sw_index +
write_index;
spin_unlock_bh(&ab->ce.ce_lock);
if (num_used > ATH12K_CE_USAGE_THRESHOLD)
ath12k_ce_poll_send_completed(ab, pipe->pipe_num);
}
if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
return -ESHUTDOWN;
spin_lock_bh(&ab->ce.ce_lock);
write_index = pipe->src_ring->write_index;
nentries_mask = pipe->src_ring->nentries_mask;
srng = &ab->hal.srng_list[pipe->src_ring->hal_ring_id];
spin_lock_bh(&srng->lock);
ath12k_hal_srng_access_begin(ab, srng);
if (unlikely(ath12k_hal_srng_src_num_free(ab, srng, false) < 1)) {
ath12k_hal_srng_access_end(ab, srng);
ret = -ENOBUFS;
goto unlock;
}
desc = ath12k_hal_srng_src_get_next_reaped(ab, srng);
if (!desc) {
ath12k_hal_srng_access_end(ab, srng);
ret = -ENOBUFS;
goto unlock;
}
if (pipe->attr_flags & CE_ATTR_BYTE_SWAP_DATA)
byte_swap_data = 1;
ath12k_hal_ce_src_set_desc(desc, ATH12K_SKB_CB(skb)->paddr,
skb->len, transfer_id, byte_swap_data);
pipe->src_ring->skb[write_index] = skb;
pipe->src_ring->write_index = CE_RING_IDX_INCR(nentries_mask,
write_index);
ath12k_hal_srng_access_end(ab, srng);
unlock:
spin_unlock_bh(&srng->lock);
spin_unlock_bh(&ab->ce.ce_lock);
return ret;
}
static void ath12k_ce_rx_pipe_cleanup(struct ath12k_ce_pipe *pipe)
{
struct ath12k_base *ab = pipe->ab;
struct ath12k_ce_ring *ring = pipe->dest_ring;
struct sk_buff *skb;
int i;
if (!(ring && pipe->buf_sz))
return;
for (i = 0; i < ring->nentries; i++) {
skb = ring->skb[i];
if (!skb)
continue;
ring->skb[i] = NULL;
dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr,
skb->len + skb_tailroom(skb), DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
}
}
void ath12k_ce_cleanup_pipes(struct ath12k_base *ab)
{
struct ath12k_ce_pipe *pipe;
int pipe_num;
for (pipe_num = 0; pipe_num < ab->hw_params->ce_count; pipe_num++) {
pipe = &ab->ce.ce_pipe[pipe_num];
ath12k_ce_rx_pipe_cleanup(pipe);
/* Cleanup any src CE's which have interrupts disabled */
ath12k_ce_poll_send_completed(ab, pipe_num);
/* NOTE: Should we also clean up tx buffer in all pipes? */
}
}
void ath12k_ce_rx_post_buf(struct ath12k_base *ab)
{
struct ath12k_ce_pipe *pipe;
int i;
int ret;
for (i = 0; i < ab->hw_params->ce_count; i++) {
pipe = &ab->ce.ce_pipe[i];
ret = ath12k_ce_rx_post_pipe(pipe);
if (ret) {
if (ret == -ENOSPC)
continue;
ath12k_warn(ab, "failed to post rx buf to pipe: %d err: %d\n",
i, ret);
mod_timer(&ab->rx_replenish_retry,
jiffies + ATH12K_CE_RX_POST_RETRY_JIFFIES);
return;
}
}
}
void ath12k_ce_rx_replenish_retry(struct timer_list *t)
{
struct ath12k_base *ab = from_timer(ab, t, rx_replenish_retry);
ath12k_ce_rx_post_buf(ab);
}
static void ath12k_ce_shadow_config(struct ath12k_base *ab)
{
int i;
for (i = 0; i < ab->hw_params->ce_count; i++) {
if (ab->hw_params->host_ce_config[i].src_nentries)
ath12k_hal_srng_update_shadow_config(ab, HAL_CE_SRC, i);
if (ab->hw_params->host_ce_config[i].dest_nentries) {
ath12k_hal_srng_update_shadow_config(ab, HAL_CE_DST, i);
ath12k_hal_srng_update_shadow_config(ab, HAL_CE_DST_STATUS, i);
}
}
}
void ath12k_ce_get_shadow_config(struct ath12k_base *ab,
u32 **shadow_cfg, u32 *shadow_cfg_len)
{
if (!ab->hw_params->supports_shadow_regs)
return;
ath12k_hal_srng_get_shadow_config(ab, shadow_cfg, shadow_cfg_len);
/* shadow is already configured */
if (*shadow_cfg_len)
return;
/* shadow isn't configured yet, configure now.
* non-CE srngs are configured firstly, then
* all CE srngs.
*/
ath12k_hal_srng_shadow_config(ab);
ath12k_ce_shadow_config(ab);
/* get the shadow configuration */
ath12k_hal_srng_get_shadow_config(ab, shadow_cfg, shadow_cfg_len);
}
int ath12k_ce_init_pipes(struct ath12k_base *ab)
{
struct ath12k_ce_pipe *pipe;
int i;
int ret;
ath12k_ce_get_shadow_config(ab, &ab->qmi.ce_cfg.shadow_reg_v3,
&ab->qmi.ce_cfg.shadow_reg_v3_len);
for (i = 0; i < ab->hw_params->ce_count; i++) {
pipe = &ab->ce.ce_pipe[i];
if (pipe->src_ring) {
ret = ath12k_ce_init_ring(ab, pipe->src_ring, i,
HAL_CE_SRC);
if (ret) {
ath12k_warn(ab, "failed to init src ring: %d\n",
ret);
/* Should we clear any partial init */
return ret;
}
pipe->src_ring->write_index = 0;
pipe->src_ring->sw_index = 0;
}
if (pipe->dest_ring) {
ret = ath12k_ce_init_ring(ab, pipe->dest_ring, i,
HAL_CE_DST);
if (ret) {
ath12k_warn(ab, "failed to init dest ring: %d\n",
ret);
/* Should we clear any partial init */
return ret;
}
pipe->rx_buf_needed = pipe->dest_ring->nentries ?
pipe->dest_ring->nentries - 2 : 0;
pipe->dest_ring->write_index = 0;
pipe->dest_ring->sw_index = 0;
}
if (pipe->status_ring) {
ret = ath12k_ce_init_ring(ab, pipe->status_ring, i,
HAL_CE_DST_STATUS);
if (ret) {
ath12k_warn(ab, "failed to init dest status ing: %d\n",
ret);
/* Should we clear any partial init */
return ret;
}
pipe->status_ring->write_index = 0;
pipe->status_ring->sw_index = 0;
}
}
return 0;
}
void ath12k_ce_free_pipes(struct ath12k_base *ab)
{
struct ath12k_ce_pipe *pipe;
int desc_sz;
int i;
for (i = 0; i < ab->hw_params->ce_count; i++) {
pipe = &ab->ce.ce_pipe[i];
if (pipe->src_ring) {
desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_SRC);
dma_free_coherent(ab->dev,
pipe->src_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
pipe->src_ring->base_addr_owner_space,
pipe->src_ring->base_addr_ce_space);
kfree(pipe->src_ring);
pipe->src_ring = NULL;
}
if (pipe->dest_ring) {
desc_sz = ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST);
dma_free_coherent(ab->dev,
pipe->dest_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
pipe->dest_ring->base_addr_owner_space,
pipe->dest_ring->base_addr_ce_space);
kfree(pipe->dest_ring);
pipe->dest_ring = NULL;
}
if (pipe->status_ring) {
desc_sz =
ath12k_hal_ce_get_desc_size(HAL_CE_DESC_DST_STATUS);
dma_free_coherent(ab->dev,
pipe->status_ring->nentries * desc_sz +
CE_DESC_RING_ALIGN,
pipe->status_ring->base_addr_owner_space,
pipe->status_ring->base_addr_ce_space);
kfree(pipe->status_ring);
pipe->status_ring = NULL;
}
}
}
int ath12k_ce_alloc_pipes(struct ath12k_base *ab)
{
struct ath12k_ce_pipe *pipe;
int i;
int ret;
const struct ce_attr *attr;
spin_lock_init(&ab->ce.ce_lock);
for (i = 0; i < ab->hw_params->ce_count; i++) {
attr = &ab->hw_params->host_ce_config[i];
pipe = &ab->ce.ce_pipe[i];
pipe->pipe_num = i;
pipe->ab = ab;
pipe->buf_sz = attr->src_sz_max;
ret = ath12k_ce_alloc_pipe(ab, i);
if (ret) {
/* Free any parial successful allocation */
ath12k_ce_free_pipes(ab);
return ret;
}
}
return 0;
}
int ath12k_ce_get_attr_flags(struct ath12k_base *ab, int ce_id)
{
if (ce_id >= ab->hw_params->ce_count)
return -EINVAL;
return ab->hw_params->host_ce_config[ce_id].flags;
}

View file

@ -0,0 +1,184 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_CE_H
#define ATH12K_CE_H
#define CE_COUNT_MAX 16
/* 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. */
#define CE_ATTR_FLAGS 0
/* Threshold to poll for tx completion in case of Interrupt disabled CE's */
#define ATH12K_CE_USAGE_THRESHOLD 32
/* 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
#define CE_HOST_IE_3_SHIFT 0xC
#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
#define ATH12K_CE_RX_POST_RETRY_JIFFIES 50
struct ath12k_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_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 ath12k_base *ab, struct sk_buff *skb);
};
#define CE_DESC_RING_ALIGN 8
struct ath12k_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 */
u32 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 */
u32 base_addr_ce_space;
/* HAL ring id */
u32 hal_ring_id;
/* keep last */
struct sk_buff *skb[];
};
struct ath12k_ce_pipe {
struct ath12k_base *ab;
u16 pipe_num;
unsigned int attr_flags;
unsigned int buf_sz;
unsigned int rx_buf_needed;
void (*send_cb)(struct ath12k_ce_pipe *pipe);
void (*recv_cb)(struct ath12k_base *ab, struct sk_buff *skb);
struct tasklet_struct intr_tq;
struct ath12k_ce_ring *src_ring;
struct ath12k_ce_ring *dest_ring;
struct ath12k_ce_ring *status_ring;
u64 timestamp;
};
struct ath12k_ce {
struct ath12k_ce_pipe ce_pipe[CE_COUNT_MAX];
/* Protects rings of all ce pipes */
spinlock_t ce_lock;
struct ath12k_hp_update_timer hp_timer[CE_COUNT_MAX];
};
extern const struct ce_attr ath12k_host_ce_config_qcn9274[];
extern const struct ce_attr ath12k_host_ce_config_wcn7850[];
void ath12k_ce_cleanup_pipes(struct ath12k_base *ab);
void ath12k_ce_rx_replenish_retry(struct timer_list *t);
void ath12k_ce_per_engine_service(struct ath12k_base *ab, u16 ce_id);
int ath12k_ce_send(struct ath12k_base *ab, struct sk_buff *skb, u8 pipe_id,
u16 transfer_id);
void ath12k_ce_rx_post_buf(struct ath12k_base *ab);
int ath12k_ce_init_pipes(struct ath12k_base *ab);
int ath12k_ce_alloc_pipes(struct ath12k_base *ab);
void ath12k_ce_free_pipes(struct ath12k_base *ab);
int ath12k_ce_get_attr_flags(struct ath12k_base *ab, int ce_id);
void ath12k_ce_poll_send_completed(struct ath12k_base *ab, u8 pipe_id);
int ath12k_ce_map_service_to_pipe(struct ath12k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
int ath12k_ce_attr_attach(struct ath12k_base *ab);
void ath12k_ce_get_shadow_config(struct ath12k_base *ab,
u32 **shadow_cfg, u32 *shadow_cfg_len);
#endif

View file

@ -0,0 +1,939 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/remoteproc.h>
#include <linux/firmware.h>
#include <linux/of.h>
#include "core.h"
#include "dp_tx.h"
#include "dp_rx.h"
#include "debug.h"
#include "hif.h"
unsigned int ath12k_debug_mask;
module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
MODULE_PARM_DESC(debug_mask, "Debugging mask");
int ath12k_core_suspend(struct ath12k_base *ab)
{
int ret;
if (!ab->hw_params->supports_suspend)
return -EOPNOTSUPP;
/* TODO: there can frames in queues so for now add delay as a hack.
* Need to implement to handle and remove this delay.
*/
msleep(500);
ret = ath12k_dp_rx_pktlog_stop(ab, true);
if (ret) {
ath12k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n",
ret);
return ret;
}
ret = ath12k_dp_rx_pktlog_stop(ab, false);
if (ret) {
ath12k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n",
ret);
return ret;
}
ath12k_hif_irq_disable(ab);
ath12k_hif_ce_irq_disable(ab);
ret = ath12k_hif_suspend(ab);
if (ret) {
ath12k_warn(ab, "failed to suspend hif: %d\n", ret);
return ret;
}
return 0;
}
int ath12k_core_resume(struct ath12k_base *ab)
{
int ret;
if (!ab->hw_params->supports_suspend)
return -EOPNOTSUPP;
ret = ath12k_hif_resume(ab);
if (ret) {
ath12k_warn(ab, "failed to resume hif during resume: %d\n", ret);
return ret;
}
ath12k_hif_ce_irq_enable(ab);
ath12k_hif_irq_enable(ab);
ret = ath12k_dp_rx_pktlog_start(ab);
if (ret) {
ath12k_warn(ab, "failed to start rx pktlog during resume: %d\n",
ret);
return ret;
}
return 0;
}
static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name,
size_t name_len)
{
/* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */
char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 };
if (ab->qmi.target.bdf_ext[0] != '\0')
scnprintf(variant, sizeof(variant), ",variant=%s",
ab->qmi.target.bdf_ext);
scnprintf(name, name_len,
"bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s",
ath12k_bus_str(ab->hif.bus),
ab->qmi.target.chip_id,
ab->qmi.target.board_id, variant);
ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot using board name '%s'\n", name);
return 0;
}
const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab,
const char *file)
{
const struct firmware *fw;
char path[100];
int ret;
if (!file)
return ERR_PTR(-ENOENT);
ath12k_core_create_firmware_path(ab, file, path, sizeof(path));
ret = firmware_request_nowarn(&fw, path, ab->dev);
if (ret)
return ERR_PTR(ret);
ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot firmware request %s size %zu\n",
path, fw->size);
return fw;
}
void ath12k_core_free_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd)
{
if (!IS_ERR(bd->fw))
release_firmware(bd->fw);
memset(bd, 0, sizeof(*bd));
}
static int ath12k_core_parse_bd_ie_board(struct ath12k_base *ab,
struct ath12k_board_data *bd,
const void *buf, size_t buf_len,
const char *boardname,
int bd_ie_type)
{
const struct ath12k_fw_ie *hdr;
bool name_match_found;
int ret, board_ie_id;
size_t board_ie_len;
const void *board_ie_data;
name_match_found = false;
/* go through ATH12K_BD_IE_BOARD_ elements */
while (buf_len > sizeof(struct ath12k_fw_ie)) {
hdr = buf;
board_ie_id = le32_to_cpu(hdr->id);
board_ie_len = le32_to_cpu(hdr->len);
board_ie_data = hdr->data;
buf_len -= sizeof(*hdr);
buf += sizeof(*hdr);
if (buf_len < ALIGN(board_ie_len, 4)) {
ath12k_err(ab, "invalid ATH12K_BD_IE_BOARD length: %zu < %zu\n",
buf_len, ALIGN(board_ie_len, 4));
ret = -EINVAL;
goto out;
}
switch (board_ie_id) {
case ATH12K_BD_IE_BOARD_NAME:
ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "board name", "",
board_ie_data, board_ie_len);
if (board_ie_len != strlen(boardname))
break;
ret = memcmp(board_ie_data, boardname, strlen(boardname));
if (ret)
break;
name_match_found = true;
ath12k_dbg(ab, ATH12K_DBG_BOOT,
"boot found match for name '%s'",
boardname);
break;
case ATH12K_BD_IE_BOARD_DATA:
if (!name_match_found)
/* no match found */
break;
ath12k_dbg(ab, ATH12K_DBG_BOOT,
"boot found board data for '%s'", boardname);
bd->data = board_ie_data;
bd->len = board_ie_len;
ret = 0;
goto out;
default:
ath12k_warn(ab, "unknown ATH12K_BD_IE_BOARD found: %d\n",
board_ie_id);
break;
}
/* jump over the padding */
board_ie_len = ALIGN(board_ie_len, 4);
buf_len -= board_ie_len;
buf += board_ie_len;
}
/* no match found */
ret = -ENOENT;
out:
return ret;
}
static int ath12k_core_fetch_board_data_api_n(struct ath12k_base *ab,
struct ath12k_board_data *bd,
const char *boardname)
{
size_t len, magic_len;
const u8 *data;
char *filename, filepath[100];
size_t ie_len;
struct ath12k_fw_ie *hdr;
int ret, ie_id;
filename = ATH12K_BOARD_API2_FILE;
if (!bd->fw)
bd->fw = ath12k_core_firmware_request(ab, filename);
if (IS_ERR(bd->fw))
return PTR_ERR(bd->fw);
data = bd->fw->data;
len = bd->fw->size;
ath12k_core_create_firmware_path(ab, filename,
filepath, sizeof(filepath));
/* magic has extra null byte padded */
magic_len = strlen(ATH12K_BOARD_MAGIC) + 1;
if (len < magic_len) {
ath12k_err(ab, "failed to find magic value in %s, file too short: %zu\n",
filepath, len);
ret = -EINVAL;
goto err;
}
if (memcmp(data, ATH12K_BOARD_MAGIC, magic_len)) {
ath12k_err(ab, "found invalid board magic\n");
ret = -EINVAL;
goto err;
}
/* magic is padded to 4 bytes */
magic_len = ALIGN(magic_len, 4);
if (len < magic_len) {
ath12k_err(ab, "failed: %s too small to contain board data, len: %zu\n",
filepath, len);
ret = -EINVAL;
goto err;
}
data += magic_len;
len -= magic_len;
while (len > sizeof(struct ath12k_fw_ie)) {
hdr = (struct ath12k_fw_ie *)data;
ie_id = le32_to_cpu(hdr->id);
ie_len = le32_to_cpu(hdr->len);
len -= sizeof(*hdr);
data = hdr->data;
if (len < ALIGN(ie_len, 4)) {
ath12k_err(ab, "invalid length for board ie_id %d ie_len %zu len %zu\n",
ie_id, ie_len, len);
ret = -EINVAL;
goto err;
}
switch (ie_id) {
case ATH12K_BD_IE_BOARD:
ret = ath12k_core_parse_bd_ie_board(ab, bd, data,
ie_len,
boardname,
ATH12K_BD_IE_BOARD);
if (ret == -ENOENT)
/* no match found, continue */
break;
else if (ret)
/* there was an error, bail out */
goto err;
/* either found or error, so stop searching */
goto out;
}
/* jump over the padding */
ie_len = ALIGN(ie_len, 4);
len -= ie_len;
data += ie_len;
}
out:
if (!bd->data || !bd->len) {
ath12k_err(ab,
"failed to fetch board data for %s from %s\n",
boardname, filepath);
ret = -ENODATA;
goto err;
}
return 0;
err:
ath12k_core_free_bdf(ab, bd);
return ret;
}
int ath12k_core_fetch_board_data_api_1(struct ath12k_base *ab,
struct ath12k_board_data *bd,
char *filename)
{
bd->fw = ath12k_core_firmware_request(ab, filename);
if (IS_ERR(bd->fw))
return PTR_ERR(bd->fw);
bd->data = bd->fw->data;
bd->len = bd->fw->size;
return 0;
}
#define BOARD_NAME_SIZE 100
int ath12k_core_fetch_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd)
{
char boardname[BOARD_NAME_SIZE];
int ret;
ret = ath12k_core_create_board_name(ab, boardname, BOARD_NAME_SIZE);
if (ret) {
ath12k_err(ab, "failed to create board name: %d", ret);
return ret;
}
ab->bd_api = 2;
ret = ath12k_core_fetch_board_data_api_n(ab, bd, boardname);
if (!ret)
goto success;
ab->bd_api = 1;
ret = ath12k_core_fetch_board_data_api_1(ab, bd, ATH12K_DEFAULT_BOARD_FILE);
if (ret) {
ath12k_err(ab, "failed to fetch board-2.bin or board.bin from %s\n",
ab->hw_params->fw.dir);
return ret;
}
success:
ath12k_dbg(ab, ATH12K_DBG_BOOT, "using board api %d\n", ab->bd_api);
return 0;
}
static void ath12k_core_stop(struct ath12k_base *ab)
{
if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
ath12k_qmi_firmware_stop(ab);
ath12k_hif_stop(ab);
ath12k_wmi_detach(ab);
ath12k_dp_rx_pdev_reo_cleanup(ab);
/* De-Init of components as needed */
}
static int ath12k_core_soc_create(struct ath12k_base *ab)
{
int ret;
ret = ath12k_qmi_init_service(ab);
if (ret) {
ath12k_err(ab, "failed to initialize qmi :%d\n", ret);
return ret;
}
ret = ath12k_hif_power_up(ab);
if (ret) {
ath12k_err(ab, "failed to power up :%d\n", ret);
goto err_qmi_deinit;
}
return 0;
err_qmi_deinit:
ath12k_qmi_deinit_service(ab);
return ret;
}
static void ath12k_core_soc_destroy(struct ath12k_base *ab)
{
ath12k_dp_free(ab);
ath12k_reg_free(ab);
ath12k_qmi_deinit_service(ab);
}
static int ath12k_core_pdev_create(struct ath12k_base *ab)
{
int ret;
ret = ath12k_mac_register(ab);
if (ret) {
ath12k_err(ab, "failed register the radio with mac80211: %d\n", ret);
return ret;
}
ret = ath12k_dp_pdev_alloc(ab);
if (ret) {
ath12k_err(ab, "failed to attach DP pdev: %d\n", ret);
goto err_mac_unregister;
}
return 0;
err_mac_unregister:
ath12k_mac_unregister(ab);
return ret;
}
static void ath12k_core_pdev_destroy(struct ath12k_base *ab)
{
ath12k_mac_unregister(ab);
ath12k_hif_irq_disable(ab);
ath12k_dp_pdev_free(ab);
}
static int ath12k_core_start(struct ath12k_base *ab,
enum ath12k_firmware_mode mode)
{
int ret;
ret = ath12k_wmi_attach(ab);
if (ret) {
ath12k_err(ab, "failed to attach wmi: %d\n", ret);
return ret;
}
ret = ath12k_htc_init(ab);
if (ret) {
ath12k_err(ab, "failed to init htc: %d\n", ret);
goto err_wmi_detach;
}
ret = ath12k_hif_start(ab);
if (ret) {
ath12k_err(ab, "failed to start HIF: %d\n", ret);
goto err_wmi_detach;
}
ret = ath12k_htc_wait_target(&ab->htc);
if (ret) {
ath12k_err(ab, "failed to connect to HTC: %d\n", ret);
goto err_hif_stop;
}
ret = ath12k_dp_htt_connect(&ab->dp);
if (ret) {
ath12k_err(ab, "failed to connect to HTT: %d\n", ret);
goto err_hif_stop;
}
ret = ath12k_wmi_connect(ab);
if (ret) {
ath12k_err(ab, "failed to connect wmi: %d\n", ret);
goto err_hif_stop;
}
ret = ath12k_htc_start(&ab->htc);
if (ret) {
ath12k_err(ab, "failed to start HTC: %d\n", ret);
goto err_hif_stop;
}
ret = ath12k_wmi_wait_for_service_ready(ab);
if (ret) {
ath12k_err(ab, "failed to receive wmi service ready event: %d\n",
ret);
goto err_hif_stop;
}
ret = ath12k_mac_allocate(ab);
if (ret) {
ath12k_err(ab, "failed to create new hw device with mac80211 :%d\n",
ret);
goto err_hif_stop;
}
ath12k_dp_cc_config(ab);
ath12k_dp_pdev_pre_alloc(ab);
ret = ath12k_dp_rx_pdev_reo_setup(ab);
if (ret) {
ath12k_err(ab, "failed to initialize reo destination rings: %d\n", ret);
goto err_mac_destroy;
}
ret = ath12k_wmi_cmd_init(ab);
if (ret) {
ath12k_err(ab, "failed to send wmi init cmd: %d\n", ret);
goto err_reo_cleanup;
}
ret = ath12k_wmi_wait_for_unified_ready(ab);
if (ret) {
ath12k_err(ab, "failed to receive wmi unified ready event: %d\n",
ret);
goto err_reo_cleanup;
}
/* put hardware to DBS mode */
if (ab->hw_params->single_pdev_only) {
ret = ath12k_wmi_set_hw_mode(ab, WMI_HOST_HW_MODE_DBS);
if (ret) {
ath12k_err(ab, "failed to send dbs mode: %d\n", ret);
goto err_reo_cleanup;
}
}
ret = ath12k_dp_tx_htt_h2t_ver_req_msg(ab);
if (ret) {
ath12k_err(ab, "failed to send htt version request message: %d\n",
ret);
goto err_reo_cleanup;
}
return 0;
err_reo_cleanup:
ath12k_dp_rx_pdev_reo_cleanup(ab);
err_mac_destroy:
ath12k_mac_destroy(ab);
err_hif_stop:
ath12k_hif_stop(ab);
err_wmi_detach:
ath12k_wmi_detach(ab);
return ret;
}
static int ath12k_core_start_firmware(struct ath12k_base *ab,
enum ath12k_firmware_mode mode)
{
int ret;
ath12k_ce_get_shadow_config(ab, &ab->qmi.ce_cfg.shadow_reg_v3,
&ab->qmi.ce_cfg.shadow_reg_v3_len);
ret = ath12k_qmi_firmware_start(ab, mode);
if (ret) {
ath12k_err(ab, "failed to send firmware start: %d\n", ret);
return ret;
}
return ret;
}
int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab)
{
int ret;
ret = ath12k_core_start_firmware(ab, ATH12K_FIRMWARE_MODE_NORMAL);
if (ret) {
ath12k_err(ab, "failed to start firmware: %d\n", ret);
return ret;
}
ret = ath12k_ce_init_pipes(ab);
if (ret) {
ath12k_err(ab, "failed to initialize CE: %d\n", ret);
goto err_firmware_stop;
}
ret = ath12k_dp_alloc(ab);
if (ret) {
ath12k_err(ab, "failed to init DP: %d\n", ret);
goto err_firmware_stop;
}
mutex_lock(&ab->core_lock);
ret = ath12k_core_start(ab, ATH12K_FIRMWARE_MODE_NORMAL);
if (ret) {
ath12k_err(ab, "failed to start core: %d\n", ret);
goto err_dp_free;
}
ret = ath12k_core_pdev_create(ab);
if (ret) {
ath12k_err(ab, "failed to create pdev core: %d\n", ret);
goto err_core_stop;
}
ath12k_hif_irq_enable(ab);
mutex_unlock(&ab->core_lock);
return 0;
err_core_stop:
ath12k_core_stop(ab);
ath12k_mac_destroy(ab);
err_dp_free:
ath12k_dp_free(ab);
mutex_unlock(&ab->core_lock);
err_firmware_stop:
ath12k_qmi_firmware_stop(ab);
return ret;
}
static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab)
{
int ret;
mutex_lock(&ab->core_lock);
ath12k_hif_irq_disable(ab);
ath12k_dp_pdev_free(ab);
ath12k_hif_stop(ab);
ath12k_wmi_detach(ab);
ath12k_dp_rx_pdev_reo_cleanup(ab);
mutex_unlock(&ab->core_lock);
ath12k_dp_free(ab);
ath12k_hal_srng_deinit(ab);
ab->free_vdev_map = (1LL << (ab->num_radios * TARGET_NUM_VDEVS)) - 1;
ret = ath12k_hal_srng_init(ab);
if (ret)
return ret;
clear_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags);
ret = ath12k_core_qmi_firmware_ready(ab);
if (ret)
goto err_hal_srng_deinit;
clear_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags);
return 0;
err_hal_srng_deinit:
ath12k_hal_srng_deinit(ab);
return ret;
}
void ath12k_core_halt(struct ath12k *ar)
{
struct ath12k_base *ab = ar->ab;
lockdep_assert_held(&ar->conf_mutex);
ar->num_created_vdevs = 0;
ar->allocated_vdev_map = 0;
ath12k_mac_scan_finish(ar);
ath12k_mac_peer_cleanup_all(ar);
cancel_delayed_work_sync(&ar->scan.timeout);
cancel_work_sync(&ar->regd_update_work);
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL);
synchronize_rcu();
INIT_LIST_HEAD(&ar->arvifs);
idr_init(&ar->txmgmt_idr);
}
static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
{
struct ath12k *ar;
struct ath12k_pdev *pdev;
int i;
spin_lock_bh(&ab->base_lock);
ab->stats.fw_crash_counter++;
spin_unlock_bh(&ab->base_lock);
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
if (!ar || ar->state == ATH12K_STATE_OFF)
continue;
ieee80211_stop_queues(ar->hw);
ath12k_mac_drain_tx(ar);
complete(&ar->scan.started);
complete(&ar->scan.completed);
complete(&ar->peer_assoc_done);
complete(&ar->peer_delete_done);
complete(&ar->install_key_done);
complete(&ar->vdev_setup_done);
complete(&ar->vdev_delete_done);
complete(&ar->bss_survey_done);
wake_up(&ar->dp.tx_empty_waitq);
idr_for_each(&ar->txmgmt_idr,
ath12k_mac_tx_mgmt_pending_free, ar);
idr_destroy(&ar->txmgmt_idr);
}
wake_up(&ab->wmi_ab.tx_credits_wq);
wake_up(&ab->peer_mapping_wq);
}
static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab)
{
struct ath12k *ar;
struct ath12k_pdev *pdev;
int i;
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
if (!ar || ar->state == ATH12K_STATE_OFF)
continue;
mutex_lock(&ar->conf_mutex);
switch (ar->state) {
case ATH12K_STATE_ON:
ar->state = ATH12K_STATE_RESTARTING;
ath12k_core_halt(ar);
ieee80211_restart_hw(ar->hw);
break;
case ATH12K_STATE_OFF:
ath12k_warn(ab,
"cannot restart radio %d that hasn't been started\n",
i);
break;
case ATH12K_STATE_RESTARTING:
break;
case ATH12K_STATE_RESTARTED:
ar->state = ATH12K_STATE_WEDGED;
fallthrough;
case ATH12K_STATE_WEDGED:
ath12k_warn(ab,
"device is wedged, will not restart radio %d\n", i);
break;
}
mutex_unlock(&ar->conf_mutex);
}
complete(&ab->driver_recovery);
}
static void ath12k_core_restart(struct work_struct *work)
{
struct ath12k_base *ab = container_of(work, struct ath12k_base, restart_work);
int ret;
if (!ab->is_reset)
ath12k_core_pre_reconfigure_recovery(ab);
ret = ath12k_core_reconfigure_on_crash(ab);
if (ret) {
ath12k_err(ab, "failed to reconfigure driver on crash recovery\n");
return;
}
if (ab->is_reset)
complete_all(&ab->reconfigure_complete);
if (!ab->is_reset)
ath12k_core_post_reconfigure_recovery(ab);
}
static void ath12k_core_reset(struct work_struct *work)
{
struct ath12k_base *ab = container_of(work, struct ath12k_base, reset_work);
int reset_count, fail_cont_count;
long time_left;
if (!(test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags))) {
ath12k_warn(ab, "ignore reset dev flags 0x%lx\n", ab->dev_flags);
return;
}
/* Sometimes the recovery will fail and then the next all recovery fail,
* this is to avoid infinite recovery since it can not recovery success
*/
fail_cont_count = atomic_read(&ab->fail_cont_count);
if (fail_cont_count >= ATH12K_RESET_MAX_FAIL_COUNT_FINAL)
return;
if (fail_cont_count >= ATH12K_RESET_MAX_FAIL_COUNT_FIRST &&
time_before(jiffies, ab->reset_fail_timeout))
return;
reset_count = atomic_inc_return(&ab->reset_count);
if (reset_count > 1) {
/* Sometimes it happened another reset worker before the previous one
* completed, then the second reset worker will destroy the previous one,
* thus below is to avoid that.
*/
ath12k_warn(ab, "already resetting count %d\n", reset_count);
reinit_completion(&ab->reset_complete);
time_left = wait_for_completion_timeout(&ab->reset_complete,
ATH12K_RESET_TIMEOUT_HZ);
if (time_left) {
ath12k_dbg(ab, ATH12K_DBG_BOOT, "to skip reset\n");
atomic_dec(&ab->reset_count);
return;
}
ab->reset_fail_timeout = jiffies + ATH12K_RESET_FAIL_TIMEOUT_HZ;
/* Record the continuous recovery fail count when recovery failed*/
fail_cont_count = atomic_inc_return(&ab->fail_cont_count);
}
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset starting\n");
ab->is_reset = true;
atomic_set(&ab->recovery_count, 0);
ath12k_core_pre_reconfigure_recovery(ab);
reinit_completion(&ab->reconfigure_complete);
ath12k_core_post_reconfigure_recovery(ab);
reinit_completion(&ab->recovery_start);
atomic_set(&ab->recovery_start_count, 0);
ath12k_dbg(ab, ATH12K_DBG_BOOT, "waiting recovery start...\n");
time_left = wait_for_completion_timeout(&ab->recovery_start,
ATH12K_RECOVER_START_TIMEOUT_HZ);
ath12k_hif_power_down(ab);
ath12k_hif_power_up(ab);
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n");
}
int ath12k_core_pre_init(struct ath12k_base *ab)
{
int ret;
ret = ath12k_hw_init(ab);
if (ret) {
ath12k_err(ab, "failed to init hw params: %d\n", ret);
return ret;
}
return 0;
}
int ath12k_core_init(struct ath12k_base *ab)
{
int ret;
ret = ath12k_core_soc_create(ab);
if (ret) {
ath12k_err(ab, "failed to create soc core: %d\n", ret);
return ret;
}
return 0;
}
void ath12k_core_deinit(struct ath12k_base *ab)
{
mutex_lock(&ab->core_lock);
ath12k_core_pdev_destroy(ab);
ath12k_core_stop(ab);
mutex_unlock(&ab->core_lock);
ath12k_hif_power_down(ab);
ath12k_mac_destroy(ab);
ath12k_core_soc_destroy(ab);
}
void ath12k_core_free(struct ath12k_base *ab)
{
destroy_workqueue(ab->workqueue_aux);
destroy_workqueue(ab->workqueue);
kfree(ab);
}
struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
enum ath12k_bus bus)
{
struct ath12k_base *ab;
ab = kzalloc(sizeof(*ab) + priv_size, GFP_KERNEL);
if (!ab)
return NULL;
init_completion(&ab->driver_recovery);
ab->workqueue = create_singlethread_workqueue("ath12k_wq");
if (!ab->workqueue)
goto err_sc_free;
ab->workqueue_aux = create_singlethread_workqueue("ath12k_aux_wq");
if (!ab->workqueue_aux)
goto err_free_wq;
mutex_init(&ab->core_lock);
spin_lock_init(&ab->base_lock);
init_completion(&ab->reset_complete);
init_completion(&ab->reconfigure_complete);
init_completion(&ab->recovery_start);
INIT_LIST_HEAD(&ab->peers);
init_waitqueue_head(&ab->peer_mapping_wq);
init_waitqueue_head(&ab->wmi_ab.tx_credits_wq);
INIT_WORK(&ab->restart_work, ath12k_core_restart);
INIT_WORK(&ab->reset_work, ath12k_core_reset);
timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0);
init_completion(&ab->htc_suspend);
ab->dev = dev;
ab->hif.bus = bus;
return ab;
err_free_wq:
destroy_workqueue(ab->workqueue);
err_sc_free:
kfree(ab);
return NULL;
}
MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11be wireless LAN cards.");
MODULE_LICENSE("Dual BSD/GPL");

View file

@ -0,0 +1,822 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_CORE_H
#define ATH12K_CORE_H
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/bitfield.h>
#include "qmi.h"
#include "htc.h"
#include "wmi.h"
#include "hal.h"
#include "dp.h"
#include "ce.h"
#include "mac.h"
#include "hw.h"
#include "hal_rx.h"
#include "reg.h"
#include "dbring.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
#define ATH12K_TX_MGMT_NUM_PENDING_MAX 512
#define ATH12K_TX_MGMT_TARGET_MAX_SUPPORT_WMI 64
/* Pending management packets threshold for dropping probe responses */
#define ATH12K_PRB_RSP_DROP_THRESHOLD ((ATH12K_TX_MGMT_TARGET_MAX_SUPPORT_WMI * 3) / 4)
#define ATH12K_INVALID_HW_MAC_ID 0xFF
#define ATH12K_RX_RATE_TABLE_NUM 320
#define ATH12K_RX_RATE_TABLE_11AX_NUM 576
#define ATH12K_MON_TIMER_INTERVAL 10
#define ATH12K_RESET_TIMEOUT_HZ (20 * HZ)
#define ATH12K_RESET_MAX_FAIL_COUNT_FIRST 3
#define ATH12K_RESET_MAX_FAIL_COUNT_FINAL 5
#define ATH12K_RESET_FAIL_TIMEOUT_HZ (20 * HZ)
#define ATH12K_RECONFIGURE_TIMEOUT_HZ (10 * HZ)
#define ATH12K_RECOVER_START_TIMEOUT_HZ (20 * HZ)
enum wme_ac {
WME_AC_BE,
WME_AC_BK,
WME_AC_VI,
WME_AC_VO,
WME_NUM_AC
};
#define ATH12K_HT_MCS_MAX 7
#define ATH12K_VHT_MCS_MAX 9
#define ATH12K_HE_MCS_MAX 11
enum ath12k_crypt_mode {
/* Only use hardware crypto engine */
ATH12K_CRYPT_MODE_HW,
/* Only use software crypto */
ATH12K_CRYPT_MODE_SW,
};
static inline enum wme_ac ath12k_tid_to_ac(u32 tid)
{
return (((tid == 0) || (tid == 3)) ? WME_AC_BE :
((tid == 1) || (tid == 2)) ? WME_AC_BK :
((tid == 4) || (tid == 5)) ? WME_AC_VI :
WME_AC_VO);
}
enum ath12k_skb_flags {
ATH12K_SKB_HW_80211_ENCAP = BIT(0),
ATH12K_SKB_CIPHER_SET = BIT(1),
};
struct ath12k_skb_cb {
dma_addr_t paddr;
struct ath12k *ar;
struct ieee80211_vif *vif;
dma_addr_t paddr_ext_desc;
u32 cipher;
u8 flags;
};
struct ath12k_skb_rxcb {
dma_addr_t paddr;
bool is_first_msdu;
bool is_last_msdu;
bool is_continuation;
bool is_mcbc;
bool is_eapol;
struct hal_rx_desc *rx_desc;
u8 err_rel_src;
u8 err_code;
u8 mac_id;
u8 unmapped;
u8 is_frag;
u8 tid;
u16 peer_id;
};
enum ath12k_hw_rev {
ATH12K_HW_QCN9274_HW10,
ATH12K_HW_QCN9274_HW20,
ATH12K_HW_WCN7850_HW20
};
enum ath12k_firmware_mode {
/* the default mode, standard 802.11 functionality */
ATH12K_FIRMWARE_MODE_NORMAL,
/* factory tests etc */
ATH12K_FIRMWARE_MODE_FTM,
};
#define ATH12K_IRQ_NUM_MAX 57
#define ATH12K_EXT_IRQ_NUM_MAX 16
struct ath12k_ext_irq_grp {
struct ath12k_base *ab;
u32 irqs[ATH12K_EXT_IRQ_NUM_MAX];
u32 num_irq;
u32 grp_id;
u64 timestamp;
struct napi_struct napi;
struct net_device napi_ndev;
};
#define HEHANDLE_CAP_PHYINFO_SIZE 3
#define HECAP_PHYINFO_SIZE 9
#define HECAP_MACINFO_SIZE 5
#define HECAP_TXRX_MCS_NSS_SIZE 2
#define HECAP_PPET16_PPET8_MAX_SIZE 25
#define HE_PPET16_PPET8_SIZE 8
/* 802.11ax PPE (PPDU packet Extension) threshold */
struct he_ppe_threshold {
u32 numss_m1;
u32 ru_mask;
u32 ppet16_ppet8_ru3_ru0[HE_PPET16_PPET8_SIZE];
};
struct ath12k_he {
u8 hecap_macinfo[HECAP_MACINFO_SIZE];
u32 hecap_rxmcsnssmap;
u32 hecap_txmcsnssmap;
u32 hecap_phyinfo[HEHANDLE_CAP_PHYINFO_SIZE];
struct he_ppe_threshold hecap_ppet;
u32 heop_param;
};
#define MAX_RADIOS 3
enum {
WMI_HOST_TP_SCALE_MAX = 0,
WMI_HOST_TP_SCALE_50 = 1,
WMI_HOST_TP_SCALE_25 = 2,
WMI_HOST_TP_SCALE_12 = 3,
WMI_HOST_TP_SCALE_MIN = 4,
WMI_HOST_TP_SCALE_SIZE = 5,
};
enum ath12k_scan_state {
ATH12K_SCAN_IDLE,
ATH12K_SCAN_STARTING,
ATH12K_SCAN_RUNNING,
ATH12K_SCAN_ABORTING,
};
enum ath12k_dev_flags {
ATH12K_CAC_RUNNING,
ATH12K_FLAG_CRASH_FLUSH,
ATH12K_FLAG_RAW_MODE,
ATH12K_FLAG_HW_CRYPTO_DISABLED,
ATH12K_FLAG_RECOVERY,
ATH12K_FLAG_UNREGISTERING,
ATH12K_FLAG_REGISTERED,
ATH12K_FLAG_QMI_FAIL,
ATH12K_FLAG_HTC_SUSPEND_COMPLETE,
};
enum ath12k_monitor_flags {
ATH12K_FLAG_MONITOR_ENABLED,
};
struct ath12k_vif {
u32 vdev_id;
enum wmi_vdev_type vdev_type;
enum wmi_vdev_subtype vdev_subtype;
u32 beacon_interval;
u32 dtim_period;
u16 ast_hash;
u16 ast_idx;
u16 tcl_metadata;
u8 hal_addr_search_flags;
u8 search_type;
struct ath12k *ar;
struct ieee80211_vif *vif;
int bank_id;
u8 vdev_id_check_en;
struct wmi_wmm_params_all_arg wmm_params;
struct list_head list;
union {
struct {
u32 uapsd;
} sta;
struct {
/* 127 stations; wmi limit */
u8 tim_bitmap[16];
u8 tim_len;
u32 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
bool hidden_ssid;
/* P2P_IE with NoA attribute for P2P_GO case */
u32 noa_len;
u8 *noa_data;
} ap;
} u;
bool is_started;
bool is_up;
u32 aid;
u8 bssid[ETH_ALEN];
struct cfg80211_bitrate_mask bitrate_mask;
int num_legacy_stations;
int rtscts_prot_mode;
int txpower;
bool rsnie_present;
bool wpaie_present;
struct ieee80211_chanctx_conf chanctx;
u32 key_cipher;
u8 tx_encap_type;
u8 vdev_stats_id;
};
struct ath12k_vif_iter {
u32 vdev_id;
struct ath12k_vif *arvif;
};
#define HAL_AST_IDX_INVALID 0xFFFF
#define HAL_RX_MAX_MCS 12
#define HAL_RX_MAX_MCS_HT 31
#define HAL_RX_MAX_MCS_VHT 9
#define HAL_RX_MAX_MCS_HE 11
#define HAL_RX_MAX_NSS 8
#define HAL_RX_MAX_NUM_LEGACY_RATES 12
#define ATH12K_RX_RATE_TABLE_11AX_NUM 576
#define ATH12K_RX_RATE_TABLE_NUM 320
struct ath12k_rx_peer_rate_stats {
u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1];
u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1];
u64 he_mcs_count[HAL_RX_MAX_MCS_HE + 1];
u64 nss_count[HAL_RX_MAX_NSS];
u64 bw_count[HAL_RX_BW_MAX];
u64 gi_count[HAL_RX_GI_MAX];
u64 legacy_count[HAL_RX_MAX_NUM_LEGACY_RATES];
u64 rx_rate[ATH12K_RX_RATE_TABLE_11AX_NUM];
};
struct ath12k_rx_peer_stats {
u64 num_msdu;
u64 num_mpdu_fcs_ok;
u64 num_mpdu_fcs_err;
u64 tcp_msdu_count;
u64 udp_msdu_count;
u64 other_msdu_count;
u64 ampdu_msdu_count;
u64 non_ampdu_msdu_count;
u64 stbc_count;
u64 beamformed_count;
u64 mcs_count[HAL_RX_MAX_MCS + 1];
u64 nss_count[HAL_RX_MAX_NSS];
u64 bw_count[HAL_RX_BW_MAX];
u64 gi_count[HAL_RX_GI_MAX];
u64 coding_count[HAL_RX_SU_MU_CODING_MAX];
u64 tid_count[IEEE80211_NUM_TIDS + 1];
u64 pream_cnt[HAL_RX_PREAMBLE_MAX];
u64 reception_type[HAL_RX_RECEPTION_TYPE_MAX];
u64 rx_duration;
u64 dcm_count;
u64 ru_alloc_cnt[HAL_RX_RU_ALLOC_TYPE_MAX];
struct ath12k_rx_peer_rate_stats pkt_stats;
struct ath12k_rx_peer_rate_stats byte_stats;
};
#define ATH12K_HE_MCS_NUM 12
#define ATH12K_VHT_MCS_NUM 10
#define ATH12K_BW_NUM 5
#define ATH12K_NSS_NUM 4
#define ATH12K_LEGACY_NUM 12
#define ATH12K_GI_NUM 4
#define ATH12K_HT_MCS_NUM 32
enum ath12k_pkt_rx_err {
ATH12K_PKT_RX_ERR_FCS,
ATH12K_PKT_RX_ERR_TKIP,
ATH12K_PKT_RX_ERR_CRYPT,
ATH12K_PKT_RX_ERR_PEER_IDX_INVAL,
ATH12K_PKT_RX_ERR_MAX,
};
enum ath12k_ampdu_subfrm_num {
ATH12K_AMPDU_SUBFRM_NUM_10,
ATH12K_AMPDU_SUBFRM_NUM_20,
ATH12K_AMPDU_SUBFRM_NUM_30,
ATH12K_AMPDU_SUBFRM_NUM_40,
ATH12K_AMPDU_SUBFRM_NUM_50,
ATH12K_AMPDU_SUBFRM_NUM_60,
ATH12K_AMPDU_SUBFRM_NUM_MORE,
ATH12K_AMPDU_SUBFRM_NUM_MAX,
};
enum ath12k_amsdu_subfrm_num {
ATH12K_AMSDU_SUBFRM_NUM_1,
ATH12K_AMSDU_SUBFRM_NUM_2,
ATH12K_AMSDU_SUBFRM_NUM_3,
ATH12K_AMSDU_SUBFRM_NUM_4,
ATH12K_AMSDU_SUBFRM_NUM_MORE,
ATH12K_AMSDU_SUBFRM_NUM_MAX,
};
enum ath12k_counter_type {
ATH12K_COUNTER_TYPE_BYTES,
ATH12K_COUNTER_TYPE_PKTS,
ATH12K_COUNTER_TYPE_MAX,
};
enum ath12k_stats_type {
ATH12K_STATS_TYPE_SUCC,
ATH12K_STATS_TYPE_FAIL,
ATH12K_STATS_TYPE_RETRY,
ATH12K_STATS_TYPE_AMPDU,
ATH12K_STATS_TYPE_MAX,
};
struct ath12k_htt_data_stats {
u64 legacy[ATH12K_COUNTER_TYPE_MAX][ATH12K_LEGACY_NUM];
u64 ht[ATH12K_COUNTER_TYPE_MAX][ATH12K_HT_MCS_NUM];
u64 vht[ATH12K_COUNTER_TYPE_MAX][ATH12K_VHT_MCS_NUM];
u64 he[ATH12K_COUNTER_TYPE_MAX][ATH12K_HE_MCS_NUM];
u64 bw[ATH12K_COUNTER_TYPE_MAX][ATH12K_BW_NUM];
u64 nss[ATH12K_COUNTER_TYPE_MAX][ATH12K_NSS_NUM];
u64 gi[ATH12K_COUNTER_TYPE_MAX][ATH12K_GI_NUM];
u64 transmit_type[ATH12K_COUNTER_TYPE_MAX][HAL_RX_RECEPTION_TYPE_MAX];
u64 ru_loc[ATH12K_COUNTER_TYPE_MAX][HAL_RX_RU_ALLOC_TYPE_MAX];
};
struct ath12k_htt_tx_stats {
struct ath12k_htt_data_stats stats[ATH12K_STATS_TYPE_MAX];
u64 tx_duration;
u64 ba_fails;
u64 ack_fails;
u16 ru_start;
u16 ru_tones;
u32 mu_group[MAX_MU_GROUP_ID];
};
struct ath12k_per_ppdu_tx_stats {
u16 succ_pkts;
u16 failed_pkts;
u16 retry_pkts;
u32 succ_bytes;
u32 failed_bytes;
u32 retry_bytes;
};
struct ath12k_wbm_tx_stats {
u64 wbm_tx_comp_stats[HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX];
};
struct ath12k_sta {
struct ath12k_vif *arvif;
/* the following are protected by ar->data_lock */
u32 changed; /* IEEE80211_RC_* */
u32 bw;
u32 nss;
u32 smps;
enum hal_pn_type pn_type;
struct work_struct update_wk;
struct rate_info txrate;
struct rate_info last_txrate;
u64 rx_duration;
u64 tx_duration;
u8 rssi_comb;
struct ath12k_rx_peer_stats *rx_stats;
struct ath12k_wbm_tx_stats *wbm_tx_stats;
};
#define ATH12K_MIN_5G_FREQ 4150
#define ATH12K_MIN_6G_FREQ 5945
#define ATH12K_MAX_6G_FREQ 7115
#define ATH12K_NUM_CHANS 100
#define ATH12K_MAX_5G_CHAN 173
enum ath12k_state {
ATH12K_STATE_OFF,
ATH12K_STATE_ON,
ATH12K_STATE_RESTARTING,
ATH12K_STATE_RESTARTED,
ATH12K_STATE_WEDGED,
/* Add other states as required */
};
/* Antenna noise floor */
#define ATH12K_DEFAULT_NOISE_FLOOR -95
struct ath12k_fw_stats {
u32 pdev_id;
u32 stats_id;
struct list_head pdevs;
struct list_head vdevs;
struct list_head bcn;
};
struct ath12k_per_peer_tx_stats {
u32 succ_bytes;
u32 retry_bytes;
u32 failed_bytes;
u32 duration;
u16 succ_pkts;
u16 retry_pkts;
u16 failed_pkts;
u16 ru_start;
u16 ru_tones;
u8 ba_fails;
u8 ppdu_type;
u32 mu_grpid;
u32 mu_pos;
bool is_ampdu;
};
#define ATH12K_FLUSH_TIMEOUT (5 * HZ)
#define ATH12K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ)
struct ath12k {
struct ath12k_base *ab;
struct ath12k_pdev *pdev;
struct ieee80211_hw *hw;
struct ieee80211_ops *ops;
struct ath12k_wmi_pdev *wmi;
struct ath12k_pdev_dp dp;
u8 mac_addr[ETH_ALEN];
u32 ht_cap_info;
u32 vht_cap_info;
struct ath12k_he ar_he;
enum ath12k_state state;
bool supports_6ghz;
struct {
struct completion started;
struct completion completed;
struct completion on_channel;
struct delayed_work timeout;
enum ath12k_scan_state state;
bool is_roc;
int vdev_id;
int roc_freq;
bool roc_notify;
} scan;
struct {
struct ieee80211_supported_band sbands[NUM_NL80211_BANDS];
struct ieee80211_sband_iftype_data
iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES];
} mac;
unsigned long dev_flags;
unsigned int filter_flags;
unsigned long monitor_flags;
u32 min_tx_power;
u32 max_tx_power;
u32 txpower_limit_2g;
u32 txpower_limit_5g;
u32 txpower_scale;
u32 power_scale;
u32 chan_tx_pwr;
u32 num_stations;
u32 max_num_stations;
bool monitor_present;
/* To synchronize concurrent synchronous mac80211 callback operations,
* concurrent debugfs configuration and concurrent FW statistics events.
*/
struct mutex conf_mutex;
/* protects the radio specific data like debug stats, ppdu_stats_info stats,
* vdev_stop_status info, scan data, ath12k_sta info, ath12k_vif info,
* channel context data, survey info, test mode data.
*/
spinlock_t data_lock;
struct list_head arvifs;
/* should never be NULL; needed for regular htt rx */
struct ieee80211_channel *rx_channel;
/* valid during scan; needed for mgmt rx during scan */
struct ieee80211_channel *scan_channel;
u8 cfg_tx_chainmask;
u8 cfg_rx_chainmask;
u8 num_rx_chains;
u8 num_tx_chains;
/* pdev_idx starts from 0 whereas pdev->pdev_id starts with 1 */
u8 pdev_idx;
u8 lmac_id;
struct completion peer_assoc_done;
struct completion peer_delete_done;
int install_key_status;
struct completion install_key_done;
int last_wmi_vdev_start_status;
struct completion vdev_setup_done;
struct completion vdev_delete_done;
int num_peers;
int max_num_peers;
u32 num_started_vdevs;
u32 num_created_vdevs;
unsigned long long allocated_vdev_map;
struct idr txmgmt_idr;
/* protects txmgmt_idr data */
spinlock_t txmgmt_idr_lock;
atomic_t num_pending_mgmt_tx;
/* cycle count is reported twice for each visited channel during scan.
* access protected by data_lock
*/
u32 survey_last_rx_clear_count;
u32 survey_last_cycle_count;
/* Channel info events are expected to come in pairs without and with
* COMPLETE flag set respectively for each channel visit during scan.
*
* However there are deviations from this rule. This flag is used to
* avoid reporting garbage data.
*/
bool ch_info_can_report_survey;
struct survey_info survey[ATH12K_NUM_CHANS];
struct completion bss_survey_done;
struct work_struct regd_update_work;
struct work_struct wmi_mgmt_tx_work;
struct sk_buff_head wmi_mgmt_tx_queue;
struct ath12k_per_peer_tx_stats peer_tx_stats;
struct list_head ppdu_stats_info;
u32 ppdu_stat_list_depth;
struct ath12k_per_peer_tx_stats cached_stats;
u32 last_ppdu_id;
u32 cached_ppdu_id;
bool dfs_block_radar_events;
bool monitor_conf_enabled;
bool monitor_vdev_created;
bool monitor_started;
int monitor_vdev_id;
};
struct ath12k_band_cap {
u32 phy_id;
u32 max_bw_supported;
u32 ht_cap_info;
u32 he_cap_info[2];
u32 he_mcs;
u32 he_cap_phy_info[PSOC_HOST_MAX_PHY_SIZE];
struct ath12k_wmi_ppe_threshold_arg he_ppet;
u16 he_6ghz_capa;
};
struct ath12k_pdev_cap {
u32 supported_bands;
u32 ampdu_density;
u32 vht_cap;
u32 vht_mcs;
u32 he_mcs;
u32 tx_chain_mask;
u32 rx_chain_mask;
u32 tx_chain_mask_shift;
u32 rx_chain_mask_shift;
struct ath12k_band_cap band[NUM_NL80211_BANDS];
};
struct mlo_timestamp {
u32 info;
u32 sync_timestamp_lo_us;
u32 sync_timestamp_hi_us;
u32 mlo_offset_lo;
u32 mlo_offset_hi;
u32 mlo_offset_clks;
u32 mlo_comp_clks;
u32 mlo_comp_timer;
};
struct ath12k_pdev {
struct ath12k *ar;
u32 pdev_id;
struct ath12k_pdev_cap cap;
u8 mac_addr[ETH_ALEN];
struct mlo_timestamp timestamp;
};
struct ath12k_board_data {
const struct firmware *fw;
const void *data;
size_t len;
};
struct ath12k_soc_dp_tx_err_stats {
/* TCL Ring Descriptor unavailable */
u32 desc_na[DP_TCL_NUM_RING_MAX];
/* Other failures during dp_tx due to mem allocation failure
* idr unavailable etc.
*/
atomic_t misc_fail;
};
struct ath12k_soc_dp_stats {
u32 err_ring_pkts;
u32 invalid_rbm;
u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX];
u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX];
u32 hal_reo_error[DP_REO_DST_RING_MAX];
struct ath12k_soc_dp_tx_err_stats tx_err;
};
/* Master structure to hold the hw data which may be used in core module */
struct ath12k_base {
enum ath12k_hw_rev hw_rev;
struct platform_device *pdev;
struct device *dev;
struct ath12k_qmi qmi;
struct ath12k_wmi_base wmi_ab;
struct completion fw_ready;
int num_radios;
/* HW channel counters frequency value in hertz common to all MACs */
u32 cc_freq_hz;
struct ath12k_htc htc;
struct ath12k_dp dp;
void __iomem *mem;
unsigned long mem_len;
struct {
enum ath12k_bus bus;
const struct ath12k_hif_ops *ops;
} hif;
struct ath12k_ce ce;
struct timer_list rx_replenish_retry;
struct ath12k_hal hal;
/* To synchronize core_start/core_stop */
struct mutex core_lock;
/* Protects data like peers */
spinlock_t base_lock;
struct ath12k_pdev pdevs[MAX_RADIOS];
struct ath12k_pdev __rcu *pdevs_active[MAX_RADIOS];
struct ath12k_wmi_hal_reg_capabilities_ext_arg hal_reg_cap[MAX_RADIOS];
unsigned long long free_vdev_map;
unsigned long long free_vdev_stats_id_map;
struct list_head peers;
wait_queue_head_t peer_mapping_wq;
u8 mac_addr[ETH_ALEN];
bool wmi_ready;
u32 wlan_init_status;
int irq_num[ATH12K_IRQ_NUM_MAX];
struct ath12k_ext_irq_grp ext_irq_grp[ATH12K_EXT_IRQ_GRP_NUM_MAX];
struct napi_struct *napi;
struct ath12k_wmi_target_cap_arg target_caps;
u32 ext_service_bitmap[WMI_SERVICE_EXT_BM_SIZE];
bool pdevs_macaddr_valid;
int bd_api;
const struct ath12k_hw_params *hw_params;
const struct firmware *cal_file;
/* Below regd's are protected by ab->data_lock */
/* This is the regd set for every radio
* by the firmware during initializatin
*/
struct ieee80211_regdomain *default_regd[MAX_RADIOS];
/* This regd is set during dynamic country setting
* This may or may not be used during the runtime
*/
struct ieee80211_regdomain *new_regd[MAX_RADIOS];
/* Current DFS Regulatory */
enum ath12k_dfs_region dfs_region;
struct ath12k_soc_dp_stats soc_stats;
unsigned long dev_flags;
struct completion driver_recovery;
struct workqueue_struct *workqueue;
struct work_struct restart_work;
struct workqueue_struct *workqueue_aux;
struct work_struct reset_work;
atomic_t reset_count;
atomic_t recovery_count;
atomic_t recovery_start_count;
bool is_reset;
struct completion reset_complete;
struct completion reconfigure_complete;
struct completion recovery_start;
/* continuous recovery fail count */
atomic_t fail_cont_count;
unsigned long reset_fail_timeout;
struct {
/* protected by data_lock */
u32 fw_crash_counter;
} stats;
u32 pktlog_defs_checksum;
struct ath12k_dbring_cap *db_caps;
u32 num_db_cap;
struct timer_list mon_reap_timer;
struct completion htc_suspend;
u64 fw_soc_drop_count;
bool static_window_map;
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab);
int ath12k_core_pre_init(struct ath12k_base *ab);
int ath12k_core_init(struct ath12k_base *ath12k);
void ath12k_core_deinit(struct ath12k_base *ath12k);
struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
enum ath12k_bus bus);
void ath12k_core_free(struct ath12k_base *ath12k);
int ath12k_core_fetch_board_data_api_1(struct ath12k_base *ab,
struct ath12k_board_data *bd,
char *filename);
int ath12k_core_fetch_bdf(struct ath12k_base *ath12k,
struct ath12k_board_data *bd);
void ath12k_core_free_bdf(struct ath12k_base *ab, struct ath12k_board_data *bd);
int ath12k_core_check_dt(struct ath12k_base *ath12k);
void ath12k_core_halt(struct ath12k *ar);
int ath12k_core_resume(struct ath12k_base *ab);
int ath12k_core_suspend(struct ath12k_base *ab);
const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab,
const char *filename);
static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
{
switch (state) {
case ATH12K_SCAN_IDLE:
return "idle";
case ATH12K_SCAN_STARTING:
return "starting";
case ATH12K_SCAN_RUNNING:
return "running";
case ATH12K_SCAN_ABORTING:
return "aborting";
}
return "unknown";
}
static inline struct ath12k_skb_cb *ATH12K_SKB_CB(struct sk_buff *skb)
{
BUILD_BUG_ON(sizeof(struct ath12k_skb_cb) >
IEEE80211_TX_INFO_DRIVER_DATA_SIZE);
return (struct ath12k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data;
}
static inline struct ath12k_skb_rxcb *ATH12K_SKB_RXCB(struct sk_buff *skb)
{
BUILD_BUG_ON(sizeof(struct ath12k_skb_rxcb) > sizeof(skb->cb));
return (struct ath12k_skb_rxcb *)skb->cb;
}
static inline struct ath12k_vif *ath12k_vif_to_arvif(struct ieee80211_vif *vif)
{
return (struct ath12k_vif *)vif->drv_priv;
}
static inline struct ath12k *ath12k_ab_to_ar(struct ath12k_base *ab,
int mac_id)
{
return ab->pdevs[ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id)].ar;
}
static inline void ath12k_core_create_firmware_path(struct ath12k_base *ab,
const char *filename,
void *buf, size_t buf_len)
{
snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR,
ab->hw_params->fw.dir, filename);
}
static inline const char *ath12k_bus_str(enum ath12k_bus bus)
{
switch (bus) {
case ATH12K_BUS_PCI:
return "pci";
}
return "unknown";
}
#endif /* _CORE_H_ */

View file

@ -0,0 +1,357 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
#include "debug.h"
static int ath12k_dbring_bufs_replenish(struct ath12k *ar,
struct ath12k_dbring *ring,
struct ath12k_dbring_element *buff,
gfp_t gfp)
{
struct ath12k_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);
ath12k_hal_srng_access_begin(ab, srng);
ptr_unaligned = buff->payload;
ptr_aligned = PTR_ALIGN(ptr_unaligned, ring->buf_align);
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);
spin_unlock_bh(&ring->idr_lock);
if (buf_id < 0) {
ret = -ENOBUFS;
goto err_dma_unmap;
}
desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
if (!desc) {
ret = -ENOENT;
goto err_idr_remove;
}
buff->paddr = paddr;
cookie = u32_encode_bits(ar->pdev_idx, DP_RXDMA_BUF_COOKIE_PDEV_ID) |
u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID);
ath12k_hal_rx_buf_addr_info_set(desc, paddr, cookie, 0);
ath12k_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:
ath12k_hal_srng_access_end(ab, srng);
return ret;
}
static int ath12k_dbring_fill_bufs(struct ath12k *ar,
struct ath12k_dbring *ring,
gfp_t gfp)
{
struct ath12k_dbring_element *buff;
struct hal_srng *srng;
struct ath12k_base *ab = ar->ab;
int num_remain, req_entries, num_free;
u32 align;
int size, ret;
srng = &ab->hal.srng_list[ring->refill_srng.ring_id];
spin_lock_bh(&srng->lock);
num_free = ath12k_hal_srng_src_num_free(ab, srng, true);
req_entries = min(num_free, ring->bufs_max);
num_remain = req_entries;
align = ring->buf_align;
size = sizeof(*buff) + ring->buf_sz + align - 1;
while (num_remain > 0) {
buff = kzalloc(size, gfp);
if (!buff)
break;
ret = ath12k_dbring_bufs_replenish(ar, ring, buff, gfp);
if (ret) {
ath12k_warn(ab, "failed to replenish db ring num_remain %d req_ent %d\n",
num_remain, req_entries);
kfree(buff);
break;
}
num_remain--;
}
spin_unlock_bh(&srng->lock);
return num_remain;
}
int ath12k_dbring_wmi_cfg_setup(struct ath12k *ar,
struct ath12k_dbring *ring,
enum wmi_direct_buffer_module id)
{
struct ath12k_wmi_pdev_dma_ring_cfg_arg arg = {0};
int ret;
if (id >= WMI_DIRECT_BUF_MAX)
return -EINVAL;
arg.pdev_id = DP_SW2HW_MACID(ring->pdev_id);
arg.module_id = id;
arg.base_paddr_lo = lower_32_bits(ring->refill_srng.paddr);
arg.base_paddr_hi = upper_32_bits(ring->refill_srng.paddr);
arg.head_idx_paddr_lo = lower_32_bits(ring->hp_addr);
arg.head_idx_paddr_hi = upper_32_bits(ring->hp_addr);
arg.tail_idx_paddr_lo = lower_32_bits(ring->tp_addr);
arg.tail_idx_paddr_hi = upper_32_bits(ring->tp_addr);
arg.num_elems = ring->bufs_max;
arg.buf_size = ring->buf_sz;
arg.num_resp_per_event = ring->num_resp_per_event;
arg.event_timeout_ms = ring->event_timeout_ms;
ret = ath12k_wmi_pdev_dma_ring_cfg(ar, &arg);
if (ret) {
ath12k_warn(ar->ab, "failed to setup db ring cfg\n");
return ret;
}
return 0;
}
int ath12k_dbring_set_cfg(struct ath12k *ar, struct ath12k_dbring *ring,
u32 num_resp_per_event, u32 event_timeout_ms,
int (*handler)(struct ath12k *,
struct ath12k_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 ath12k_dbring_buf_setup(struct ath12k *ar,
struct ath12k_dbring *ring,
struct ath12k_dbring_cap *db_cap)
{
struct ath12k_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 /
ath12k_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 = ath12k_hal_srng_get_hp_addr(ab, srng);
ring->tp_addr = ath12k_hal_srng_get_tp_addr(ab, srng);
ret = ath12k_dbring_fill_bufs(ar, ring, GFP_KERNEL);
return ret;
}
int ath12k_dbring_srng_setup(struct ath12k *ar, struct ath12k_dbring *ring,
int ring_num, int num_entries)
{
int ret;
ret = ath12k_dp_srng_setup(ar->ab, &ring->refill_srng, HAL_RXDMA_DIR_BUF,
ring_num, ar->pdev_idx, num_entries);
if (ret < 0) {
ath12k_warn(ar->ab, "failed to setup srng: %d ring_id %d\n",
ret, ring_num);
goto err;
}
return 0;
err:
ath12k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
return ret;
}
int ath12k_dbring_get_cap(struct ath12k_base *ab,
u8 pdev_idx,
enum wmi_direct_buffer_module id,
struct ath12k_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 ath12k_dbring_buffer_release_event(struct ath12k_base *ab,
struct ath12k_dbring_buf_release_event *ev)
{
struct ath12k_dbring *ring;
struct hal_srng *srng;
struct ath12k *ar;
struct ath12k_dbring_element *buff;
struct ath12k_dbring_data handler_data;
struct ath12k_buffer_addr desc;
u8 *vaddr_unalign;
u32 num_entry, num_buff_reaped;
u8 pdev_idx, rbm;
u32 cookie;
int buf_id;
int size;
dma_addr_t paddr;
int ret = 0;
pdev_idx = le32_to_cpu(ev->fixed.pdev_id);
if (pdev_idx >= ab->num_radios) {
ath12k_warn(ab, "Invalid pdev id %d\n", pdev_idx);
return -EINVAL;
}
if (ev->fixed.num_buf_release_entry !=
ev->fixed.num_meta_data_entry) {
ath12k_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:
break;
default:
ring = NULL;
ath12k_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 = le32_to_cpu(ev->fixed.num_buf_release_entry);
size = sizeof(*buff) + 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++;
ath12k_hal_rx_buf_addr_info_get(&desc, &paddr, &cookie, &rbm);
buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID);
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);
if (ring->handler) {
vaddr_unalign = buff->payload;
handler_data.data = PTR_ALIGN(vaddr_unalign,
ring->buf_align);
handler_data.data_sz = ring->buf_sz;
ring->handler(ar, &handler_data);
}
memset(buff, 0, size);
ath12k_dbring_bufs_replenish(ar, ring, buff, GFP_ATOMIC);
}
spin_unlock_bh(&srng->lock);
rcu_unlock:
rcu_read_unlock();
return ret;
}
void ath12k_dbring_srng_cleanup(struct ath12k *ar, struct ath12k_dbring *ring)
{
ath12k_dp_srng_cleanup(ar->ab, &ring->refill_srng);
}
void ath12k_dbring_buf_cleanup(struct ath12k *ar, struct ath12k_dbring *ring)
{
struct ath12k_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);
}
idr_destroy(&ring->bufs_idr);
spin_unlock_bh(&ring->idr_lock);
}

View file

@ -0,0 +1,80 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DBRING_H
#define ATH12K_DBRING_H
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/spinlock.h>
#include "dp.h"
struct ath12k_dbring_element {
dma_addr_t paddr;
u8 payload[];
};
struct ath12k_dbring_data {
void *data;
u32 data_sz;
struct ath12k_wmi_dma_buf_release_meta_data_params meta;
};
struct ath12k_dbring_buf_release_event {
struct ath12k_wmi_dma_buf_release_fixed_params fixed;
const struct ath12k_wmi_dma_buf_release_entry_params *buf_entry;
const struct ath12k_wmi_dma_buf_release_meta_data_params *meta_data;
u32 num_buf_entry;
u32 num_meta;
};
struct ath12k_dbring_cap {
u32 pdev_id;
enum wmi_direct_buffer_module id;
u32 min_elem;
u32 min_buf_sz;
u32 min_buf_align;
};
struct ath12k_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 ath12k *ar, struct ath12k_dbring_data *data);
};
int ath12k_dbring_set_cfg(struct ath12k *ar,
struct ath12k_dbring *ring,
u32 num_resp_per_event,
u32 event_timeout_ms,
int (*handler)(struct ath12k *,
struct ath12k_dbring_data *));
int ath12k_dbring_wmi_cfg_setup(struct ath12k *ar,
struct ath12k_dbring *ring,
enum wmi_direct_buffer_module id);
int ath12k_dbring_buf_setup(struct ath12k *ar,
struct ath12k_dbring *ring,
struct ath12k_dbring_cap *db_cap);
int ath12k_dbring_srng_setup(struct ath12k *ar, struct ath12k_dbring *ring,
int ring_num, int num_entries);
int ath12k_dbring_buffer_release_event(struct ath12k_base *ab,
struct ath12k_dbring_buf_release_event *ev);
int ath12k_dbring_get_cap(struct ath12k_base *ab,
u8 pdev_idx,
enum wmi_direct_buffer_module id,
struct ath12k_dbring_cap *db_cap);
void ath12k_dbring_srng_cleanup(struct ath12k *ar, struct ath12k_dbring *ring);
void ath12k_dbring_buf_cleanup(struct ath12k *ar, struct ath12k_dbring *ring);
#endif /* ATH12K_DBRING_H */

View file

@ -0,0 +1,102 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/vmalloc.h>
#include "core.h"
#include "debug.h"
void ath12k_info(struct ath12k_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);
/* TODO: Trace the log */
va_end(args);
}
void ath12k_err(struct ath12k_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);
/* TODO: Trace the log */
va_end(args);
}
void ath12k_warn(struct ath12k_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);
/* TODO: Trace the log */
va_end(args);
}
#ifdef CONFIG_ATH12K_DEBUG
void __ath12k_dbg(struct ath12k_base *ab, enum ath12k_debug_mask mask,
const char *fmt, ...)
{
struct va_format vaf;
va_list args;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
if (ath12k_debug_mask & mask)
dev_dbg(ab->dev, "%pV", &vaf);
/* TODO: trace log */
va_end(args);
}
void ath12k_dbg_dump(struct ath12k_base *ab,
enum ath12k_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 (ath12k_debug_mask & mask) {
if (msg)
__ath12k_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_dbg(ab->dev, "%s\n", linebuf);
}
}
}
#endif /* CONFIG_ATH12K_DEBUG */

View file

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _ATH12K_DEBUG_H_
#define _ATH12K_DEBUG_H_
#include "trace.h"
enum ath12k_debug_mask {
ATH12K_DBG_AHB = 0x00000001,
ATH12K_DBG_WMI = 0x00000002,
ATH12K_DBG_HTC = 0x00000004,
ATH12K_DBG_DP_HTT = 0x00000008,
ATH12K_DBG_MAC = 0x00000010,
ATH12K_DBG_BOOT = 0x00000020,
ATH12K_DBG_QMI = 0x00000040,
ATH12K_DBG_DATA = 0x00000080,
ATH12K_DBG_MGMT = 0x00000100,
ATH12K_DBG_REG = 0x00000200,
ATH12K_DBG_TESTMODE = 0x00000400,
ATH12K_DBG_HAL = 0x00000800,
ATH12K_DBG_PCI = 0x00001000,
ATH12K_DBG_DP_TX = 0x00002000,
ATH12K_DBG_DP_RX = 0x00004000,
ATH12K_DBG_ANY = 0xffffffff,
};
__printf(2, 3) void ath12k_info(struct ath12k_base *ab, const char *fmt, ...);
__printf(2, 3) void ath12k_err(struct ath12k_base *ab, const char *fmt, ...);
__printf(2, 3) void ath12k_warn(struct ath12k_base *ab, const char *fmt, ...);
extern unsigned int ath12k_debug_mask;
#ifdef CONFIG_ATH12K_DEBUG
__printf(3, 4) void __ath12k_dbg(struct ath12k_base *ab,
enum ath12k_debug_mask mask,
const char *fmt, ...);
void ath12k_dbg_dump(struct ath12k_base *ab,
enum ath12k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len);
#else /* CONFIG_ATH12K_DEBUG */
static inline void __ath12k_dbg(struct ath12k_base *ab,
enum ath12k_debug_mask dbg_mask,
const char *fmt, ...)
{
}
static inline void ath12k_dbg_dump(struct ath12k_base *ab,
enum ath12k_debug_mask mask,
const char *msg, const char *prefix,
const void *buf, size_t len)
{
}
#endif /* CONFIG_ATH12K_DEBUG */
#define ath12k_dbg(ar, dbg_mask, fmt, ...) \
do { \
typeof(dbg_mask) mask = (dbg_mask); \
if (ath12k_debug_mask & mask) \
__ath12k_dbg(ar, mask, fmt, ##__VA_ARGS__); \
} while (0)
#endif /* _ATH12K_DEBUG_H_ */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_MON_H
#define ATH12K_DP_MON_H
#include "core.h"
enum dp_monitor_mode {
ATH12K_DP_TX_MONITOR_MODE,
ATH12K_DP_RX_MONITOR_MODE
};
enum dp_mon_tx_ppdu_info_type {
DP_MON_TX_PROT_PPDU_INFO,
DP_MON_TX_DATA_PPDU_INFO
};
enum dp_mon_tx_tlv_status {
DP_MON_TX_FES_SETUP,
DP_MON_TX_FES_STATUS_END,
DP_MON_RX_RESPONSE_REQUIRED_INFO,
DP_MON_RESPONSE_END_STATUS_INFO,
DP_MON_TX_MPDU_START,
DP_MON_TX_MSDU_START,
DP_MON_TX_BUFFER_ADDR,
DP_MON_TX_DATA,
DP_MON_TX_STATUS_PPDU_NOT_DONE,
};
enum dp_mon_tx_medium_protection_type {
DP_MON_TX_MEDIUM_NO_PROTECTION,
DP_MON_TX_MEDIUM_RTS_LEGACY,
DP_MON_TX_MEDIUM_RTS_11AC_STATIC_BW,
DP_MON_TX_MEDIUM_RTS_11AC_DYNAMIC_BW,
DP_MON_TX_MEDIUM_CTS2SELF,
DP_MON_TX_MEDIUM_QOS_NULL_NO_ACK_3ADDR,
DP_MON_TX_MEDIUM_QOS_NULL_NO_ACK_4ADDR
};
struct dp_mon_qosframe_addr4 {
__le16 frame_control;
__le16 duration;
u8 addr1[ETH_ALEN];
u8 addr2[ETH_ALEN];
u8 addr3[ETH_ALEN];
__le16 seq_ctrl;
u8 addr4[ETH_ALEN];
__le16 qos_ctrl;
} __packed;
struct dp_mon_frame_min_one {
__le16 frame_control;
__le16 duration;
u8 addr1[ETH_ALEN];
} __packed;
struct dp_mon_packet_info {
u64 cookie;
u16 dma_length;
bool msdu_continuation;
bool truncated;
};
struct dp_mon_tx_ppdu_info {
u32 ppdu_id;
u8 num_users;
bool is_used;
struct hal_rx_mon_ppdu_info rx_status;
struct list_head dp_tx_mon_mpdu_list;
struct dp_mon_mpdu *tx_mon_mpdu;
};
enum hal_rx_mon_status
ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar,
struct ath12k_mon_data *pmon,
int mac_id, struct sk_buff *skb,
struct napi_struct *napi);
int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab,
struct dp_rxdma_ring *buf_ring,
int req_entries);
int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id,
int *budget, enum dp_monitor_mode monitor_mode,
struct napi_struct *napi);
int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id,
struct napi_struct *napi, int budget,
enum dp_monitor_mode monitor_mode);
struct sk_buff *ath12k_dp_mon_tx_alloc_skb(void);
enum dp_mon_tx_tlv_status
ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag,
struct hal_tlv_hdr *tx_tlv,
u8 *num_users);
enum hal_rx_mon_status
ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar,
struct ath12k_mon_data *pmon,
int mac_id,
struct sk_buff *skb,
struct napi_struct *napi,
u32 ppdu_id);
void ath12k_dp_mon_rx_process_ulofdma(struct hal_rx_mon_ppdu_info *ppdu_info);
int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id,
struct napi_struct *napi, int *budget);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,145 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_RX_H
#define ATH12K_DP_RX_H
#include "core.h"
#include "rx_desc.h"
#include "debug.h"
#define DP_MAX_NWIFI_HDR_LEN 30
struct ath12k_dp_rx_tid {
u8 tid;
u32 *vaddr;
dma_addr_t paddr;
u32 size;
u32 ba_win_sz;
bool active;
/* Info related to rx fragments */
u32 cur_sn;
u16 last_frag_no;
u16 rx_frag_bitmap;
struct sk_buff_head rx_frags;
struct hal_reo_dest_ring *dst_ring_desc;
/* Timer info related to fragments */
struct timer_list frag_timer;
struct ath12k_base *ab;
};
struct ath12k_dp_rx_reo_cache_flush_elem {
struct list_head list;
struct ath12k_dp_rx_tid data;
unsigned long ts;
};
struct ath12k_dp_rx_reo_cmd {
struct list_head list;
struct ath12k_dp_rx_tid data;
int cmd_num;
void (*handler)(struct ath12k_dp *dp, void *ctx,
enum hal_reo_cmd_status status);
};
#define ATH12K_DP_RX_REO_DESC_FREE_THRES 64
#define ATH12K_DP_RX_REO_DESC_FREE_TIMEOUT_MS 1000
enum ath12k_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 ath12k_dp_rx_rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctrl;
u8 snap_oui[3];
__be16 snap_type;
} __packed;
static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi)
{
u32 ret = 0;
switch (sgi) {
case RX_MSDU_START_SGI_0_8_US:
ret = NL80211_RATE_INFO_HE_GI_0_8;
break;
case RX_MSDU_START_SGI_1_6_US:
ret = NL80211_RATE_INFO_HE_GI_1_6;
break;
case RX_MSDU_START_SGI_3_2_US:
ret = NL80211_RATE_INFO_HE_GI_3_2;
break;
}
return ret;
}
int ath12k_dp_rx_ampdu_start(struct ath12k *ar,
struct ieee80211_ampdu_params *params);
int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
struct ieee80211_ampdu_params *params);
int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_vif *arvif,
const u8 *peer_addr,
enum set_key_cmd key_cmd,
struct ieee80211_key_conf *key);
void ath12k_dp_rx_peer_tid_cleanup(struct ath12k *ar, struct ath12k_peer *peer);
void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar,
struct ath12k_peer *peer, u8 tid);
int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id,
u8 tid, u32 ba_win_sz, u16 ssn,
enum hal_pn_type pn_type);
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
struct sk_buff *skb);
int ath12k_dp_rx_pdev_reo_setup(struct ath12k_base *ab);
void ath12k_dp_rx_pdev_reo_cleanup(struct ath12k_base *ab);
int ath12k_dp_rx_htt_setup(struct ath12k_base *ab);
int ath12k_dp_rx_alloc(struct ath12k_base *ab);
void ath12k_dp_rx_free(struct ath12k_base *ab);
int ath12k_dp_rx_pdev_alloc(struct ath12k_base *ab, int pdev_idx);
void ath12k_dp_rx_pdev_free(struct ath12k_base *ab, int pdev_idx);
void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab);
void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab);
int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab,
struct napi_struct *napi, int budget);
int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
int budget);
int ath12k_dp_rx_process(struct ath12k_base *ab, int mac_id,
struct napi_struct *napi,
int budget);
int ath12k_dp_rx_bufs_replenish(struct ath12k_base *ab, int mac_id,
struct dp_rxdma_ring *rx_ring,
int req_entries,
enum hal_rx_buf_return_buf_manager mgr,
bool hw_cc);
int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar);
int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id);
int ath12k_dp_rx_pktlog_start(struct ath12k_base *ab);
int ath12k_dp_rx_pktlog_stop(struct ath12k_base *ab, bool stop_timer);
u8 ath12k_dp_rx_h_l3pad(struct ath12k_base *ab,
struct hal_rx_desc *desc);
struct ath12k_peer *
ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu);
u8 ath12k_dp_rx_h_decap_type(struct ath12k_base *ab,
struct hal_rx_desc *desc);
u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab,
struct hal_rx_desc *desc);
void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc,
struct ieee80211_rx_status *rx_status);
struct ath12k_peer *
ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu);
int ath12k_dp_rxdma_ring_sel_config_qcn9274(struct ath12k_base *ab);
int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab);
#endif /* ATH12K_DP_RX_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_DP_TX_H
#define ATH12K_DP_TX_H
#include "core.h"
#include "hal_tx.h"
struct ath12k_dp_htt_wbm_tx_status {
bool acked;
int ack_rssi;
};
int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab);
int ath12k_dp_tx(struct ath12k *ar, struct ath12k_vif *arvif,
struct sk_buff *skb);
void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id);
int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask);
int
ath12k_dp_tx_htt_h2t_ext_stats_req(struct ath12k *ar, u8 type,
struct htt_ext_stats_cfg_params *cfg_params,
u64 cookie);
int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset);
int ath12k_dp_tx_htt_rx_filter_setup(struct ath12k_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);
void ath12k_dp_tx_put_bank_profile(struct ath12k_dp *dp, u8 bank_id);
int ath12k_dp_tx_htt_tx_filter_setup(struct ath12k_base *ab, u32 ring_id,
int mac_id, enum hal_ring_type ring_type,
int tx_buf_size,
struct htt_tx_ring_tlv_filter *htt_tlv_filter);
int ath12k_dp_tx_htt_tx_monitor_mode_ring_config(struct ath12k *ar, bool reset);
int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset);
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,853 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "debug.h"
#include "hal.h"
#include "hal_tx.h"
#include "hal_rx.h"
#include "hal_desc.h"
#include "hif.h"
static void ath12k_hal_reo_set_desc_hdr(struct hal_desc_header *hdr,
u8 owner, u8 buffer_type, u32 magic)
{
hdr->info0 = le32_encode_bits(owner, HAL_DESC_HDR_INFO0_OWNER) |
le32_encode_bits(buffer_type, HAL_DESC_HDR_INFO0_BUF_TYPE);
/* Magic pattern in reserved bits for debugging */
hdr->info0 |= le32_encode_bits(magic, HAL_DESC_HDR_INFO0_DBG_RESERVED);
}
static int ath12k_hal_reo_cmd_queue_stats(struct hal_tlv_64_hdr *tlv,
struct ath12k_hal_reo_cmd *cmd)
{
struct hal_reo_get_queue_stats *desc;
tlv->tl = u32_encode_bits(HAL_REO_GET_QUEUE_STATS, HAL_TLV_HDR_TAG) |
u32_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN);
desc = (struct hal_reo_get_queue_stats *)tlv->value;
memset(&desc->queue_addr_lo, 0,
(sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr)));
desc->cmd.info0 &= ~cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED);
if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS)
desc->cmd.info0 |= cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED);
desc->queue_addr_lo = cpu_to_le32(cmd->addr_lo);
desc->info0 = le32_encode_bits(cmd->addr_hi,
HAL_REO_GET_QUEUE_STATS_INFO0_QUEUE_ADDR_HI);
if (cmd->flag & HAL_REO_CMD_FLG_STATS_CLEAR)
desc->info0 |= cpu_to_le32(HAL_REO_GET_QUEUE_STATS_INFO0_CLEAR_STATS);
return le32_get_bits(desc->cmd.info0, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER);
}
static int ath12k_hal_reo_cmd_flush_cache(struct ath12k_hal *hal,
struct hal_tlv_64_hdr *tlv,
struct ath12k_hal_reo_cmd *cmd)
{
struct hal_reo_flush_cache *desc;
u8 avail_slot = ffz(hal->avail_blk_resource);
if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) {
if (avail_slot >= HAL_MAX_AVAIL_BLK_RES)
return -ENOSPC;
hal->current_blk_index = avail_slot;
}
tlv->tl = u32_encode_bits(HAL_REO_FLUSH_CACHE, HAL_TLV_HDR_TAG) |
u32_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN);
desc = (struct hal_reo_flush_cache *)tlv->value;
memset(&desc->cache_addr_lo, 0,
(sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr)));
desc->cmd.info0 &= ~cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED);
if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS)
desc->cmd.info0 |= cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED);
desc->cache_addr_lo = cpu_to_le32(cmd->addr_lo);
desc->info0 = le32_encode_bits(cmd->addr_hi,
HAL_REO_FLUSH_CACHE_INFO0_CACHE_ADDR_HI);
if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS)
desc->info0 |= cpu_to_le32(HAL_REO_FLUSH_CACHE_INFO0_FWD_ALL_MPDUS);
if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_BLOCK_LATER) {
desc->info0 |= cpu_to_le32(HAL_REO_FLUSH_CACHE_INFO0_BLOCK_CACHE_USAGE);
desc->info0 |=
le32_encode_bits(avail_slot,
HAL_REO_FLUSH_CACHE_INFO0_BLOCK_RESRC_IDX);
}
if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_NO_INVAL)
desc->info0 |= cpu_to_le32(HAL_REO_FLUSH_CACHE_INFO0_FLUSH_WO_INVALIDATE);
if (cmd->flag & HAL_REO_CMD_FLG_FLUSH_ALL)
desc->info0 |= cpu_to_le32(HAL_REO_FLUSH_CACHE_INFO0_FLUSH_ALL);
return le32_get_bits(desc->cmd.info0, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER);
}
static int ath12k_hal_reo_cmd_update_rx_queue(struct hal_tlv_64_hdr *tlv,
struct ath12k_hal_reo_cmd *cmd)
{
struct hal_reo_update_rx_queue *desc;
tlv->tl = u32_encode_bits(HAL_REO_UPDATE_RX_REO_QUEUE, HAL_TLV_HDR_TAG) |
u32_encode_bits(sizeof(*desc), HAL_TLV_HDR_LEN);
desc = (struct hal_reo_update_rx_queue *)tlv->value;
memset(&desc->queue_addr_lo, 0,
(sizeof(*desc) - sizeof(struct hal_reo_cmd_hdr)));
desc->cmd.info0 &= ~cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED);
if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS)
desc->cmd.info0 |= cpu_to_le32(HAL_REO_CMD_HDR_INFO0_STATUS_REQUIRED);
desc->queue_addr_lo = cpu_to_le32(cmd->addr_lo);
desc->info0 =
le32_encode_bits(cmd->addr_hi,
HAL_REO_UPD_RX_QUEUE_INFO0_QUEUE_ADDR_HI) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_RX_QUEUE_NUM),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RX_QUEUE_NUM) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_VLD),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_VLD) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_ALDC),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_ASSOC_LNK_DESC_CNT) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_DIS_DUP_DETECTION),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_DIS_DUP_DETECTION) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_SOFT_REORDER_EN),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SOFT_REORDER_EN) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_AC),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_AC) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_BAR),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BAR) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_RETRY),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_RETRY) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_CHECK_2K_MODE),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_CHECK_2K_MODE) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_OOR_MODE),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_OOR_MODE) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_BA_WINDOW_SIZE),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_BA_WINDOW_SIZE) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_CHECK),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_CHECK) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_EVEN_PN),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_EVEN_PN) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_UNEVEN_PN),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_UNEVEN_PN) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_HANDLE_ENABLE),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_HANDLE_ENABLE) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_SIZE),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_SIZE) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_IGNORE_AMPDU_FLG),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_IGNORE_AMPDU_FLG) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_SVLD),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SVLD) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_SSN),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SSN) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_SEQ_2K_ERR),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_SEQ_2K_ERR) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_PN_VALID),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN_VALID) |
le32_encode_bits(!!(cmd->upd0 & HAL_REO_CMD_UPD0_PN),
HAL_REO_UPD_RX_QUEUE_INFO0_UPD_PN);
desc->info1 =
le32_encode_bits(cmd->rx_queue_num,
HAL_REO_UPD_RX_QUEUE_INFO1_RX_QUEUE_NUMBER) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_VLD),
HAL_REO_UPD_RX_QUEUE_INFO1_VLD) |
le32_encode_bits(u32_get_bits(cmd->upd1, HAL_REO_CMD_UPD1_ALDC),
HAL_REO_UPD_RX_QUEUE_INFO1_ASSOC_LNK_DESC_COUNTER) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_DIS_DUP_DETECTION),
HAL_REO_UPD_RX_QUEUE_INFO1_DIS_DUP_DETECTION) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_SOFT_REORDER_EN),
HAL_REO_UPD_RX_QUEUE_INFO1_SOFT_REORDER_EN) |
le32_encode_bits(u32_get_bits(cmd->upd1, HAL_REO_CMD_UPD1_AC),
HAL_REO_UPD_RX_QUEUE_INFO1_AC) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_BAR),
HAL_REO_UPD_RX_QUEUE_INFO1_BAR) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_CHECK_2K_MODE),
HAL_REO_UPD_RX_QUEUE_INFO1_CHECK_2K_MODE) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_RETRY),
HAL_REO_UPD_RX_QUEUE_INFO1_RETRY) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_OOR_MODE),
HAL_REO_UPD_RX_QUEUE_INFO1_OOR_MODE) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_CHECK),
HAL_REO_UPD_RX_QUEUE_INFO1_PN_CHECK) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_EVEN_PN),
HAL_REO_UPD_RX_QUEUE_INFO1_EVEN_PN) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_UNEVEN_PN),
HAL_REO_UPD_RX_QUEUE_INFO1_UNEVEN_PN) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_PN_HANDLE_ENABLE),
HAL_REO_UPD_RX_QUEUE_INFO1_PN_HANDLE_ENABLE) |
le32_encode_bits(!!(cmd->upd1 & HAL_REO_CMD_UPD1_IGNORE_AMPDU_FLG),
HAL_REO_UPD_RX_QUEUE_INFO1_IGNORE_AMPDU_FLG);
if (cmd->pn_size == 24)
cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_24;
else if (cmd->pn_size == 48)
cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_48;
else if (cmd->pn_size == 128)
cmd->pn_size = HAL_RX_REO_QUEUE_PN_SIZE_128;
if (cmd->ba_window_size < 1)
cmd->ba_window_size = 1;
if (cmd->ba_window_size == 1)
cmd->ba_window_size++;
desc->info2 =
le32_encode_bits(cmd->ba_window_size - 1,
HAL_REO_UPD_RX_QUEUE_INFO2_BA_WINDOW_SIZE) |
le32_encode_bits(cmd->pn_size, HAL_REO_UPD_RX_QUEUE_INFO2_PN_SIZE) |
le32_encode_bits(!!(cmd->upd2 & HAL_REO_CMD_UPD2_SVLD),
HAL_REO_UPD_RX_QUEUE_INFO2_SVLD) |
le32_encode_bits(u32_get_bits(cmd->upd2, HAL_REO_CMD_UPD2_SSN),
HAL_REO_UPD_RX_QUEUE_INFO2_SSN) |
le32_encode_bits(!!(cmd->upd2 & HAL_REO_CMD_UPD2_SEQ_2K_ERR),
HAL_REO_UPD_RX_QUEUE_INFO2_SEQ_2K_ERR) |
le32_encode_bits(!!(cmd->upd2 & HAL_REO_CMD_UPD2_PN_ERR),
HAL_REO_UPD_RX_QUEUE_INFO2_PN_ERR);
return le32_get_bits(desc->cmd.info0, HAL_REO_CMD_HDR_INFO0_CMD_NUMBER);
}
int ath12k_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng,
enum hal_reo_cmd_type type,
struct ath12k_hal_reo_cmd *cmd)
{
struct hal_tlv_64_hdr *reo_desc;
int ret;
spin_lock_bh(&srng->lock);
ath12k_hal_srng_access_begin(ab, srng);
reo_desc = ath12k_hal_srng_src_get_next_entry(ab, srng);
if (!reo_desc) {
ret = -ENOBUFS;
goto out;
}
switch (type) {
case HAL_REO_CMD_GET_QUEUE_STATS:
ret = ath12k_hal_reo_cmd_queue_stats(reo_desc, cmd);
break;
case HAL_REO_CMD_FLUSH_CACHE:
ret = ath12k_hal_reo_cmd_flush_cache(&ab->hal, reo_desc, cmd);
break;
case HAL_REO_CMD_UPDATE_RX_QUEUE:
ret = ath12k_hal_reo_cmd_update_rx_queue(reo_desc, cmd);
break;
case HAL_REO_CMD_FLUSH_QUEUE:
case HAL_REO_CMD_UNBLOCK_CACHE:
case HAL_REO_CMD_FLUSH_TIMEOUT_LIST:
ath12k_warn(ab, "Unsupported reo command %d\n", type);
ret = -ENOTSUPP;
break;
default:
ath12k_warn(ab, "Unknown reo command %d\n", type);
ret = -EINVAL;
break;
}
out:
ath12k_hal_srng_access_end(ab, srng);
spin_unlock_bh(&srng->lock);
return ret;
}
void ath12k_hal_rx_buf_addr_info_set(struct ath12k_buffer_addr *binfo,
dma_addr_t paddr, u32 cookie, u8 manager)
{
u32 paddr_lo, paddr_hi;
paddr_lo = lower_32_bits(paddr);
paddr_hi = upper_32_bits(paddr);
binfo->info0 = le32_encode_bits(paddr_lo, BUFFER_ADDR_INFO0_ADDR);
binfo->info1 = le32_encode_bits(paddr_hi, BUFFER_ADDR_INFO1_ADDR) |
le32_encode_bits(cookie, BUFFER_ADDR_INFO1_SW_COOKIE) |
le32_encode_bits(manager, BUFFER_ADDR_INFO1_RET_BUF_MGR);
}
void ath12k_hal_rx_buf_addr_info_get(struct ath12k_buffer_addr *binfo,
dma_addr_t *paddr,
u32 *cookie, u8 *rbm)
{
*paddr = (((u64)le32_get_bits(binfo->info1, BUFFER_ADDR_INFO1_ADDR)) << 32) |
le32_get_bits(binfo->info0, BUFFER_ADDR_INFO0_ADDR);
*cookie = le32_get_bits(binfo->info1, BUFFER_ADDR_INFO1_SW_COOKIE);
*rbm = le32_get_bits(binfo->info1, BUFFER_ADDR_INFO1_RET_BUF_MGR);
}
void ath12k_hal_rx_msdu_link_info_get(struct hal_rx_msdu_link *link, u32 *num_msdus,
u32 *msdu_cookies,
enum hal_rx_buf_return_buf_manager *rbm)
{
struct hal_rx_msdu_details *msdu;
u32 val;
int i;
*num_msdus = HAL_NUM_RX_MSDUS_PER_LINK_DESC;
msdu = &link->msdu_link[0];
*rbm = le32_get_bits(msdu->buf_addr_info.info1,
BUFFER_ADDR_INFO1_RET_BUF_MGR);
for (i = 0; i < *num_msdus; i++) {
msdu = &link->msdu_link[i];
val = le32_get_bits(msdu->buf_addr_info.info0,
BUFFER_ADDR_INFO0_ADDR);
if (val == 0) {
*num_msdus = i;
break;
}
*msdu_cookies = le32_get_bits(msdu->buf_addr_info.info1,
BUFFER_ADDR_INFO1_SW_COOKIE);
msdu_cookies++;
}
}
int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
struct hal_reo_dest_ring *desc,
dma_addr_t *paddr, u32 *desc_bank)
{
enum hal_reo_dest_ring_push_reason push_reason;
enum hal_reo_dest_ring_error_code err_code;
u32 cookie, val;
push_reason = le32_get_bits(desc->info0,
HAL_REO_DEST_RING_INFO0_PUSH_REASON);
err_code = le32_get_bits(desc->info0,
HAL_REO_DEST_RING_INFO0_ERROR_CODE);
ab->soc_stats.reo_error[err_code]++;
if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED &&
push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) {
ath12k_warn(ab, "expected error push reason code, received %d\n",
push_reason);
return -EINVAL;
}
val = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE);
if (val != HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) {
ath12k_warn(ab, "expected buffer type link_desc");
return -EINVAL;
}
ath12k_hal_rx_reo_ent_paddr_get(ab, &desc->buf_addr_info, paddr, &cookie);
*desc_bank = u32_get_bits(cookie, DP_LINK_DESC_BANK_MASK);
return 0;
}
int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc,
struct hal_rx_wbm_rel_info *rel_info)
{
struct hal_wbm_release_ring *wbm_desc = desc;
struct hal_wbm_release_ring_cc_rx *wbm_cc_desc = desc;
enum hal_wbm_rel_desc_type type;
enum hal_wbm_rel_src_module rel_src;
bool hw_cc_done;
u64 desc_va;
u32 val;
type = le32_get_bits(wbm_desc->info0, HAL_WBM_RELEASE_INFO0_DESC_TYPE);
/* We expect only WBM_REL buffer type */
if (type != HAL_WBM_REL_DESC_TYPE_REL_MSDU) {
WARN_ON(1);
return -EINVAL;
}
rel_src = le32_get_bits(wbm_desc->info0,
HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE);
if (rel_src != HAL_WBM_REL_SRC_MODULE_RXDMA &&
rel_src != HAL_WBM_REL_SRC_MODULE_REO)
return -EINVAL;
/* The format of wbm rel ring desc changes based on the
* hw cookie conversion status
*/
hw_cc_done = le32_get_bits(wbm_desc->info0,
HAL_WBM_RELEASE_RX_INFO0_CC_STATUS);
if (!hw_cc_done) {
val = le32_get_bits(wbm_desc->buf_addr_info.info1,
BUFFER_ADDR_INFO1_RET_BUF_MGR);
if (val != HAL_RX_BUF_RBM_SW3_BM) {
ab->soc_stats.invalid_rbm++;
return -EINVAL;
}
rel_info->cookie = le32_get_bits(wbm_desc->buf_addr_info.info1,
BUFFER_ADDR_INFO1_SW_COOKIE);
rel_info->rx_desc = NULL;
} else {
val = le32_get_bits(wbm_cc_desc->info0,
HAL_WBM_RELEASE_RX_CC_INFO0_RBM);
if (val != HAL_RX_BUF_RBM_SW3_BM) {
ab->soc_stats.invalid_rbm++;
return -EINVAL;
}
rel_info->cookie = le32_get_bits(wbm_cc_desc->info1,
HAL_WBM_RELEASE_RX_CC_INFO1_COOKIE);
desc_va = ((u64)le32_to_cpu(wbm_cc_desc->buf_va_hi) << 32 |
le32_to_cpu(wbm_cc_desc->buf_va_lo));
rel_info->rx_desc =
(struct ath12k_rx_desc_info *)((unsigned long)desc_va);
}
rel_info->err_rel_src = rel_src;
rel_info->hw_cc_done = hw_cc_done;
rel_info->first_msdu = le32_get_bits(wbm_desc->info3,
HAL_WBM_RELEASE_INFO3_FIRST_MSDU);
rel_info->last_msdu = le32_get_bits(wbm_desc->info3,
HAL_WBM_RELEASE_INFO3_LAST_MSDU);
rel_info->continuation = le32_get_bits(wbm_desc->info3,
HAL_WBM_RELEASE_INFO3_CONTINUATION);
if (rel_info->err_rel_src == HAL_WBM_REL_SRC_MODULE_REO) {
rel_info->push_reason =
le32_get_bits(wbm_desc->info0,
HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON);
rel_info->err_code =
le32_get_bits(wbm_desc->info0,
HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE);
} else {
rel_info->push_reason =
le32_get_bits(wbm_desc->info0,
HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON);
rel_info->err_code =
le32_get_bits(wbm_desc->info0,
HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE);
}
return 0;
}
void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab,
struct ath12k_buffer_addr *buff_addr,
dma_addr_t *paddr, u32 *cookie)
{
*paddr = ((u64)(le32_get_bits(buff_addr->info1,
BUFFER_ADDR_INFO1_ADDR)) << 32) |
le32_get_bits(buff_addr->info0, BUFFER_ADDR_INFO0_ADDR);
*cookie = le32_get_bits(buff_addr->info1, BUFFER_ADDR_INFO1_SW_COOKIE);
}
void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab,
struct hal_wbm_release_ring *dst_desc,
struct hal_wbm_release_ring *src_desc,
enum hal_wbm_rel_bm_act action)
{
dst_desc->buf_addr_info = src_desc->buf_addr_info;
dst_desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW,
HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) |
le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) |
le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK,
HAL_WBM_RELEASE_INFO0_DESC_TYPE);
}
void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct hal_reo_get_queue_stats_status *desc =
(struct hal_reo_get_queue_stats_status *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
ath12k_dbg(ab, ATH12K_DBG_HAL, "Queue stats status:\n");
ath12k_dbg(ab, ATH12K_DBG_HAL, "header: cmd_num %d status %d\n",
status->uniform_hdr.cmd_num,
status->uniform_hdr.cmd_status);
ath12k_dbg(ab, ATH12K_DBG_HAL, "ssn %u cur_idx %u\n",
le32_get_bits(desc->info0,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_SSN),
le32_get_bits(desc->info0,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO0_CUR_IDX));
ath12k_dbg(ab, ATH12K_DBG_HAL, "pn = [%08x, %08x, %08x, %08x]\n",
desc->pn[0], desc->pn[1], desc->pn[2], desc->pn[3]);
ath12k_dbg(ab, ATH12K_DBG_HAL, "last_rx: enqueue_tstamp %08x dequeue_tstamp %08x\n",
desc->last_rx_enqueue_timestamp,
desc->last_rx_dequeue_timestamp);
ath12k_dbg(ab, ATH12K_DBG_HAL, "rx_bitmap [%08x %08x %08x %08x %08x %08x %08x %08x]\n",
desc->rx_bitmap[0], desc->rx_bitmap[1], desc->rx_bitmap[2],
desc->rx_bitmap[3], desc->rx_bitmap[4], desc->rx_bitmap[5],
desc->rx_bitmap[6], desc->rx_bitmap[7]);
ath12k_dbg(ab, ATH12K_DBG_HAL, "count: cur_mpdu %u cur_msdu %u\n",
le32_get_bits(desc->info1,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MPDU_COUNT),
le32_get_bits(desc->info1,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO1_MSDU_COUNT));
ath12k_dbg(ab, ATH12K_DBG_HAL, "fwd_timeout %u fwd_bar %u dup_count %u\n",
le32_get_bits(desc->info2,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_TIMEOUT_COUNT),
le32_get_bits(desc->info2,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_FDTB_COUNT),
le32_get_bits(desc->info2,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_DUPLICATE_COUNT));
ath12k_dbg(ab, ATH12K_DBG_HAL, "frames_in_order %u bar_rcvd %u\n",
le32_get_bits(desc->info3,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_FIO_COUNT),
le32_get_bits(desc->info3,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO3_BAR_RCVD_CNT));
ath12k_dbg(ab, ATH12K_DBG_HAL, "num_mpdus %d num_msdus %d total_bytes %d\n",
desc->num_mpdu_frames, desc->num_msdu_frames,
desc->total_bytes);
ath12k_dbg(ab, ATH12K_DBG_HAL, "late_rcvd %u win_jump_2k %u hole_cnt %u\n",
le32_get_bits(desc->info4,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_LATE_RX_MPDU),
le32_get_bits(desc->info2,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO2_WINDOW_JMP2K),
le32_get_bits(desc->info4,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO4_HOLE_COUNT));
ath12k_dbg(ab, ATH12K_DBG_HAL, "looping count %u\n",
le32_get_bits(desc->info5,
HAL_REO_GET_QUEUE_STATS_STATUS_INFO5_LOOPING_CNT));
}
void ath12k_hal_reo_flush_queue_status(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct hal_reo_flush_queue_status *desc =
(struct hal_reo_flush_queue_status *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
status->u.flush_queue.err_detected =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_QUEUE_INFO0_ERR_DETECTED);
}
void ath12k_hal_reo_flush_cache_status(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct ath12k_hal *hal = &ab->hal;
struct hal_reo_flush_cache_status *desc =
(struct hal_reo_flush_cache_status *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
status->u.flush_cache.err_detected =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_IS_ERR);
status->u.flush_cache.err_code =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_BLOCK_ERR_CODE);
if (!status->u.flush_cache.err_code)
hal->avail_blk_resource |= BIT(hal->current_blk_index);
status->u.flush_cache.cache_controller_flush_status_hit =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_STATUS_HIT);
status->u.flush_cache.cache_controller_flush_status_desc_type =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_DESC_TYPE);
status->u.flush_cache.cache_controller_flush_status_client_id =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_CLIENT_ID);
status->u.flush_cache.cache_controller_flush_status_err =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_ERR);
status->u.flush_cache.cache_controller_flush_status_cnt =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_CACHE_STATUS_INFO0_FLUSH_COUNT);
}
void ath12k_hal_reo_unblk_cache_status(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct ath12k_hal *hal = &ab->hal;
struct hal_reo_unblock_cache_status *desc =
(struct hal_reo_unblock_cache_status *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
status->u.unblock_cache.err_detected =
le32_get_bits(desc->info0,
HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_IS_ERR);
status->u.unblock_cache.unblock_type =
le32_get_bits(desc->info0,
HAL_REO_UNBLOCK_CACHE_STATUS_INFO0_TYPE);
if (!status->u.unblock_cache.err_detected &&
status->u.unblock_cache.unblock_type ==
HAL_REO_STATUS_UNBLOCK_BLOCKING_RESOURCE)
hal->avail_blk_resource &= ~BIT(hal->current_blk_index);
}
void ath12k_hal_reo_flush_timeout_list_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct hal_reo_flush_timeout_list_status *desc =
(struct hal_reo_flush_timeout_list_status *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
status->u.timeout_list.err_detected =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_IS_ERR);
status->u.timeout_list.list_empty =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_TIMEOUT_STATUS_INFO0_LIST_EMPTY);
status->u.timeout_list.release_desc_cnt =
le32_get_bits(desc->info1,
HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_REL_DESC_COUNT);
status->u.timeout_list.fwd_buf_cnt =
le32_get_bits(desc->info0,
HAL_REO_FLUSH_TIMEOUT_STATUS_INFO1_FWD_BUF_COUNT);
}
void ath12k_hal_reo_desc_thresh_reached_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct hal_reo_desc_thresh_reached_status *desc =
(struct hal_reo_desc_thresh_reached_status *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->hdr.info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
status->u.desc_thresh_reached.threshold_idx =
le32_get_bits(desc->info0,
HAL_REO_DESC_THRESH_STATUS_INFO0_THRESH_INDEX);
status->u.desc_thresh_reached.link_desc_counter0 =
le32_get_bits(desc->info1,
HAL_REO_DESC_THRESH_STATUS_INFO1_LINK_DESC_COUNTER0);
status->u.desc_thresh_reached.link_desc_counter1 =
le32_get_bits(desc->info2,
HAL_REO_DESC_THRESH_STATUS_INFO2_LINK_DESC_COUNTER1);
status->u.desc_thresh_reached.link_desc_counter2 =
le32_get_bits(desc->info3,
HAL_REO_DESC_THRESH_STATUS_INFO3_LINK_DESC_COUNTER2);
status->u.desc_thresh_reached.link_desc_counter_sum =
le32_get_bits(desc->info4,
HAL_REO_DESC_THRESH_STATUS_INFO4_LINK_DESC_COUNTER_SUM);
}
void ath12k_hal_reo_update_rx_reo_queue_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status)
{
struct hal_reo_status_hdr *desc =
(struct hal_reo_status_hdr *)tlv->value;
status->uniform_hdr.cmd_num =
le32_get_bits(desc->info0,
HAL_REO_STATUS_HDR_INFO0_STATUS_NUM);
status->uniform_hdr.cmd_status =
le32_get_bits(desc->info0,
HAL_REO_STATUS_HDR_INFO0_EXEC_STATUS);
}
u32 ath12k_hal_reo_qdesc_size(u32 ba_window_size, u8 tid)
{
u32 num_ext_desc;
if (ba_window_size <= 1) {
if (tid != HAL_DESC_REO_NON_QOS_TID)
num_ext_desc = 1;
else
num_ext_desc = 0;
} else if (ba_window_size <= 105) {
num_ext_desc = 1;
} else if (ba_window_size <= 210) {
num_ext_desc = 2;
} else {
num_ext_desc = 3;
}
return sizeof(struct hal_rx_reo_queue) +
(num_ext_desc * sizeof(struct hal_rx_reo_queue_ext));
}
void ath12k_hal_reo_qdesc_setup(struct hal_rx_reo_queue *qdesc,
int tid, u32 ba_window_size,
u32 start_seq, enum hal_pn_type type)
{
struct hal_rx_reo_queue_ext *ext_desc;
memset(qdesc, 0, sizeof(*qdesc));
ath12k_hal_reo_set_desc_hdr(&qdesc->desc_hdr, HAL_DESC_REO_OWNED,
HAL_DESC_REO_QUEUE_DESC,
REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_0);
qdesc->rx_queue_num = le32_encode_bits(tid, HAL_RX_REO_QUEUE_RX_QUEUE_NUMBER);
qdesc->info0 =
le32_encode_bits(1, HAL_RX_REO_QUEUE_INFO0_VLD) |
le32_encode_bits(1, HAL_RX_REO_QUEUE_INFO0_ASSOC_LNK_DESC_COUNTER) |
le32_encode_bits(ath12k_tid_to_ac(tid), HAL_RX_REO_QUEUE_INFO0_AC);
if (ba_window_size < 1)
ba_window_size = 1;
if (ba_window_size == 1 && tid != HAL_DESC_REO_NON_QOS_TID)
ba_window_size++;
if (ba_window_size == 1)
qdesc->info0 |= le32_encode_bits(1, HAL_RX_REO_QUEUE_INFO0_RETRY);
qdesc->info0 |= le32_encode_bits(ba_window_size - 1,
HAL_RX_REO_QUEUE_INFO0_BA_WINDOW_SIZE);
switch (type) {
case HAL_PN_TYPE_NONE:
case HAL_PN_TYPE_WAPI_EVEN:
case HAL_PN_TYPE_WAPI_UNEVEN:
break;
case HAL_PN_TYPE_WPA:
qdesc->info0 |=
le32_encode_bits(1, HAL_RX_REO_QUEUE_INFO0_PN_CHECK) |
le32_encode_bits(HAL_RX_REO_QUEUE_PN_SIZE_48,
HAL_RX_REO_QUEUE_INFO0_PN_SIZE);
break;
}
/* TODO: Set Ignore ampdu flags based on BA window size and/or
* AMPDU capabilities
*/
qdesc->info0 |= le32_encode_bits(1, HAL_RX_REO_QUEUE_INFO0_IGNORE_AMPDU_FLG);
qdesc->info1 |= le32_encode_bits(0, HAL_RX_REO_QUEUE_INFO1_SVLD);
if (start_seq <= 0xfff)
qdesc->info1 = le32_encode_bits(start_seq,
HAL_RX_REO_QUEUE_INFO1_SSN);
if (tid == HAL_DESC_REO_NON_QOS_TID)
return;
ext_desc = qdesc->ext_desc;
/* TODO: HW queue descriptors are currently allocated for max BA
* window size for all QOS TIDs so that same descriptor can be used
* later when ADDBA request is received. This should be changed to
* allocate HW queue descriptors based on BA window size being
* negotiated (0 for non BA cases), and reallocate when BA window
* size changes and also send WMI message to FW to change the REO
* queue descriptor in Rx peer entry as part of dp_rx_tid_update.
*/
memset(ext_desc, 0, 3 * sizeof(*ext_desc));
ath12k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED,
HAL_DESC_REO_QUEUE_EXT_DESC,
REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_1);
ext_desc++;
ath12k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED,
HAL_DESC_REO_QUEUE_EXT_DESC,
REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_2);
ext_desc++;
ath12k_hal_reo_set_desc_hdr(&ext_desc->desc_hdr, HAL_DESC_REO_OWNED,
HAL_DESC_REO_QUEUE_EXT_DESC,
REO_QUEUE_DESC_MAGIC_DEBUG_PATTERN_3);
}
void ath12k_hal_reo_init_cmd_ring(struct ath12k_base *ab,
struct hal_srng *srng)
{
struct hal_srng_params params;
struct hal_tlv_64_hdr *tlv;
struct hal_reo_get_queue_stats *desc;
int i, cmd_num = 1;
int entry_size;
u8 *entry;
memset(&params, 0, sizeof(params));
entry_size = ath12k_hal_srng_get_entrysize(ab, HAL_REO_CMD);
ath12k_hal_srng_get_params(ab, srng, &params);
entry = (u8 *)params.ring_base_vaddr;
for (i = 0; i < params.num_entries; i++) {
tlv = (struct hal_tlv_64_hdr *)entry;
desc = (struct hal_reo_get_queue_stats *)tlv->value;
desc->cmd.info0 = le32_encode_bits(cmd_num++,
HAL_REO_CMD_HDR_INFO0_CMD_NUMBER);
entry += entry_size;
}
}
void ath12k_hal_reo_hw_setup(struct ath12k_base *ab, u32 ring_hash_map)
{
u32 reo_base = HAL_SEQ_WCSS_UMAC_REO_REG;
u32 val;
val = ath12k_hif_read32(ab, reo_base + HAL_REO1_GEN_ENABLE);
val |= u32_encode_bits(1, HAL_REO1_GEN_ENABLE_AGING_LIST_ENABLE) |
u32_encode_bits(1, HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE);
ath12k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val);
val = ath12k_hif_read32(ab, reo_base + HAL_REO1_MISC_CTRL_ADDR(ab));
val &= ~(HAL_REO1_MISC_CTL_FRAG_DST_RING |
HAL_REO1_MISC_CTL_BAR_DST_RING);
val |= u32_encode_bits(HAL_SRNG_RING_ID_REO2SW0,
HAL_REO1_MISC_CTL_FRAG_DST_RING);
val |= u32_encode_bits(HAL_SRNG_RING_ID_REO2SW0,
HAL_REO1_MISC_CTL_BAR_DST_RING);
ath12k_hif_write32(ab, reo_base + HAL_REO1_MISC_CTRL_ADDR(ab), val);
ath12k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0(ab),
HAL_DEFAULT_BE_BK_VI_REO_TIMEOUT_USEC);
ath12k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_1(ab),
HAL_DEFAULT_BE_BK_VI_REO_TIMEOUT_USEC);
ath12k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_2(ab),
HAL_DEFAULT_BE_BK_VI_REO_TIMEOUT_USEC);
ath12k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3(ab),
HAL_DEFAULT_VO_REO_TIMEOUT_USEC);
ath12k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_2,
ring_hash_map);
ath12k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3,
ring_hash_map);
}

View file

@ -0,0 +1,704 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HAL_RX_H
#define ATH12K_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;
bool continuation;
void *rx_desc;
bool hw_cc_done;
};
#define HAL_INVALID_PEERID 0xffff
#define VHT_SIG_SU_NSS_MASK 0x7
#define HAL_RX_MAX_MCS 12
#define HAL_RX_MAX_NSS 8
#define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \
le32_get_bits((__val), GENMASK(7, 0))
#define HAL_RX_MPDU_INFO_PN_GET_BYTE2(__val) \
le32_get_bits((__val), GENMASK(15, 8))
#define HAL_RX_MPDU_INFO_PN_GET_BYTE3(__val) \
le32_get_bits((__val), GENMASK(23, 16))
#define HAL_RX_MPDU_INFO_PN_GET_BYTE4(__val) \
le32_get_bits((__val), GENMASK(31, 24))
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,
};
enum hal_rx_legacy_rate {
HAL_RX_LEGACY_RATE_1_MBPS,
HAL_RX_LEGACY_RATE_2_MBPS,
HAL_RX_LEGACY_RATE_5_5_MBPS,
HAL_RX_LEGACY_RATE_6_MBPS,
HAL_RX_LEGACY_RATE_9_MBPS,
HAL_RX_LEGACY_RATE_11_MBPS,
HAL_RX_LEGACY_RATE_12_MBPS,
HAL_RX_LEGACY_RATE_18_MBPS,
HAL_RX_LEGACY_RATE_24_MBPS,
HAL_RX_LEGACY_RATE_36_MBPS,
HAL_RX_LEGACY_RATE_48_MBPS,
HAL_RX_LEGACY_RATE_54_MBPS,
HAL_RX_LEGACY_RATE_INVALID,
};
#define HAL_TLV_STATUS_PPDU_NOT_DONE 0
#define HAL_TLV_STATUS_PPDU_DONE 1
#define HAL_TLV_STATUS_BUF_DONE 2
#define HAL_TLV_STATUS_PPDU_NON_STD_DONE 3
#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,
};
#define HAL_RX_MAX_MPDU 256
#define HAL_RX_NUM_WORDS_PER_PPDU_BITMAP (HAL_RX_MAX_MPDU >> 5)
struct hal_rx_user_status {
u32 mcs:4,
nss:3,
ofdma_info_valid:1,
ul_ofdma_ru_start_index:7,
ul_ofdma_ru_width:7,
ul_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 tcp_ack_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;
u8 ldpc;
u32 mpdu_cnt_fcs_ok;
u32 mpdu_cnt_fcs_err;
u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP];
u32 mpdu_ok_byte_count;
u32 mpdu_err_byte_count;
};
#define HAL_MAX_UL_MU_USERS 37
struct hal_rx_mon_ppdu_info {
u32 ppdu_id;
u32 last_ppdu_id;
u64 ppdu_ts;
u32 num_mpdu_fcs_ok;
u32 num_mpdu_fcs_err;
u32 preamble_type;
u32 mpdu_len;
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 sgi;
u8 ldpc;
u8 beamformed;
u8 rssi_comb;
u16 tid;
u8 fc_valid;
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;
s8 rssi_chain[8][8];
u32 num_users;
u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP];
u8 addr1[ETH_ALEN];
u8 addr2[ETH_ALEN];
u8 addr3[ETH_ALEN];
u8 addr4[ETH_ALEN];
struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS];
u8 userid;
u16 ampdu_id[HAL_MAX_UL_MU_USERS];
bool first_msdu_in_mpdu;
bool is_ampdu;
u8 medium_prot_type;
};
#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_INFO6_TID_BITMAP GENMASK(15, 0)
#define HAL_RX_PPDU_END_USER_STATS_INFO6_TID_EOSP_BITMAP GENMASK(31, 16)
#define HAL_RX_PPDU_END_USER_STATS_MPDU_DELIM_OK_BYTE_COUNT GENMASK(24, 0)
#define HAL_RX_PPDU_END_USER_STATS_MPDU_DELIM_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 usr_resp_ref;
__le32 info6;
__le32 rsvd3[4];
__le32 mpdu_ok_cnt;
__le32 rsvd4;
__le32 mpdu_err_cnt;
__le32 rsvd5[2];
__le32 usr_resp_ref_ext;
__le32 rsvd6;
} __packed;
struct hal_rx_ppdu_end_user_stats_ext {
__le32 info0;
__le32 info1;
__le32 info2;
__le32 info3;
__le32 info4;
__le32 info5;
__le32 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 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 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_INFO0_UL_FLAG BIT(1)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_MCS_OF_SIGB GENMASK(3, 1)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_DCM_OF_SIGB BIT(4)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_BSS_COLOR GENMASK(10, 5)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_SPATIAL_REUSE GENMASK(14, 11)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_TRANSMIT_BW GENMASK(17, 15)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_NUM_SIGB_SYMB GENMASK(21, 18)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_COMP_MODE_SIGB BIT(22)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_CP_LTF_SIZE GENMASK(24, 23)
#define HAL_RX_HE_SIG_A_MU_DL_INFO0_DOPPLER_INDICATION BIT(25)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_TXOP_DURATION GENMASK(6, 0)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_CODING BIT(7)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_NUM_LTF_SYMB GENMASK(10, 8)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_LDPC_EXTRA BIT(11)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_STBC BIT(12)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_TXBF BIT(10)
#define HAL_RX_HE_SIG_A_MU_DL_INFO1_PKT_EXT_FACTOR GENMASK(14, 13)
#define HAL_RX_HE_SIG_A_MU_DL_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;
enum hal_rx_ul_reception_type {
HAL_RECEPTION_TYPE_ULOFMDA,
HAL_RECEPTION_TYPE_ULMIMO,
HAL_RECEPTION_TYPE_OTHER,
HAL_RECEPTION_TYPE_FRAMELESS
};
#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RSSI_COMB GENMASK(15, 8)
#define HAL_RX_PHYRX_RSSI_LEGACY_INFO_RSVD1_RECEPTION GENMASK(3, 0)
struct hal_rx_phyrx_rssi_legacy_info {
__le32 rsvd[35];
__le32 info0;
} __packed;
#define HAL_RX_MPDU_START_INFO0_PPDU_ID GENMASK(31, 16)
#define HAL_RX_MPDU_START_INFO1_PEERID GENMASK(31, 16)
#define HAL_RX_MPDU_START_INFO2_MPDU_LEN GENMASK(13, 0)
struct hal_rx_mpdu_start {
__le32 info0;
__le32 info1;
__le32 rsvd1[11];
__le32 info2;
__le32 rsvd2[9];
} __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];
};
#define HAL_RX_FBM_ACK_INFO0_ADDR1_31_0 GENMASK(31, 0)
#define HAL_RX_FBM_ACK_INFO1_ADDR1_47_32 GENMASK(15, 0)
#define HAL_RX_FBM_ACK_INFO1_ADDR2_15_0 GENMASK(31, 16)
#define HAL_RX_FBM_ACK_INFO2_ADDR2_47_16 GENMASK(31, 0)
struct hal_rx_frame_bitmap_ack {
__le32 reserved;
__le32 info0;
__le32 info1;
__le32 info2;
__le32 reserved1[10];
} __packed;
#define HAL_RX_RESP_REQ_INFO0_PPDU_ID GENMASK(15, 0)
#define HAL_RX_RESP_REQ_INFO0_RECEPTION_TYPE BIT(16)
#define HAL_RX_RESP_REQ_INFO1_DURATION GENMASK(15, 0)
#define HAL_RX_RESP_REQ_INFO1_RATE_MCS GENMASK(24, 21)
#define HAL_RX_RESP_REQ_INFO1_SGI GENMASK(26, 25)
#define HAL_RX_RESP_REQ_INFO1_STBC BIT(27)
#define HAL_RX_RESP_REQ_INFO1_LDPC BIT(28)
#define HAL_RX_RESP_REQ_INFO1_IS_AMPDU BIT(29)
#define HAL_RX_RESP_REQ_INFO2_NUM_USER GENMASK(6, 0)
#define HAL_RX_RESP_REQ_INFO3_ADDR1_31_0 GENMASK(31, 0)
#define HAL_RX_RESP_REQ_INFO4_ADDR1_47_32 GENMASK(15, 0)
#define HAL_RX_RESP_REQ_INFO4_ADDR1_15_0 GENMASK(31, 16)
#define HAL_RX_RESP_REQ_INFO5_ADDR1_47_16 GENMASK(31, 0)
struct hal_rx_resp_req_info {
__le32 info0;
__le32 reserved[1];
__le32 info1;
__le32 info2;
__le32 reserved1[2];
__le32 info3;
__le32 info4;
__le32 info5;
__le32 reserved2[5];
} __packed;
#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
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VALID BIT(30)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W0_VER BIT(31)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_NSS GENMASK(2, 0)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_MCS GENMASK(6, 3)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_LDPC BIT(7)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_DCM BIT(8)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_RU_START GENMASK(15, 9)
#define HAL_RX_UL_OFDMA_USER_INFO_V0_W1_RU_SIZE GENMASK(18, 16)
/* HE Radiotap data1 Mask */
#define HE_SU_FORMAT_TYPE 0x0000
#define HE_EXT_SU_FORMAT_TYPE 0x0001
#define HE_MU_FORMAT_TYPE 0x0002
#define HE_TRIG_FORMAT_TYPE 0x0003
#define HE_BEAM_CHANGE_KNOWN 0x0008
#define HE_DL_UL_KNOWN 0x0010
#define HE_MCS_KNOWN 0x0020
#define HE_DCM_KNOWN 0x0040
#define HE_CODING_KNOWN 0x0080
#define HE_LDPC_EXTRA_SYMBOL_KNOWN 0x0100
#define HE_STBC_KNOWN 0x0200
#define HE_DATA_BW_RU_KNOWN 0x4000
#define HE_DOPPLER_KNOWN 0x8000
#define HE_BSS_COLOR_KNOWN 0x0004
/* HE Radiotap data2 Mask */
#define HE_GI_KNOWN 0x0002
#define HE_TXBF_KNOWN 0x0010
#define HE_PE_DISAMBIGUITY_KNOWN 0x0020
#define HE_TXOP_KNOWN 0x0040
#define HE_LTF_SYMBOLS_KNOWN 0x0004
#define HE_PRE_FEC_PADDING_KNOWN 0x0008
#define HE_MIDABLE_PERIODICITY_KNOWN 0x0080
/* HE radiotap data3 shift values */
#define HE_BEAM_CHANGE_SHIFT 6
#define HE_DL_UL_SHIFT 7
#define HE_TRANSMIT_MCS_SHIFT 8
#define HE_DCM_SHIFT 12
#define HE_CODING_SHIFT 13
#define HE_LDPC_EXTRA_SYMBOL_SHIFT 14
#define HE_STBC_SHIFT 15
/* HE radiotap data4 shift values */
#define HE_STA_ID_SHIFT 4
/* HE radiotap data5 */
#define HE_GI_SHIFT 4
#define HE_LTF_SIZE_SHIFT 6
#define HE_LTF_SYM_SHIFT 8
#define HE_TXBF_SHIFT 14
#define HE_PE_DISAMBIGUITY_SHIFT 15
#define HE_PRE_FEC_PAD_SHIFT 12
/* HE radiotap data6 */
#define HE_DOPPLER_SHIFT 4
#define HE_TXOP_SHIFT 8
/* HE radiotap HE-MU flags1 */
#define HE_SIG_B_MCS_KNOWN 0x0010
#define HE_SIG_B_DCM_KNOWN 0x0040
#define HE_SIG_B_SYM_NUM_KNOWN 0x8000
#define HE_RU_0_KNOWN 0x0100
#define HE_RU_1_KNOWN 0x0200
#define HE_RU_2_KNOWN 0x0400
#define HE_RU_3_KNOWN 0x0800
#define HE_DCM_FLAG_1_SHIFT 5
#define HE_SPATIAL_REUSE_MU_KNOWN 0x0100
#define HE_SIG_B_COMPRESSION_FLAG_1_KNOWN 0x4000
/* HE radiotap HE-MU flags2 */
#define HE_SIG_B_COMPRESSION_FLAG_2_SHIFT 3
#define HE_BW_KNOWN 0x0004
#define HE_NUM_SIG_B_SYMBOLS_SHIFT 4
#define HE_SIG_B_COMPRESSION_FLAG_2_KNOWN 0x0100
#define HE_NUM_SIG_B_FLAG_2_SHIFT 9
#define HE_LTF_FLAG_2_SYMBOLS_SHIFT 12
#define HE_LTF_KNOWN 0x8000
/* HE radiotap per_user_1 */
#define HE_STA_SPATIAL_SHIFT 11
#define HE_TXBF_SHIFT 14
#define HE_RESERVED_SET_TO_1_SHIFT 19
#define HE_STA_CODING_SHIFT 20
/* HE radiotap per_user_2 */
#define HE_STA_MCS_SHIFT 4
#define HE_STA_DCM_SHIFT 5
/* HE radiotap per user known */
#define HE_USER_FIELD_POSITION_KNOWN 0x01
#define HE_STA_ID_PER_USER_KNOWN 0x02
#define HE_STA_NSTS_KNOWN 0x04
#define HE_STA_TX_BF_KNOWN 0x08
#define HE_STA_SPATIAL_CONFIG_KNOWN 0x10
#define HE_STA_MCS_KNOWN 0x20
#define HE_STA_DCM_KNOWN 0x40
#define HE_STA_CODING_KNOWN 0x80
#define HAL_RX_MPDU_ERR_FCS BIT(0)
#define HAL_RX_MPDU_ERR_DECRYPT BIT(1)
#define HAL_RX_MPDU_ERR_TKIP_MIC BIT(2)
#define HAL_RX_MPDU_ERR_AMSDU_ERR BIT(3)
#define HAL_RX_MPDU_ERR_OVERFLOW BIT(4)
#define HAL_RX_MPDU_ERR_MSDU_LEN BIT(5)
#define HAL_RX_MPDU_ERR_MPDU_LEN BIT(6)
#define HAL_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7)
static inline
enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones)
{
enum nl80211_he_ru_alloc ret;
switch (ru_tones) {
case RU_52:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_52;
break;
case RU_106:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_106;
break;
case RU_242:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_242;
break;
case RU_484:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_484;
break;
case RU_996:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_996;
break;
case RU_26:
fallthrough;
default:
ret = NL80211_RATE_INFO_HE_RU_ALLOC_26;
break;
}
return ret;
}
void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_reo_flush_queue_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_reo_flush_cache_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_reo_unblk_cache_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_reo_flush_timeout_list_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_reo_desc_thresh_reached_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_reo_update_rx_reo_queue_status(struct ath12k_base *ab,
struct hal_tlv_64_hdr *tlv,
struct hal_reo_status *status);
void ath12k_hal_rx_msdu_link_info_get(struct hal_rx_msdu_link *link, u32 *num_msdus,
u32 *msdu_cookies,
enum hal_rx_buf_return_buf_manager *rbm);
void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab,
struct hal_wbm_release_ring *dst_desc,
struct hal_wbm_release_ring *src_desc,
enum hal_wbm_rel_bm_act action);
void ath12k_hal_rx_buf_addr_info_set(struct ath12k_buffer_addr *binfo,
dma_addr_t paddr, u32 cookie, u8 manager);
void ath12k_hal_rx_buf_addr_info_get(struct ath12k_buffer_addr *binfo,
dma_addr_t *paddr,
u32 *cookie, u8 *rbm);
int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
struct hal_reo_dest_ring *desc,
dma_addr_t *paddr, u32 *desc_bank);
int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc,
struct hal_rx_wbm_rel_info *rel_info);
void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab,
struct ath12k_buffer_addr *buff_addr,
dma_addr_t *paddr, u32 *cookie);
#endif

View file

@ -0,0 +1,145 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#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
*=================
* 000xxx 0
* 001xxx 1
* 010xxx 2
* 011xxx 3
* 100xxx 4
* 101xxx 5
* 110xxx 6
* 111xxx 7
*/
static inline u8 dscp2tid(u8 dscp)
{
return dscp >> 3;
}
void ath12k_hal_tx_cmd_desc_setup(struct ath12k_base *ab,
struct hal_tcl_data_cmd *tcl_cmd,
struct hal_tx_info *ti)
{
tcl_cmd->buf_addr_info.info0 =
le32_encode_bits(ti->paddr, BUFFER_ADDR_INFO0_ADDR);
tcl_cmd->buf_addr_info.info1 =
le32_encode_bits(((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT),
BUFFER_ADDR_INFO1_ADDR);
tcl_cmd->buf_addr_info.info1 |=
le32_encode_bits((ti->rbm_id), BUFFER_ADDR_INFO1_RET_BUF_MGR) |
le32_encode_bits(ti->desc_id, BUFFER_ADDR_INFO1_SW_COOKIE);
tcl_cmd->info0 =
le32_encode_bits(ti->type, HAL_TCL_DATA_CMD_INFO0_DESC_TYPE) |
le32_encode_bits(ti->bank_id, HAL_TCL_DATA_CMD_INFO0_BANK_ID);
tcl_cmd->info1 =
le32_encode_bits(ti->meta_data_flags,
HAL_TCL_DATA_CMD_INFO1_CMD_NUM);
tcl_cmd->info2 = cpu_to_le32(ti->flags0) |
le32_encode_bits(ti->data_len, HAL_TCL_DATA_CMD_INFO2_DATA_LEN) |
le32_encode_bits(ti->pkt_offset, HAL_TCL_DATA_CMD_INFO2_PKT_OFFSET);
tcl_cmd->info3 = cpu_to_le32(ti->flags1) |
le32_encode_bits(ti->tid, HAL_TCL_DATA_CMD_INFO3_TID) |
le32_encode_bits(ti->lmac_id, HAL_TCL_DATA_CMD_INFO3_PMAC_ID) |
le32_encode_bits(ti->vdev_id, HAL_TCL_DATA_CMD_INFO3_VDEV_ID);
tcl_cmd->info4 = le32_encode_bits(ti->bss_ast_idx,
HAL_TCL_DATA_CMD_INFO4_SEARCH_INDEX) |
le32_encode_bits(ti->bss_ast_hash,
HAL_TCL_DATA_CMD_INFO4_CACHE_SET_NUM);
tcl_cmd->info5 = 0;
}
void ath12k_hal_tx_set_dscp_tid_map(struct ath12k_base *ab, int id)
{
u32 ctrl_reg_val;
u32 addr;
u8 hw_map_val[HAL_DSCP_TID_TBL_SIZE], dscp, tid;
int i;
u32 value;
ctrl_reg_val = ath12k_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;
ath12k_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, dscp = 0; i < HAL_DSCP_TID_TBL_SIZE; i += 3) {
tid = dscp2tid(dscp);
value = u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP0);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP1);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP2);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP3);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP4);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP5);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP6);
dscp++;
tid = dscp2tid(dscp);
value |= u32_encode_bits(tid, HAL_TCL1_RING_FIELD_DSCP_TID_MAP7);
dscp++;
memcpy(&hw_map_val[i], &value, 3);
}
for (i = 0; i < HAL_DSCP_TID_TBL_SIZE; i += 4) {
ath12k_hif_write32(ab, addr, *(u32 *)&hw_map_val[i]);
addr += 4;
}
/* Disable read/write access */
ctrl_reg_val = ath12k_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;
ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_TCL_REG +
HAL_TCL1_RING_CMN_CTRL_REG,
ctrl_reg_val);
}
void ath12k_hal_tx_configure_bank_register(struct ath12k_base *ab, u32 bank_config,
u8 bank_id)
{
ath12k_hif_write32(ab, HAL_TCL_SW_CONFIG_BANK_ADDR + 4 * bank_id,
bank_config);
}

View file

@ -0,0 +1,194 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HAL_TX_H
#define ATH12K_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
/* TODO: check all these data can be managed with struct ath12k_tx_desc_info for perf */
struct hal_tx_info {
u16 meta_data_flags; /* %HAL_TCL_DATA_CMD_INFO0_META_ */
u8 ring_id;
u8 rbm_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 vdev_id;
u8 dscp_tid_tbl_idx;
bool enable_mesh;
int bank_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;
u8 ack_rssi;
u32 flags; /* %HAL_TX_STATUS_FLAGS_ */
u32 ppdu_id;
u8 try_cnt;
u8 tid;
u16 peer_id;
u32 rate_stats;
};
#define HAL_TX_PHY_DESC_INFO0_BF_TYPE GENMASK(17, 16)
#define HAL_TX_PHY_DESC_INFO0_PREAMBLE_11B BIT(20)
#define HAL_TX_PHY_DESC_INFO0_PKT_TYPE GENMASK(24, 21)
#define HAL_TX_PHY_DESC_INFO0_BANDWIDTH GENMASK(30, 28)
#define HAL_TX_PHY_DESC_INFO1_MCS GENMASK(3, 0)
#define HAL_TX_PHY_DESC_INFO1_STBC BIT(6)
#define HAL_TX_PHY_DESC_INFO2_NSS GENMASK(23, 21)
#define HAL_TX_PHY_DESC_INFO3_AP_PKT_BW GENMASK(6, 4)
#define HAL_TX_PHY_DESC_INFO3_LTF_SIZE GENMASK(20, 19)
#define HAL_TX_PHY_DESC_INFO3_ACTIVE_CHANNEL GENMASK(17, 15)
struct hal_tx_phy_desc {
__le32 info0;
__le32 info1;
__le32 info2;
__le32 info3;
} __packed;
#define HAL_TX_FES_STAT_PROT_INFO0_STRT_FRM_TS_15_0 GENMASK(15, 0)
#define HAL_TX_FES_STAT_PROT_INFO0_STRT_FRM_TS_31_16 GENMASK(31, 16)
#define HAL_TX_FES_STAT_PROT_INFO1_END_FRM_TS_15_0 GENMASK(15, 0)
#define HAL_TX_FES_STAT_PROT_INFO1_END_FRM_TS_31_16 GENMASK(31, 16)
struct hal_tx_fes_status_prot {
__le64 reserved;
__le32 info0;
__le32 info1;
__le32 reserved1[11];
} __packed;
#define HAL_TX_FES_STAT_USR_PPDU_INFO0_DURATION GENMASK(15, 0)
struct hal_tx_fes_status_user_ppdu {
__le64 reserved;
__le32 info0;
__le32 reserved1[3];
} __packed;
#define HAL_TX_FES_STAT_STRT_INFO0_PROT_TS_LOWER_32 GENMASK(31, 0)
#define HAL_TX_FES_STAT_STRT_INFO1_PROT_TS_UPPER_32 GENMASK(31, 0)
struct hal_tx_fes_status_start_prot {
__le32 info0;
__le32 info1;
__le64 reserved;
} __packed;
#define HAL_TX_FES_STATUS_START_INFO0_MEDIUM_PROT_TYPE GENMASK(29, 27)
struct hal_tx_fes_status_start {
__le32 reserved;
__le32 info0;
__le64 reserved1;
} __packed;
#define HAL_TX_Q_EXT_INFO0_FRAME_CTRL GENMASK(15, 0)
#define HAL_TX_Q_EXT_INFO0_QOS_CTRL GENMASK(31, 16)
#define HAL_TX_Q_EXT_INFO1_AMPDU_FLAG BIT(0)
struct hal_tx_queue_exten {
__le32 info0;
__le32 info1;
} __packed;
#define HAL_TX_FES_SETUP_INFO0_NUM_OF_USERS GENMASK(28, 23)
struct hal_tx_fes_setup {
__le32 schedule_id;
__le32 info0;
__le64 reserved;
} __packed;
#define HAL_TX_PPDU_SETUP_INFO0_MEDIUM_PROT_TYPE GENMASK(2, 0)
#define HAL_TX_PPDU_SETUP_INFO1_PROT_FRAME_ADDR1_31_0 GENMASK(31, 0)
#define HAL_TX_PPDU_SETUP_INFO2_PROT_FRAME_ADDR1_47_32 GENMASK(15, 0)
#define HAL_TX_PPDU_SETUP_INFO2_PROT_FRAME_ADDR2_15_0 GENMASK(31, 16)
#define HAL_TX_PPDU_SETUP_INFO3_PROT_FRAME_ADDR2_47_16 GENMASK(31, 0)
#define HAL_TX_PPDU_SETUP_INFO4_PROT_FRAME_ADDR3_31_0 GENMASK(31, 0)
#define HAL_TX_PPDU_SETUP_INFO5_PROT_FRAME_ADDR3_47_32 GENMASK(15, 0)
#define HAL_TX_PPDU_SETUP_INFO5_PROT_FRAME_ADDR4_15_0 GENMASK(31, 16)
#define HAL_TX_PPDU_SETUP_INFO6_PROT_FRAME_ADDR4_47_16 GENMASK(31, 0)
struct hal_tx_pcu_ppdu_setup_init {
__le32 info0;
__le32 info1;
__le32 info2;
__le32 info3;
__le32 reserved;
__le32 info4;
__le32 info5;
__le32 info6;
} __packed;
#define HAL_TX_FES_STATUS_END_INFO0_START_TIMESTAMP_15_0 GENMASK(15, 0)
#define HAL_TX_FES_STATUS_END_INFO0_START_TIMESTAMP_31_16 GENMASK(31, 16)
struct hal_tx_fes_status_end {
__le32 reserved[2];
__le32 info0;
__le32 reserved1[19];
} __packed;
#define HAL_TX_BANK_CONFIG_EPD BIT(0)
#define HAL_TX_BANK_CONFIG_ENCAP_TYPE GENMASK(2, 1)
#define HAL_TX_BANK_CONFIG_ENCRYPT_TYPE GENMASK(6, 3)
#define HAL_TX_BANK_CONFIG_SRC_BUFFER_SWAP BIT(7)
#define HAL_TX_BANK_CONFIG_LINK_META_SWAP BIT(8)
#define HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN BIT(9)
#define HAL_TX_BANK_CONFIG_ADDRX_EN BIT(10)
#define HAL_TX_BANK_CONFIG_ADDRY_EN BIT(11)
#define HAL_TX_BANK_CONFIG_MESH_EN GENMASK(13, 12)
#define HAL_TX_BANK_CONFIG_VDEV_ID_CHECK_EN BIT(14)
#define HAL_TX_BANK_CONFIG_PMAC_ID GENMASK(16, 15)
/* STA mode will have MCAST_PKT_CTRL instead of DSCP_TID_MAP bitfield */
#define HAL_TX_BANK_CONFIG_DSCP_TIP_MAP_ID GENMASK(22, 17)
void ath12k_hal_tx_cmd_desc_setup(struct ath12k_base *ab,
struct hal_tcl_data_cmd *tcl_cmd,
struct hal_tx_info *ti);
void ath12k_hal_tx_set_dscp_tid_map(struct ath12k_base *ab, int id);
int ath12k_hal_reo_cmd_send(struct ath12k_base *ab, struct hal_srng *srng,
enum hal_reo_cmd_type type,
struct ath12k_hal_reo_cmd *cmd);
void ath12k_hal_tx_configure_bank_register(struct ath12k_base *ab, u32 bank_config,
u8 bank_id);
#endif

View file

@ -0,0 +1,144 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HIF_H
#define ATH12K_HIF_H
#include "core.h"
struct ath12k_hif_ops {
u32 (*read32)(struct ath12k_base *sc, u32 address);
void (*write32)(struct ath12k_base *sc, u32 address, u32 data);
void (*irq_enable)(struct ath12k_base *sc);
void (*irq_disable)(struct ath12k_base *sc);
int (*start)(struct ath12k_base *sc);
void (*stop)(struct ath12k_base *sc);
int (*power_up)(struct ath12k_base *sc);
void (*power_down)(struct ath12k_base *sc);
int (*suspend)(struct ath12k_base *ab);
int (*resume)(struct ath12k_base *ab);
int (*map_service_to_pipe)(struct ath12k_base *sc, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
int (*get_user_msi_vector)(struct ath12k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector);
void (*get_msi_address)(struct ath12k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi);
void (*ce_irq_enable)(struct ath12k_base *ab);
void (*ce_irq_disable)(struct ath12k_base *ab);
void (*get_ce_msi_idx)(struct ath12k_base *ab, u32 ce_id, u32 *msi_idx);
};
static inline int ath12k_hif_map_service_to_pipe(struct ath12k_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 ath12k_hif_get_user_msi_vector(struct ath12k_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 ath12k_hif_get_msi_address(struct ath12k_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 ath12k_hif_get_ce_msi_idx(struct ath12k_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 ath12k_hif_ce_irq_enable(struct ath12k_base *ab)
{
if (ab->hif.ops->ce_irq_enable)
ab->hif.ops->ce_irq_enable(ab);
}
static inline void ath12k_hif_ce_irq_disable(struct ath12k_base *ab)
{
if (ab->hif.ops->ce_irq_disable)
ab->hif.ops->ce_irq_disable(ab);
}
static inline void ath12k_hif_irq_enable(struct ath12k_base *ab)
{
ab->hif.ops->irq_enable(ab);
}
static inline void ath12k_hif_irq_disable(struct ath12k_base *ab)
{
ab->hif.ops->irq_disable(ab);
}
static inline int ath12k_hif_suspend(struct ath12k_base *ab)
{
if (ab->hif.ops->suspend)
return ab->hif.ops->suspend(ab);
return 0;
}
static inline int ath12k_hif_resume(struct ath12k_base *ab)
{
if (ab->hif.ops->resume)
return ab->hif.ops->resume(ab);
return 0;
}
static inline int ath12k_hif_start(struct ath12k_base *ab)
{
return ab->hif.ops->start(ab);
}
static inline void ath12k_hif_stop(struct ath12k_base *ab)
{
ab->hif.ops->stop(ab);
}
static inline u32 ath12k_hif_read32(struct ath12k_base *ab, u32 address)
{
return ab->hif.ops->read32(ab, address);
}
static inline void ath12k_hif_write32(struct ath12k_base *ab, u32 address,
u32 data)
{
ab->hif.ops->write32(ab, address, data);
}
static inline int ath12k_hif_power_up(struct ath12k_base *ab)
{
return ab->hif.ops->power_up(ab);
}
static inline void ath12k_hif_power_down(struct ath12k_base *ab)
{
ab->hif.ops->power_down(ab);
}
#endif /* ATH12K_HIF_H */

View file

@ -0,0 +1,789 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
#include "debug.h"
#include "hif.h"
struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ab, int size)
{
struct sk_buff *skb;
skb = dev_alloc_skb(size + sizeof(struct ath12k_htc_hdr));
if (!skb)
return NULL;
skb_reserve(skb, sizeof(struct ath12k_htc_hdr));
/* FW/HTC requires 4-byte aligned streams */
if (!IS_ALIGNED((unsigned long)skb->data, 4))
ath12k_warn(ab, "Unaligned HTC tx skb\n");
return skb;
}
static void ath12k_htc_control_tx_complete(struct ath12k_base *ab,
struct sk_buff *skb)
{
kfree_skb(skb);
}
static struct sk_buff *ath12k_htc_build_tx_ctrl_skb(void)
{
struct sk_buff *skb;
struct ath12k_skb_cb *skb_cb;
skb = dev_alloc_skb(ATH12K_HTC_CONTROL_BUFFER_SIZE);
if (!skb)
return NULL;
skb_reserve(skb, sizeof(struct ath12k_htc_hdr));
WARN_ON_ONCE(!IS_ALIGNED((unsigned long)skb->data, 4));
skb_cb = ATH12K_SKB_CB(skb);
memset(skb_cb, 0, sizeof(*skb_cb));
return skb;
}
static void ath12k_htc_prepare_tx_skb(struct ath12k_htc_ep *ep,
struct sk_buff *skb)
{
struct ath12k_htc_hdr *hdr;
hdr = (struct ath12k_htc_hdr *)skb->data;
memset(hdr, 0, sizeof(*hdr));
hdr->htc_info = le32_encode_bits(ep->eid, HTC_HDR_ENDPOINTID) |
le32_encode_bits((skb->len - sizeof(*hdr)),
HTC_HDR_PAYLOADLEN);
if (ep->tx_credit_flow_enabled)
hdr->htc_info |= le32_encode_bits(ATH12K_HTC_FLAG_NEED_CREDIT_UPDATE,
HTC_HDR_FLAGS);
spin_lock_bh(&ep->htc->tx_lock);
hdr->ctrl_info = le32_encode_bits(ep->seq_no++, HTC_HDR_CONTROLBYTES1);
spin_unlock_bh(&ep->htc->tx_lock);
}
int ath12k_htc_send(struct ath12k_htc *htc,
enum ath12k_htc_ep_id eid,
struct sk_buff *skb)
{
struct ath12k_htc_ep *ep = &htc->endpoint[eid];
struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb);
struct device *dev = htc->ab->dev;
struct ath12k_base *ab = htc->ab;
int credits = 0;
int ret;
if (eid >= ATH12K_HTC_EP_COUNT) {
ath12k_warn(ab, "Invalid endpoint id: %d\n", eid);
return -ENOENT;
}
skb_push(skb, sizeof(struct ath12k_htc_hdr));
if (ep->tx_credit_flow_enabled) {
credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
spin_lock_bh(&htc->tx_lock);
if (ep->tx_credits < credits) {
ath12k_dbg(ab, ATH12K_DBG_HTC,
"htc insufficient credits ep %d required %d available %d\n",
eid, credits, ep->tx_credits);
spin_unlock_bh(&htc->tx_lock);
ret = -EAGAIN;
goto err_pull;
}
ep->tx_credits -= credits;
ath12k_dbg(ab, ATH12K_DBG_HTC,
"htc ep %d consumed %d credits (total %d)\n",
eid, credits, ep->tx_credits);
spin_unlock_bh(&htc->tx_lock);
}
ath12k_htc_prepare_tx_skb(ep, skb);
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;
}
ret = ath12k_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 (ep->tx_credit_flow_enabled) {
spin_lock_bh(&htc->tx_lock);
ep->tx_credits += credits;
ath12k_dbg(ab, ATH12K_DBG_HTC,
"htc ep %d reverted %d credits back (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 ath12k_htc_hdr));
return ret;
}
static void
ath12k_htc_process_credit_report(struct ath12k_htc *htc,
const struct ath12k_htc_credit_report *report,
int len,
enum ath12k_htc_ep_id eid)
{
struct ath12k_base *ab = htc->ab;
struct ath12k_htc_ep *ep;
int i, n_reports;
if (len % sizeof(*report))
ath12k_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 >= ATH12K_HTC_EP_COUNT)
break;
ep = &htc->endpoint[report->eid];
ep->tx_credits += report->credits;
ath12k_dbg(ab, ATH12K_DBG_HTC, "htc ep %d got %d credits (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 ath12k_htc_process_trailer(struct ath12k_htc *htc,
u8 *buffer,
int length,
enum ath12k_htc_ep_id src_eid)
{
struct ath12k_base *ab = htc->ab;
int status = 0;
struct ath12k_htc_record *record;
size_t len;
while (length > 0) {
record = (struct ath12k_htc_record *)buffer;
if (length < sizeof(record->hdr)) {
status = -EINVAL;
break;
}
if (record->hdr.len > length) {
/* no room left in buffer for record */
ath12k_warn(ab, "Invalid record length: %d\n",
record->hdr.len);
status = -EINVAL;
break;
}
switch (record->hdr.id) {
case ATH12K_HTC_RECORD_CREDITS:
len = sizeof(struct ath12k_htc_credit_report);
if (record->hdr.len < len) {
ath12k_warn(ab, "Credit report too long\n");
status = -EINVAL;
break;
}
ath12k_htc_process_credit_report(htc,
record->credit_report,
record->hdr.len,
src_eid);
break;
default:
ath12k_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 ath12k_htc_suspend_complete(struct ath12k_base *ab, bool ack)
{
ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot suspend complete %d\n", ack);
if (ack)
set_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
else
clear_bit(ATH12K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
complete(&ab->htc_suspend);
}
void ath12k_htc_rx_completion_handler(struct ath12k_base *ab,
struct sk_buff *skb)
{
int status = 0;
struct ath12k_htc *htc = &ab->htc;
struct ath12k_htc_hdr *hdr;
struct ath12k_htc_ep *ep;
u16 payload_len;
u32 trailer_len = 0;
size_t min_len;
u8 eid;
bool trailer_present;
hdr = (struct ath12k_htc_hdr *)skb->data;
skb_pull(skb, sizeof(*hdr));
eid = le32_get_bits(hdr->htc_info, HTC_HDR_ENDPOINTID);
if (eid >= ATH12K_HTC_EP_COUNT) {
ath12k_warn(ab, "HTC Rx: invalid eid %d\n", eid);
goto out;
}
ep = &htc->endpoint[eid];
payload_len = le32_get_bits(hdr->htc_info, HTC_HDR_PAYLOADLEN);
if (payload_len + sizeof(*hdr) > ATH12K_HTC_MAX_LEN) {
ath12k_warn(ab, "HTC rx frame too long, len: %zu\n",
payload_len + sizeof(*hdr));
goto out;
}
if (skb->len < payload_len) {
ath12k_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 = le32_get_bits(hdr->htc_info, HTC_HDR_FLAGS) &
ATH12K_HTC_FLAG_TRAILER_PRESENT;
if (trailer_present) {
u8 *trailer;
trailer_len = le32_get_bits(hdr->ctrl_info,
HTC_HDR_CONTROLBYTES0);
min_len = sizeof(struct ath12k_htc_record_hdr);
if ((trailer_len < min_len) ||
(trailer_len > payload_len)) {
ath12k_warn(ab, "Invalid trailer length: %d\n",
trailer_len);
goto out;
}
trailer = (u8 *)hdr;
trailer += sizeof(*hdr);
trailer += payload_len;
trailer -= trailer_len;
status = ath12k_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 == ATH12K_HTC_EP_0) {
struct ath12k_htc_msg *msg = (struct ath12k_htc_msg *)skb->data;
switch (le32_get_bits(msg->msg_svc_id, HTC_MSG_MESSAGEID)) {
case ATH12K_HTC_MSG_READY_ID:
case ATH12K_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
*/
ath12k_warn(ab, "HTC rx ctrl still processing\n");
complete(&htc->ctl_resp);
goto out;
}
htc->control_resp_len =
min_t(int, skb->len,
ATH12K_HTC_MAX_CTRL_MSG_LEN);
memcpy(htc->control_resp_buffer, skb->data,
htc->control_resp_len);
complete(&htc->ctl_resp);
break;
case ATH12K_HTC_MSG_SEND_SUSPEND_COMPLETE:
ath12k_htc_suspend_complete(ab, true);
break;
case ATH12K_HTC_MSG_NACK_SUSPEND:
ath12k_htc_suspend_complete(ab, false);
break;
case ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
break;
default:
ath12k_warn(ab, "ignoring unsolicited htc ep0 event %u\n",
le32_get_bits(msg->msg_svc_id, HTC_MSG_MESSAGEID));
break;
}
goto out;
}
ath12k_dbg(ab, ATH12K_DBG_HTC, "htc rx completion ep %d skb %pK\n",
eid, skb);
ep->ep_ops.ep_rx_complete(ab, skb);
/* poll tx completion for interrupt disabled CE's */
ath12k_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 ath12k_htc_control_rx_complete(struct ath12k_base *ab,
struct sk_buff *skb)
{
/* This is unexpected. FW is not supposed to send regular rx on this
* endpoint.
*/
ath12k_warn(ab, "unexpected htc rx\n");
kfree_skb(skb);
}
static const char *htc_service_name(enum ath12k_htc_svc_id id)
{
switch (id) {
case ATH12K_HTC_SVC_ID_RESERVED:
return "Reserved";
case ATH12K_HTC_SVC_ID_RSVD_CTRL:
return "Control";
case ATH12K_HTC_SVC_ID_WMI_CONTROL:
return "WMI";
case ATH12K_HTC_SVC_ID_WMI_DATA_BE:
return "DATA BE";
case ATH12K_HTC_SVC_ID_WMI_DATA_BK:
return "DATA BK";
case ATH12K_HTC_SVC_ID_WMI_DATA_VI:
return "DATA VI";
case ATH12K_HTC_SVC_ID_WMI_DATA_VO:
return "DATA VO";
case ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1:
return "WMI MAC1";
case ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2:
return "WMI MAC2";
case ATH12K_HTC_SVC_ID_NMI_CONTROL:
return "NMI Control";
case ATH12K_HTC_SVC_ID_NMI_DATA:
return "NMI Data";
case ATH12K_HTC_SVC_ID_HTT_DATA_MSG:
return "HTT Data";
case ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS:
return "RAW";
case ATH12K_HTC_SVC_ID_IPA_TX:
return "IPA TX";
case ATH12K_HTC_SVC_ID_PKT_LOG:
return "PKT LOG";
case ATH12K_HTC_SVC_ID_WMI_CONTROL_DIAG:
return "WMI DIAG";
}
return "Unknown";
}
static void ath12k_htc_reset_endpoint_states(struct ath12k_htc *htc)
{
struct ath12k_htc_ep *ep;
int i;
for (i = ATH12K_HTC_EP_0; i < ATH12K_HTC_EP_COUNT; i++) {
ep = &htc->endpoint[i];
ep->service_id = ATH12K_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 ath12k_htc_get_credit_allocation(struct ath12k_htc *htc,
u16 service_id)
{
struct ath12k_htc_svc_tx_credits *serv_entry;
u8 i, allocation = 0;
serv_entry = htc->service_alloc_table;
for (i = 0; i < ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) {
if (serv_entry[i].service_id == service_id) {
allocation = serv_entry[i].credit_allocation;
break;
}
}
return allocation;
}
static int ath12k_htc_setup_target_buffer_assignments(struct ath12k_htc *htc)
{
struct ath12k_htc_svc_tx_credits *serv_entry;
static const u32 svc_id[] = {
ATH12K_HTC_SVC_ID_WMI_CONTROL,
ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1,
ATH12K_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 ath12k_htc_wait_target(struct ath12k_htc *htc)
{
int i, status = 0;
struct ath12k_base *ab = htc->ab;
unsigned long time_left;
struct ath12k_htc_ready *ready;
u16 message_id;
u16 credit_count;
u16 credit_size;
time_left = wait_for_completion_timeout(&htc->ctl_resp,
ATH12K_HTC_WAIT_TIMEOUT_HZ);
if (!time_left) {
ath12k_warn(ab, "failed to receive control response completion, polling..\n");
for (i = 0; i < ab->hw_params->ce_count; i++)
ath12k_ce_per_engine_service(htc->ab, i);
time_left =
wait_for_completion_timeout(&htc->ctl_resp,
ATH12K_HTC_WAIT_TIMEOUT_HZ);
if (!time_left)
status = -ETIMEDOUT;
}
if (status < 0) {
ath12k_warn(ab, "ctl_resp never came in (%d)\n", status);
return status;
}
if (htc->control_resp_len < sizeof(*ready)) {
ath12k_warn(ab, "Invalid HTC ready msg len:%d\n",
htc->control_resp_len);
return -ECOMM;
}
ready = (struct ath12k_htc_ready *)htc->control_resp_buffer;
message_id = le32_get_bits(ready->id_credit_count, HTC_MSG_MESSAGEID);
credit_count = le32_get_bits(ready->id_credit_count,
HTC_READY_MSG_CREDITCOUNT);
credit_size = le32_get_bits(ready->size_ep, HTC_READY_MSG_CREDITSIZE);
if (message_id != ATH12K_HTC_MSG_READY_ID) {
ath12k_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;
ath12k_dbg(ab, ATH12K_DBG_HTC,
"Target ready! transmit resources: %d size:%d\n",
htc->total_transmit_credits, htc->target_credit_size);
if ((htc->total_transmit_credits == 0) ||
(htc->target_credit_size == 0)) {
ath12k_warn(ab, "Invalid credit size received\n");
return -ECOMM;
}
ath12k_htc_setup_target_buffer_assignments(htc);
return 0;
}
int ath12k_htc_connect_service(struct ath12k_htc *htc,
struct ath12k_htc_svc_conn_req *conn_req,
struct ath12k_htc_svc_conn_resp *conn_resp)
{
struct ath12k_base *ab = htc->ab;
struct ath12k_htc_conn_svc *req_msg;
struct ath12k_htc_conn_svc_resp resp_msg_dummy;
struct ath12k_htc_conn_svc_resp *resp_msg = &resp_msg_dummy;
enum ath12k_htc_ep_id assigned_eid = ATH12K_HTC_EP_COUNT;
struct ath12k_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 == ATH12K_HTC_SVC_ID_RSVD_CTRL) {
disable_credit_flow_ctrl = true;
assigned_eid = ATH12K_HTC_EP_0;
max_msg_size = ATH12K_HTC_MAX_CTRL_MSG_LEN;
memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy));
goto setup;
}
tx_alloc = ath12k_htc_get_credit_allocation(htc,
conn_req->service_id);
if (!tx_alloc)
ath12k_dbg(ab, ATH12K_DBG_BOOT,
"boot htc service %s does not allocate target credits\n",
htc_service_name(conn_req->service_id));
skb = ath12k_htc_build_tx_ctrl_skb();
if (!skb) {
ath12k_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 ath12k_htc_conn_svc *)skb->data;
req_msg->msg_svc_id = le32_encode_bits(ATH12K_HTC_MSG_CONNECT_SERVICE_ID,
HTC_MSG_MESSAGEID);
flags |= u32_encode_bits(tx_alloc, ATH12K_HTC_CONN_FLAGS_RECV_ALLOC);
/* Only enable credit flow control for WMI ctrl service */
if (!(conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL ||
conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1 ||
conn_req->service_id == ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2)) {
flags |= ATH12K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
disable_credit_flow_ctrl = true;
}
req_msg->flags_len = le32_encode_bits(flags, HTC_SVC_MSG_CONNECTIONFLAGS);
req_msg->msg_svc_id |= le32_encode_bits(conn_req->service_id,
HTC_SVC_MSG_SERVICE_ID);
reinit_completion(&htc->ctl_resp);
status = ath12k_htc_send(htc, ATH12K_HTC_EP_0, skb);
if (status) {
kfree_skb(skb);
return status;
}
/* wait for response */
time_left = wait_for_completion_timeout(&htc->ctl_resp,
ATH12K_HTC_CONN_SVC_TIMEOUT_HZ);
if (!time_left) {
ath12k_err(ab, "Service connect timeout\n");
return -ETIMEDOUT;
}
/* we controlled the buffer creation, it's aligned */
resp_msg = (struct ath12k_htc_conn_svc_resp *)htc->control_resp_buffer;
message_id = le32_get_bits(resp_msg->msg_svc_id, HTC_MSG_MESSAGEID);
service_id = le32_get_bits(resp_msg->msg_svc_id,
HTC_SVC_RESP_MSG_SERVICEID);
if ((message_id != ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID) ||
(htc->control_resp_len < sizeof(*resp_msg))) {
ath12k_err(ab, "Invalid resp message ID 0x%x", message_id);
return -EPROTO;
}
ath12k_dbg(ab, ATH12K_DBG_HTC,
"HTC Service %s connect response: status: %u, assigned ep: %u\n",
htc_service_name(service_id),
le32_get_bits(resp_msg->flags_len, HTC_SVC_RESP_MSG_STATUS),
le32_get_bits(resp_msg->flags_len, HTC_SVC_RESP_MSG_ENDPOINTID));
conn_resp->connect_resp_code = le32_get_bits(resp_msg->flags_len,
HTC_SVC_RESP_MSG_STATUS);
/* check response status */
if (conn_resp->connect_resp_code != ATH12K_HTC_CONN_SVC_STATUS_SUCCESS) {
ath12k_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 = le32_get_bits(resp_msg->flags_len,
HTC_SVC_RESP_MSG_ENDPOINTID);
max_msg_size = le32_get_bits(resp_msg->flags_len,
HTC_SVC_RESP_MSG_MAXMSGSIZE);
setup:
if (assigned_eid >= ATH12K_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 != ATH12K_HTC_SVC_ID_UNUSED)
return -EPROTO;
/* return assigned endpoint to caller */
conn_resp->eid = assigned_eid;
conn_resp->max_msg_len = le32_get_bits(resp_msg->flags_len,
HTC_SVC_RESP_MSG_MAXMSGSIZE);
/* 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 = le32_get_bits(resp_msg->flags_len,
HTC_SVC_RESP_MSG_MAXMSGSIZE);
ep->tx_credits = tx_alloc;
/* copy all the callbacks */
ep->ep_ops = conn_req->ep_ops;
status = ath12k_hif_map_service_to_pipe(htc->ab,
ep->service_id,
&ep->ul_pipe_id,
&ep->dl_pipe_id);
if (status)
return status;
ath12k_dbg(ab, ATH12K_DBG_BOOT,
"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;
ath12k_dbg(ab, ATH12K_DBG_BOOT,
"boot htc service '%s' eid %d TX flow control disabled\n",
htc_service_name(ep->service_id), assigned_eid);
}
return status;
}
int ath12k_htc_start(struct ath12k_htc *htc)
{
struct sk_buff *skb;
int status;
struct ath12k_base *ab = htc->ab;
struct ath12k_htc_setup_complete_extended *msg;
skb = ath12k_htc_build_tx_ctrl_skb();
if (!skb)
return -ENOMEM;
skb_put(skb, sizeof(*msg));
memset(skb->data, 0, skb->len);
msg = (struct ath12k_htc_setup_complete_extended *)skb->data;
msg->msg_id = le32_encode_bits(ATH12K_HTC_MSG_SETUP_COMPLETE_EX_ID,
HTC_MSG_MESSAGEID);
ath12k_dbg(ab, ATH12K_DBG_HTC, "HTC is using TX credit flow control\n");
status = ath12k_htc_send(htc, ATH12K_HTC_EP_0, skb);
if (status) {
kfree_skb(skb);
return status;
}
return 0;
}
int ath12k_htc_init(struct ath12k_base *ab)
{
struct ath12k_htc *htc = &ab->htc;
struct ath12k_htc_svc_conn_req conn_req = { };
struct ath12k_htc_svc_conn_resp conn_resp = { };
int ret;
spin_lock_init(&htc->tx_lock);
ath12k_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 */
conn_req.ep_ops.ep_tx_complete = ath12k_htc_control_tx_complete;
conn_req.ep_ops.ep_rx_complete = ath12k_htc_control_rx_complete;
conn_req.max_send_queue_depth = ATH12K_NUM_CONTROL_TX_BUFFERS;
conn_req.service_id = ATH12K_HTC_SVC_ID_RSVD_CTRL;
/* connect fake service */
ret = ath12k_htc_connect_service(htc, &conn_req, &conn_resp);
if (ret) {
ath12k_err(ab, "could not connect to htc service (%d)\n", ret);
return ret;
}
init_completion(&htc->ctl_resp);
return 0;
}

View file

@ -0,0 +1,316 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HTC_H
#define ATH12K_HTC_H
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/bug.h>
#include <linux/skbuff.h>
#include <linux/timer.h>
struct ath12k_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 ath12k_htc_tx_flags {
ATH12K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
ATH12K_HTC_FLAG_SEND_BUNDLE = 0x02
};
enum ath12k_htc_rx_flags {
ATH12K_HTC_FLAG_TRAILER_PRESENT = 0x02,
ATH12K_HTC_FLAG_BUNDLE_MASK = 0xF0
};
struct ath12k_htc_hdr {
__le32 htc_info;
__le32 ctrl_info;
} __packed __aligned(4);
enum ath12k_htc_msg_id {
ATH12K_HTC_MSG_READY_ID = 1,
ATH12K_HTC_MSG_CONNECT_SERVICE_ID = 2,
ATH12K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3,
ATH12K_HTC_MSG_SETUP_COMPLETE_ID = 4,
ATH12K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5,
ATH12K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6,
ATH12K_HTC_MSG_NACK_SUSPEND = 7,
ATH12K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID = 8,
};
enum ath12k_htc_version {
ATH12K_HTC_VERSION_2P0 = 0x00, /* 2.0 */
ATH12K_HTC_VERSION_2P1 = 0x01, /* 2.1 */
};
enum ath12k_htc_conn_flag_threshold_level {
ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH,
ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF,
ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS,
ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY,
};
#define ATH12K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK GENMASK(1, 0)
#define ATH12K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE BIT(2)
#define ATH12K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL BIT(3)
#define ATH12K_HTC_CONN_FLAGS_RECV_ALLOC GENMASK(15, 8)
enum ath12k_htc_conn_svc_status {
ATH12K_HTC_CONN_SVC_STATUS_SUCCESS = 0,
ATH12K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1,
ATH12K_HTC_CONN_SVC_STATUS_FAILED = 2,
ATH12K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3,
ATH12K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4
};
struct ath12k_htc_ready {
__le32 id_credit_count;
__le32 size_ep;
} __packed;
struct ath12k_htc_ready_extended {
struct ath12k_htc_ready base;
__le32 ver_bundle;
} __packed;
struct ath12k_htc_conn_svc {
__le32 msg_svc_id;
__le32 flags_len;
} __packed;
struct ath12k_htc_conn_svc_resp {
__le32 msg_svc_id;
__le32 flags_len;
__le32 svc_meta_pad;
} __packed;
struct ath12k_htc_setup_complete_extended {
__le32 msg_id;
__le32 flags;
__le32 max_msgs_per_bundled_recv;
} __packed;
struct ath12k_htc_msg {
__le32 msg_svc_id;
__le32 flags_len;
} __packed __aligned(4);
enum ath12k_htc_record_id {
ATH12K_HTC_RECORD_NULL = 0,
ATH12K_HTC_RECORD_CREDITS = 1
};
struct ath12k_htc_record_hdr {
u8 id; /* @enum ath12k_htc_record_id */
u8 len;
u8 pad0;
u8 pad1;
} __packed;
struct ath12k_htc_credit_report {
u8 eid; /* @enum ath12k_htc_ep_id */
u8 credits;
u8 pad0;
u8 pad1;
} __packed;
struct ath12k_htc_record {
struct ath12k_htc_record_hdr hdr;
struct ath12k_htc_credit_report credit_report[];
} __packed __aligned(4);
/* HTC FRAME structure layout draft
*
* note: the trailer offset is dynamic depending
* on payload length. this is only a struct layout draft
*
*=======================================================
*
* HTC HEADER
*
*=======================================================
* |
* HTC message | payload
* (variable length) | (variable length)
*=======================================================
*
* HTC Record
*
*=======================================================
*/
enum ath12k_htc_svc_gid {
ATH12K_HTC_SVC_GRP_RSVD = 0,
ATH12K_HTC_SVC_GRP_WMI = 1,
ATH12K_HTC_SVC_GRP_NMI = 2,
ATH12K_HTC_SVC_GRP_HTT = 3,
ATH12K_HTC_SVC_GRP_CFG = 4,
ATH12K_HTC_SVC_GRP_IPA = 5,
ATH12K_HTC_SVC_GRP_PKTLOG = 6,
ATH12K_HTC_SVC_GRP_TEST = 254,
ATH12K_HTC_SVC_GRP_LAST = 255,
};
#define SVC(group, idx) \
(int)(((int)(group) << 8) | (int)(idx))
enum ath12k_htc_svc_id {
/* NOTE: service ID of 0x0000 is reserved and should never be used */
ATH12K_HTC_SVC_ID_RESERVED = 0x0000,
ATH12K_HTC_SVC_ID_UNUSED = ATH12K_HTC_SVC_ID_RESERVED,
ATH12K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH12K_HTC_SVC_GRP_RSVD, 1),
ATH12K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH12K_HTC_SVC_GRP_WMI, 0),
ATH12K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH12K_HTC_SVC_GRP_WMI, 1),
ATH12K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH12K_HTC_SVC_GRP_WMI, 2),
ATH12K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH12K_HTC_SVC_GRP_WMI, 3),
ATH12K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH12K_HTC_SVC_GRP_WMI, 4),
ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC1 = SVC(ATH12K_HTC_SVC_GRP_WMI, 5),
ATH12K_HTC_SVC_ID_WMI_CONTROL_MAC2 = SVC(ATH12K_HTC_SVC_GRP_WMI, 6),
ATH12K_HTC_SVC_ID_WMI_CONTROL_DIAG = SVC(ATH12K_HTC_SVC_GRP_WMI, 7),
ATH12K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH12K_HTC_SVC_GRP_NMI, 0),
ATH12K_HTC_SVC_ID_NMI_DATA = SVC(ATH12K_HTC_SVC_GRP_NMI, 1),
ATH12K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH12K_HTC_SVC_GRP_HTT, 0),
/* raw stream service (i.e. flash, tcmd, calibration apps) */
ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH12K_HTC_SVC_GRP_TEST, 0),
ATH12K_HTC_SVC_ID_IPA_TX = SVC(ATH12K_HTC_SVC_GRP_IPA, 0),
ATH12K_HTC_SVC_ID_PKT_LOG = SVC(ATH12K_HTC_SVC_GRP_PKTLOG, 0),
};
#undef SVC
enum ath12k_htc_ep_id {
ATH12K_HTC_EP_UNUSED = -1,
ATH12K_HTC_EP_0 = 0,
ATH12K_HTC_EP_1 = 1,
ATH12K_HTC_EP_2,
ATH12K_HTC_EP_3,
ATH12K_HTC_EP_4,
ATH12K_HTC_EP_5,
ATH12K_HTC_EP_6,
ATH12K_HTC_EP_7,
ATH12K_HTC_EP_8,
ATH12K_HTC_EP_COUNT,
};
struct ath12k_htc_ep_ops {
void (*ep_tx_complete)(struct ath12k_base *ab, struct sk_buff *skb);
void (*ep_rx_complete)(struct ath12k_base *ab, struct sk_buff *skb);
void (*ep_tx_credits)(struct ath12k_base *ab);
};
/* service connection information */
struct ath12k_htc_svc_conn_req {
u16 service_id;
struct ath12k_htc_ep_ops ep_ops;
int max_send_queue_depth;
};
/* service connection response information */
struct ath12k_htc_svc_conn_resp {
u8 buffer_len;
u8 actual_len;
enum ath12k_htc_ep_id eid;
unsigned int max_msg_len;
u8 connect_resp_code;
};
#define ATH12K_NUM_CONTROL_TX_BUFFERS 2
#define ATH12K_HTC_MAX_LEN 4096
#define ATH12K_HTC_MAX_CTRL_MSG_LEN 256
#define ATH12K_HTC_WAIT_TIMEOUT_HZ (1 * HZ)
#define ATH12K_HTC_CONTROL_BUFFER_SIZE (ATH12K_HTC_MAX_CTRL_MSG_LEN + \
sizeof(struct ath12k_htc_hdr))
#define ATH12K_HTC_CONN_SVC_TIMEOUT_HZ (1 * HZ)
#define ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES 8
struct ath12k_htc_ep {
struct ath12k_htc *htc;
enum ath12k_htc_ep_id eid;
enum ath12k_htc_svc_id service_id;
struct ath12k_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 ath12k_htc_svc_tx_credits {
u16 service_id;
u8 credit_allocation;
};
struct ath12k_htc {
struct ath12k_base *ab;
struct ath12k_htc_ep endpoint[ATH12K_HTC_EP_COUNT];
/* protects endpoints */
spinlock_t tx_lock;
u8 control_resp_buffer[ATH12K_HTC_MAX_CTRL_MSG_LEN];
int control_resp_len;
struct completion ctl_resp;
int total_transmit_credits;
struct ath12k_htc_svc_tx_credits
service_alloc_table[ATH12K_HTC_MAX_SERVICE_ALLOC_ENTRIES];
int target_credit_size;
u8 wmi_ep_count;
};
int ath12k_htc_init(struct ath12k_base *ar);
int ath12k_htc_wait_target(struct ath12k_htc *htc);
int ath12k_htc_start(struct ath12k_htc *htc);
int ath12k_htc_connect_service(struct ath12k_htc *htc,
struct ath12k_htc_svc_conn_req *conn_req,
struct ath12k_htc_svc_conn_resp *conn_resp);
int ath12k_htc_send(struct ath12k_htc *htc, enum ath12k_htc_ep_id eid,
struct sk_buff *packet);
struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ar, int size);
void ath12k_htc_rx_completion_handler(struct ath12k_base *ar,
struct sk_buff *skb);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,312 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_HW_H
#define ATH12K_HW_H
#include <linux/mhi.h>
#include "wmi.h"
#include "hal.h"
/* Target configuration defines */
/* Num VDEVS per radio */
#define TARGET_NUM_VDEVS (16 + 1)
#define TARGET_NUM_PEERS_PDEV (512 + TARGET_NUM_VDEVS)
/* Num of peers for Single Radio mode */
#define TARGET_NUM_PEERS_SINGLE (TARGET_NUM_PEERS_PDEV)
/* Num of peers for DBS */
#define TARGET_NUM_PEERS_DBS (2 * TARGET_NUM_PEERS_PDEV)
/* Num of peers for DBS_SBS */
#define TARGET_NUM_PEERS_DBS_SBS (3 * TARGET_NUM_PEERS_PDEV)
/* Max num of stations (per radio) */
#define TARGET_NUM_STATIONS 512
#define TARGET_NUM_PEERS(x) TARGET_NUM_PEERS_##x
#define TARGET_NUM_PEER_KEYS 2
#define TARGET_NUM_TIDS(x) (2 * TARGET_NUM_PEERS(x) + \
4 * TARGET_NUM_VDEVS + 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 ATH12K_HW_MAX_QUEUES 4
#define ATH12K_QUEUE_LEN 4096
#define ATH12K_HW_RATECODE_CCK_SHORT_PREAM_MASK 0x4
#define ATH12K_FW_DIR "ath12k"
#define ATH12K_BOARD_MAGIC "QCA-ATH12K-BOARD"
#define ATH12K_BOARD_API2_FILE "board-2.bin"
#define ATH12K_DEFAULT_BOARD_FILE "board.bin"
#define ATH12K_DEFAULT_CAL_FILE "caldata.bin"
#define ATH12K_AMSS_FILE "amss.bin"
#define ATH12K_M3_FILE "m3.bin"
#define ATH12K_REGDB_FILE_NAME "regdb.bin"
enum ath12k_hw_rate_cck {
ATH12K_HW_RATE_CCK_LP_11M = 0,
ATH12K_HW_RATE_CCK_LP_5_5M,
ATH12K_HW_RATE_CCK_LP_2M,
ATH12K_HW_RATE_CCK_LP_1M,
ATH12K_HW_RATE_CCK_SP_11M,
ATH12K_HW_RATE_CCK_SP_5_5M,
ATH12K_HW_RATE_CCK_SP_2M,
};
enum ath12k_hw_rate_ofdm {
ATH12K_HW_RATE_OFDM_48M = 0,
ATH12K_HW_RATE_OFDM_24M,
ATH12K_HW_RATE_OFDM_12M,
ATH12K_HW_RATE_OFDM_6M,
ATH12K_HW_RATE_OFDM_54M,
ATH12K_HW_RATE_OFDM_36M,
ATH12K_HW_RATE_OFDM_18M,
ATH12K_HW_RATE_OFDM_9M,
};
enum ath12k_bus {
ATH12K_BUS_PCI,
};
#define ATH12K_EXT_IRQ_GRP_NUM_MAX 11
struct hal_rx_desc;
struct hal_tcl_data_cmd;
struct htt_rx_ring_tlv_filter;
enum hal_encrypt_type;
struct ath12k_hw_ring_mask {
u8 tx[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_mon_dest[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_err[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 rx_wbm_rel[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 reo_status[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 host2rxdma[ATH12K_EXT_IRQ_GRP_NUM_MAX];
u8 tx_mon_dest[ATH12K_EXT_IRQ_GRP_NUM_MAX];
};
struct ath12k_hw_hal_params {
enum hal_rx_buf_return_buf_manager rx_buf_rbm;
u32 wbm2sw_cc_enable;
};
struct ath12k_hw_params {
const char *name;
u16 hw_rev;
struct {
const char *dir;
size_t board_size;
size_t cal_offset;
} fw;
u8 max_radios;
bool single_pdev_only:1;
u32 qmi_service_ins_id;
bool internal_sleep_clock:1;
const struct ath12k_hw_ops *hw_ops;
const struct ath12k_hw_ring_mask *ring_mask;
const struct ath12k_hw_regs *regs;
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 ath12k_hw_hal_params *hal_params;
bool rxdma1_enable:1;
int num_rxmda_per_pdev;
int num_rxdma_dst_ring;
bool rx_mac_buf_ring:1;
bool vdev_start_delay:1;
u16 interface_modes;
bool supports_monitor:1;
bool idle_ps:1;
bool download_calib:1;
bool supports_suspend:1;
bool tcl_ring_retry:1;
bool reoq_lut_support:1;
bool supports_shadow_regs:1;
u32 hal_desc_sz;
u32 num_tcl_banks;
u32 max_tx_ring;
const struct mhi_controller_config *mhi_config;
void (*wmi_init)(struct ath12k_base *ab,
struct ath12k_wmi_resource_config_arg *config);
const struct hal_ops *hal_ops;
};
struct ath12k_hw_ops {
u8 (*get_hw_mac_from_pdev_id)(int pdev_id);
int (*mac_id_to_pdev_id)(const struct ath12k_hw_params *hw, int mac_id);
int (*mac_id_to_srng_id)(const struct ath12k_hw_params *hw, int mac_id);
int (*rxdma_ring_sel_config)(struct ath12k_base *ab);
u8 (*get_ring_selector)(struct sk_buff *skb);
bool (*dp_srng_is_tx_comp_ring)(int ring_num);
};
static inline
int ath12k_hw_get_mac_from_pdev_id(const struct ath12k_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 ath12k_hw_mac_id_to_pdev_id(const struct ath12k_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 ath12k_hw_mac_id_to_srng_id(const struct ath12k_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 ath12k_fw_ie {
__le32 id;
__le32 len;
u8 data[];
};
enum ath12k_bd_ie_board_type {
ATH12K_BD_IE_BOARD_NAME = 0,
ATH12K_BD_IE_BOARD_DATA = 1,
};
enum ath12k_bd_ie_type {
/* contains sub IEs of enum ath12k_bd_ie_board_type */
ATH12K_BD_IE_BOARD = 0,
ATH12K_BD_IE_BOARD_EXT = 1,
};
struct ath12k_hw_regs {
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_tcl_ring_base_lsb;
u32 hal_tcl_status_ring_base_lsb;
u32 hal_wbm_idle_ring_base_lsb;
u32 hal_wbm_idle_ring_misc_addr;
u32 hal_wbm_r0_idle_list_cntl_addr;
u32 hal_wbm_r0_idle_list_size_addr;
u32 hal_wbm_scattered_ring_base_lsb;
u32 hal_wbm_scattered_ring_base_msb;
u32 hal_wbm_scattered_desc_head_info_ix0;
u32 hal_wbm_scattered_desc_head_info_ix1;
u32 hal_wbm_scattered_desc_tail_info_ix0;
u32 hal_wbm_scattered_desc_tail_info_ix1;
u32 hal_wbm_scattered_desc_ptr_hp_addr;
u32 hal_wbm_sw_release_ring_base_lsb;
u32 hal_wbm_sw1_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_ppe_rel_ring_base;
u32 hal_reo2_ring_base;
u32 hal_reo1_misc_ctrl_addr;
u32 hal_reo1_sw_cookie_cfg0;
u32 hal_reo1_sw_cookie_cfg1;
u32 hal_reo1_qdesc_lut_base0;
u32 hal_reo1_qdesc_lut_base1;
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_reo1_aging_thres_ix0;
u32 hal_reo1_aging_thres_ix1;
u32 hal_reo1_aging_thres_ix2;
u32 hal_reo1_aging_thres_ix3;
u32 hal_reo2_sw0_ring_base;
u32 hal_sw2reo_ring_base;
u32 hal_sw2reo1_ring_base;
u32 hal_reo_cmd_ring_base;
u32 hal_reo_status_ring_base;
};
int ath12k_hw_init(struct ath12k_base *ab);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_MAC_H
#define ATH12K_MAC_H
#include <net/mac80211.h>
#include <net/cfg80211.h>
struct ath12k;
struct ath12k_base;
struct ath12k_generic_iter {
struct ath12k *ar;
int ret;
};
/* number of failed packets (20 packets with 16 sw reties each) */
#define ATH12K_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 ATH12K_KEEPALIVE_MIN_IDLE 3747
#define ATH12K_KEEPALIVE_MAX_IDLE 3895
#define ATH12K_KEEPALIVE_MAX_UNRESPONSIVE 3900
/* 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 ATH12K_CHAN_WIDTH_NUM 8
#define ATH12K_TX_POWER_MAX_VAL 70
#define ATH12K_TX_POWER_MIN_VAL 0
enum ath12k_supported_bw {
ATH12K_BW_20 = 0,
ATH12K_BW_40 = 1,
ATH12K_BW_80 = 2,
ATH12K_BW_160 = 3,
};
extern const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default;
void ath12k_mac_destroy(struct ath12k_base *ab);
void ath12k_mac_unregister(struct ath12k_base *ab);
int ath12k_mac_register(struct ath12k_base *ab);
int ath12k_mac_allocate(struct ath12k_base *ab);
int ath12k_mac_hw_ratecode_to_legacy_rate(u8 hw_rc, u8 preamble, u8 *rateidx,
u16 *rate);
u8 ath12k_mac_bitrate_to_idx(const struct ieee80211_supported_band *sband,
u32 bitrate);
u8 ath12k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
u8 hw_rate, bool cck);
void __ath12k_mac_scan_finish(struct ath12k *ar);
void ath12k_mac_scan_finish(struct ath12k *ar);
struct ath12k_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id);
struct ath12k_vif *ath12k_mac_get_arvif_by_vdev_id(struct ath12k_base *ab,
u32 vdev_id);
struct ath12k *ath12k_mac_get_ar_by_vdev_id(struct ath12k_base *ab, u32 vdev_id);
struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id);
void ath12k_mac_drain_tx(struct ath12k *ar);
void ath12k_mac_peer_cleanup_all(struct ath12k *ar);
int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
enum rate_info_bw ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw);
enum ath12k_supported_bw ath12k_mac_mac80211_bw_to_ath12k_bw(enum rate_info_bw bw);
enum hal_encrypt_type ath12k_dp_tx_get_encrypt_type(u32 cipher);
#endif

View file

@ -0,0 +1,616 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/msi.h>
#include <linux/pci.h>
#include "core.h"
#include "debug.h"
#include "mhi.h"
#include "pci.h"
#define MHI_TIMEOUT_DEFAULT_MS 90000
static const struct mhi_channel_config ath12k_mhi_channels_qcn9274[] = {
{
.num = 0,
.name = "LOOPBACK",
.num_elements = 32,
.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,
.auto_queue = false,
},
{
.num = 1,
.name = "LOOPBACK",
.num_elements = 32,
.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,
.auto_queue = false,
},
{
.num = 20,
.name = "IPCR",
.num_elements = 32,
.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,
.auto_queue = false,
},
{
.num = 21,
.name = "IPCR",
.num_elements = 32,
.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,
.auto_queue = true,
},
};
static struct mhi_event_config ath12k_mhi_events_qcn9274[] = {
{
.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,
},
};
const struct mhi_controller_config ath12k_mhi_config_qcn9274 = {
.max_channels = 30,
.timeout_ms = 10000,
.use_bounce_buf = false,
.buf_len = 0,
.num_channels = ARRAY_SIZE(ath12k_mhi_channels_qcn9274),
.ch_cfg = ath12k_mhi_channels_qcn9274,
.num_events = ARRAY_SIZE(ath12k_mhi_events_qcn9274),
.event_cfg = ath12k_mhi_events_qcn9274,
};
static const struct mhi_channel_config ath12k_mhi_channels_wcn7850[] = {
{
.num = 0,
.name = "LOOPBACK",
.num_elements = 32,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = 0x4,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
},
{
.num = 1,
.name = "LOOPBACK",
.num_elements = 32,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = 0x4,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
},
{
.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,
.auto_queue = 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,
.auto_queue = true,
},
};
static struct mhi_event_config ath12k_mhi_events_wcn7850[] = {
{
.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,
},
};
const struct mhi_controller_config ath12k_mhi_config_wcn7850 = {
.max_channels = 128,
.timeout_ms = 2000,
.use_bounce_buf = false,
.buf_len = 0,
.num_channels = ARRAY_SIZE(ath12k_mhi_channels_wcn7850),
.ch_cfg = ath12k_mhi_channels_wcn7850,
.num_events = ARRAY_SIZE(ath12k_mhi_events_wcn7850),
.event_cfg = ath12k_mhi_events_wcn7850,
};
void ath12k_mhi_set_mhictrl_reset(struct ath12k_base *ab)
{
u32 val;
val = ath12k_pci_read32(ab, MHISTATUS);
ath12k_dbg(ab, ATH12K_DBG_PCI, "MHISTATUS 0x%x\n", val);
/* Observed on some targets that after SOC_GLOBAL_RESET, MHISTATUS
* has SYSERR bit set and thus need to set MHICTRL_RESET
* to clear SYSERR.
*/
ath12k_pci_write32(ab, MHICTRL, MHICTRL_RESET_MASK);
mdelay(10);
}
static void ath12k_mhi_reset_txvecdb(struct ath12k_base *ab)
{
ath12k_pci_write32(ab, PCIE_TXVECDB, 0);
}
static void ath12k_mhi_reset_txvecstatus(struct ath12k_base *ab)
{
ath12k_pci_write32(ab, PCIE_TXVECSTATUS, 0);
}
static void ath12k_mhi_reset_rxvecdb(struct ath12k_base *ab)
{
ath12k_pci_write32(ab, PCIE_RXVECDB, 0);
}
static void ath12k_mhi_reset_rxvecstatus(struct ath12k_base *ab)
{
ath12k_pci_write32(ab, PCIE_RXVECSTATUS, 0);
}
void ath12k_mhi_clear_vector(struct ath12k_base *ab)
{
ath12k_mhi_reset_txvecdb(ab);
ath12k_mhi_reset_txvecstatus(ab);
ath12k_mhi_reset_rxvecdb(ab);
ath12k_mhi_reset_rxvecstatus(ab);
}
static int ath12k_mhi_get_msi(struct ath12k_pci *ab_pci)
{
struct ath12k_base *ab = ab_pci->ab;
u32 user_base_data, base_vector;
int ret, num_vectors, i;
int *irq;
ret = ath12k_pci_get_user_msi_assignment(ab,
"MHI", &num_vectors,
&user_base_data, &base_vector);
if (ret)
return ret;
ath12k_dbg(ab, ATH12K_DBG_PCI, "Number of assigned MSI for MHI is %d, base vector is %d\n",
num_vectors, base_vector);
irq = kcalloc(num_vectors, sizeof(*irq), GFP_KERNEL);
if (!irq)
return -ENOMEM;
for (i = 0; i < num_vectors; i++)
irq[i] = ath12k_pci_get_msi_irq(ab->dev,
base_vector + i);
ab_pci->mhi_ctrl->irq = irq;
ab_pci->mhi_ctrl->nr_irqs = num_vectors;
return 0;
}
static int ath12k_mhi_op_runtime_get(struct mhi_controller *mhi_cntrl)
{
return 0;
}
static void ath12k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl)
{
}
static char *ath12k_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 ath12k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
enum mhi_callback cb)
{
struct ath12k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
ath12k_dbg(ab, ATH12K_DBG_BOOT, "mhi notify status reason %s\n",
ath12k_mhi_op_callback_to_str(cb));
switch (cb) {
case MHI_CB_SYS_ERROR:
ath12k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");
break;
case MHI_CB_EE_RDDM:
if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags)))
queue_work(ab->workqueue_aux, &ab->reset_work);
break;
default:
break;
}
}
static int ath12k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *addr,
u32 *out)
{
*out = readl(addr);
return 0;
}
static void ath12k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl,
void __iomem *addr,
u32 val)
{
writel(val, addr);
}
int ath12k_mhi_register(struct ath12k_pci *ab_pci)
{
struct ath12k_base *ab = ab_pci->ab;
struct mhi_controller *mhi_ctrl;
int ret;
mhi_ctrl = mhi_alloc_controller();
if (!mhi_ctrl)
return -ENOMEM;
ath12k_core_create_firmware_path(ab, ATH12K_AMSS_FILE,
ab_pci->amss_path,
sizeof(ab_pci->amss_path));
ab_pci->mhi_ctrl = mhi_ctrl;
mhi_ctrl->cntrl_dev = ab->dev;
mhi_ctrl->fw_image = ab_pci->amss_path;
mhi_ctrl->regs = ab->mem;
mhi_ctrl->reg_len = ab->mem_len;
ret = ath12k_mhi_get_msi(ab_pci);
if (ret) {
ath12k_err(ab, "failed to get msi for mhi\n");
mhi_free_controller(mhi_ctrl);
return ret;
}
mhi_ctrl->iova_start = 0;
mhi_ctrl->iova_stop = 0xffffffff;
mhi_ctrl->sbl_size = SZ_512K;
mhi_ctrl->seg_len = SZ_512K;
mhi_ctrl->fbc_download = true;
mhi_ctrl->runtime_get = ath12k_mhi_op_runtime_get;
mhi_ctrl->runtime_put = ath12k_mhi_op_runtime_put;
mhi_ctrl->status_cb = ath12k_mhi_op_status_cb;
mhi_ctrl->read_reg = ath12k_mhi_op_read_reg;
mhi_ctrl->write_reg = ath12k_mhi_op_write_reg;
ret = mhi_register_controller(mhi_ctrl, ab->hw_params->mhi_config);
if (ret) {
ath12k_err(ab, "failed to register to mhi bus, err = %d\n", ret);
mhi_free_controller(mhi_ctrl);
return ret;
}
return 0;
}
void ath12k_mhi_unregister(struct ath12k_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);
ab_pci->mhi_ctrl = NULL;
}
static char *ath12k_mhi_state_to_str(enum ath12k_mhi_state mhi_state)
{
switch (mhi_state) {
case ATH12K_MHI_INIT:
return "INIT";
case ATH12K_MHI_DEINIT:
return "DEINIT";
case ATH12K_MHI_POWER_ON:
return "POWER_ON";
case ATH12K_MHI_POWER_OFF:
return "POWER_OFF";
case ATH12K_MHI_FORCE_POWER_OFF:
return "FORCE_POWER_OFF";
case ATH12K_MHI_SUSPEND:
return "SUSPEND";
case ATH12K_MHI_RESUME:
return "RESUME";
case ATH12K_MHI_TRIGGER_RDDM:
return "TRIGGER_RDDM";
case ATH12K_MHI_RDDM_DONE:
return "RDDM_DONE";
default:
return "UNKNOWN";
}
};
static void ath12k_mhi_set_state_bit(struct ath12k_pci *ab_pci,
enum ath12k_mhi_state mhi_state)
{
struct ath12k_base *ab = ab_pci->ab;
switch (mhi_state) {
case ATH12K_MHI_INIT:
set_bit(ATH12K_MHI_INIT, &ab_pci->mhi_state);
break;
case ATH12K_MHI_DEINIT:
clear_bit(ATH12K_MHI_INIT, &ab_pci->mhi_state);
break;
case ATH12K_MHI_POWER_ON:
set_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state);
break;
case ATH12K_MHI_POWER_OFF:
case ATH12K_MHI_FORCE_POWER_OFF:
clear_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state);
clear_bit(ATH12K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state);
clear_bit(ATH12K_MHI_RDDM_DONE, &ab_pci->mhi_state);
break;
case ATH12K_MHI_SUSPEND:
set_bit(ATH12K_MHI_SUSPEND, &ab_pci->mhi_state);
break;
case ATH12K_MHI_RESUME:
clear_bit(ATH12K_MHI_SUSPEND, &ab_pci->mhi_state);
break;
case ATH12K_MHI_TRIGGER_RDDM:
set_bit(ATH12K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state);
break;
case ATH12K_MHI_RDDM_DONE:
set_bit(ATH12K_MHI_RDDM_DONE, &ab_pci->mhi_state);
break;
default:
ath12k_err(ab, "unhandled mhi state (%d)\n", mhi_state);
}
}
static int ath12k_mhi_check_state_bit(struct ath12k_pci *ab_pci,
enum ath12k_mhi_state mhi_state)
{
struct ath12k_base *ab = ab_pci->ab;
switch (mhi_state) {
case ATH12K_MHI_INIT:
if (!test_bit(ATH12K_MHI_INIT, &ab_pci->mhi_state))
return 0;
break;
case ATH12K_MHI_DEINIT:
case ATH12K_MHI_POWER_ON:
if (test_bit(ATH12K_MHI_INIT, &ab_pci->mhi_state) &&
!test_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state))
return 0;
break;
case ATH12K_MHI_FORCE_POWER_OFF:
if (test_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state))
return 0;
break;
case ATH12K_MHI_POWER_OFF:
case ATH12K_MHI_SUSPEND:
if (test_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state) &&
!test_bit(ATH12K_MHI_SUSPEND, &ab_pci->mhi_state))
return 0;
break;
case ATH12K_MHI_RESUME:
if (test_bit(ATH12K_MHI_SUSPEND, &ab_pci->mhi_state))
return 0;
break;
case ATH12K_MHI_TRIGGER_RDDM:
if (test_bit(ATH12K_MHI_POWER_ON, &ab_pci->mhi_state) &&
!test_bit(ATH12K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state))
return 0;
break;
case ATH12K_MHI_RDDM_DONE:
return 0;
default:
ath12k_err(ab, "unhandled mhi state: %s(%d)\n",
ath12k_mhi_state_to_str(mhi_state), mhi_state);
}
ath12k_err(ab, "failed to set mhi state %s(%d) in current mhi state (0x%lx)\n",
ath12k_mhi_state_to_str(mhi_state), mhi_state,
ab_pci->mhi_state);
return -EINVAL;
}
static int ath12k_mhi_set_state(struct ath12k_pci *ab_pci,
enum ath12k_mhi_state mhi_state)
{
struct ath12k_base *ab = ab_pci->ab;
int ret;
ret = ath12k_mhi_check_state_bit(ab_pci, mhi_state);
if (ret)
goto out;
ath12k_dbg(ab, ATH12K_DBG_PCI, "setting mhi state: %s(%d)\n",
ath12k_mhi_state_to_str(mhi_state), mhi_state);
switch (mhi_state) {
case ATH12K_MHI_INIT:
ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl);
break;
case ATH12K_MHI_DEINIT:
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
ret = 0;
break;
case ATH12K_MHI_POWER_ON:
ret = mhi_async_power_up(ab_pci->mhi_ctrl);
break;
case ATH12K_MHI_POWER_OFF:
mhi_power_down(ab_pci->mhi_ctrl, true);
ret = 0;
break;
case ATH12K_MHI_FORCE_POWER_OFF:
mhi_power_down(ab_pci->mhi_ctrl, false);
ret = 0;
break;
case ATH12K_MHI_SUSPEND:
ret = mhi_pm_suspend(ab_pci->mhi_ctrl);
break;
case ATH12K_MHI_RESUME:
ret = mhi_pm_resume(ab_pci->mhi_ctrl);
break;
case ATH12K_MHI_TRIGGER_RDDM:
ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl);
break;
case ATH12K_MHI_RDDM_DONE:
break;
default:
ath12k_err(ab, "unhandled MHI state (%d)\n", mhi_state);
ret = -EINVAL;
}
if (ret)
goto out;
ath12k_mhi_set_state_bit(ab_pci, mhi_state);
return 0;
out:
ath12k_err(ab, "failed to set mhi state: %s(%d)\n",
ath12k_mhi_state_to_str(mhi_state), mhi_state);
return ret;
}
int ath12k_mhi_start(struct ath12k_pci *ab_pci)
{
int ret;
ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS;
ret = ath12k_mhi_set_state(ab_pci, ATH12K_MHI_INIT);
if (ret)
goto out;
ret = ath12k_mhi_set_state(ab_pci, ATH12K_MHI_POWER_ON);
if (ret)
goto out;
return 0;
out:
return ret;
}
void ath12k_mhi_stop(struct ath12k_pci *ab_pci)
{
ath12k_mhi_set_state(ab_pci, ATH12K_MHI_POWER_OFF);
ath12k_mhi_set_state(ab_pci, ATH12K_MHI_DEINIT);
}
void ath12k_mhi_suspend(struct ath12k_pci *ab_pci)
{
ath12k_mhi_set_state(ab_pci, ATH12K_MHI_SUSPEND);
}
void ath12k_mhi_resume(struct ath12k_pci *ab_pci)
{
ath12k_mhi_set_state(ab_pci, ATH12K_MHI_RESUME);
}

View file

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2020-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _ATH12K_MHI_H
#define _ATH12K_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
enum ath12k_mhi_state {
ATH12K_MHI_INIT,
ATH12K_MHI_DEINIT,
ATH12K_MHI_POWER_ON,
ATH12K_MHI_POWER_OFF,
ATH12K_MHI_FORCE_POWER_OFF,
ATH12K_MHI_SUSPEND,
ATH12K_MHI_RESUME,
ATH12K_MHI_TRIGGER_RDDM,
ATH12K_MHI_RDDM,
ATH12K_MHI_RDDM_DONE,
};
extern const struct mhi_controller_config ath12k_mhi_config_qcn9274;
extern const struct mhi_controller_config ath12k_mhi_config_wcn7850;
int ath12k_mhi_start(struct ath12k_pci *ar_pci);
void ath12k_mhi_stop(struct ath12k_pci *ar_pci);
int ath12k_mhi_register(struct ath12k_pci *ar_pci);
void ath12k_mhi_unregister(struct ath12k_pci *ar_pci);
void ath12k_mhi_set_mhictrl_reset(struct ath12k_base *ab);
void ath12k_mhi_clear_vector(struct ath12k_base *ab);
void ath12k_mhi_suspend(struct ath12k_pci *ar_pci);
void ath12k_mhi_resume(struct ath12k_pci *ar_pci);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_PCI_H
#define ATH12K_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 0x1e38338
#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
#define PCI_BAR_WINDOW0_BASE 0x1E00000
#define PCI_BAR_WINDOW0_END 0x1E7FFFC
#define PCI_SOC_RANGE_MASK 0x3FFF
#define PCI_SOC_PCI_REG_BASE 0x1E04000
#define PCI_SOC_PCI_REG_END 0x1E07FFC
#define PCI_PARF_BASE 0x1E08000
#define PCI_PARF_END 0x1E0BFFC
#define PCI_MHIREGLEN_REG 0x1E0E100
#define PCI_MHI_REGION_END 0x1E0EFFC
#define QRTR_PCI_DOMAIN_NR_MASK GENMASK(7, 4)
#define QRTR_PCI_BUS_NUMBER_MASK GENMASK(3, 0)
#define ATH12K_PCI_SOC_HW_VERSION_1 1
#define ATH12K_PCI_SOC_HW_VERSION_2 2
struct ath12k_msi_user {
const char *name;
int num_vectors;
u32 base_vector;
};
struct ath12k_msi_config {
int total_vectors;
int total_users;
const struct ath12k_msi_user *users;
};
enum ath12k_pci_flags {
ATH12K_PCI_FLAG_INIT_DONE,
ATH12K_PCI_FLAG_IS_MSI_64,
ATH12K_PCI_ASPM_RESTORE,
};
struct ath12k_pci {
struct pci_dev *pdev;
struct ath12k_base *ab;
u16 dev_id;
char amss_path[100];
u32 msi_ep_base_data;
struct mhi_controller *mhi_ctrl;
const struct ath12k_msi_config *msi_config;
unsigned long mhi_state;
u32 register_window;
/* protects register_window above */
spinlock_t window_lock;
/* enum ath12k_pci_flags */
unsigned long flags;
u16 link_ctl;
};
static inline struct ath12k_pci *ath12k_pci_priv(struct ath12k_base *ab)
{
return (struct ath12k_pci *)ab->drv_priv;
}
int ath12k_pci_get_user_msi_assignment(struct ath12k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector);
int ath12k_pci_get_msi_irq(struct device *dev, unsigned int vector);
void ath12k_pci_write32(struct ath12k_base *ab, u32 offset, u32 value);
u32 ath12k_pci_read32(struct ath12k_base *ab, u32 offset);
int ath12k_pci_map_service_to_pipe(struct ath12k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
void ath12k_pci_get_msi_address(struct ath12k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi);
void ath12k_pci_get_ce_msi_idx(struct ath12k_base *ab, u32 ce_id,
u32 *msi_idx);
void ath12k_pci_hif_ce_irq_enable(struct ath12k_base *ab);
void ath12k_pci_hif_ce_irq_disable(struct ath12k_base *ab);
void ath12k_pci_ext_irq_enable(struct ath12k_base *ab);
void ath12k_pci_ext_irq_disable(struct ath12k_base *ab);
int ath12k_pci_hif_suspend(struct ath12k_base *ab);
int ath12k_pci_hif_resume(struct ath12k_base *ab);
void ath12k_pci_stop(struct ath12k_base *ab);
int ath12k_pci_start(struct ath12k_base *ab);
int ath12k_pci_power_up(struct ath12k_base *ab);
void ath12k_pci_power_down(struct ath12k_base *ab);
#endif /* ATH12K_PCI_H */

View file

@ -0,0 +1,342 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
#include "peer.h"
#include "debug.h"
struct ath12k_peer *ath12k_peer_find(struct ath12k_base *ab, int vdev_id,
const u8 *addr)
{
struct ath12k_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;
}
static struct ath12k_peer *ath12k_peer_find_by_pdev_idx(struct ath12k_base *ab,
u8 pdev_idx, const u8 *addr)
{
struct ath12k_peer *peer;
lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) {
if (peer->pdev_idx != pdev_idx)
continue;
if (!ether_addr_equal(peer->addr, addr))
continue;
return peer;
}
return NULL;
}
struct ath12k_peer *ath12k_peer_find_by_addr(struct ath12k_base *ab,
const u8 *addr)
{
struct ath12k_peer *peer;
lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) {
if (!ether_addr_equal(peer->addr, addr))
continue;
return peer;
}
return NULL;
}
struct ath12k_peer *ath12k_peer_find_by_id(struct ath12k_base *ab,
int peer_id)
{
struct ath12k_peer *peer;
lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list)
if (peer_id == peer->peer_id)
return peer;
return NULL;
}
bool ath12k_peer_exist_by_vdev_id(struct ath12k_base *ab, int vdev_id)
{
struct ath12k_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 true;
}
}
spin_unlock_bh(&ab->base_lock);
return false;
}
struct ath12k_peer *ath12k_peer_find_by_ast(struct ath12k_base *ab,
int ast_hash)
{
struct ath12k_peer *peer;
lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list)
if (ast_hash == peer->ast_hash)
return peer;
return NULL;
}
void ath12k_peer_unmap_event(struct ath12k_base *ab, u16 peer_id)
{
struct ath12k_peer *peer;
spin_lock_bh(&ab->base_lock);
peer = ath12k_peer_find_by_id(ab, peer_id);
if (!peer) {
ath12k_warn(ab, "peer-unmap-event: unknown peer id %d\n",
peer_id);
goto exit;
}
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "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 ath12k_peer_map_event(struct ath12k_base *ab, u8 vdev_id, u16 peer_id,
u8 *mac_addr, u16 ast_hash, u16 hw_peer_id)
{
struct ath12k_peer *peer;
spin_lock_bh(&ab->base_lock);
peer = ath12k_peer_find(ab, vdev_id, mac_addr);
if (!peer) {
peer = kzalloc(sizeof(*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);
}
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "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 ath12k_wait_for_peer_common(struct ath12k_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 = !!ath12k_peer_find(ab, vdev_id, addr);
spin_unlock_bh(&ab->base_lock);
(mapped == expect_mapped ||
test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags));
}), 3 * HZ);
if (ret <= 0)
return -ETIMEDOUT;
return 0;
}
void ath12k_peer_cleanup(struct ath12k *ar, u32 vdev_id)
{
struct ath12k_peer *peer, *tmp;
struct ath12k_base *ab = ar->ab;
lockdep_assert_held(&ar->conf_mutex);
spin_lock_bh(&ab->base_lock);
list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
if (peer->vdev_id != vdev_id)
continue;
ath12k_warn(ab, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id);
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
}
spin_unlock_bh(&ab->base_lock);
}
static int ath12k_wait_for_peer_deleted(struct ath12k *ar, int vdev_id, const u8 *addr)
{
return ath12k_wait_for_peer_common(ar->ab, vdev_id, addr, false);
}
int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id,
const u8 *addr)
{
int ret;
unsigned long time_left;
ret = ath12k_wait_for_peer_deleted(ar, vdev_id, addr);
if (ret) {
ath12k_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) {
ath12k_warn(ar->ab, "Timeout in receiving peer delete response\n");
return -ETIMEDOUT;
}
return 0;
}
int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
reinit_completion(&ar->peer_delete_done);
ret = ath12k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);
if (ret) {
ath12k_warn(ar->ab,
"failed to delete peer vdev_id %d addr %pM ret %d\n",
vdev_id, addr, ret);
return ret;
}
ret = ath12k_wait_for_peer_delete_done(ar, vdev_id, addr);
if (ret)
return ret;
ar->num_peers--;
return 0;
}
static int ath12k_wait_for_peer_created(struct ath12k *ar, int vdev_id, const u8 *addr)
{
return ath12k_wait_for_peer_common(ar->ab, vdev_id, addr, true);
}
int ath12k_peer_create(struct ath12k *ar, struct ath12k_vif *arvif,
struct ieee80211_sta *sta,
struct ath12k_wmi_peer_create_arg *arg)
{
struct ath12k_peer *peer;
int ret;
lockdep_assert_held(&ar->conf_mutex);
if (ar->num_peers > (ar->max_num_peers - 1)) {
ath12k_warn(ar->ab,
"failed to create peer due to insufficient peer entry resource in firmware\n");
return -ENOBUFS;
}
spin_lock_bh(&ar->ab->base_lock);
peer = ath12k_peer_find_by_pdev_idx(ar->ab, ar->pdev_idx, arg->peer_addr);
if (peer) {
spin_unlock_bh(&ar->ab->base_lock);
return -EINVAL;
}
spin_unlock_bh(&ar->ab->base_lock);
ret = ath12k_wmi_send_peer_create_cmd(ar, arg);
if (ret) {
ath12k_warn(ar->ab,
"failed to send peer create vdev_id %d ret %d\n",
arg->vdev_id, ret);
return ret;
}
ret = ath12k_wait_for_peer_created(ar, arg->vdev_id,
arg->peer_addr);
if (ret)
return ret;
spin_lock_bh(&ar->ab->base_lock);
peer = ath12k_peer_find(ar->ab, arg->vdev_id, arg->peer_addr);
if (!peer) {
spin_unlock_bh(&ar->ab->base_lock);
ath12k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",
arg->peer_addr, arg->vdev_id);
reinit_completion(&ar->peer_delete_done);
ret = ath12k_wmi_send_peer_delete_cmd(ar, arg->peer_addr,
arg->vdev_id);
if (ret) {
ath12k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n",
arg->vdev_id, arg->peer_addr);
return ret;
}
ret = ath12k_wait_for_peer_delete_done(ar, arg->vdev_id,
arg->peer_addr);
if (ret)
return ret;
return -ENOENT;
}
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;
ar->num_peers++;
spin_unlock_bh(&ar->ab->base_lock);
return 0;
}

View file

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_PEER_H
#define ATH12K_PEER_H
#include "dp_rx.h"
struct ppdu_user_delayba {
u16 sw_peer_id;
u32 info0;
u16 ru_end;
u16 ru_start;
u32 info1;
u32 rate_flags;
u32 resp_rate_flags;
};
struct ath12k_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 ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];
/* Info used in MMIC verification of
* RX fragments
*/
struct crypto_shash *tfm_mmic;
u8 mcast_keyidx;
u8 ucast_keyidx;
u16 sec_type;
u16 sec_type_grp;
struct ppdu_user_delayba ppdu_stats_delayba;
bool delayba_flag;
bool is_authorized;
};
void ath12k_peer_unmap_event(struct ath12k_base *ab, u16 peer_id);
void ath12k_peer_map_event(struct ath12k_base *ab, u8 vdev_id, u16 peer_id,
u8 *mac_addr, u16 ast_hash, u16 hw_peer_id);
struct ath12k_peer *ath12k_peer_find(struct ath12k_base *ab, int vdev_id,
const u8 *addr);
struct ath12k_peer *ath12k_peer_find_by_addr(struct ath12k_base *ab,
const u8 *addr);
struct ath12k_peer *ath12k_peer_find_by_id(struct ath12k_base *ab, int peer_id);
void ath12k_peer_cleanup(struct ath12k *ar, u32 vdev_id);
int ath12k_peer_delete(struct ath12k *ar, u32 vdev_id, u8 *addr);
int ath12k_peer_create(struct ath12k *ar, struct ath12k_vif *arvif,
struct ieee80211_sta *sta,
struct ath12k_wmi_peer_create_arg *arg);
int ath12k_wait_for_peer_delete_done(struct ath12k *ar, u32 vdev_id,
const u8 *addr);
bool ath12k_peer_exist_by_vdev_id(struct ath12k_base *ab, int vdev_id);
struct ath12k_peer *ath12k_peer_find_by_ast(struct ath12k_base *ab, int ast_hash);
#endif /* _PEER_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,568 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_QMI_H
#define ATH12K_QMI_H
#include <linux/mutex.h>
#include <linux/soc/qcom/qmi.h>
#define ATH12K_HOST_VERSION_STRING "WIN"
#define ATH12K_QMI_WLANFW_TIMEOUT_MS 10000
#define ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE 64
#define ATH12K_QMI_CALDB_ADDRESS 0x4BA00000
#define ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128
#define ATH12K_QMI_WLFW_NODE_ID_BASE 0x07
#define ATH12K_QMI_WLFW_SERVICE_ID_V01 0x45
#define ATH12K_QMI_WLFW_SERVICE_VERS_V01 0x01
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01 0x02
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9274 0x07
#define ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32
#define ATH12K_QMI_RESP_LEN_MAX 8192
#define ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52
#define ATH12K_QMI_CALDB_SIZE 0x480000
#define ATH12K_QMI_BDF_EXT_STR_LENGTH 0x20
#define ATH12K_QMI_FW_MEM_REQ_SEGMENT_CNT 3
#define ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01 4
#define ATH12K_QMI_DEVMEM_CMEM_INDEX 0
#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035
#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037
#define QMI_WLFW_FW_READY_IND_V01 0x0038
#define QMI_WLANFW_MAX_DATA_SIZE_V01 6144
#define ATH12K_FIRMWARE_MODE_OFF 4
#define ATH12K_QMI_TARGET_MEM_MODE_DEFAULT 0
#define ATH12K_BOARD_ID_DEFAULT 0xFF
struct ath12k_base;
enum ath12k_qmi_file_type {
ATH12K_QMI_FILE_TYPE_BDF_GOLDEN,
ATH12K_QMI_FILE_TYPE_CALDATA,
ATH12K_QMI_FILE_TYPE_EEPROM,
ATH12K_QMI_MAX_FILE_TYPE,
};
enum ath12k_qmi_bdf_type {
ATH12K_QMI_BDF_TYPE_BIN = 0,
ATH12K_QMI_BDF_TYPE_ELF = 1,
ATH12K_QMI_BDF_TYPE_REGDB = 4,
};
enum ath12k_qmi_event_type {
ATH12K_QMI_EVENT_SERVER_ARRIVE,
ATH12K_QMI_EVENT_SERVER_EXIT,
ATH12K_QMI_EVENT_REQUEST_MEM,
ATH12K_QMI_EVENT_FW_MEM_READY,
ATH12K_QMI_EVENT_FW_READY,
ATH12K_QMI_EVENT_REGISTER_DRIVER,
ATH12K_QMI_EVENT_UNREGISTER_DRIVER,
ATH12K_QMI_EVENT_RECOVERY,
ATH12K_QMI_EVENT_FORCE_FW_ASSERT,
ATH12K_QMI_EVENT_POWER_UP,
ATH12K_QMI_EVENT_POWER_DOWN,
ATH12K_QMI_EVENT_MAX,
};
struct ath12k_qmi_driver_event {
struct list_head list;
enum ath12k_qmi_event_type type;
void *data;
};
struct ath12k_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_v3;
int shadow_reg_v3_len;
};
struct ath12k_qmi_event_msg {
struct list_head list;
enum ath12k_qmi_event_type type;
};
struct target_mem_chunk {
u32 size;
u32 type;
dma_addr_t paddr;
union {
void __iomem *ioaddr;
void *addr;
} v;
};
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[ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1];
char fw_build_id[ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1];
char bdf_ext[ATH12K_QMI_BDF_EXT_STR_LENGTH];
};
struct m3_mem_region {
u32 size;
dma_addr_t paddr;
void *vaddr;
};
struct dev_mem_info {
u64 start;
u64 size;
};
struct ath12k_qmi {
struct ath12k_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 ath12k_qmi_ce_cfg ce_cfg;
struct target_mem_chunk target_mem[ATH12K_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;
struct dev_mem_info dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01];
};
#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_WLANFW_MAX_PLATFORM_NAME_LEN_V01 64
#define QMI_WLANFW_MAX_HOST_DDR_RANGE_SIZE_V01 3
struct qmi_wlanfw_host_ddr_range {
u64 start;
u64 size;
};
enum ath12k_qmi_target_mem {
HOST_DDR_REGION_TYPE = 0x1,
BDF_MEM_REGION_TYPE = 0x2,
M3_DUMP_REGION_TYPE = 0x3,
CALDB_MEM_REGION_TYPE = 0x4,
PAGEABLE_MEM_REGION_TYPE = 0x9,
};
enum qmi_wlanfw_host_build_type {
WLANFW_HOST_BUILD_TYPE_ENUM_MIN_VAL_V01 = INT_MIN,
QMI_WLANFW_HOST_BUILD_TYPE_UNSPECIFIED_V01 = 0,
QMI_WLANFW_HOST_BUILD_TYPE_PRIMARY_V01 = 1,
QMI_WLANFW_HOST_BUILD_TYPE_SECONDARY_V01 = 2,
WLANFW_HOST_BUILD_TYPE_ENUM_MAX_VAL_V01 = INT_MAX,
};
#define QMI_WLFW_MAX_NUM_MLO_CHIPS_V01 3
#define QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01 2
struct wlfw_host_mlo_chip_info_s_v01 {
u8 chip_id;
u8 num_local_links;
u8 hw_link_id[QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01];
u8 valid_mlo_link_id[QMI_WLFW_MAX_NUM_MLO_LINKS_PER_CHIP_V01];
};
enum ath12k_qmi_cnss_feature {
CNSS_FEATURE_MIN_ENUM_VAL_V01 = INT_MIN,
CNSS_QDSS_CFG_MISS_V01 = 3,
CNSS_MAX_FEATURE_V01 = 64,
CNSS_FEATURE_MAX_ENUM_VAL_V01 = INT_MAX,
};
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;
u8 cal_duration_valid;
u16 cal_duraiton;
u8 platform_name_valid;
char platform_name[QMI_WLANFW_MAX_PLATFORM_NAME_LEN_V01 + 1];
u8 ddr_range_valid;
struct qmi_wlanfw_host_ddr_range ddr_range[QMI_WLANFW_MAX_HOST_DDR_RANGE_SIZE_V01];
u8 host_build_type_valid;
enum qmi_wlanfw_host_build_type host_build_type;
u8 mlo_capable_valid;
u8 mlo_capable;
u8 mlo_chip_id_valid;
u16 mlo_chip_id;
u8 mlo_group_id_valid;
u8 mlo_group_id;
u8 max_mlo_peer_valid;
u16 max_mlo_peer;
u8 mlo_num_chips_valid;
u8 mlo_num_chips;
u8 mlo_chip_info_valid;
struct wlfw_host_mlo_chip_info_s_v01 mlo_chip_info[QMI_WLFW_MAX_NUM_MLO_CHIPS_V01];
u8 feature_list_valid;
u64 feature_list;
};
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
#define QMI_WLANFW_MAX_STR_LEN_V01 16
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[ATH12K_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[ATH12K_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;
};
#define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0
#define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 207
#define QMI_WLANFW_CAP_REQ_V01 0x0024
#define QMI_WLANFW_CAP_RESP_V01 0x0024
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_v3_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[ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 + 1];
};
struct qmi_wlanfw_dev_mem_info_s_v01 {
u64 start;
u64 size;
};
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,
};
enum qmi_wlanfw_rd_card_chain_cap_v01 {
WLFW_RD_CARD_CHAIN_CAP_MIN_VAL_V01 = INT_MIN,
WLFW_RD_CARD_CHAIN_CAP_UNSPECIFIED_V01 = 0,
WLFW_RD_CARD_CHAIN_CAP_1x1_V01 = 1,
WLFW_RD_CARD_CHAIN_CAP_2x2_V01 = 2,
WLFW_RD_CARD_CHAIN_CAP_MAX_VAL_V01 = INT_MAX,
};
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[ATH12K_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_caldata_read_timeout_valid;
u32 eeprom_caldata_read_timeout;
u8 fw_caps_valid;
u64 fw_caps;
u8 rd_card_chain_cap_valid;
enum qmi_wlanfw_rd_card_chain_cap_v01 rd_card_chain_cap;
u8 dev_mem_info_valid;
struct qmi_wlanfw_dev_mem_info_s_v01 dev_mem[ATH12K_QMI_WLFW_MAX_DEV_MEM_NUM_V01];
};
struct qmi_wlanfw_cap_req_msg_v01 {
char placeholder;
};
#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_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_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_V3_V01 60
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_v3_valid;
u32 shadow_reg_v3_len;
struct qmi_wlanfw_shadow_reg_v3_cfg_s_v01
shadow_reg_v3[QMI_WLANFW_MAX_NUM_SHADOW_REG_V3_V01];
};
struct qmi_wlanfw_wlan_cfg_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
int ath12k_qmi_firmware_start(struct ath12k_base *ab,
u32 mode);
void ath12k_qmi_firmware_stop(struct ath12k_base *ab);
void ath12k_qmi_event_work(struct work_struct *work);
void ath12k_qmi_msg_recv_work(struct work_struct *work);
void ath12k_qmi_deinit_service(struct ath12k_base *ab);
int ath12k_qmi_init_service(struct ath12k_base *ab);
#endif

View file

@ -0,0 +1,732 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/rtnetlink.h>
#include "core.h"
#include "debug.h"
/* World regdom to be used in case default regd from fw is unavailable */
#define ATH12K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0)
#define ATH12K_5GHZ_5150_5350 REG_RULE(5150 - 10, 5350 + 10, 80, 0, 30,\
NL80211_RRF_NO_IR)
#define ATH12K_5GHZ_5725_5850 REG_RULE(5725 - 10, 5850 + 10, 80, 0, 30,\
NL80211_RRF_NO_IR)
#define ETSI_WEATHER_RADAR_BAND_LOW 5590
#define ETSI_WEATHER_RADAR_BAND_HIGH 5650
#define ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT 600000
static const struct ieee80211_regdomain ath12k_world_regd = {
.n_reg_rules = 3,
.alpha2 = "00",
.reg_rules = {
ATH12K_2GHZ_CH01_11,
ATH12K_5GHZ_5150_5350,
ATH12K_5GHZ_5725_5850,
}
};
static bool ath12k_regdom_changes(struct ath12k *ar, char *alpha2)
{
const struct ieee80211_regdomain *regd;
regd = rcu_dereference_rtnl(ar->hw->wiphy->regd);
/* This can happen during wiphy registration where the previous
* user request is received before we update the regd received
* from firmware.
*/
if (!regd)
return true;
return memcmp(regd->alpha2, alpha2, 2) != 0;
}
static void
ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
{
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct ath12k_wmi_init_country_arg arg;
struct ath12k *ar = hw->priv;
int ret;
ath12k_dbg(ar->ab, ATH12K_DBG_REG,
"Regulatory Notification received for %s\n", wiphy_name(wiphy));
/* Currently supporting only General User Hints. Cell base user
* hints to be handled later.
* Hints from other sources like Core, Beacons are not expected for
* self managed wiphy's
*/
if (!(request->initiator == NL80211_REGDOM_SET_BY_USER &&
request->user_reg_hint_type == NL80211_USER_REG_HINT_USER)) {
ath12k_warn(ar->ab, "Unexpected Regulatory event for this wiphy\n");
return;
}
if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) {
ath12k_dbg(ar->ab, ATH12K_DBG_REG,
"Country Setting is not allowed\n");
return;
}
if (!ath12k_regdom_changes(ar, request->alpha2)) {
ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Country is already set\n");
return;
}
/* Set the country code to the firmware and wait for
* the WMI_REG_CHAN_LIST_CC EVENT for updating the
* reg info
*/
arg.flags = ALPHA_IS_SET;
memcpy(&arg.cc_info.alpha2, request->alpha2, 2);
arg.cc_info.alpha2[2] = 0;
ret = ath12k_wmi_send_init_country_cmd(ar, &arg);
if (ret)
ath12k_warn(ar->ab,
"INIT Country code set to fw failed : %d\n", ret);
}
int ath12k_reg_update_chan_list(struct ath12k *ar)
{
struct ieee80211_supported_band **bands;
struct ath12k_wmi_scan_chan_list_arg *arg;
struct ieee80211_channel *channel;
struct ieee80211_hw *hw = ar->hw;
struct ath12k_wmi_channel_arg *ch;
enum nl80211_band band;
int num_channels = 0;
int i, ret;
bands = hw->wiphy->bands;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
if (!bands[band])
continue;
for (i = 0; i < bands[band]->n_channels; i++) {
if (bands[band]->channels[i].flags &
IEEE80211_CHAN_DISABLED)
continue;
num_channels++;
}
}
if (WARN_ON(!num_channels))
return -EINVAL;
arg = kzalloc(struct_size(arg, channel, num_channels), GFP_KERNEL);
if (!arg)
return -ENOMEM;
arg->pdev_id = ar->pdev->pdev_id;
arg->nallchans = num_channels;
ch = arg->channel;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
if (!bands[band])
continue;
for (i = 0; i < bands[band]->n_channels; i++) {
channel = &bands[band]->channels[i];
if (channel->flags & IEEE80211_CHAN_DISABLED)
continue;
/* TODO: Set to true/false based on some condition? */
ch->allow_ht = true;
ch->allow_vht = true;
ch->allow_he = true;
ch->dfs_set =
!!(channel->flags & IEEE80211_CHAN_RADAR);
ch->is_chan_passive = !!(channel->flags &
IEEE80211_CHAN_NO_IR);
ch->is_chan_passive |= ch->dfs_set;
ch->mhz = channel->center_freq;
ch->cfreq1 = channel->center_freq;
ch->minpower = 0;
ch->maxpower = channel->max_power * 2;
ch->maxregpower = channel->max_reg_power * 2;
ch->antennamax = channel->max_antenna_gain * 2;
/* TODO: Use appropriate phymodes */
if (channel->band == NL80211_BAND_2GHZ)
ch->phy_mode = MODE_11G;
else
ch->phy_mode = MODE_11A;
if (channel->band == NL80211_BAND_6GHZ &&
cfg80211_channel_is_psc(channel))
ch->psc_channel = true;
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n",
i, arg->nallchans,
ch->mhz, ch->maxpower, ch->maxregpower,
ch->antennamax, ch->phy_mode);
ch++;
/* TODO: use quarrter/half rate, cfreq12, dfs_cfreq2
* set_agile, reg_class_idx
*/
}
}
ret = ath12k_wmi_send_scan_chan_list_cmd(ar, arg);
kfree(arg);
return ret;
}
static void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig,
struct ieee80211_regdomain *regd_copy)
{
u8 i;
/* The caller should have checked error conditions */
memcpy(regd_copy, regd_orig, sizeof(*regd_orig));
for (i = 0; i < regd_orig->n_reg_rules; i++)
memcpy(&regd_copy->reg_rules[i], &regd_orig->reg_rules[i],
sizeof(struct ieee80211_reg_rule));
}
int ath12k_regd_update(struct ath12k *ar, bool init)
{
struct ieee80211_regdomain *regd, *regd_copy = NULL;
int ret, regd_len, pdev_id;
struct ath12k_base *ab;
ab = ar->ab;
pdev_id = ar->pdev_idx;
spin_lock_bh(&ab->base_lock);
if (init) {
/* Apply the regd received during init through
* WMI_REG_CHAN_LIST_CC event. In case of failure to
* receive the regd, initialize with a default world
* regulatory.
*/
if (ab->default_regd[pdev_id]) {
regd = ab->default_regd[pdev_id];
} else {
ath12k_warn(ab,
"failed to receive default regd during init\n");
regd = (struct ieee80211_regdomain *)&ath12k_world_regd;
}
} else {
regd = ab->new_regd[pdev_id];
}
if (!regd) {
ret = -EINVAL;
spin_unlock_bh(&ab->base_lock);
goto err;
}
regd_len = sizeof(*regd) + (regd->n_reg_rules *
sizeof(struct ieee80211_reg_rule));
regd_copy = kzalloc(regd_len, GFP_ATOMIC);
if (regd_copy)
ath12k_copy_regd(regd, regd_copy);
spin_unlock_bh(&ab->base_lock);
if (!regd_copy) {
ret = -ENOMEM;
goto err;
}
rtnl_lock();
wiphy_lock(ar->hw->wiphy);
ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy);
wiphy_unlock(ar->hw->wiphy);
rtnl_unlock();
kfree(regd_copy);
if (ret)
goto err;
if (ar->state == ATH12K_STATE_ON) {
ret = ath12k_reg_update_chan_list(ar);
if (ret)
goto err;
}
return 0;
err:
ath12k_warn(ab, "failed to perform regd update : %d\n", ret);
return ret;
}
static enum nl80211_dfs_regions
ath12k_map_fw_dfs_region(enum ath12k_dfs_region dfs_region)
{
switch (dfs_region) {
case ATH12K_DFS_REG_FCC:
case ATH12K_DFS_REG_CN:
return NL80211_DFS_FCC;
case ATH12K_DFS_REG_ETSI:
case ATH12K_DFS_REG_KR:
return NL80211_DFS_ETSI;
case ATH12K_DFS_REG_MKK:
case ATH12K_DFS_REG_MKK_N:
return NL80211_DFS_JP;
default:
return NL80211_DFS_UNSET;
}
}
static u32 ath12k_map_fw_reg_flags(u16 reg_flags)
{
u32 flags = 0;
if (reg_flags & REGULATORY_CHAN_NO_IR)
flags = NL80211_RRF_NO_IR;
if (reg_flags & REGULATORY_CHAN_RADAR)
flags |= NL80211_RRF_DFS;
if (reg_flags & REGULATORY_CHAN_NO_OFDM)
flags |= NL80211_RRF_NO_OFDM;
if (reg_flags & REGULATORY_CHAN_INDOOR_ONLY)
flags |= NL80211_RRF_NO_OUTDOOR;
if (reg_flags & REGULATORY_CHAN_NO_HT40)
flags |= NL80211_RRF_NO_HT40;
if (reg_flags & REGULATORY_CHAN_NO_80MHZ)
flags |= NL80211_RRF_NO_80MHZ;
if (reg_flags & REGULATORY_CHAN_NO_160MHZ)
flags |= NL80211_RRF_NO_160MHZ;
return flags;
}
static bool
ath12k_reg_can_intersect(struct ieee80211_reg_rule *rule1,
struct ieee80211_reg_rule *rule2)
{
u32 start_freq1, end_freq1;
u32 start_freq2, end_freq2;
start_freq1 = rule1->freq_range.start_freq_khz;
start_freq2 = rule2->freq_range.start_freq_khz;
end_freq1 = rule1->freq_range.end_freq_khz;
end_freq2 = rule2->freq_range.end_freq_khz;
if ((start_freq1 >= start_freq2 &&
start_freq1 < end_freq2) ||
(start_freq2 > start_freq1 &&
start_freq2 < end_freq1))
return true;
/* TODO: Should we restrict intersection feasibility
* based on min bandwidth of the intersected region also,
* say the intersected rule should have a min bandwidth
* of 20MHz?
*/
return false;
}
static void ath12k_reg_intersect_rules(struct ieee80211_reg_rule *rule1,
struct ieee80211_reg_rule *rule2,
struct ieee80211_reg_rule *new_rule)
{
u32 start_freq1, end_freq1;
u32 start_freq2, end_freq2;
u32 freq_diff, max_bw;
start_freq1 = rule1->freq_range.start_freq_khz;
start_freq2 = rule2->freq_range.start_freq_khz;
end_freq1 = rule1->freq_range.end_freq_khz;
end_freq2 = rule2->freq_range.end_freq_khz;
new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1,
start_freq2);
new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2);
freq_diff = new_rule->freq_range.end_freq_khz -
new_rule->freq_range.start_freq_khz;
max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz,
rule2->freq_range.max_bandwidth_khz);
new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff);
new_rule->power_rule.max_antenna_gain =
min_t(u32, rule1->power_rule.max_antenna_gain,
rule2->power_rule.max_antenna_gain);
new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp,
rule2->power_rule.max_eirp);
/* Use the flags of both the rules */
new_rule->flags = rule1->flags | rule2->flags;
/* To be safe, lts use the max cac timeout of both rules */
new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms,
rule2->dfs_cac_ms);
}
static struct ieee80211_regdomain *
ath12k_regd_intersect(struct ieee80211_regdomain *default_regd,
struct ieee80211_regdomain *curr_regd)
{
u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules;
struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule;
struct ieee80211_regdomain *new_regd = NULL;
u8 i, j, k;
num_old_regd_rules = default_regd->n_reg_rules;
num_curr_regd_rules = curr_regd->n_reg_rules;
num_new_regd_rules = 0;
/* Find the number of intersecting rules to allocate new regd memory */
for (i = 0; i < num_old_regd_rules; i++) {
old_rule = default_regd->reg_rules + i;
for (j = 0; j < num_curr_regd_rules; j++) {
curr_rule = curr_regd->reg_rules + j;
if (ath12k_reg_can_intersect(old_rule, curr_rule))
num_new_regd_rules++;
}
}
if (!num_new_regd_rules)
return NULL;
new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules *
sizeof(struct ieee80211_reg_rule)),
GFP_ATOMIC);
if (!new_regd)
return NULL;
/* We set the new country and dfs region directly and only trim
* the freq, power, antenna gain by intersecting with the
* default regdomain. Also MAX of the dfs cac timeout is selected.
*/
new_regd->n_reg_rules = num_new_regd_rules;
memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2));
new_regd->dfs_region = curr_regd->dfs_region;
new_rule = new_regd->reg_rules;
for (i = 0, k = 0; i < num_old_regd_rules; i++) {
old_rule = default_regd->reg_rules + i;
for (j = 0; j < num_curr_regd_rules; j++) {
curr_rule = curr_regd->reg_rules + j;
if (ath12k_reg_can_intersect(old_rule, curr_rule))
ath12k_reg_intersect_rules(old_rule, curr_rule,
(new_rule + k++));
}
}
return new_regd;
}
static const char *
ath12k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region)
{
switch (dfs_region) {
case NL80211_DFS_FCC:
return "FCC";
case NL80211_DFS_ETSI:
return "ETSI";
case NL80211_DFS_JP:
return "JP";
default:
return "UNSET";
}
}
static u16
ath12k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw)
{
u16 bw;
bw = end_freq - start_freq;
bw = min_t(u16, bw, max_bw);
if (bw >= 80 && bw < 160)
bw = 80;
else if (bw >= 40 && bw < 80)
bw = 40;
else if (bw < 40)
bw = 20;
return bw;
}
static void
ath12k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq,
u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr,
u32 reg_flags)
{
reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq);
reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq);
reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw);
reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain);
reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr);
reg_rule->flags = reg_flags;
}
static void
ath12k_reg_update_weather_radar_band(struct ath12k_base *ab,
struct ieee80211_regdomain *regd,
struct ath12k_reg_rule *reg_rule,
u8 *rule_idx, u32 flags, u16 max_bw)
{
u32 end_freq;
u16 bw;
u8 i;
i = *rule_idx;
bw = ath12k_reg_adjust_bw(reg_rule->start_freq,
ETSI_WEATHER_RADAR_BAND_LOW, max_bw);
ath12k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq,
ETSI_WEATHER_RADAR_BAND_LOW, bw,
reg_rule->ant_gain, reg_rule->reg_power,
flags);
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
i + 1, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW,
bw, reg_rule->ant_gain, reg_rule->reg_power,
regd->reg_rules[i].dfs_cac_ms,
flags);
if (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_HIGH)
end_freq = ETSI_WEATHER_RADAR_BAND_HIGH;
else
end_freq = reg_rule->end_freq;
bw = ath12k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_LOW, end_freq,
max_bw);
i++;
ath12k_reg_update_rule(regd->reg_rules + i,
ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw,
reg_rule->ant_gain, reg_rule->reg_power,
flags);
regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT;
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
i + 1, ETSI_WEATHER_RADAR_BAND_LOW, end_freq,
bw, reg_rule->ant_gain, reg_rule->reg_power,
regd->reg_rules[i].dfs_cac_ms,
flags);
if (end_freq == reg_rule->end_freq) {
regd->n_reg_rules--;
*rule_idx = i;
return;
}
bw = ath12k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_HIGH,
reg_rule->end_freq, max_bw);
i++;
ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH,
reg_rule->end_freq, bw,
reg_rule->ant_gain, reg_rule->reg_power,
flags);
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
i + 1, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq,
bw, reg_rule->ant_gain, reg_rule->reg_power,
regd->reg_rules[i].dfs_cac_ms,
flags);
*rule_idx = i;
}
struct ieee80211_regdomain *
ath12k_reg_build_regd(struct ath12k_base *ab,
struct ath12k_reg_info *reg_info, bool intersect)
{
struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
struct ath12k_reg_rule *reg_rule;
u8 i = 0, j = 0, k = 0;
u8 num_rules;
u16 max_bw;
u32 flags;
char alpha2[3];
num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules;
/* FIXME: Currently taking reg rules for 6G only from Indoor AP mode list.
* This can be updated to choose the combination dynamically based on AP
* type and client type, after complete 6G regulatory support is added.
*/
if (reg_info->is_ext_reg_event)
num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP];
if (!num_rules)
goto ret;
/* Add max additional rules to accommodate weather radar band */
if (reg_info->dfs_region == ATH12K_DFS_REG_ETSI)
num_rules += 2;
tmp_regd = kzalloc(sizeof(*tmp_regd) +
(num_rules * sizeof(struct ieee80211_reg_rule)),
GFP_ATOMIC);
if (!tmp_regd)
goto ret;
memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1);
alpha2[2] = '\0';
tmp_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region);
ath12k_dbg(ab, ATH12K_DBG_REG,
"\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n",
alpha2, ath12k_reg_get_regdom_str(tmp_regd->dfs_region),
reg_info->dfs_region, num_rules);
/* Update reg_rules[] below. Firmware is expected to
* send these rules in order(2G rules first and then 5G)
*/
for (; i < num_rules; i++) {
if (reg_info->num_2g_reg_rules &&
(i < reg_info->num_2g_reg_rules)) {
reg_rule = reg_info->reg_rules_2g_ptr + i;
max_bw = min_t(u16, reg_rule->max_bw,
reg_info->max_bw_2g);
flags = 0;
} else if (reg_info->num_5g_reg_rules &&
(j < reg_info->num_5g_reg_rules)) {
reg_rule = reg_info->reg_rules_5g_ptr + j++;
max_bw = min_t(u16, reg_rule->max_bw,
reg_info->max_bw_5g);
/* FW doesn't pass NL80211_RRF_AUTO_BW flag for
* BW Auto correction, we can enable this by default
* for all 5G rules here. The regulatory core performs
* BW correction if required and applies flags as
* per other BW rule flags we pass from here
*/
flags = NL80211_RRF_AUTO_BW;
} else if (reg_info->is_ext_reg_event &&
reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] &&
(k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) {
reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++;
max_bw = min_t(u16, reg_rule->max_bw,
reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]);
flags = NL80211_RRF_AUTO_BW;
} else {
break;
}
flags |= ath12k_map_fw_reg_flags(reg_rule->flags);
ath12k_reg_update_rule(tmp_regd->reg_rules + i,
reg_rule->start_freq,
reg_rule->end_freq, max_bw,
reg_rule->ant_gain, reg_rule->reg_power,
flags);
/* Update dfs cac timeout if the dfs domain is ETSI and the
* new rule covers weather radar band.
* Default value of '0' corresponds to 60s timeout, so no
* need to update that for other rules.
*/
if (flags & NL80211_RRF_DFS &&
reg_info->dfs_region == ATH12K_DFS_REG_ETSI &&
(reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW &&
reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){
ath12k_reg_update_weather_radar_band(ab, tmp_regd,
reg_rule, &i,
flags, max_bw);
continue;
}
if (reg_info->is_ext_reg_event) {
ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n",
i + 1, reg_rule->start_freq, reg_rule->end_freq,
max_bw, reg_rule->ant_gain, reg_rule->reg_power,
tmp_regd->reg_rules[i].dfs_cac_ms,
flags, reg_rule->psd_flag, reg_rule->psd_eirp);
} else {
ath12k_dbg(ab, ATH12K_DBG_REG,
"\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n",
i + 1, reg_rule->start_freq, reg_rule->end_freq,
max_bw, reg_rule->ant_gain, reg_rule->reg_power,
tmp_regd->reg_rules[i].dfs_cac_ms,
flags);
}
}
tmp_regd->n_reg_rules = i;
if (intersect) {
default_regd = ab->default_regd[reg_info->phy_id];
/* Get a new regd by intersecting the received regd with
* our default regd.
*/
new_regd = ath12k_regd_intersect(default_regd, tmp_regd);
kfree(tmp_regd);
if (!new_regd) {
ath12k_warn(ab, "Unable to create intersected regdomain\n");
goto ret;
}
} else {
new_regd = tmp_regd;
}
ret:
return new_regd;
}
void ath12k_regd_update_work(struct work_struct *work)
{
struct ath12k *ar = container_of(work, struct ath12k,
regd_update_work);
int ret;
ret = ath12k_regd_update(ar, false);
if (ret) {
/* Firmware has already moved to the new regd. We need
* to maintain channel consistency across FW, Host driver
* and userspace. Hence as a fallback mechanism we can set
* the prev or default country code to the firmware.
*/
/* TODO: Implement Fallback Mechanism */
}
}
void ath12k_reg_init(struct ath12k *ar)
{
ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
ar->hw->wiphy->reg_notifier = ath12k_reg_notifier;
}
void ath12k_reg_free(struct ath12k_base *ab)
{
int i;
for (i = 0; i < ab->hw_params->max_radios; i++) {
kfree(ab->default_regd[i]);
kfree(ab->new_regd[i]);
}
}

View file

@ -0,0 +1,95 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef ATH12K_REG_H
#define ATH12K_REG_H
#include <linux/kernel.h>
#include <net/regulatory.h>
struct ath12k_base;
struct ath12k;
/* DFS regdomains supported by Firmware */
enum ath12k_dfs_region {
ATH12K_DFS_REG_UNSET,
ATH12K_DFS_REG_FCC,
ATH12K_DFS_REG_ETSI,
ATH12K_DFS_REG_MKK,
ATH12K_DFS_REG_CN,
ATH12K_DFS_REG_KR,
ATH12K_DFS_REG_MKK_N,
ATH12K_DFS_REG_UNDEF,
};
enum ath12k_reg_cc_code {
REG_SET_CC_STATUS_PASS = 0,
REG_CURRENT_ALPHA2_NOT_FOUND = 1,
REG_INIT_ALPHA2_NOT_FOUND = 2,
REG_SET_CC_CHANGE_NOT_ALLOWED = 3,
REG_SET_CC_STATUS_NO_MEMORY = 4,
REG_SET_CC_STATUS_FAIL = 5,
};
struct ath12k_reg_rule {
u16 start_freq;
u16 end_freq;
u16 max_bw;
u8 reg_power;
u8 ant_gain;
u16 flags;
bool psd_flag;
u16 psd_eirp;
};
struct ath12k_reg_info {
enum ath12k_reg_cc_code status_code;
u8 num_phy;
u8 phy_id;
u16 reg_dmn_pair;
u16 ctry_code;
u8 alpha2[REG_ALPHA2_LEN + 1];
u32 dfs_region;
u32 phybitmap;
bool is_ext_reg_event;
u32 min_bw_2g;
u32 max_bw_2g;
u32 min_bw_5g;
u32 max_bw_5g;
u32 num_2g_reg_rules;
u32 num_5g_reg_rules;
struct ath12k_reg_rule *reg_rules_2g_ptr;
struct ath12k_reg_rule *reg_rules_5g_ptr;
enum wmi_reg_6g_client_type client_type;
bool rnr_tpe_usable;
bool unspecified_ap_usable;
/* TODO: All 6G related info can be stored only for required
* combination instead of all types, to optimize memory usage.
*/
u8 domain_code_6g_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
u8 domain_code_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
u32 domain_code_6g_super_id;
u32 min_bw_6g_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
u32 max_bw_6g_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
u32 min_bw_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
u32 max_bw_6g_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
u32 num_6g_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE];
u32 num_6g_reg_rules_cl[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
struct ath12k_reg_rule *reg_rules_6g_ap_ptr[WMI_REG_CURRENT_MAX_AP_TYPE];
struct ath12k_reg_rule *reg_rules_6g_client_ptr
[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE];
};
void ath12k_reg_init(struct ath12k *ar);
void ath12k_reg_free(struct ath12k_base *ab);
void ath12k_regd_update_work(struct work_struct *work);
struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab,
struct ath12k_reg_info *reg_info,
bool intersect);
int ath12k_regd_update(struct ath12k *ar, bool init);
int ath12k_reg_update_chan_list(struct ath12k *ar);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/module.h>
#define CREATE_TRACE_POINTS
#include "trace.h"

View file

@ -0,0 +1,152 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#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_ATH12K_TRACING)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif /* !CONFIG_ATH12K_TRACING || __CHECKER__ */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ath12k
TRACE_EVENT(ath12k_htt_pktlog,
TP_PROTO(struct ath12k *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, dev_name(ar->ab->dev));
__assign_str(driver, dev_driver_string(ar->ab->dev));
__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(ath12k_htt_ppdu_stats,
TP_PROTO(struct ath12k *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)
__field(u32, info)
__field(u32, sync_tstmp_lo_us)
__field(u32, sync_tstmp_hi_us)
__field(u32, mlo_offset_lo)
__field(u32, mlo_offset_hi)
__field(u32, mlo_offset_clks)
__field(u32, mlo_comp_clks)
__field(u32, mlo_comp_timer)
__dynamic_array(u8, ppdu, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->ab->dev));
__assign_str(driver, dev_driver_string(ar->ab->dev));
__entry->len = len;
__entry->info = ar->pdev->timestamp.info;
__entry->sync_tstmp_lo_us = ar->pdev->timestamp.sync_timestamp_hi_us;
__entry->sync_tstmp_hi_us = ar->pdev->timestamp.sync_timestamp_lo_us;
__entry->mlo_offset_lo = ar->pdev->timestamp.mlo_offset_lo;
__entry->mlo_offset_hi = ar->pdev->timestamp.mlo_offset_hi;
__entry->mlo_offset_clks = ar->pdev->timestamp.mlo_offset_clks;
__entry->mlo_comp_clks = ar->pdev->timestamp.mlo_comp_clks;
__entry->mlo_comp_timer = ar->pdev->timestamp.mlo_comp_timer;
memcpy(__get_dynamic_array(ppdu), data, len);
),
TP_printk(
"%s %s ppdu len %d",
__get_str(driver),
__get_str(device),
__entry->len
)
);
TRACE_EVENT(ath12k_htt_rxdesc,
TP_PROTO(struct ath12k *ar, const void *data, size_t type, size_t len),
TP_ARGS(ar, data, 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, type)
__field(u32, info)
__field(u32, sync_tstmp_lo_us)
__field(u32, sync_tstmp_hi_us)
__field(u32, mlo_offset_lo)
__field(u32, mlo_offset_hi)
__field(u32, mlo_offset_clks)
__field(u32, mlo_comp_clks)
__field(u32, mlo_comp_timer)
__dynamic_array(u8, rxdesc, len)
),
TP_fast_assign(
__assign_str(device, dev_name(ar->ab->dev));
__assign_str(driver, dev_driver_string(ar->ab->dev));
__entry->len = len;
__entry->type = type;
__entry->info = ar->pdev->timestamp.info;
__entry->sync_tstmp_lo_us = ar->pdev->timestamp.sync_timestamp_hi_us;
__entry->sync_tstmp_hi_us = ar->pdev->timestamp.sync_timestamp_lo_us;
__entry->mlo_offset_lo = ar->pdev->timestamp.mlo_offset_lo;
__entry->mlo_offset_hi = ar->pdev->timestamp.mlo_offset_hi;
__entry->mlo_offset_clks = ar->pdev->timestamp.mlo_offset_clks;
__entry->mlo_comp_clks = ar->pdev->timestamp.mlo_comp_clks;
__entry->mlo_comp_timer = ar->pdev->timestamp.mlo_comp_timer;
memcpy(__get_dynamic_array(rxdesc), data, len);
),
TP_printk(
"%s %s rxdesc len %d",
__get_str(driver),
__get_str(device),
__entry->len
)
);
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff