#include <linux/log2.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
+#include <linux/uacce.h>
+#include <linux/uaccess.h>
+#include <uapi/misc/uacce/hisi_qm.h>
 #include "qm.h"
 
 /* eq/aeq irq enable */
 
 static void qm_poll_qp(struct hisi_qp *qp, struct hisi_qm *qm)
 {
-       struct qm_cqe *cqe = qp->cqe + qp->qp_status.cq_head;
+       if (qp->event_cb) {
+               qp->event_cb(qp);
+               return;
+       }
 
        if (qp->req_cb) {
+               struct qm_cqe *cqe = qp->cqe + qp->qp_status.cq_head;
+
                while (QM_CQE_PHASE(cqe) == qp->qp_status.cqc_phase) {
                        dma_rmb();
                        qp->req_cb(qp, qp->sqe + qm->sqe_size *
  * @qp: The qp we want to start to run.
  * @arg: Accelerator specific argument.
  *
- * After this function, qp can receive request from user. Return qp_id if
+ * After this function, qp can receive request from user. Return 0 if
  * successful, Return -EBUSY if failed.
  */
 int hisi_qm_start_qp(struct hisi_qp *qp, unsigned long arg)
 
        dev_dbg(dev, "queue %d started\n", qp_id);
 
-       return qp_id;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(hisi_qm_start_qp);
 
        }
 }
 
+static void qm_qp_event_notifier(struct hisi_qp *qp)
+{
+       wake_up_interruptible(&qp->uacce_q->wait);
+}
+
+static int hisi_qm_get_available_instances(struct uacce_device *uacce)
+{
+       int i, ret;
+       struct hisi_qm *qm = uacce->priv;
+
+       read_lock(&qm->qps_lock);
+       for (i = 0, ret = 0; i < qm->qp_num; i++)
+               if (!qm->qp_array[i])
+                       ret++;
+       read_unlock(&qm->qps_lock);
+
+       return ret;
+}
+
+static int hisi_qm_uacce_get_queue(struct uacce_device *uacce,
+                                  unsigned long arg,
+                                  struct uacce_queue *q)
+{
+       struct hisi_qm *qm = uacce->priv;
+       struct hisi_qp *qp;
+       u8 alg_type = 0;
+
+       qp = hisi_qm_create_qp(qm, alg_type);
+       if (IS_ERR(qp))
+               return PTR_ERR(qp);
+
+       q->priv = qp;
+       q->uacce = uacce;
+       qp->uacce_q = q;
+       qp->event_cb = qm_qp_event_notifier;
+       qp->pasid = arg;
+
+       return 0;
+}
+
+static void hisi_qm_uacce_put_queue(struct uacce_queue *q)
+{
+       struct hisi_qp *qp = q->priv;
+
+       hisi_qm_cache_wb(qp->qm);
+       hisi_qm_release_qp(qp);
+}
+
+/* map sq/cq/doorbell to user space */
+static int hisi_qm_uacce_mmap(struct uacce_queue *q,
+                             struct vm_area_struct *vma,
+                             struct uacce_qfile_region *qfr)
+{
+       struct hisi_qp *qp = q->priv;
+       struct hisi_qm *qm = qp->qm;
+       size_t sz = vma->vm_end - vma->vm_start;
+       struct pci_dev *pdev = qm->pdev;
+       struct device *dev = &pdev->dev;
+       unsigned long vm_pgoff;
+       int ret;
+
+       switch (qfr->type) {
+       case UACCE_QFRT_MMIO:
+               if (qm->ver == QM_HW_V2) {
+                       if (sz > PAGE_SIZE * (QM_DOORBELL_PAGE_NR +
+                           QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE))
+                               return -EINVAL;
+               } else {
+                       if (sz > PAGE_SIZE * QM_DOORBELL_PAGE_NR)
+                               return -EINVAL;
+               }
+
+               vma->vm_flags |= VM_IO;
+
+               return remap_pfn_range(vma, vma->vm_start,
+                                      qm->phys_base >> PAGE_SHIFT,
+                                      sz, pgprot_noncached(vma->vm_page_prot));
+       case UACCE_QFRT_DUS:
+               if (sz != qp->qdma.size)
+                       return -EINVAL;
+
+               /*
+                * dma_mmap_coherent() requires vm_pgoff as 0
+                * restore vm_pfoff to initial value for mmap()
+                */
+               vm_pgoff = vma->vm_pgoff;
+               vma->vm_pgoff = 0;
+               ret = dma_mmap_coherent(dev, vma, qp->qdma.va,
+                                       qp->qdma.dma, sz);
+               vma->vm_pgoff = vm_pgoff;
+               return ret;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int hisi_qm_uacce_start_queue(struct uacce_queue *q)
+{
+       struct hisi_qp *qp = q->priv;
+
+       return hisi_qm_start_qp(qp, qp->pasid);
+}
+
+static void hisi_qm_uacce_stop_queue(struct uacce_queue *q)
+{
+       hisi_qm_stop_qp(q->priv);
+}
+
+static int qm_set_sqctype(struct uacce_queue *q, u16 type)
+{
+       struct hisi_qm *qm = q->uacce->priv;
+       struct hisi_qp *qp = q->priv;
+
+       write_lock(&qm->qps_lock);
+       qp->alg_type = type;
+       write_unlock(&qm->qps_lock);
+
+       return 0;
+}
+
+static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd,
+                               unsigned long arg)
+{
+       struct hisi_qp *qp = q->priv;
+       struct hisi_qp_ctx qp_ctx;
+
+       if (cmd == UACCE_CMD_QM_SET_QP_CTX) {
+               if (copy_from_user(&qp_ctx, (void __user *)arg,
+                                  sizeof(struct hisi_qp_ctx)))
+                       return -EFAULT;
+
+               if (qp_ctx.qc_type != 0 && qp_ctx.qc_type != 1)
+                       return -EINVAL;
+
+               qm_set_sqctype(q, qp_ctx.qc_type);
+               qp_ctx.id = qp->qp_id;
+
+               if (copy_to_user((void __user *)arg, &qp_ctx,
+                                sizeof(struct hisi_qp_ctx)))
+                       return -EFAULT;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct uacce_ops uacce_qm_ops = {
+       .get_available_instances = hisi_qm_get_available_instances,
+       .get_queue = hisi_qm_uacce_get_queue,
+       .put_queue = hisi_qm_uacce_put_queue,
+       .start_queue = hisi_qm_uacce_start_queue,
+       .stop_queue = hisi_qm_uacce_stop_queue,
+       .mmap = hisi_qm_uacce_mmap,
+       .ioctl = hisi_qm_uacce_ioctl,
+};
+
+static int qm_alloc_uacce(struct hisi_qm *qm)
+{
+       struct pci_dev *pdev = qm->pdev;
+       struct uacce_device *uacce;
+       unsigned long mmio_page_nr;
+       unsigned long dus_page_nr;
+       struct uacce_interface interface = {
+               .flags = UACCE_DEV_SVA,
+               .ops = &uacce_qm_ops,
+       };
+
+       strncpy(interface.name, pdev->driver->name, sizeof(interface.name));
+
+       uacce = uacce_alloc(&pdev->dev, &interface);
+       if (IS_ERR(uacce))
+               return PTR_ERR(uacce);
+
+       if (uacce->flags & UACCE_DEV_SVA) {
+               qm->use_sva = true;
+       } else {
+               /* only consider sva case */
+               uacce_remove(uacce);
+               qm->uacce = NULL;
+               return -EINVAL;
+       }
+
+       uacce->is_vf = pdev->is_virtfn;
+       uacce->priv = qm;
+       uacce->algs = qm->algs;
+
+       if (qm->ver == QM_HW_V1) {
+               mmio_page_nr = QM_DOORBELL_PAGE_NR;
+               uacce->api_ver = HISI_QM_API_VER_BASE;
+       } else {
+               mmio_page_nr = QM_DOORBELL_PAGE_NR +
+                       QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE;
+               uacce->api_ver = HISI_QM_API_VER2_BASE;
+       }
+
+       dus_page_nr = (PAGE_SIZE - 1 + qm->sqe_size * QM_Q_DEPTH +
+                      sizeof(struct qm_cqe) * QM_Q_DEPTH) >> PAGE_SHIFT;
+
+       uacce->qf_pg_num[UACCE_QFRT_MMIO] = mmio_page_nr;
+       uacce->qf_pg_num[UACCE_QFRT_DUS]  = dus_page_nr;
+
+       qm->uacce = uacce;
+
+       return 0;
+}
+
 /**
  * hisi_qm_get_free_qp_num() - Get free number of qp in qm.
  * @qm: The qm which want to get free qp.
                return -EINVAL;
        }
 
+       ret = qm_alloc_uacce(qm);
+       if (ret < 0)
+               dev_warn(&pdev->dev, "fail to alloc uacce (%d)\n", ret);
+
        ret = pci_enable_device_mem(pdev);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to enable device mem!\n");
-               return ret;
+               goto err_remove_uacce;
        }
 
        ret = pci_request_mem_regions(pdev, qm->dev_name);
                goto err_disable_pcidev;
        }
 
-       qm->io_base = ioremap(pci_resource_start(pdev, PCI_BAR_2),
-                             pci_resource_len(qm->pdev, PCI_BAR_2));
+       qm->phys_base = pci_resource_start(pdev, PCI_BAR_2);
+       qm->phys_size = pci_resource_len(qm->pdev, PCI_BAR_2);
+       qm->io_base = ioremap(qm->phys_base, qm->phys_size);
        if (!qm->io_base) {
                ret = -EIO;
                goto err_release_mem_regions;
        pci_release_mem_regions(pdev);
 err_disable_pcidev:
        pci_disable_device(pdev);
+err_remove_uacce:
+       uacce_remove(qm->uacce);
+       qm->uacce = NULL;
 
        return ret;
 }
        struct pci_dev *pdev = qm->pdev;
        struct device *dev = &pdev->dev;
 
+       uacce_remove(qm->uacce);
+       qm->uacce = NULL;
+
        if (qm->use_dma_api && qm->qdma.va) {
                hisi_qm_cache_wb(qm);
                dma_free_coherent(dev, qm->qdma.size,