* Queue Interface backend functionality
  *
  * Copyright 2013-2016 Freescale Semiconductor, Inc.
- * Copyright 2016-2017, 2019 NXP
+ * Copyright 2016-2017, 2019-2020 NXP
  */
 
 #include <linux/cpumask.h>
 
        do {
                ret = qman_enqueue(req->drv_ctx->req_fq, &fd);
-               if (likely(!ret))
+               if (likely(!ret)) {
+                       refcount_inc(&req->drv_ctx->refcnt);
                        return 0;
+               }
 
                if (ret != -EBUSY)
                        break;
 
        fd = &msg->ern.fd;
 
-       if (qm_fd_get_format(fd) != qm_fd_compound) {
-               dev_err(qidev, "Non-compound FD from CAAM\n");
-               return;
-       }
-
        drv_req = caam_iova_to_virt(priv->domain, qm_fd_addr_get64(fd));
        if (!drv_req) {
                dev_err(qidev,
                return;
        }
 
+       refcount_dec(&drv_req->drv_ctx->refcnt);
+
+       if (qm_fd_get_format(fd) != qm_fd_compound) {
+               dev_err(qidev, "Non-compound FD from CAAM\n");
+               return;
+       }
+
        dma_unmap_single(drv_req->drv_ctx->qidev, qm_fd_addr(fd),
                         sizeof(drv_req->fd_sgt), DMA_BIDIRECTIONAL);
 
        return ret;
 }
 
-static int empty_caam_fq(struct qman_fq *fq)
+static int empty_caam_fq(struct qman_fq *fq, struct caam_drv_ctx *drv_ctx)
 {
        int ret;
+       int retries = 10;
        struct qm_mcr_queryfq_np np;
 
        /* Wait till the older CAAM FQ get empty */
                msleep(20);
        } while (1);
 
-       /*
-        * Give extra time for pending jobs from this FQ in holding tanks
-        * to get processed
-        */
-       msleep(20);
+       /* Wait until pending jobs from this FQ are processed by CAAM */
+       do {
+               if (refcount_read(&drv_ctx->refcnt) == 1)
+                       break;
+
+               msleep(20);
+       } while (--retries);
+
+       if (!retries)
+               dev_warn_once(drv_ctx->qidev, "%d frames from FQID %u still pending in CAAM\n",
+                             refcount_read(&drv_ctx->refcnt), fq->fqid);
+
        return 0;
 }
 
        drv_ctx->req_fq = new_fq;
 
        /* Empty and remove the older FQ */
-       ret = empty_caam_fq(old_fq);
+       ret = empty_caam_fq(old_fq, drv_ctx);
        if (ret) {
                dev_err(qidev, "Old CAAM FQ empty failed: %d\n", ret);
 
                return ERR_PTR(-ENOMEM);
        }
 
+       /* init reference counter used to track references to request FQ */
+       refcount_set(&drv_ctx->refcnt, 1);
+
        drv_ctx->qidev = qidev;
        return drv_ctx;
 }
                return qman_cb_dqrr_stop;
 
        fd = &dqrr->fd;
+
+       drv_req = caam_iova_to_virt(priv->domain, qm_fd_addr_get64(fd));
+       if (unlikely(!drv_req)) {
+               dev_err(qidev,
+                       "Can't find original request for caam response\n");
+               return qman_cb_dqrr_consume;
+       }
+
+       refcount_dec(&drv_req->drv_ctx->refcnt);
+
        status = be32_to_cpu(fd->status);
        if (unlikely(status)) {
                u32 ssrc = status & JRSTA_SSRC_MASK;
                return qman_cb_dqrr_consume;
        }
 
-       drv_req = caam_iova_to_virt(priv->domain, qm_fd_addr_get64(fd));
-       if (unlikely(!drv_req)) {
-               dev_err(qidev,
-                       "Can't find original request for caam response\n");
-               return qman_cb_dqrr_consume;
-       }
-
        dma_unmap_single(drv_req->drv_ctx->qidev, qm_fd_addr(fd),
                         sizeof(drv_req->fd_sgt), DMA_BIDIRECTIONAL);
 
 
  * Public definitions for the CAAM/QI (Queue Interface) backend.
  *
  * Copyright 2013-2016 Freescale Semiconductor, Inc.
- * Copyright 2016-2017 NXP
+ * Copyright 2016-2017, 2020 NXP
  */
 
 #ifndef __QI_H__
  * @context_a: shared descriptor dma address
  * @req_fq: to-CAAM request frame queue
  * @rsp_fq: from-CAAM response frame queue
+ * @refcnt: reference counter incremented for each frame enqueued in to-CAAM FQ
  * @cpu: cpu on which to receive CAAM response
  * @op_type: operation type
  * @qidev: device pointer for CAAM/QI backend
        dma_addr_t context_a;
        struct qman_fq *req_fq;
        struct qman_fq *rsp_fq;
+       refcount_t refcnt;
        int cpu;
        enum optype op_type;
        struct device *qidev;