// SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/ieee80211.h>
        }
 }
 
+static enum dp_mon_status_buf_state
+ath11k_dp_rx_mon_buf_done(struct ath11k_base *ab, struct hal_srng *srng,
+                         struct dp_rxdma_ring *rx_ring)
+{
+       struct ath11k_skb_rxcb *rxcb;
+       struct hal_tlv_hdr *tlv;
+       struct sk_buff *skb;
+       void *status_desc;
+       dma_addr_t paddr;
+       u32 cookie;
+       int buf_id;
+       u8 rbm;
+
+       status_desc = ath11k_hal_srng_src_next_peek(ab, srng);
+       if (!status_desc)
+               return DP_MON_STATUS_NO_DMA;
+
+       ath11k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm);
+
+       buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie);
+
+       spin_lock_bh(&rx_ring->idr_lock);
+       skb = idr_find(&rx_ring->bufs_idr, buf_id);
+       spin_unlock_bh(&rx_ring->idr_lock);
+
+       if (!skb)
+               return DP_MON_STATUS_NO_DMA;
+
+       rxcb = ATH11K_SKB_RXCB(skb);
+       dma_sync_single_for_cpu(ab->dev, rxcb->paddr,
+                               skb->len + skb_tailroom(skb),
+                               DMA_FROM_DEVICE);
+
+       tlv = (struct hal_tlv_hdr *)skb->data;
+       if (FIELD_GET(HAL_TLV_HDR_TAG, tlv->tl) != HAL_RX_STATUS_BUFFER_DONE)
+               return DP_MON_STATUS_NO_DMA;
+
+       return DP_MON_STATUS_REPLINISH;
+}
+
 static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id,
                                             int *budget, struct sk_buff_head *skb_list)
 {
        struct ath11k *ar;
        const struct ath11k_hw_hal_params *hal_params;
+       enum dp_mon_status_buf_state reap_status;
        struct ath11k_pdev_dp *dp;
        struct dp_rxdma_ring *rx_ring;
        struct ath11k_mon_data *pmon;
                                ath11k_warn(ab, "mon status DONE not set %lx, buf_id %d\n",
                                            FIELD_GET(HAL_TLV_HDR_TAG,
                                                      tlv->tl), buf_id);
-                               /* If done status is missing, hold onto status
-                                * ring until status is done for this status
-                                * ring buffer.
-                                * Keep HP in mon_status_ring unchanged,
-                                * and break from here.
-                                * Check status for same buffer for next time
+                               /* RxDMA status done bit might not be set even
+                                * though tp is moved by HW.
                                 */
-                               pmon->buf_state = DP_MON_STATUS_NO_DMA;
-                               break;
+
+                               /* If done status is missing:
+                                * 1. As per MAC team's suggestion,
+                                *    when HP + 1 entry is peeked and if DMA
+                                *    is not done and if HP + 2 entry's DMA done
+                                *    is set. skip HP + 1 entry and
+                                *    start processing in next interrupt.
+                                * 2. If HP + 2 entry's DMA done is not set,
+                                *    poll onto HP + 1 entry DMA done to be set.
+                                *    Check status for same buffer for next time
+                                *    dp_rx_mon_status_srng_process
+                                */
+
+                               reap_status = ath11k_dp_rx_mon_buf_done(ab, srng,
+                                                                       rx_ring);
+                               if (reap_status == DP_MON_STATUS_NO_DMA)
+                                       continue;
+
+                               spin_lock_bh(&rx_ring->idr_lock);
+                               idr_remove(&rx_ring->bufs_idr, buf_id);
+                               spin_unlock_bh(&rx_ring->idr_lock);
+
+                               dma_unmap_single(ab->dev, rxcb->paddr,
+                                                skb->len + skb_tailroom(skb),
+                                                DMA_FROM_DEVICE);
+
+                               dev_kfree_skb_any(skb);
+                               pmon->buf_state = DP_MON_STATUS_REPLINISH;
+                               goto move_next;
                        }
 
                        spin_lock_bh(&rx_ring->idr_lock);
 
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #include <linux/dma-mapping.h>
 #include "hal_tx.h"
        return desc;
 }
 
+u32 *ath11k_hal_srng_src_next_peek(struct ath11k_base *ab, struct hal_srng *srng)
+{
+       u32 next_hp;
+
+       lockdep_assert_held(&srng->lock);
+
+       next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size;
+
+       if (next_hp != srng->u.src_ring.cached_tp)
+               return srng->ring_base_vaddr + next_hp;
+
+       return NULL;
+}
+
 u32 *ath11k_hal_srng_src_peek(struct ath11k_base *ab, struct hal_srng *srng)
 {
        lockdep_assert_held(&srng->lock);