Based on one mtk_iommu_domain, this patch supports multiple
mtk_iommu_domains to realize different iova regions.

Every module has one smi_larb port, so we can create different
mtk_iommu_domains by smi_larb port define. So we will add port_mask
variable to mtk_domain_data, if some modules need special iova regions,
they can write smi_larb port which corresponding to themselves to
post_mask variable and specify the start and end address of iova region.
The form of port_mask can use "MTK_M4U_ID(larb, port)", larb and port can
refer to "mtxxxx-larb-port.h(ex: mt6779-larb-port.h)" file.

The architecture diagram is as below:

                                mtk_iommu_pgtable
                                        |
                                mtk_domain_data
                                        |
                -------------------------------------------------
                |                       |                       |
        mtk_iommu_domain1       mtk_iommu_domain2       mtk_iommu_domain3

Signed-off-by: Chao Hao <chao....@mediatek.com>
---
 drivers/iommu/mtk_iommu.c | 48 +++++++++++++++++++++++++++++++++------
 drivers/iommu/mtk_iommu.h | 11 ++++++++-
 2 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index c0cd7da71c2c..c33ea55a1841 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -130,6 +130,8 @@ struct mtk_iommu_pgtable {
        struct io_pgtable_ops   *iop;
        struct device           *init_dev;
        struct list_head        m4u_dom_v2;
+       spinlock_t              domain_lock; /* lock for domain count */
+       u32                     domain_count;
        const struct mtk_domain_data    *dom_region;
 };
 
@@ -172,11 +174,15 @@ static LIST_HEAD(m4ulist);        /* List all the M4U HWs 
*/
 static u32 get_domain_id(struct mtk_iommu_data *data, u32 portid)
 {
        u32 dom_id = 0;
-       int i;
+       const struct mtk_domain_data *mtk_dom_array = data->plat_data->dom_data;
+       int i, j;
 
-       /* only support one mtk_iommu_domain currently(dom_cnt = 1) */
-       for (i = 0; i < data->plat_data->dom_cnt; i++)
-               return i;
+       for (i = 0; i < data->plat_data->dom_cnt; i++) {
+               for (j = 0; j < MTK_MAX_PORT_NUM; j++) {
+                       if (portid == mtk_dom_array[i].port_mask[j])
+                               return i;
+               }
+       }
 
        return dom_id;
 }
@@ -416,6 +422,8 @@ static struct mtk_iommu_pgtable *create_pgtable(struct 
mtk_iommu_data *data)
        if (!pgtable)
                return ERR_PTR(-ENOMEM);
 
+       spin_lock_init(&pgtable->domain_lock);
+       pgtable->domain_count = 0;
        INIT_LIST_HEAD(&pgtable->m4u_dom_v2);
 
        pgtable->cfg = (struct io_pgtable_cfg) {
@@ -476,6 +484,7 @@ static struct iommu_domain *mtk_iommu_domain_alloc(unsigned 
type)
        struct mtk_iommu_data *data;
        struct mtk_iommu_domain *dom;
        struct device *dev;
+       unsigned long flags;
 
        if (type != IOMMU_DOMAIN_DMA)
                return NULL;
@@ -503,18 +512,34 @@ static struct iommu_domain 
*mtk_iommu_domain_alloc(unsigned type)
        if (dom->id >= data->plat_data->dom_cnt)
                goto  put_dma_cookie;
 
+       spin_lock_irqsave(&pgtable->domain_lock, flags);
+       if (pgtable->domain_count >= data->plat_data->dom_cnt) {
+               spin_unlock_irqrestore(&pgtable->domain_lock, flags);
+               dev_err(dev, "%s, too many domain, count=%u\n",
+                       __func__, pgtable->domain_count);
+               goto  put_dma_cookie;
+       }
+       pgtable->domain_count++;
+       spin_unlock_irqrestore(&pgtable->domain_lock, flags);
        dom->data = data;
        dom->group = data->m4u_group;
+
        /* Update our support page sizes bitmap */
        dom->domain.pgsize_bitmap = pgtable->cfg.pgsize_bitmap;
 
        dom->domain.geometry.aperture_start =
-                               pgtable->dom_region->min_iova;
+                               pgtable->dom_region[dom->id].min_iova;
        dom->domain.geometry.aperture_end =
-                               pgtable->dom_region->max_iova;
+                               pgtable->dom_region[dom->id].max_iova;
        dom->domain.geometry.force_aperture = true;
        list_add_tail(&dom->list, &pgtable->m4u_dom_v2);
 
+       dev_info(dev, "%s: dom_id:%u, start:%pa, end:%pa, dom_cnt:%u\n",
+                __func__, dom->id,
+                &dom->domain.geometry.aperture_start,
+                &dom->domain.geometry.aperture_end,
+                pgtable->domain_count);
+
        return &dom->domain;
 
 put_dma_cookie:
@@ -527,9 +552,17 @@ static struct iommu_domain 
*mtk_iommu_domain_alloc(unsigned type)
 static void mtk_iommu_domain_free(struct iommu_domain *domain)
 {
        struct mtk_iommu_pgtable *pgtable = mtk_iommu_get_pgtable();
+       unsigned long flags;
 
        iommu_put_dma_cookie(domain);
        kfree(to_mtk_domain(domain));
+       spin_lock_irqsave(&pgtable->domain_lock, flags);
+       pgtable->domain_count--;
+       if (pgtable->domain_count > 0) {
+               spin_unlock_irqrestore(&pgtable->domain_lock, flags);
+               return;
+       }
+       spin_unlock_irqrestore(&pgtable->domain_lock, flags);
        free_io_pgtable_ops(pgtable->iop);
        kfree(pgtable);
 }
@@ -703,6 +736,7 @@ static void mtk_iommu_get_resv_regions(struct device *dev,
 {
        struct mtk_iommu_data *data = dev_iommu_fwspec_get(dev)->iommu_priv;
        unsigned int i, total_cnt = data->plat_data->resv_cnt;
+       u32 dom_id = mtk_iommu_get_domain_id(dev);
        const struct mtk_iommu_resv_iova_region *resv_data;
        struct iommu_resv_region *region;
        unsigned long base = 0;
@@ -717,7 +751,7 @@ static void mtk_iommu_get_resv_regions(struct device *dev,
                        base = (unsigned long)resv_data[i].iova_base;
                        size = resv_data[i].iova_size;
                }
-               if (!size)
+               if (!size || resv_data[i].dom_id != dom_id)
                        continue;
 
                region = iommu_alloc_resv_region(base, size, prot,
diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h
index 10476b23adee..345c0a0c2881 100644
--- a/drivers/iommu/mtk_iommu.h
+++ b/drivers/iommu/mtk_iommu.h
@@ -37,6 +37,7 @@ enum mtk_iommu_plat {
 };
 
 struct mtk_iommu_resv_iova_region {
+       u32                     dom_id;
        dma_addr_t              iova_base;
        size_t                  iova_size;
        enum iommu_resv_type    type;
@@ -50,12 +51,20 @@ struct mtk_iommu_resv_iova_region {
  * struct mtk_domain_data:     domain configuration
  * @min_iova:  Start address of iova
  * @max_iova:  End address of iova
- * Note: one user can only belong to one domain
+ * @port_mask: User can specify mtk_iommu_domain by smi larb and port.
+ *             Different mtk_iommu_domain have different iova space,
+ *             port_mask is made up of larb_id and port_id.
+ *             The format of larb and port can refer to mtxxxx-larb-port.h.
+ *             bit[4:0] = port_id  bit[11:5] = larb_id.
+ * Note: one user can only belong to one domain,
+ * the port mask is in unit of SMI larb.
  */
+#define MTK_MAX_PORT_NUM       5
 
 struct mtk_domain_data {
        dma_addr_t      min_iova;
        dma_addr_t      max_iova;
+       u32             port_mask[MTK_MAX_PORT_NUM];
 };
 
 struct mtk_iommu_plat_data {
-- 
2.18.0

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to