#include "trace.h"
 #include "sdio.h"
 
+#define ATH10K_SDIO_VSG_BUF_SIZE       (32 * 1024)
+
 /* inlined helper functions */
 
 static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio,
        return ret;
 }
 
-static int ath10k_sdio_mbox_alloc_pkt_bundle(struct ath10k *ar,
-                                            struct ath10k_sdio_rx_data *rx_pkts,
-                                            struct ath10k_htc_hdr *htc_hdr,
-                                            size_t full_len, size_t act_len,
-                                            size_t *bndl_cnt)
+static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar,
+                                        struct ath10k_sdio_rx_data *rx_pkts,
+                                        struct ath10k_htc_hdr *htc_hdr,
+                                        size_t full_len, size_t act_len,
+                                        size_t *bndl_cnt)
 {
        int ret, i;
 
        size_t full_len, act_len;
        bool last_in_bundle;
        int ret, i;
+       int pkt_cnt = 0;
 
        if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
                ath10k_warn(ar,
                         */
                        size_t bndl_cnt;
 
-                       ret = ath10k_sdio_mbox_alloc_pkt_bundle(ar,
-                                                               &ar_sdio->rx_pkts[i],
-                                                               htc_hdr,
-                                                               full_len,
-                                                               act_len,
-                                                               &bndl_cnt);
+                       ret = ath10k_sdio_mbox_alloc_bundle(ar,
+                                                           &ar_sdio->rx_pkts[pkt_cnt],
+                                                           htc_hdr,
+                                                           full_len,
+                                                           act_len,
+                                                           &bndl_cnt);
 
                        if (ret) {
                                ath10k_warn(ar, "alloc_bundle error %d\n", ret);
                                goto err;
                        }
 
-                       n_lookaheads += bndl_cnt;
-                       i += bndl_cnt;
+                       pkt_cnt += bndl_cnt;
                        /*Next buffer will be the last in the bundle */
                        last_in_bundle = true;
                }
                if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK)
                        full_len += ATH10K_HIF_MBOX_BLOCK_SIZE;
 
-               ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i],
+               ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt],
                                                    act_len,
                                                    full_len,
                                                    last_in_bundle,
                        ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret);
                        goto err;
                }
+
+               pkt_cnt++;
        }
 
-       ar_sdio->n_rx_pkts = i;
+       ar_sdio->n_rx_pkts = pkt_cnt;
 
        return 0;
 
        return ret;
 }
 
-static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar,
-                                     struct ath10k_sdio_rx_data *pkt)
+static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
 {
        struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+       struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0];
        struct sk_buff *skb = pkt->skb;
        struct ath10k_htc_hdr *htc_hdr;
        int ret;
 
        ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
                                 skb->data, pkt->alloc_len);
-       if (ret)
-               goto out;
 
-       /* Update actual length. The original length may be incorrect,
-        * as the FW will bundle multiple packets as long as their sizes
-        * fit within the same aligned length (pkt->alloc_len).
-        */
-       htc_hdr = (struct ath10k_htc_hdr *)skb->data;
-       pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
-       if (pkt->act_len > pkt->alloc_len) {
-               ath10k_warn(ar, "rx packet too large (%zu > %zu)\n",
-                           pkt->act_len, pkt->alloc_len);
-               ret = -EMSGSIZE;
-               goto out;
+       if (ret) {
+               ar_sdio->n_rx_pkts = 0;
+               ath10k_sdio_mbox_free_rx_pkt(pkt);
+               return ret;
        }
 
-       skb_put(skb, pkt->act_len);
-
-out:
+       htc_hdr = (struct ath10k_htc_hdr *)skb->data;
+       pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
        pkt->status = ret;
+       skb_put(skb, pkt->act_len);
 
        return ret;
 }
 
-static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
+static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar)
 {
        struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+       struct ath10k_sdio_rx_data *pkt;
+       struct ath10k_htc_hdr *htc_hdr;
        int ret, i;
+       u32 pkt_offset, virt_pkt_len;
 
+       virt_pkt_len = 0;
+       for (i = 0; i < ar_sdio->n_rx_pkts; i++)
+               virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len;
+
+       if (virt_pkt_len > ATH10K_SDIO_VSG_BUF_SIZE) {
+               ath10k_warn(ar, "sdio vsg buffer size limit: %d\n", virt_pkt_len);
+               ret = -E2BIG;
+               goto err;
+       }
+
+       ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
+                                ar_sdio->vsg_buffer, virt_pkt_len);
+       if (ret) {
+               ath10k_warn(ar, "failed to read bundle packets: %d", ret);
+               goto err;
+       }
+
+       pkt_offset = 0;
        for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
-               ret = ath10k_sdio_mbox_rx_packet(ar,
-                                                &ar_sdio->rx_pkts[i]);
-               if (ret)
-                       goto err;
+               pkt = &ar_sdio->rx_pkts[i];
+               htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
+               pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
+
+               skb_put_data(pkt->skb, htc_hdr, pkt->act_len);
+               pkt->status = 0;
+               pkt_offset += pkt->alloc_len;
        }
 
        return 0;
 
 err:
        /* Free all packets that was not successfully fetched. */
-       for (; i < ar_sdio->n_rx_pkts; i++)
+       for (i = 0; i < ar_sdio->n_rx_pkts; i++)
                ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
 
+       ar_sdio->n_rx_pkts = 0;
+
        return ret;
 }
 
                         */
                        *done = false;
 
-               ret = ath10k_sdio_mbox_rx_fetch(ar);
+               if (ar_sdio->n_rx_pkts > 1)
+                       ret = ath10k_sdio_mbox_rx_fetch_bundle(ar);
+               else
+                       ret = ath10k_sdio_mbox_rx_fetch(ar);
 
                /* Process fetched packets. This will potentially update
                 * n_lookaheads depending on if the packets contain lookahead
                goto err_core_destroy;
        }
 
+       ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL);
+       if (!ar_sdio->vsg_buffer) {
+               ret = -ENOMEM;
+               goto err_core_destroy;
+       }
+
        ar_sdio->irq_data.irq_en_reg =
                devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
                             GFP_KERNEL);