[PATCH ath-next v3 11/13] wifi: ath12k: ng: add RX TID REO helpers
From: Pradeep Kumar Chitrapu
Date: Tue Nov 03 2026 - 20:17:31 EST
The common RX TID path routes queue setup, update, flush and teardown
through architecture hooks. Wi-Fi 8 needs matching helpers before those
hooks can be enabled for QCN9625.
Add helpers for queue update, qref programming, PN configuration, flush
handling and fragment cleanup. Keep the helpers disconnected until the
final wiring patch so intermediate commits do not expose a partial RX TID
implementation.
Tested-on: Compilation only, M=drivers/net/wireless/ath/ath12k
Signed-off-by: Pradeep Kumar Chitrapu <pradeep.chitrapu@xxxxxxxxxxxxxxxx>
---
drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c | 288 +++++++++++++++++++++++++-
drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h | 31 +++
2 files changed, 315 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c
index 14117430c43b..0b8a9d32e125 100644
--- a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.c
@@ -283,6 +283,287 @@ int ath12k_wifi8_dp_reo_cmd_send_highprio_n(struct ath12k_base *ab,
dp_descs, n);
}
+static struct ath12k_reo_queue_ref *
+ath12k_wifi8_peer_rx_tid_qref(struct ath12k_dp *dp, u16 peer_id, u16 tid)
+{
+ struct ath12k_reo_q_addr_lut *lut = &dp->reoq_lut;
+
+ if (!lut->vaddr)
+ return NULL;
+
+ peer_id &= ~ATH12K_PEER_ML_ID_VALID;
+
+ return (struct ath12k_reo_queue_ref *)lut->vaddr +
+ peer_id * (IEEE80211_NUM_TIDS + 1) + tid;
+}
+
+void ath12k_wifi8_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id,
+ u16 tid, dma_addr_t paddr)
+{
+ struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+ struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
+ struct ath12k_base *central_ab = central_dp->ab;
+ struct ath12k_reo_queue_ref *qref;
+
+ if (!central_ab->hw_params->reoq_lut_support)
+ return;
+
+ if ((peer_id & ~ATH12K_PEER_ML_ID_VALID) > DP_MAX_PEER_ID) {
+ ath12k_warn(central_ab, "peer id %d exceeds reo qref table\n",
+ peer_id);
+ return;
+ }
+
+ qref = ath12k_wifi8_peer_rx_tid_qref(central_dp, peer_id, tid);
+ if (!qref) {
+ ath12k_warn(central_ab, "reo qref table is not setup\n");
+ return;
+ }
+
+ qref->info0 = u32_encode_bits(lower_32_bits(paddr),
+ BUFFER_ADDR_INFO0_ADDR);
+ qref->info1 = u32_encode_bits(upper_32_bits(paddr),
+ BUFFER_ADDR_INFO1_ADDR) |
+ u32_encode_bits(tid, DP_REO_QREF_NUM);
+
+ ath12k_hal_reo_shared_qaddr_cache_clear(central_ab);
+}
+
+void ath12k_wifi8_peer_rx_tid_qref_reset(struct ath12k_base *ab, u16 peer_id,
+ u16 tid)
+{
+ struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+ struct ath12k_dp *central_dp = ath12k_get_central_dp(dp);
+ struct ath12k_base *central_ab = central_dp->ab;
+ struct ath12k_reo_queue_ref *qref;
+
+ if (!central_ab->hw_params->reoq_lut_support)
+ return;
+
+ if ((peer_id & ~ATH12K_PEER_ML_ID_VALID) > DP_MAX_PEER_ID) {
+ ath12k_warn(central_ab, "peer id %d exceeds reo qref table\n",
+ peer_id);
+ return;
+ }
+
+ qref = ath12k_wifi8_peer_rx_tid_qref(central_dp, peer_id, tid);
+ if (!qref) {
+ ath12k_warn(central_ab, "reo qref table is not setup\n");
+ return;
+ }
+
+ qref->info0 = u32_encode_bits(0, BUFFER_ADDR_INFO0_ADDR);
+ qref->info1 = u32_encode_bits(0, BUFFER_ADDR_INFO1_ADDR) |
+ u32_encode_bits(tid, DP_REO_QREF_NUM);
+
+ ath12k_hal_reo_shared_qaddr_cache_clear(central_ab);
+}
+
+void ath12k_wifi8_dp_rx_peer_tid_delete(struct ath12k_base *ab,
+ struct ath12k_dp_link_peer *peer,
+ u8 tid)
+{
+ struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+
+ if (!(peer->rx_tid_active_bitmask & BIT(tid)))
+ return;
+
+ ath12k_dp_mark_tid_as_inactive(dp, peer->peer_id, tid);
+ ath12k_dp_rx_process_reo_cmd_update_rx_queue_list(dp);
+}
+
+int ath12k_wifi8_peer_rx_tid_reo_update(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u32 ba_win_sz, u16 ssn,
+ bool update_ssn)
+{
+ struct ath12k_dp_rx_tid_rxq rx_tid_rxq;
+ struct ath12k_hal_reo_cmd cmd = {};
+ struct ath12k_base *ab = dp->ab;
+ int ret;
+
+ ath12k_dp_init_rx_tid_rxq(&rx_tid_rxq, rx_tid,
+ !!(peer->rx_tid_active_bitmask &
+ BIT(rx_tid->tid)));
+
+ cmd.addr_lo = lower_32_bits(rx_tid_rxq.qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid_rxq.qbuf.paddr_aligned);
+ cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
+ cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE;
+ cmd.ba_window_size = ba_win_sz;
+
+ if (update_ssn) {
+ cmd.upd0 |= HAL_REO_CMD_UPD0_SSN;
+ cmd.upd2 = u32_encode_bits(ssn, HAL_REO_CMD_UPD2_SSN);
+ }
+
+ ret = ath12k_wifi8_dp_reo_cmd_send(ab, &rx_tid_rxq,
+ sizeof(rx_tid_rxq),
+ HAL_REO_CMD_UPDATE_RX_QUEUE,
+ &cmd, NULL);
+ if (ret) {
+ ath12k_warn(ab, "failed to update rx tid queue, tid %d (%d)\n",
+ rx_tid_rxq.tid, ret);
+ return ret;
+ }
+
+ rx_tid->ba_win_sz = ba_win_sz;
+
+ ath12k_dbg(ab, ATH12K_DBG_DP_RX,
+ "rx tid queue update successful for tid: %u, peer_id: %d, peer: %pM, link_id: %u ba_win_sz: %u\n",
+ rx_tid->tid, peer->peer_id, peer->addr, peer->link_id,
+ rx_tid->ba_win_sz);
+
+ return 0;
+}
+
+int ath12k_wifi8_dp_reo_cache_flush(struct ath12k_base *ab,
+ struct ath12k_dp_rx_tid_rxq *rx_tid)
+{
+ struct ath12k_hal_reo_cmd cmd = {};
+
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS |
+ HAL_REO_CMD_FLG_FLUSH_FWD_ALL_MPDUS;
+
+ if (rx_tid->tid != HAL_DESC_REO_NON_QOS_TID)
+ cmd.flag |= HAL_REO_CMD_FLG_FLUSH_QUEUE_1K_DESC;
+
+ return ath12k_wifi8_dp_reo_cmd_send(ab, rx_tid, sizeof(*rx_tid),
+ HAL_REO_CMD_FLUSH_CACHE, &cmd,
+ ath12k_dp_reo_cmd_free);
+}
+
+int ath12k_wifi8_dp_rx_assign_reoq(struct ath12k_base *ab,
+ struct ath12k_dp_peer *dp_peer,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u16 ssn, enum hal_pn_type pn_type)
+{
+ u32 ba_win_sz = rx_tid->ba_win_sz;
+ struct ath12k_reoq_buf *buf;
+ void *vaddr, *vaddr_aligned;
+ dma_addr_t paddr_aligned;
+ u8 tid = rx_tid->tid;
+ u32 hw_desc_sz;
+ int ret;
+
+ buf = &dp_peer->reoq_bufs[tid];
+ if (!buf->vaddr) {
+ if (tid == HAL_DESC_REO_NON_QOS_TID)
+ hw_desc_sz = ath12k_wifi8_hal_reo_qdesc_size(ba_win_sz,
+ tid);
+ else
+ hw_desc_sz = ath12k_wifi8_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX,
+ tid);
+
+ vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1,
+ GFP_ATOMIC);
+ if (!vaddr)
+ return -ENOMEM;
+
+ vaddr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN);
+
+ ath12k_wifi8_hal_reo_qdesc_setup(vaddr_aligned, tid,
+ ba_win_sz, ssn, pn_type);
+
+ paddr_aligned = dma_map_single(ab->dev, vaddr_aligned,
+ hw_desc_sz,
+ DMA_BIDIRECTIONAL);
+ ret = dma_mapping_error(ab->dev, paddr_aligned);
+ if (ret) {
+ kfree(vaddr);
+ return ret;
+ }
+
+ buf->vaddr = vaddr;
+ buf->paddr_aligned = paddr_aligned;
+ buf->size = hw_desc_sz;
+ }
+
+ rx_tid->qbuf = *buf;
+
+ return 0;
+}
+
+int ath12k_wifi8_dp_rx_tid_delete_handler(struct ath12k_base *ab,
+ struct ath12k_dp_rx_tid_rxq *rx_tid)
+{
+ struct ath12k_hal_reo_cmd cmd = {};
+
+ cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS;
+ cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
+ /* Keep the queue descriptor valid until the delete completion path
+ * flushes it from the REO cache. Clearing VLD here can make the
+ * subsequent flush-cache command fail.
+ */
+ cmd.upd0 |= HAL_REO_CMD_UPD0_VLD;
+ cmd.upd1 |= HAL_REO_CMD_UPD1_VLD;
+
+ return ath12k_wifi8_dp_reo_cmd_send(ab, rx_tid, sizeof(*rx_tid),
+ HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd,
+ ath12k_dp_rx_tid_del_func);
+}
+
+void ath12k_wifi8_dp_rx_frags_cleanup(struct ath12k_dp_rx_tid *rx_tid,
+ bool rel_link_desc)
+{
+ enum hal_wbm_rel_bm_act act = HAL_WBM_REL_BM_ACT_PUT_IN_IDLE;
+ struct ath12k_buffer_addr *buf_addr_info;
+ struct ath12k_dp *dp = rx_tid->dp;
+
+ lockdep_assert_held(&dp->dp_lock);
+
+ if (rx_tid->dst_ring_desc) {
+ if (rel_link_desc) {
+ buf_addr_info = &rx_tid->dst_ring_desc->buf_addr_info;
+ ath12k_wifi8_dp_rx_link_desc_return(dp, buf_addr_info,
+ act);
+ }
+
+ kfree(rx_tid->dst_ring_desc);
+ rx_tid->dst_ring_desc = NULL;
+ }
+
+ rx_tid->cur_sn = 0;
+ rx_tid->last_frag_no = 0;
+ rx_tid->rx_frag_bitmap = 0;
+ __skb_queue_purge(&rx_tid->rx_frags);
+}
+
+void ath12k_wifi8_dp_setup_pn_check_reo_cmd(struct ath12k_hal_reo_cmd *cmd,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u32 cipher,
+ enum set_key_cmd key_cmd)
+{
+ cmd->flag = HAL_REO_CMD_FLG_NEED_STATUS;
+ cmd->upd0 = HAL_REO_CMD_UPD0_PN |
+ HAL_REO_CMD_UPD0_PN_SIZE |
+ HAL_REO_CMD_UPD0_PN_VALID |
+ HAL_REO_CMD_UPD0_PN_CHECK |
+ HAL_REO_CMD_UPD0_SVLD;
+
+ switch (cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ if (key_cmd == SET_KEY) {
+ cmd->upd1 |= HAL_REO_CMD_UPD1_PN_CHECK;
+ cmd->pn_size = 48;
+ }
+ break;
+ default:
+ break;
+ }
+
+ cmd->addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned);
+ cmd->addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned);
+}
+
/**
* ath12k_wifi8_rx_spd_buf_alloc - allocate per-ring SPD scratch-pad arrays
* @dp_hw_grp_wifi8: Wi-Fi 8 hardware-group private data
@@ -1318,10 +1599,9 @@ ath12k_wifi8_hal_rx_msdu_link_info_get(struct hal_wifi8_rx_msdu_link *link,
* ath12k_wifi8_dp_rx_link_desc_return - return a link descriptor to the WBM
* idle pool.
*/
-static int
-ath12k_wifi8_dp_rx_link_desc_return(struct ath12k_dp *dp,
- struct ath12k_buffer_addr *buf_addr_info,
- enum hal_wbm_rel_bm_act action)
+int ath12k_wifi8_dp_rx_link_desc_return(struct ath12k_dp *dp,
+ struct ath12k_buffer_addr *buf_addr_info,
+ enum hal_wbm_rel_bm_act action)
{
struct ath12k_base *ab = dp->ab;
struct hal_wbm_release_ring *desc;
diff --git a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h
index 2fb56776adb0..c5ee01e703a5 100644
--- a/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h
+++ b/drivers/net/wireless/ath/ath12k/wifi8/dp_rx.h
@@ -8,6 +8,7 @@
#define ATH12K_DP_RX_WIFI8_H
#include "../core.h"
+#include "../dp_rx.h"
#include "hal_desc.h"
struct ath12k_dp_hw_group_wifi8;
@@ -171,6 +172,36 @@ int ath12k_wifi8_dp_reo_cmd_send_highprio_n(struct ath12k_base *ab,
struct ath12k_reo_cmd_entry *entries,
struct ath12k_reo_dp_cmd_desc *dp_descs,
int n);
+void ath12k_wifi8_dp_setup_pn_check_reo_cmd(struct ath12k_hal_reo_cmd *cmd,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u32 cipher,
+ enum set_key_cmd key_cmd);
+int ath12k_wifi8_dp_rx_assign_reoq(struct ath12k_base *ab,
+ struct ath12k_dp_peer *dp_peer,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u16 ssn, enum hal_pn_type pn_type);
+int ath12k_wifi8_dp_rx_link_desc_return(struct ath12k_dp *dp,
+ struct ath12k_buffer_addr *buf_addr_info,
+ enum hal_wbm_rel_bm_act action);
+void ath12k_wifi8_dp_rx_frags_cleanup(struct ath12k_dp_rx_tid *rx_tid,
+ bool rel_link_desc);
+void ath12k_wifi8_peer_rx_tid_qref_setup(struct ath12k_base *ab,
+ u16 peer_id, u16 tid,
+ dma_addr_t paddr);
+void ath12k_wifi8_dp_rx_peer_tid_delete(struct ath12k_base *ab,
+ struct ath12k_dp_link_peer *peer,
+ u8 tid);
+int ath12k_wifi8_dp_reo_cache_flush(struct ath12k_base *ab,
+ struct ath12k_dp_rx_tid_rxq *rx_tid);
+int ath12k_wifi8_peer_rx_tid_reo_update(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer,
+ struct ath12k_dp_rx_tid *rx_tid,
+ u32 ba_win_sz, u16 ssn,
+ bool update_ssn);
+void ath12k_wifi8_peer_rx_tid_qref_reset(struct ath12k_base *ab,
+ u16 peer_id, u16 tid);
+int ath12k_wifi8_dp_rx_tid_delete_handler(struct ath12k_base *ab,
+ struct ath12k_dp_rx_tid_rxq *rx_tid);
int ath12k_wifi8_dp_rx_ring_setup(struct ath12k_base *ab);
int ath12k_wifi8_dp_rx_ring_free(struct ath12k_base *ab);
int ath12k_wifi8_dp_rxdma_ring_sel_config_qcn9625(struct ath12k_base *ab);
--
2.43.0