[PATCH 3/3] iommu/mediatek-v1: add mt6572 support

From: Roman Vivchar via B4 Relay

Date: Wed Sep 09 2026 - 11:17:13 EST


From: Roman Vivchar <rva333@xxxxxxxxxxxxxx>

mt6572 SoC utilizes an earlier version of the IOMMU, that shares the
same v1 architecture, but has minor hardware differences compared to
mt2701.

Introduce mtk_iommu_type to distinguish between mt2701 and mt6572
variant and apply the necessary logic.

Finally, add a platform data for the mt6572 SoC.

Signed-off-by: Roman Vivchar <rva333@xxxxxxxxxxxxxx>
---
Based on my downstream findings, this change may also support mt6582 SoC,
but I don't have the hardware to test if it actually works.
---
drivers/iommu/mtk_iommu_v1.c | 72 +++++++++++++++++++++++++++++++++-----------
1 file changed, 55 insertions(+), 17 deletions(-)

diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 72355f41c36a..b6f547b100d1 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -7,6 +7,7 @@
*
* Based on driver/iommu/mtk_iommu.c
*/
+#include <linux/bitfield.h>
#include <linux/bug.h>
#include <linux/clk.h>
#include <linux/component.h>
@@ -85,6 +86,13 @@ struct dma_iommu_mapping {
#define F_DESC_NONSEC BIT(3)
#define MT2701_M4U_TF_LARB(TF) (6 - (((TF) >> 13) & 0x7))
#define MT2701_M4U_TF_PORT(TF) (((TF) >> 8) & 0xF)
+
+#define MT6572_MMU_INT_ID_PORT_ID GENMASK(12, 8)
+#define MT6572_MMU_INT_ID_LARB_ID GENMASK(14, 13)
+
+#define MT6572_M4U_TF_PORT(TF) FIELD_GET(MT6572_MMU_INT_ID_PORT_ID, TF)
+#define MT6572_M4U_TF_LARB(TF) (FIELD_GET(MT6572_MMU_INT_ID_LARB_ID, TF) - 1)
+
/* MTK generation one iommu HW only support 4K size mapping */
#define MT2701_IOMMU_PAGE_SHIFT 12
#define MT2701_IOMMU_PAGE_SIZE (1UL << MT2701_IOMMU_PAGE_SHIFT)
@@ -96,6 +104,11 @@ struct dma_iommu_mapping {
*/
#define M2701_IOMMU_PGT_SIZE SZ_4M

+enum mtk_iommu_type {
+ MTK_IOMMU_MT6572,
+ MTK_IOMMU_V1,
+};
+
struct mtk_iommu_v1_suspend_reg {
u32 standard_axi_mode;
u32 dcm_dis;
@@ -116,6 +129,8 @@ struct mtk_iommu_v1_data {
struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];

struct mtk_iommu_v1_suspend_reg reg;
+
+ enum mtk_iommu_type type;
};

struct mtk_iommu_v1_domain {
@@ -170,8 +185,12 @@ static inline int mt2701_m4u_to_port(int id)

static void mtk_iommu_v1_tlb_flush_all(struct mtk_iommu_v1_data *data)
{
- writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
- data->base + REG_MMU_INV_SEL);
+ u32 val = F_INVLD_EN0;
+
+ if (data->type == MTK_IOMMU_V1)
+ val |= F_INVLD_EN1;
+
+ writel_relaxed(val, data->base + REG_MMU_INV_SEL);
writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
wmb(); /* Make sure the tlb flush all done */
}
@@ -180,25 +199,33 @@ static void mtk_iommu_v1_tlb_flush_range(struct mtk_iommu_v1_data *data,
unsigned long iova, size_t size)
{
int ret;
- u32 tmp;
+ u32 tmp, val = F_INVLD_EN0;

- writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
- data->base + REG_MMU_INV_SEL);
+ if (data->type == MTK_IOMMU_V1)
+ val |= F_INVLD_EN1;
+
+ writel_relaxed(val, data->base + REG_MMU_INV_SEL);
writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
data->base + REG_MMU_INVLD_START_A);
writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
data->base + REG_MMU_INVLD_END_A);
writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);

- ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
- tmp, tmp != 0, 10, 100000);
- if (ret) {
- dev_warn(data->dev,
- "Partial TLB flush timed out, falling back to full flush\n");
- mtk_iommu_v1_tlb_flush_all(data);
+ if (data->type == MTK_IOMMU_V1) {
+ ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
+ tmp, tmp != 0, 10, 100000);
+ if (ret) {
+ dev_warn(data->dev,
+ "Partial TLB flush timed out, falling back to full flush\n");
+ mtk_iommu_v1_tlb_flush_all(data);
+ }
+
+ /* Clear the CPE status */
+ writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+ } else {
+ /* Make sure the TLB flush is done */
+ wmb();
}
- /* Clear the CPE status */
- writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
}

static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
@@ -215,8 +242,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
fault_iova &= F_MMU_FAULT_VA_MSK;
fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
regval = readl_relaxed(data->base + REG_MMU_INT_ID);
- fault_larb = MT2701_M4U_TF_LARB(regval);
- fault_port = MT2701_M4U_TF_PORT(regval);
+
+ if (data->type == MTK_IOMMU_V1) {
+ fault_larb = MT2701_M4U_TF_LARB(regval);
+ fault_port = MT2701_M4U_TF_PORT(regval);
+ } else {
+ fault_larb = MT6572_M4U_TF_LARB(regval);
+ fault_port = MT6572_M4U_TF_PORT(regval);
+ }

/*
* MTK v1 iommu HW could not determine whether the fault is read or
@@ -539,7 +572,10 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
return ret;
}

- regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2);
+ regval = F_MMU_TF_PROTECT_SEL(2);
+ if (data->type == MTK_IOMMU_V1)
+ regval |= F_MMU_CTRL_COHERENT_EN;
+
writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);

regval = F_INT_TRANSLATION_FAULT |
@@ -588,7 +624,8 @@ static const struct iommu_ops mtk_iommu_v1_ops = {
};

static const struct of_device_id mtk_iommu_v1_of_ids[] = {
- { .compatible = "mediatek,mt2701-m4u", },
+ { .compatible = "mediatek,mt2701-m4u", .data = (void *)MTK_IOMMU_V1 },
+ { .compatible = "mediatek,mt6572-m4u", .data = (void *)MTK_IOMMU_MT6572 },
{}
};
MODULE_DEVICE_TABLE(of, mtk_iommu_v1_of_ids);
@@ -612,6 +649,7 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
return -ENOMEM;

data->dev = dev;
+ data->type = (enum mtk_iommu_type)(uintptr_t)of_device_get_match_data(dev);

/* Protect memory. HW will access here while translation fault.*/
protect = devm_kcalloc(dev, 2, MTK_PROTECT_PA_ALIGN,

--
2.55.0