As Hash based reo destination selection is configured, the decapped packets reach different reo destintion rings based on the destintaion ring selected for the computed hash (based on the 5-tuple {ip src/ip dst/src port/dst port/protocol}) by hw and as configured by driver. Hence the current implementation of amsdu list based processing after all the subframes of amsdu are received (since all msdu's for a pdev are received in same reo dest ring), is not applicable here and hence is replaced with per msdu based handling as these subframes can be received in different reo dest rings. Also, as some of the rx descriptor fields might be valid only for the first msdu (for ex. received 80211 header, encryption type, etc), it might not be useful now as we cannot sync between different subframes received in different rings. Hence do not rely on those fields and replace them with fieds valid only on per msdu descriptors. Also cache other details such as encryption type for a peer so that it can be reused when a packet is received from it. Co-developed-by: Tamizh Chelvam Raja <tamizhr@codeaurora.org> Signed-off-by: Tamizh Chelvam Raja <tamizhr@codeaurora.org> Signed-off-by: Sriram R <srirrama@codeaurora.org> Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
931 lines
24 KiB
C
931 lines
24 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/*
|
|
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <crypto/hash.h>
|
|
#include "core.h"
|
|
#include "dp_tx.h"
|
|
#include "hal_tx.h"
|
|
#include "debug.h"
|
|
#include "dp_rx.h"
|
|
#include "peer.h"
|
|
|
|
static void ath11k_dp_htt_htc_tx_complete(struct ath11k_base *ab,
|
|
struct sk_buff *skb)
|
|
{
|
|
dev_kfree_skb_any(skb);
|
|
}
|
|
|
|
void ath11k_dp_peer_cleanup(struct ath11k *ar, int vdev_id, const u8 *addr)
|
|
{
|
|
struct ath11k_base *ab = ar->ab;
|
|
struct ath11k_peer *peer;
|
|
|
|
/* TODO: Any other peer specific DP cleanup */
|
|
|
|
spin_lock_bh(&ab->base_lock);
|
|
peer = ath11k_peer_find(ab, vdev_id, addr);
|
|
if (!peer) {
|
|
ath11k_warn(ab, "failed to lookup peer %pM on vdev %d\n",
|
|
addr, vdev_id);
|
|
spin_unlock_bh(&ab->base_lock);
|
|
return;
|
|
}
|
|
|
|
ath11k_peer_rx_tid_cleanup(ar, peer);
|
|
crypto_free_shash(peer->tfm_mmic);
|
|
spin_unlock_bh(&ab->base_lock);
|
|
}
|
|
|
|
int ath11k_dp_peer_setup(struct ath11k *ar, int vdev_id, const u8 *addr)
|
|
{
|
|
struct ath11k_base *ab = ar->ab;
|
|
struct ath11k_peer *peer;
|
|
u32 reo_dest;
|
|
int ret = 0, tid;
|
|
|
|
/* NOTE: reo_dest ring id starts from 1 unlike mac_id which starts from 0 */
|
|
reo_dest = ar->dp.mac_id + 1;
|
|
ret = ath11k_wmi_set_peer_param(ar, addr, vdev_id,
|
|
WMI_PEER_SET_DEFAULT_ROUTING,
|
|
DP_RX_HASH_ENABLE | (reo_dest << 1));
|
|
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set default routing %d peer :%pM vdev_id :%d\n",
|
|
ret, addr, vdev_id);
|
|
return ret;
|
|
}
|
|
|
|
for (tid = 0; tid <= IEEE80211_NUM_TIDS; tid++) {
|
|
ret = ath11k_peer_rx_tid_setup(ar, addr, vdev_id, tid, 1, 0,
|
|
HAL_PN_TYPE_NONE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to setup rxd tid queue for tid %d: %d\n",
|
|
tid, ret);
|
|
goto peer_clean;
|
|
}
|
|
}
|
|
|
|
ret = ath11k_peer_rx_frag_setup(ar, addr, vdev_id);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to setup rx defrag context\n");
|
|
return ret;
|
|
}
|
|
|
|
/* TODO: Setup other peer specific resource used in data path */
|
|
|
|
return 0;
|
|
|
|
peer_clean:
|
|
spin_lock_bh(&ab->base_lock);
|
|
|
|
peer = ath11k_peer_find(ab, vdev_id, addr);
|
|
if (!peer) {
|
|
ath11k_warn(ab, "failed to find the peer to del rx tid\n");
|
|
spin_unlock_bh(&ab->base_lock);
|
|
return -ENOENT;
|
|
}
|
|
|
|
for (; tid >= 0; tid--)
|
|
ath11k_peer_rx_tid_delete(ar, peer, tid);
|
|
|
|
spin_unlock_bh(&ab->base_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring)
|
|
{
|
|
if (!ring->vaddr_unaligned)
|
|
return;
|
|
|
|
dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned,
|
|
ring->paddr_unaligned);
|
|
|
|
ring->vaddr_unaligned = NULL;
|
|
}
|
|
|
|
int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
|
|
enum hal_ring_type type, int ring_num,
|
|
int mac_id, int num_entries)
|
|
{
|
|
struct hal_srng_params params = { 0 };
|
|
int entry_sz = ath11k_hal_srng_get_entrysize(type);
|
|
int max_entries = ath11k_hal_srng_get_max_entries(type);
|
|
int ret;
|
|
|
|
if (max_entries < 0 || entry_sz < 0)
|
|
return -EINVAL;
|
|
|
|
if (num_entries > max_entries)
|
|
num_entries = max_entries;
|
|
|
|
ring->size = (num_entries * entry_sz) + HAL_RING_BASE_ALIGN - 1;
|
|
ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size,
|
|
&ring->paddr_unaligned,
|
|
GFP_KERNEL);
|
|
if (!ring->vaddr_unaligned)
|
|
return -ENOMEM;
|
|
|
|
ring->vaddr = PTR_ALIGN(ring->vaddr_unaligned, HAL_RING_BASE_ALIGN);
|
|
ring->paddr = ring->paddr_unaligned + ((unsigned long)ring->vaddr -
|
|
(unsigned long)ring->vaddr_unaligned);
|
|
|
|
params.ring_base_vaddr = ring->vaddr;
|
|
params.ring_base_paddr = ring->paddr;
|
|
params.num_entries = num_entries;
|
|
|
|
switch (type) {
|
|
case HAL_REO_DST:
|
|
params.intr_batch_cntr_thres_entries =
|
|
HAL_SRNG_INT_BATCH_THRESHOLD_RX;
|
|
params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX;
|
|
break;
|
|
case HAL_RXDMA_BUF:
|
|
case HAL_RXDMA_MONITOR_BUF:
|
|
case HAL_RXDMA_MONITOR_STATUS:
|
|
params.low_threshold = num_entries >> 3;
|
|
params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN;
|
|
params.intr_batch_cntr_thres_entries = 0;
|
|
params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX;
|
|
break;
|
|
case HAL_WBM2SW_RELEASE:
|
|
if (ring_num < 3) {
|
|
params.intr_batch_cntr_thres_entries =
|
|
HAL_SRNG_INT_BATCH_THRESHOLD_TX;
|
|
params.intr_timer_thres_us =
|
|
HAL_SRNG_INT_TIMER_THRESHOLD_TX;
|
|
break;
|
|
}
|
|
/* follow through when ring_num >= 3 */
|
|
/* fall through */
|
|
case HAL_REO_EXCEPTION:
|
|
case HAL_REO_REINJECT:
|
|
case HAL_REO_CMD:
|
|
case HAL_REO_STATUS:
|
|
case HAL_TCL_DATA:
|
|
case HAL_TCL_CMD:
|
|
case HAL_TCL_STATUS:
|
|
case HAL_WBM_IDLE_LINK:
|
|
case HAL_SW2WBM_RELEASE:
|
|
case HAL_RXDMA_DST:
|
|
case HAL_RXDMA_MONITOR_DST:
|
|
case HAL_RXDMA_MONITOR_DESC:
|
|
case HAL_RXDMA_DIR_BUF:
|
|
params.intr_batch_cntr_thres_entries =
|
|
HAL_SRNG_INT_BATCH_THRESHOLD_OTHER;
|
|
params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_OTHER;
|
|
break;
|
|
default:
|
|
ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms);
|
|
if (ret < 0) {
|
|
ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n",
|
|
ret, ring_num);
|
|
return ret;
|
|
}
|
|
|
|
ring->ring_id = ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
int i;
|
|
|
|
ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring);
|
|
for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) {
|
|
ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_data_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->tx_ring[i].tcl_comp_ring);
|
|
}
|
|
ath11k_dp_srng_cleanup(ab, &dp->reo_reinject_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->rx_rel_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->reo_except_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->reo_cmd_ring);
|
|
ath11k_dp_srng_cleanup(ab, &dp->reo_status_ring);
|
|
}
|
|
|
|
static int ath11k_dp_srng_common_setup(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
struct hal_srng *srng;
|
|
int i, ret;
|
|
u32 ring_hash_map;
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring,
|
|
HAL_SW2WBM_RELEASE, 0, 0,
|
|
DP_WBM_RELEASE_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up wbm2sw_release ring :%d\n",
|
|
ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->tcl_cmd_ring, HAL_TCL_CMD, 0, 0,
|
|
DP_TCL_CMD_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up tcl_cmd ring :%d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->tcl_status_ring, HAL_TCL_STATUS,
|
|
0, 0, DP_TCL_STATUS_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up tcl_status ring :%d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) {
|
|
ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_data_ring,
|
|
HAL_TCL_DATA, i, 0,
|
|
DP_TCL_DATA_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up tcl_data ring (%d) :%d\n",
|
|
i, ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->tx_ring[i].tcl_comp_ring,
|
|
HAL_WBM2SW_RELEASE, i, 0,
|
|
DP_TX_COMP_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up tcl_comp ring ring (%d) :%d\n",
|
|
i, ret);
|
|
goto err;
|
|
}
|
|
|
|
srng = &ab->hal.srng_list[dp->tx_ring[i].tcl_data_ring.ring_id];
|
|
ath11k_hal_tx_init_data_ring(ab, srng);
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->reo_reinject_ring, HAL_REO_REINJECT,
|
|
0, 0, DP_REO_REINJECT_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up reo_reinject ring :%d\n",
|
|
ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->rx_rel_ring, HAL_WBM2SW_RELEASE,
|
|
3, 0, DP_RX_RELEASE_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up rx_rel ring :%d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->reo_except_ring, HAL_REO_EXCEPTION,
|
|
0, 0, DP_REO_EXCEPTION_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up reo_exception ring :%d\n",
|
|
ret);
|
|
goto err;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->reo_cmd_ring, HAL_REO_CMD,
|
|
0, 0, DP_REO_CMD_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up reo_cmd ring :%d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
srng = &ab->hal.srng_list[dp->reo_cmd_ring.ring_id];
|
|
ath11k_hal_reo_init_cmd_ring(ab, srng);
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->reo_status_ring, HAL_REO_STATUS,
|
|
0, 0, DP_REO_STATUS_RING_SIZE);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to set up reo_status ring :%d\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
/* When hash based routing of rx packet is enabled, 32 entries to map
|
|
* the hash values to the ring will be configured. Each hash entry uses
|
|
* three bits to map to a particular ring. The ring mapping will be
|
|
* 0:TCL, 1:SW1, 2:SW2, 3:SW3, 4:SW4, 5:Release, 6:FW and 7:Not used.
|
|
*/
|
|
ring_hash_map = HAL_HASH_ROUTING_RING_SW1 << 0 |
|
|
HAL_HASH_ROUTING_RING_SW2 << 3 |
|
|
HAL_HASH_ROUTING_RING_SW3 << 6 |
|
|
HAL_HASH_ROUTING_RING_SW4 << 9 |
|
|
HAL_HASH_ROUTING_RING_SW1 << 12 |
|
|
HAL_HASH_ROUTING_RING_SW2 << 15 |
|
|
HAL_HASH_ROUTING_RING_SW3 << 18 |
|
|
HAL_HASH_ROUTING_RING_SW4 << 21;
|
|
|
|
ath11k_hal_reo_hw_setup(ab, ring_hash_map);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
ath11k_dp_srng_common_cleanup(ab);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ath11k_dp_scatter_idle_link_desc_cleanup(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
struct hal_wbm_idle_scatter_list *slist = dp->scatter_list;
|
|
int i;
|
|
|
|
for (i = 0; i < DP_IDLE_SCATTER_BUFS_MAX; i++) {
|
|
if (!slist[i].vaddr)
|
|
continue;
|
|
|
|
dma_free_coherent(ab->dev, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX,
|
|
slist[i].vaddr, slist[i].paddr);
|
|
slist[i].vaddr = NULL;
|
|
}
|
|
}
|
|
|
|
static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab,
|
|
int size,
|
|
u32 n_link_desc_bank,
|
|
u32 n_link_desc,
|
|
u32 last_bank_sz)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
struct dp_link_desc_bank *link_desc_banks = dp->link_desc_banks;
|
|
struct hal_wbm_idle_scatter_list *slist = dp->scatter_list;
|
|
u32 n_entries_per_buf;
|
|
int num_scatter_buf, scatter_idx;
|
|
struct hal_wbm_link_desc *scatter_buf;
|
|
int align_bytes, n_entries;
|
|
dma_addr_t paddr;
|
|
int rem_entries;
|
|
int i;
|
|
int ret = 0;
|
|
u32 end_offset;
|
|
|
|
n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE /
|
|
ath11k_hal_srng_get_entrysize(HAL_WBM_IDLE_LINK);
|
|
num_scatter_buf = DIV_ROUND_UP(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE);
|
|
|
|
if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < num_scatter_buf; i++) {
|
|
slist[i].vaddr = dma_alloc_coherent(ab->dev,
|
|
HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX,
|
|
&slist[i].paddr, GFP_KERNEL);
|
|
if (!slist[i].vaddr) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
scatter_idx = 0;
|
|
scatter_buf = slist[scatter_idx].vaddr;
|
|
rem_entries = n_entries_per_buf;
|
|
|
|
for (i = 0; i < n_link_desc_bank; i++) {
|
|
align_bytes = link_desc_banks[i].vaddr -
|
|
link_desc_banks[i].vaddr_unaligned;
|
|
n_entries = (DP_LINK_DESC_ALLOC_SIZE_THRESH - align_bytes) /
|
|
HAL_LINK_DESC_SIZE;
|
|
paddr = link_desc_banks[i].paddr;
|
|
while (n_entries) {
|
|
ath11k_hal_set_link_desc_addr(scatter_buf, i, paddr);
|
|
n_entries--;
|
|
paddr += HAL_LINK_DESC_SIZE;
|
|
if (rem_entries) {
|
|
rem_entries--;
|
|
scatter_buf++;
|
|
continue;
|
|
}
|
|
|
|
rem_entries = n_entries_per_buf;
|
|
scatter_idx++;
|
|
scatter_buf = slist[scatter_idx].vaddr;
|
|
}
|
|
}
|
|
|
|
end_offset = (scatter_buf - slist[scatter_idx].vaddr) *
|
|
sizeof(struct hal_wbm_link_desc);
|
|
ath11k_hal_setup_link_idle_list(ab, slist, num_scatter_buf,
|
|
n_link_desc, end_offset);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
ath11k_dp_scatter_idle_link_desc_cleanup(ab);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
ath11k_dp_link_desc_bank_free(struct ath11k_base *ab,
|
|
struct dp_link_desc_bank *link_desc_banks)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < DP_LINK_DESC_BANKS_MAX; i++) {
|
|
if (link_desc_banks[i].vaddr_unaligned) {
|
|
dma_free_coherent(ab->dev,
|
|
link_desc_banks[i].size,
|
|
link_desc_banks[i].vaddr_unaligned,
|
|
link_desc_banks[i].paddr_unaligned);
|
|
link_desc_banks[i].vaddr_unaligned = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab,
|
|
struct dp_link_desc_bank *desc_bank,
|
|
int n_link_desc_bank,
|
|
int last_bank_sz)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
int i;
|
|
int ret = 0;
|
|
int desc_sz = DP_LINK_DESC_ALLOC_SIZE_THRESH;
|
|
|
|
for (i = 0; i < n_link_desc_bank; i++) {
|
|
if (i == (n_link_desc_bank - 1) && last_bank_sz)
|
|
desc_sz = last_bank_sz;
|
|
|
|
desc_bank[i].vaddr_unaligned =
|
|
dma_alloc_coherent(ab->dev, desc_sz,
|
|
&desc_bank[i].paddr_unaligned,
|
|
GFP_KERNEL);
|
|
if (!desc_bank[i].vaddr_unaligned) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
desc_bank[i].vaddr = PTR_ALIGN(desc_bank[i].vaddr_unaligned,
|
|
HAL_LINK_DESC_ALIGN);
|
|
desc_bank[i].paddr = desc_bank[i].paddr_unaligned +
|
|
((unsigned long)desc_bank[i].vaddr -
|
|
(unsigned long)desc_bank[i].vaddr_unaligned);
|
|
desc_bank[i].size = desc_sz;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
ath11k_dp_link_desc_bank_free(ab, dp->link_desc_banks);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ath11k_dp_link_desc_cleanup(struct ath11k_base *ab,
|
|
struct dp_link_desc_bank *desc_bank,
|
|
u32 ring_type, struct dp_srng *ring)
|
|
{
|
|
ath11k_dp_link_desc_bank_free(ab, desc_bank);
|
|
|
|
if (ring_type != HAL_RXDMA_MONITOR_DESC) {
|
|
ath11k_dp_srng_cleanup(ab, ring);
|
|
ath11k_dp_scatter_idle_link_desc_cleanup(ab);
|
|
}
|
|
}
|
|
|
|
static int ath11k_wbm_idle_ring_setup(struct ath11k_base *ab, u32 *n_link_desc)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
u32 n_mpdu_link_desc, n_mpdu_queue_desc;
|
|
u32 n_tx_msdu_link_desc, n_rx_msdu_link_desc;
|
|
int ret = 0;
|
|
|
|
n_mpdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX) /
|
|
HAL_NUM_MPDUS_PER_LINK_DESC;
|
|
|
|
n_mpdu_queue_desc = n_mpdu_link_desc /
|
|
HAL_NUM_MPDU_LINKS_PER_QUEUE_DESC;
|
|
|
|
n_tx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_FLOWS_PER_TID *
|
|
DP_AVG_MSDUS_PER_FLOW) /
|
|
HAL_NUM_TX_MSDUS_PER_LINK_DESC;
|
|
|
|
n_rx_msdu_link_desc = (DP_NUM_TIDS_MAX * DP_AVG_MPDUS_PER_TID_MAX *
|
|
DP_AVG_MSDUS_PER_MPDU) /
|
|
HAL_NUM_RX_MSDUS_PER_LINK_DESC;
|
|
|
|
*n_link_desc = n_mpdu_link_desc + n_mpdu_queue_desc +
|
|
n_tx_msdu_link_desc + n_rx_msdu_link_desc;
|
|
|
|
if (*n_link_desc & (*n_link_desc - 1))
|
|
*n_link_desc = 1 << fls(*n_link_desc);
|
|
|
|
ret = ath11k_dp_srng_setup(ab, &dp->wbm_idle_ring,
|
|
HAL_WBM_IDLE_LINK, 0, 0, *n_link_desc);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret);
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ath11k_dp_link_desc_setup(struct ath11k_base *ab,
|
|
struct dp_link_desc_bank *link_desc_banks,
|
|
u32 ring_type, struct hal_srng *srng,
|
|
u32 n_link_desc)
|
|
{
|
|
u32 tot_mem_sz;
|
|
u32 n_link_desc_bank, last_bank_sz;
|
|
u32 entry_sz, align_bytes, n_entries;
|
|
u32 paddr;
|
|
u32 *desc;
|
|
int i, ret;
|
|
|
|
tot_mem_sz = n_link_desc * HAL_LINK_DESC_SIZE;
|
|
tot_mem_sz += HAL_LINK_DESC_ALIGN;
|
|
|
|
if (tot_mem_sz <= DP_LINK_DESC_ALLOC_SIZE_THRESH) {
|
|
n_link_desc_bank = 1;
|
|
last_bank_sz = tot_mem_sz;
|
|
} else {
|
|
n_link_desc_bank = tot_mem_sz /
|
|
(DP_LINK_DESC_ALLOC_SIZE_THRESH -
|
|
HAL_LINK_DESC_ALIGN);
|
|
last_bank_sz = tot_mem_sz %
|
|
(DP_LINK_DESC_ALLOC_SIZE_THRESH -
|
|
HAL_LINK_DESC_ALIGN);
|
|
|
|
if (last_bank_sz)
|
|
n_link_desc_bank += 1;
|
|
}
|
|
|
|
if (n_link_desc_bank > DP_LINK_DESC_BANKS_MAX)
|
|
return -EINVAL;
|
|
|
|
ret = ath11k_dp_link_desc_bank_alloc(ab, link_desc_banks,
|
|
n_link_desc_bank, last_bank_sz);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Setup link desc idle list for HW internal usage */
|
|
entry_sz = ath11k_hal_srng_get_entrysize(ring_type);
|
|
tot_mem_sz = entry_sz * n_link_desc;
|
|
|
|
/* Setup scatter desc list when the total memory requirement is more */
|
|
if (tot_mem_sz > DP_LINK_DESC_ALLOC_SIZE_THRESH &&
|
|
ring_type != HAL_RXDMA_MONITOR_DESC) {
|
|
ret = ath11k_dp_scatter_idle_link_desc_setup(ab, tot_mem_sz,
|
|
n_link_desc_bank,
|
|
n_link_desc,
|
|
last_bank_sz);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to setup scatting idle list descriptor :%d\n",
|
|
ret);
|
|
goto fail_desc_bank_free;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
spin_lock_bh(&srng->lock);
|
|
|
|
ath11k_hal_srng_access_begin(ab, srng);
|
|
|
|
for (i = 0; i < n_link_desc_bank; i++) {
|
|
align_bytes = link_desc_banks[i].vaddr -
|
|
link_desc_banks[i].vaddr_unaligned;
|
|
n_entries = (link_desc_banks[i].size - align_bytes) /
|
|
HAL_LINK_DESC_SIZE;
|
|
paddr = link_desc_banks[i].paddr;
|
|
while (n_entries &&
|
|
(desc = ath11k_hal_srng_src_get_next_entry(ab, srng))) {
|
|
ath11k_hal_set_link_desc_addr((struct hal_wbm_link_desc *)desc,
|
|
i, paddr);
|
|
n_entries--;
|
|
paddr += HAL_LINK_DESC_SIZE;
|
|
}
|
|
}
|
|
|
|
ath11k_hal_srng_access_end(ab, srng);
|
|
|
|
spin_unlock_bh(&srng->lock);
|
|
|
|
return 0;
|
|
|
|
fail_desc_bank_free:
|
|
ath11k_dp_link_desc_bank_free(ab, link_desc_banks);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ath11k_dp_service_srng(struct ath11k_base *ab,
|
|
struct ath11k_ext_irq_grp *irq_grp,
|
|
int budget)
|
|
{
|
|
struct napi_struct *napi = &irq_grp->napi;
|
|
int grp_id = irq_grp->grp_id;
|
|
int work_done = 0;
|
|
int i = 0;
|
|
int tot_work_done = 0;
|
|
|
|
while (ath11k_tx_ring_mask[grp_id] >> i) {
|
|
if (ath11k_tx_ring_mask[grp_id] & BIT(i))
|
|
ath11k_dp_tx_completion_handler(ab, i);
|
|
i++;
|
|
}
|
|
|
|
if (ath11k_rx_err_ring_mask[grp_id]) {
|
|
work_done = ath11k_dp_process_rx_err(ab, napi, budget);
|
|
budget -= work_done;
|
|
tot_work_done += work_done;
|
|
if (budget <= 0)
|
|
goto done;
|
|
}
|
|
|
|
if (ath11k_rx_wbm_rel_ring_mask[grp_id]) {
|
|
work_done = ath11k_dp_rx_process_wbm_err(ab,
|
|
napi,
|
|
budget);
|
|
budget -= work_done;
|
|
tot_work_done += work_done;
|
|
|
|
if (budget <= 0)
|
|
goto done;
|
|
}
|
|
|
|
if (ath11k_rx_ring_mask[grp_id]) {
|
|
i = fls(ath11k_rx_ring_mask[grp_id]) - 1;
|
|
work_done = ath11k_dp_process_rx(ab, i, napi,
|
|
budget);
|
|
budget -= work_done;
|
|
tot_work_done += work_done;
|
|
if (budget <= 0)
|
|
goto done;
|
|
}
|
|
|
|
if (rx_mon_status_ring_mask[grp_id]) {
|
|
for (i = 0; i < ab->num_radios; i++) {
|
|
if (rx_mon_status_ring_mask[grp_id] & BIT(i)) {
|
|
work_done =
|
|
ath11k_dp_rx_process_mon_rings(ab,
|
|
i, napi,
|
|
budget);
|
|
budget -= work_done;
|
|
tot_work_done += work_done;
|
|
}
|
|
if (budget <= 0)
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (ath11k_reo_status_ring_mask[grp_id])
|
|
ath11k_dp_process_reo_status(ab);
|
|
|
|
for (i = 0; i < ab->num_radios; i++) {
|
|
if (ath11k_rxdma2host_ring_mask[grp_id] & BIT(i)) {
|
|
work_done = ath11k_dp_process_rxdma_err(ab, i, budget);
|
|
budget -= work_done;
|
|
tot_work_done += work_done;
|
|
}
|
|
|
|
if (budget <= 0)
|
|
goto done;
|
|
|
|
if (ath11k_host2rxdma_ring_mask[grp_id] & BIT(i)) {
|
|
struct ath11k_pdev_dp *dp = &ab->pdevs[i].ar->dp;
|
|
struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
|
|
|
|
ath11k_dp_rxbufs_replenish(ab, i, rx_ring, 0,
|
|
HAL_RX_BUF_RBM_SW3_BM,
|
|
GFP_ATOMIC);
|
|
}
|
|
}
|
|
/* TODO: Implement handler for other interrupts */
|
|
|
|
done:
|
|
return tot_work_done;
|
|
}
|
|
|
|
void ath11k_dp_pdev_free(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k *ar;
|
|
int i;
|
|
|
|
for (i = 0; i < ab->num_radios; i++) {
|
|
ar = ab->pdevs[i].ar;
|
|
ath11k_dp_rx_pdev_free(ab, i);
|
|
ath11k_debug_unregister(ar);
|
|
ath11k_dp_rx_pdev_mon_detach(ar);
|
|
}
|
|
}
|
|
|
|
void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k *ar;
|
|
struct ath11k_pdev_dp *dp;
|
|
int i;
|
|
|
|
for (i = 0; i < ab->num_radios; i++) {
|
|
ar = ab->pdevs[i].ar;
|
|
dp = &ar->dp;
|
|
dp->mac_id = i;
|
|
idr_init(&dp->rx_refill_buf_ring.bufs_idr);
|
|
spin_lock_init(&dp->rx_refill_buf_ring.idr_lock);
|
|
atomic_set(&dp->num_tx_pending, 0);
|
|
init_waitqueue_head(&dp->tx_empty_waitq);
|
|
idr_init(&dp->rx_mon_status_refill_ring.bufs_idr);
|
|
spin_lock_init(&dp->rx_mon_status_refill_ring.idr_lock);
|
|
idr_init(&dp->rxdma_mon_buf_ring.bufs_idr);
|
|
spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock);
|
|
}
|
|
}
|
|
|
|
int ath11k_dp_pdev_alloc(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k *ar;
|
|
int ret;
|
|
int i;
|
|
|
|
/* TODO:Per-pdev rx ring unlike tx ring which is mapped to different AC's */
|
|
for (i = 0; i < ab->num_radios; i++) {
|
|
ar = ab->pdevs[i].ar;
|
|
ret = ath11k_dp_rx_pdev_alloc(ab, i);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to allocate pdev rx for pdev_id :%d\n",
|
|
i);
|
|
goto err;
|
|
}
|
|
ret = ath11k_dp_rx_pdev_mon_attach(ar);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to initialize mon pdev %d\n",
|
|
i);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
ath11k_dp_pdev_free(ab);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ath11k_dp_htt_connect(struct ath11k_dp *dp)
|
|
{
|
|
struct ath11k_htc_svc_conn_req conn_req;
|
|
struct ath11k_htc_svc_conn_resp conn_resp;
|
|
int status;
|
|
|
|
memset(&conn_req, 0, sizeof(conn_req));
|
|
memset(&conn_resp, 0, sizeof(conn_resp));
|
|
|
|
conn_req.ep_ops.ep_tx_complete = ath11k_dp_htt_htc_tx_complete;
|
|
conn_req.ep_ops.ep_rx_complete = ath11k_dp_htt_htc_t2h_msg_handler;
|
|
|
|
/* connect to control service */
|
|
conn_req.service_id = ATH11K_HTC_SVC_ID_HTT_DATA_MSG;
|
|
|
|
status = ath11k_htc_connect_service(&dp->ab->htc, &conn_req,
|
|
&conn_resp);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
dp->eid = conn_resp.eid;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif)
|
|
{
|
|
/* For STA mode, enable address search index,
|
|
* tcl uses ast_hash value in the descriptor.
|
|
*/
|
|
switch (arvif->vdev_type) {
|
|
case WMI_VDEV_TYPE_STA:
|
|
arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN;
|
|
arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX;
|
|
break;
|
|
case WMI_VDEV_TYPE_AP:
|
|
case WMI_VDEV_TYPE_IBSS:
|
|
arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN;
|
|
arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT;
|
|
break;
|
|
case WMI_VDEV_TYPE_MONITOR:
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ath11k_dp_vdev_tx_attach(struct ath11k *ar, struct ath11k_vif *arvif)
|
|
{
|
|
arvif->tcl_metadata |= FIELD_PREP(HTT_TCL_META_DATA_TYPE, 1) |
|
|
FIELD_PREP(HTT_TCL_META_DATA_VDEV_ID,
|
|
arvif->vdev_id) |
|
|
FIELD_PREP(HTT_TCL_META_DATA_PDEV_ID,
|
|
ar->pdev->pdev_id);
|
|
|
|
/* set HTT extension valid bit to 0 by default */
|
|
arvif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT;
|
|
|
|
ath11k_dp_update_vdev_search(arvif);
|
|
}
|
|
|
|
static int ath11k_dp_tx_pending_cleanup(int buf_id, void *skb, void *ctx)
|
|
{
|
|
struct ath11k_base *ab = (struct ath11k_base *)ctx;
|
|
struct sk_buff *msdu = skb;
|
|
|
|
dma_unmap_single(ab->dev, ATH11K_SKB_CB(msdu)->paddr, msdu->len,
|
|
DMA_TO_DEVICE);
|
|
|
|
dev_kfree_skb_any(msdu);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ath11k_dp_free(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
int i;
|
|
|
|
ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks,
|
|
HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring);
|
|
|
|
ath11k_dp_srng_common_cleanup(ab);
|
|
|
|
ath11k_dp_reo_cmd_list_cleanup(ab);
|
|
|
|
for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) {
|
|
spin_lock_bh(&dp->tx_ring[i].tx_idr_lock);
|
|
idr_for_each(&dp->tx_ring[i].txbuf_idr,
|
|
ath11k_dp_tx_pending_cleanup, ab);
|
|
idr_destroy(&dp->tx_ring[i].txbuf_idr);
|
|
spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock);
|
|
kfree(dp->tx_ring[i].tx_status);
|
|
}
|
|
|
|
/* Deinit any SOC level resource */
|
|
}
|
|
|
|
int ath11k_dp_alloc(struct ath11k_base *ab)
|
|
{
|
|
struct ath11k_dp *dp = &ab->dp;
|
|
struct hal_srng *srng = NULL;
|
|
size_t size = 0;
|
|
u32 n_link_desc = 0;
|
|
int ret;
|
|
int i;
|
|
|
|
dp->ab = ab;
|
|
|
|
INIT_LIST_HEAD(&dp->reo_cmd_list);
|
|
INIT_LIST_HEAD(&dp->reo_cmd_cache_flush_list);
|
|
spin_lock_init(&dp->reo_cmd_lock);
|
|
|
|
ret = ath11k_wbm_idle_ring_setup(ab, &n_link_desc);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
srng = &ab->hal.srng_list[dp->wbm_idle_ring.ring_id];
|
|
|
|
ret = ath11k_dp_link_desc_setup(ab, dp->link_desc_banks,
|
|
HAL_WBM_IDLE_LINK, srng, n_link_desc);
|
|
if (ret) {
|
|
ath11k_warn(ab, "failed to setup link desc: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = ath11k_dp_srng_common_setup(ab);
|
|
if (ret)
|
|
goto fail_link_desc_cleanup;
|
|
|
|
size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE;
|
|
|
|
for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) {
|
|
idr_init(&dp->tx_ring[i].txbuf_idr);
|
|
spin_lock_init(&dp->tx_ring[i].tx_idr_lock);
|
|
dp->tx_ring[i].tcl_data_ring_id = i;
|
|
|
|
dp->tx_ring[i].tx_status_head = 0;
|
|
dp->tx_ring[i].tx_status_tail = DP_TX_COMP_RING_SIZE - 1;
|
|
dp->tx_ring[i].tx_status = kmalloc(size, GFP_KERNEL);
|
|
if (!dp->tx_ring[i].tx_status)
|
|
goto fail_cmn_srng_cleanup;
|
|
}
|
|
|
|
for (i = 0; i < HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX; i++)
|
|
ath11k_hal_tx_set_dscp_tid_map(ab, i);
|
|
|
|
/* Init any SOC level resource for DP */
|
|
|
|
return 0;
|
|
|
|
fail_cmn_srng_cleanup:
|
|
ath11k_dp_srng_common_cleanup(ab);
|
|
|
|
fail_link_desc_cleanup:
|
|
ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks,
|
|
HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring);
|
|
|
|
return ret;
|
|
}
|