Skip to content

MT6589 DRM and Blade panels#63

Draft
akku1139 wants to merge 10 commits into
blade/v7.1from
dev/v7.1/mt6589-drm
Draft

MT6589 DRM and Blade panels#63
akku1139 wants to merge 10 commits into
blade/v7.1from
dev/v7.1/mt6589-drm

Conversation

@akku1139

@akku1139 akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member

from: #33

for 7.1 dev

Link: #33
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member Author
  • use interrupts for m4u l2 cache

@akku1139

akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member Author
diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
index 67107e42358b..94f48005fd65 100644
--- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
@@ -72,7 +72,7 @@ properties:
       - enum:
           - mediatek,mt2701-m4u  # generation one
           - mediatek,mt2712-m4u  # generation two
-          - mediatek,mt6589-m4u
+          - mediatek,mt6589-m4u  # generation one (dual core)
           - mediatek,mt6779-m4u  # generation two
           - mediatek,mt6795-m4u  # generation two
           - mediatek,mt6893-iommu-mm         # generation two
@@ -98,10 +98,20 @@ properties:
           - const: mediatek,mt2701-m4u
 
   reg:
-    maxItems: 1
+    minItems: 1
+    maxItems: 4  # MT6589 needs 4 regions (global, m4u0, m4u1, l2cache)
+
+  reg-names:
+    minItems: 1
+    maxItems: 4
 
   interrupts:
-    maxItems: 1
+    minItems: 1
+    maxItems: 2  # MT6589 has one IRQ per core
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 2
 
   clocks:
     items:
@@ -134,6 +144,7 @@ properties:
       dt-binding/memory/mediatek,mt8188-memory-port.h for mt8188,
       dt-binding/memory/mediatek,mt8189-memory-port.h for mt8189,
       dt-binding/memory/mt2701-larb-port.h for mt2701 and mt7623,
+      dt-binding/memory/mt6589-larb-port.h for mt6589,
       dt-binding/memory/mt2712-larb-port.h for mt2712,
       dt-binding/memory/mt6779-larb-port.h for mt6779,
       dt-binding/memory/mt6795-larb-port.h for mt6795,
@@ -156,6 +167,42 @@ required:
   - '#iommu-cells'
 
 allOf:
+  - if:
+      properties:
+        compatible:
+          not:
+            contains:
+              const: mediatek,mt6589-m4u
+    then:
+      properties:
+        reg:
+          maxItems: 1
+        interrupts:
+          maxItems: 1
+      required:
+        - reg
+    else:
+      properties:
+        reg:
+          minItems: 4
+          maxItems: 4
+        interrupts:
+          minItems: 2
+          maxItems: 2
+        reg-names:
+          items:
+            - const: global
+            - const: m4u0
+            - const: m4u1
+            - const: l2cache
+        interrupt-names:
+          items:
+            - const: m4u0
+            - const: m4u1
+      required:
+        - reg-names
+        - interrupt-names
+
   - if:
       properties:
         compatible:
@@ -163,6 +210,7 @@ allOf:
             enum:
               - mediatek,mt2701-m4u
               - mediatek,mt2712-m4u
+              - mediatek,mt6589-m4u
               - mediatek,mt6795-m4u
               - mediatek,mt6893-iommu-mm
               - mediatek,mt8173-m4u
@@ -241,3 +289,23 @@ examples:
                              <&larb3>, <&larb4>, <&larb5>;
             #iommu-cells = <1>;
     };
+
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    iommu@10208000 {
+        compatible = "mediatek,mt6589-m4u";
+        reg = <0x10205000 0x200>,
+              <0x10205200 0x600>,
+              <0x10205800 0x600>,
+              <0x10205100 0x100>;
+        reg-names = "global", "m4u0", "m4u1", "l2cache";
+        interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_LOW>,
+                     <GIC_SPI 142 IRQ_TYPE_LEVEL_LOW>;
+        interrupt-names = "m4u0", "m4u1";
+        clocks = <&infracfg CLK_INFRA_M4U>;
+        clock-names = "bclk";
+        mediatek,larbs = <&larb0>, <&larb1>, <&larb2>,
+                         <&larb3>, <&larb4>, <&larb5>;
+        #iommu-cells = <1>;
+    };
diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi
index 6ea8b76af910..241fa2afbd86 100644
--- a/arch/arm/boot/dts/mediatek/mt6589.dtsi
+++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi
@@ -431,8 +431,9 @@ iommu: iommu@10205000 {
 			compatible = "mediatek,mt6589-m4u";
 			reg = <0x10205000 0x200>,
 			      <0x10205200 0x600>,
-			      <0x10205800 0x600>;
-			reg-names = "global", "m4u0", "m4u1";
+			      <0x10205800 0x600>,
+			      <0x10205100 0x100>;
+			reg-names = "global", "m4u0", "m4u1", "l2cache";
 			interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_LOW>,
 				     <GIC_SPI 106 IRQ_TYPE_LEVEL_LOW>,
 				     <GIC_SPI 107 IRQ_TYPE_LEVEL_LOW>,
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 591ffa66a728..dd822b95a7ed 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -7,6 +7,8 @@
  *
  * Based on driver/iommu/mtk_iommu.c
  */
+#include "linux/types.h"
+#include <linux/array_size.h>
 #include <linux/bug.h>
 #include <linux/clk.h>
 #include <linux/component.h>
@@ -29,6 +31,7 @@
 #include <asm/barrier.h>
 #include <dt-bindings/memory/mtk-memory-port.h>
 #include <dt-bindings/memory/mt2701-larb-port.h>
+#include <dt-bindings/memory/mt6589-larb-port.h>
 #include <soc/mediatek/smi.h>
 
 #if defined(CONFIG_ARM)
@@ -51,6 +54,7 @@ struct dma_iommu_mapping {
 #define F_MMU_FAULT_VA_MSK			0xfffff000
 #define MTK_PROTECT_PA_ALIGN			128
 
+/* -------- Common M4U v1 register definitions (MT2701 and MT6589 core) -------- */
 #define REG_MMU_CTRL_REG			0x210
 #define F_MMU_CTRL_COHERENT_EN			BIT(8)
 #define REG_MMU_IVRP_PADDR			0x214
@@ -71,6 +75,8 @@ struct dma_iommu_mapping {
 #define REG_MMU_FAULT_VA			0x228
 #define REG_MMU_INVLD_PA			0x22C
 #define REG_MMU_INT_ID				0x388
+
+/* MT2701 specific (core space) */
 #define REG_MMU_INVALIDATE			0x5c0
 #define REG_MMU_INVLD_START_A			0x5c4
 #define REG_MMU_INVLD_END_A			0x5c8
@@ -81,14 +87,80 @@ struct dma_iommu_mapping {
 #define REG_MMU_DCM				0x5f0
 #define F_MMU_DCM_ON				BIT(1)
 #define REG_MMU_CPE_DONE			0x60c
+
+/* MT6589 global space registers */
+#define REG_MMUg_CTRL				0x00
+#define F_MMUg_CTRL_INV_EN0			BIT(0)
+#define F_MMUg_CTRL_INV_EN1			BIT(1)
+#define F_MMUg_CTRL_INV_EN2			BIT(2)	/* L2 */
+#define F_MMUg_CTRL_PRE_LOCK(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_CTRL_PRE_EN			BIT(4)
+
+#define REG_MMUg_INVLD				0x04
+#define F_MMUg_INV_ALL				0x2
+#define F_MMUg_INV_RANGE			0x1
+
+#define REG_MMUg_INVLD_SA			0x08
+#define REG_MMUg_INVLD_EA			0x0C
+#define REG_MMUg_PT_BASE			0x10
+#define F_MMUg_PT_VA_MSK			0xffff0000
+
+#define REG_MMUg_L2_SEL				0x18
+#define F_MMUg_L2_SEL_FLUSH_EN(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_L2_SEL_L2_ULTRA(en)		((en) ? BIT(2) : 0)
+#define F_MMUg_L2_SEL_L2_SHARE(en)		((en) ? BIT(1) : 0)
+#define F_MMUg_L2_SEL_L2_BUS_SEL(go_emi)	((go_emi) ? BIT(0) : 0)
+
+#define REG_MMUg_DCM				0x1C
+#define F_MMUg_DCM_ON(on)			((on) ? BIT(0) : 0)
+
+/* L2 cache registers (MT6589) */
+#define REG_L2_GDC_STATE			0x00
+#define F_L2_GDC_ST_EVENT_MSK			GENMASK(7,6)
+#define F_L2_GDC_ST_EVENT_VAL(val)		(((val) & 0x3) << 6)
+
+#define REG_L2_GDC_OP				0x04
+#define F_L2_GDC_BYPASS(en)			((en) ? BIT(10) : 0)
+#define F_L2_GDC_PERF_MASK(msk)			(((msk) & 0x7) << 7)
+#define GDC_PERF_MASK_HIT_MISS			0
+#define F_L2_GDC_LOCK_ALERT_DIS(dis)		((dis) ? BIT(6) : 0)
+#define F_L2_GDC_PERF_EN(en)			((en) ? BIT(5) : 0)
+#define F_L2_GDC_LOCK_TH(th)			(((th) & 0x3) << 2)
+#define F_L2_GDC_PAUSE_OP(op)			((op) & 0x3)
+#define GDC_NO_PAUSE				0
+
+#define REG_L2_GPE_STATUS			0x18
+#define F_L2_GPE_ST_RANGE_INV_DONE		BIT(1)
+#define F_L2_GPE_ST_PREFETCH_DONE		BIT(0)
+
+/* MT6589 core PFH distance / direction registers */
+#define REG_MMU_PFH_DIST(port)			(0x80 + (((port) >> 3) << 2))
+#define F_MMU_PFH_DIST_VAL(port, val)		(((val) & 0xf) << (((port) & 0x7) << 2))
+#define F_MMU_PFH_DIST_MASK(port)		F_MMU_PFH_DIST_VAL(port, 0xf)
+
+#define REG_MMU_PFH_DIR(port)			(((port) < 32) ? 0xF0 : 0xF4)
+#define F_MMU_PFH_DIR(port, val)		((!!(val)) << ((port) & 0x1f))
+
+/* SMI common registers (temporary, to be moved to SMI driver) */
+#define SMI_COMMON_EXT_BASE			0x10202000
+#define SMI_COMMON_AO_BASE			0x1000E000
+#define REG_SMI_L1LEN				0x200
+#define F_SMI_L1LEN_AXROUTE_G3D_EMI(en)		((en) ? BIT(2) : 0)
+#define F_SMI_L1LEN_AXROUTE_AUDIO_EMI(en)	((en) ? BIT(5) : 0)
+#define REG_SMI_BUS_SEL				0x220
+#define F_SMI_BUS_SEL_larb0(mmu)		(((mmu) & 0x3) << 0)
+#define F_SMI_BUS_SEL_larb1(mmu)		(((mmu) & 0x3) << 2)
+#define F_SMI_BUS_SEL_larb2(mmu)		(((mmu) & 0x3) << 4)
+#define F_SMI_BUS_SEL_larb3(mmu)		(((mmu) & 0x3) << 6)
+#define F_SMI_BUS_SEL_larb4(mmu)		(((mmu) & 0x3) << 8)
+#define REG_SMI_SECUR_CON(x)			(0x05C0 + ((x) << 2))
+
+/* Common page table descriptor bits */
 #define F_DESC_VALID				0x2
 #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)
 /* 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)
-#define MT2701_LARB_NR_MAX			3
 
 /*
  * MTK m4u support 4GB iova address space, and only support 4K page
@@ -96,26 +168,75 @@ struct dma_iommu_mapping {
  */
 #define M2701_IOMMU_PGT_SIZE			SZ_4M
 
+#define MAX_M4U_CORES				2
+
+struct mtk_iommu_v1_data;
+
+struct mtk_iommu_v1_soc_data {
+	const char *compatible;
+	unsigned int num_cores;
+	bool has_global_base;
+	bool has_l2_cache;
+
+	void (*tlb_flush_all)(struct mtk_iommu_v1_data *data);
+	void (*tlb_flush_range)(struct mtk_iommu_v1_data *data,
+				unsigned long iova, size_t size);
+
+	void (*get_fault_larb_port)(u32 int_id, unsigned int *larb,
+				    unsigned int *port);
+
+	int (*hw_init)(struct mtk_iommu_v1_data *data);
+
+	u32 pt_base_reg_offset;
+	bool pt_base_in_global;
+
+	const int *larb_port_offsets;
+	unsigned int num_larb;
+};
+
+struct mtk_iommu_v1_core {
+	void __iomem *base;
+	int irq;
+	struct mtk_iommu_v1_data *data;
+	unsigned int id;
+};
+
 struct mtk_iommu_v1_suspend_reg {
+	/* MT2701 fields */
 	u32			standard_axi_mode;
 	u32			dcm_dis;
 	u32			ctrl_reg;
 	u32			int_control0;
+
+	/* MT6589 additional fields */
+	u32			mmug_ctrl;
+	u32			mmug_pt_base;
+	u32			mmug_l2_sel;
+	u32			mmug_dcm;
+	u32			l2_gdc_op;
+	u32			smi_l1len;
+	u32			smi_bus_sel;
+	u32			smi_secur_con[7];
 };
 
 struct mtk_iommu_v1_data {
-	void __iomem			*base;
-	int				irq;
-	struct device			*dev;
-	struct clk			*bclk;
-	phys_addr_t			protect_base; /* protect memory base */
-	struct mtk_iommu_v1_domain	*m4u_dom;
-
-	struct iommu_device		iommu;
-	struct dma_iommu_mapping	*mapping;
-	struct mtk_smi_larb_iommu	larb_imu[MTK_LARB_NR_MAX];
-
-	struct mtk_iommu_v1_suspend_reg	reg;
+	const struct mtk_iommu_v1_soc_data *soc;
+	struct device *dev;
+
+	struct mtk_iommu_v1_core cores[MAX_M4U_CORES];
+	void __iomem *global_base;
+	void __iomem *l2_base;
+
+	struct clk *bclk;
+	phys_addr_t protect_base;
+	struct mtk_iommu_v1_domain *m4u_dom;
+
+	struct iommu_device iommu;
+	struct dma_iommu_mapping *mapping;
+	struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
+
+	struct mtk_iommu_v1_suspend_reg reg;
+	struct page *dummy_page; /* Physical address of guard dummy page */
 };
 
 struct mtk_iommu_v1_domain {
@@ -150,73 +271,143 @@ static const int mt2701_m4u_in_larb[] = {
 	MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET
 };
 
-static inline int mt2701_m4u_to_larb(int id)
+static const int mt6589_m4u_in_larb[] = {
+	MT6589_LARB0_PORT_OFFSET, MT6589_LARB1_PORT_OFFSET,
+	MT6589_LARB2_PORT_OFFSET, MT6589_LARB3_PORT_OFFSET,
+	MT6589_LARB4_PORT_OFFSET, MT6589_LARB5_PORT_OFFSET
+};
+
+static inline int mtk_iommu_v1_to_larb(struct mtk_iommu_v1_data *data, int id)
 {
+	const int *offsets = data->soc->larb_port_offsets;
+	int num = data->soc->num_larb;
 	int i;
 
-	for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--)
-		if ((id) >= mt2701_m4u_in_larb[i])
+	for (i = num - 1; i >= 0; i--)
+		if (id >= offsets[i])
 			return i;
 
 	return 0;
 }
 
-static inline int mt2701_m4u_to_port(int id)
+static inline int mtk_iommu_v1_to_port(struct mtk_iommu_v1_data *data, int id)
 {
-	int larb = mt2701_m4u_to_larb(id);
+	int larb = mtk_iommu_v1_to_larb(data, id);
+	return id - data->soc->larb_port_offsets[larb];
+}
 
-	return id - mt2701_m4u_in_larb[larb];
+static inline void m4u_set_field(void __iomem *base, u32 reg, u32 mask, u32 val)
+{
+	u32 regval = readl_relaxed(base + reg);
+	regval = (regval & ~mask) | val;
+	writel_relaxed(regval, base + reg);
 }
 
-static void mtk_iommu_v1_tlb_flush_all(struct mtk_iommu_v1_data *data)
+/* MT2701 (single core, no global space) */
+static void mt2701_tlb_flush_all(struct mtk_iommu_v1_data *data)
 {
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-			data->base + REG_MMU_INV_SEL);
-	writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
-	wmb(); /* Make sure the tlb flush all done */
+	void __iomem *base = data->cores[0].base;
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(F_ALL_INVLD, base + REG_MMU_INVALIDATE);
+	wmb();
 }
 
-static void mtk_iommu_v1_tlb_flush_range(struct mtk_iommu_v1_data *data,
-					 unsigned long iova, size_t size)
+static void mt2701_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
 {
-	int ret;
+	void __iomem *base = data->cores[0].base;
 	u32 tmp;
+	int ret;
 
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-		data->base + REG_MMU_INV_SEL);
-	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
-		data->base + REG_MMU_INVLD_START_A);
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK, 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);
+		   base + REG_MMU_INVLD_END_A);
+	writel_relaxed(F_MMU_INV_RANGE, base + REG_MMU_INVALIDATE);
 
-	ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
-				tmp, tmp != 0, 10, 100000);
+	ret = readl_poll_timeout_atomic(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);
+		mt2701_tlb_flush_all(data);
+	}
+	writel_relaxed(0, base + REG_MMU_CPE_DONE);
+}
+
+/* MT6589 (global control, L2) */
+static void mt6589_tlb_flush_all(struct mtk_iommu_v1_data *data)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(F_MMUg_INV_ALL, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 event;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GDC_STATE,
+					 event,
+					 event & F_L2_GDC_ST_EVENT_MSK,
+					 10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GDC_STATE);
 	}
-	/* Clear the CPE status */
-	writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+}
+
+static void mt6589_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_SA);
+	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_EA);
+	writel_relaxed(F_MMUg_INV_RANGE, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 status;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GPE_STATUS,
+					  status,
+					  status & F_L2_GPE_ST_RANGE_INV_DONE,
+					  10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GPE_STATUS);
+	}
+}
+
+static void mt2701_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - ((int_id >> 13) & 0x7);
+	*port = (int_id >> 8) & 0xF;
+}
+
+static void mt6589_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - (int_id >> 12) & 0x7;
+	*port = (int_id >> 8) & 0xF;
 }
 
 static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 {
-	struct mtk_iommu_v1_data *data = dev_id;
+	struct mtk_iommu_v1_core *core = dev_id;
+	struct mtk_iommu_v1_data *data = core->data;
 	struct mtk_iommu_v1_domain *dom = data->m4u_dom;
 	u32 int_state, regval, fault_iova, fault_pa;
 	unsigned int fault_larb, fault_port;
 
 	/* Read error information from registers */
-	int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST);
-	fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
+	int_state = readl_relaxed(core->base + REG_MMU_FAULT_ST);
+	fault_iova = readl_relaxed(core->base + REG_MMU_FAULT_VA) & F_MMU_FAULT_VA_MSK;
+	fault_pa = readl_relaxed(core->base + REG_MMU_INVLD_PA);
+	regval = readl_relaxed(core->base + REG_MMU_INT_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);
+	data->soc->get_fault_larb_port(regval, &fault_larb, &fault_port);
 
 	/*
 	 * MTK v1 iommu HW could not determine whether the fault is read or
@@ -225,16 +416,16 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 	if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
 			IOMMU_FAULT_READ))
 		dev_err_ratelimited(data->dev,
-			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n",
+			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d core=%d\n",
 			int_state, fault_iova, fault_pa,
-			fault_larb, fault_port);
+			fault_larb, fault_port, core->id);
 
 	/* Interrupt clear */
-	regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL);
+	regval = readl_relaxed(core->base + REG_MMU_INT_CONTROL);
 	regval |= F_INT_CLR_BIT;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, core->base + REG_MMU_INT_CONTROL);
 
-	mtk_iommu_v1_tlb_flush_all(data);
+	data->soc->tlb_flush_all(data);
 
 	return IRQ_HANDLED;
 }
@@ -242,14 +433,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 				struct device *dev, bool enable)
 {
-	struct mtk_smi_larb_iommu    *larb_mmu;
-	unsigned int                 larbid, portid;
+	struct mtk_smi_larb_iommu *larb_mmu;
 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	int i;
+	unsigned int larbid, portid, i, mmu_id;
+	void __iomem *base;
 
 	for (i = 0; i < fwspec->num_ids; ++i) {
-		larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
-		portid = mt2701_m4u_to_port(fwspec->ids[i]);
+		larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+		portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
 		larb_mmu = &data->larb_imu[larbid];
 
 		dev_dbg(dev, "%s iommu port: %d\n",
@@ -260,6 +451,26 @@ static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 		else
 			larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid);
 	}
+
+	/* MT6589 specific: set default prefetch distance & direction */
+	if (data->soc->has_global_base) {
+		for (i = 0; i < fwspec->num_ids; i++) {
+			portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
+			larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+
+			/* Determine which M4U core this LARB is connected to */
+			mmu_id = (larbid == 0 || larbid == 2) ? 0 : 1;
+			base = data->cores[mmu_id].base;
+
+			/* Set distance = 1 */
+			m4u_set_field(base, REG_MMU_PFH_DIST(portid),
+				      F_MMU_PFH_DIST_MASK(portid),
+				      F_MMU_PFH_DIST_VAL(portid, 1));
+			/* Set direction = 0 */
+			m4u_set_field(base, REG_MMU_PFH_DIR(portid),
+				      1 << (portid & 0x1f), 0);
+		}
+	}
 }
 
 static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
@@ -273,7 +484,10 @@ static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
 	if (!dom->pgt_va)
 		return -ENOMEM;
 
-	writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR);
+	if (data->soc->pt_base_in_global)
+		writel(dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+	else
+		writel(dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
 
 	dom->data = data;
 
@@ -354,10 +568,12 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 			    int prot, gfp_t gfp, size_t *mapped)
 {
 	struct mtk_iommu_v1_domain *dom = to_mtk_domain(domain);
+	struct mtk_iommu_v1_data *data = dom->data;
 	unsigned long flags;
-	unsigned int i;
-	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT);
+	unsigned int i, guard_pages = (data->soc->has_global_base) ? 4 : 0;
+	u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT);
 	u32 pabase = (u32)paddr;
+	phys_addr_t dummy_pa = page_to_phys(data->dummy_page);
 
 	spin_lock_irqsave(&dom->pgtlock, flags);
 	for (i = 0; i < pgcount; i++) {
@@ -367,10 +583,19 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 		pabase += MT2701_IOMMU_PAGE_SIZE;
 	}
 
+	if (guard_pages && i == pgcount) {
+		for (i = pgcount; i < pgcount + guard_pages; i++) {
+			if ((iova >> MT2701_IOMMU_PAGE_SHIFT) + i >= (M2701_IOMMU_PGT_SIZE / sizeof(u32)))
+				break;
+			if (pgt_base_iova[i] == 0)
+				pgt_base_iova[i] = dummy_pa | F_DESC_VALID | F_DESC_NONSEC;
+		}
+	}
+
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
 	*mapped = i * MT2701_IOMMU_PAGE_SIZE;
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, *mapped);
+	data->soc->tlb_flush_range(data, iova, *mapped);
 
 	return i == pgcount ? 0 : -EEXIST;
 }
@@ -388,7 +613,7 @@ static size_t mtk_iommu_v1_unmap(struct iommu_domain *domain, unsigned long iova
 	memset(pgt_base_iova, 0, pgcount * sizeof(u32));
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, size);
+	dom->data->soc->tlb_flush_range(dom->data, iova, size);
 
 	return size;
 }
@@ -489,12 +714,12 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev)
 	data = dev_iommu_priv_get(dev);
 
 	/* Link the consumer device with the smi-larb device(supplier) */
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
-	if (larbid >= MT2701_LARB_NR_MAX)
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
+	if (larbid >= MTK_LARB_NR_MAX)
 		return ERR_PTR(-EINVAL);
 
 	for (idx = 1; idx < fwspec->num_ids; idx++) {
-		larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]);
+		larbidx = mtk_iommu_v1_to_larb(data, fwspec->ids[idx]);
 		if (larbid != larbidx) {
 			dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
 				larbid, larbidx);
@@ -532,12 +757,12 @@ static void mtk_iommu_v1_release_device(struct device *dev)
 	unsigned int larbid;
 
 	data = dev_iommu_priv_get(dev);
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
 	larbdev = data->larb_imu[larbid].dev;
 	device_link_remove(dev, larbdev);
 }
 
-static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
+static int mt2701_hw_init(struct mtk_iommu_v1_data *data)
 {
 	u32 regval;
 	int ret;
@@ -549,7 +774,7 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 	}
 
 	regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2);
-	writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_CTRL_REG);
 
 	regval = F_INT_TRANSLATION_FAULT |
 		F_INT_MAIN_MULTI_HIT_FAULT |
@@ -559,23 +784,104 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 		F_INT_TLB_MISS_FAULT |
 		F_INT_PFH_DMA_FIFO_OVERFLOW |
 		F_INT_MISS_DMA_FIFO_OVERFLOW;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_INT_CONTROL);
 
-	/* protect memory,hw will write here while translation fault */
-	writel_relaxed(data->protect_base,
-			data->base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(data->protect_base, data->cores[0].base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(F_MMU_DCM_ON, data->cores[0].base + REG_MMU_DCM);
 
-	writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM);
+	return 0;
+}
 
-	if (devm_request_irq(data->dev, data->irq, mtk_iommu_v1_isr, 0,
-			     dev_name(data->dev), (void *)data)) {
-		writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
-		clk_disable_unprepare(data->bclk);
-		dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
-		return -ENODEV;
+static int mt6589_hw_init(struct mtk_iommu_v1_data *data)
+{
+	u32 regval;
+	int i, ret;
+	void __iomem *smi_ext, *smi_ao;
+
+	/* Map SMI common regions temporarily (should be done by SMI driver) */
+	smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+	smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+	if (!smi_ext || !smi_ao) {
+		dev_err(data->dev, "Failed to map SMI common registers\n");
+		ret = -ENOMEM;
+		goto err_iomap;
+	}
+
+	ret = clk_prepare_enable(data->bclk);
+	if (ret) {
+		dev_err(data->dev, "Failed to enable bclk\n");
+		goto err_clk;
 	}
 
+	/* ---- SMI routing (LARB to M4U core) ---- */
+	/* LARB 0 -> M4U0, 1 -> M4U1, 2 -> M4U0, 3 -> M4U1, 4 -> M4U1 */
+	regval = F_SMI_BUS_SEL_larb0(0) | F_SMI_BUS_SEL_larb1(1) |
+		 F_SMI_BUS_SEL_larb2(0) | F_SMI_BUS_SEL_larb3(1) |
+		 F_SMI_BUS_SEL_larb4(1);
+	writel_relaxed(regval, smi_ext + REG_SMI_BUS_SEL);
+
+	/* Default security: non-secure, domain=3, physical */
+	for (i = 0; i < 7; i++)
+		writel_relaxed(0x66666666, smi_ao + REG_SMI_SECUR_CON(i));
+
+	/* Route G3D and AUDIO to EMI */
+	m4u_set_field(smi_ext, REG_SMI_L1LEN,
+		      F_SMI_L1LEN_AXROUTE_G3D_EMI(1),
+		      F_SMI_L1LEN_AXROUTE_G3D_EMI(1));
+	m4u_set_field(smi_ext, REG_SMI_L1LEN,
+		      F_SMI_L1LEN_AXROUTE_AUDIO_EMI(1),
+		      F_SMI_L1LEN_AXROUTE_AUDIO_EMI(1));
+
+	/* ---- Global registers ---- */
+	writel_relaxed(F_MMUg_L2_SEL_FLUSH_EN(1) | F_MMUg_L2_SEL_L2_ULTRA(1) |
+		   F_MMUg_L2_SEL_L2_SHARE(0) | F_MMUg_L2_SEL_L2_BUS_SEL(1),
+		   data->global_base + REG_MMUg_L2_SEL);
+	writel_relaxed(F_MMUg_DCM_ON(1), data->global_base + REG_MMUg_DCM);
+
+	/* ---- L2 cache ---- */
+	if (data->l2_base) {
+		regval = F_L2_GDC_BYPASS(0) |
+			 F_L2_GDC_PERF_MASK(GDC_PERF_MASK_HIT_MISS) |
+			 F_L2_GDC_LOCK_ALERT_DIS(0) |
+			 F_L2_GDC_LOCK_TH(3) |
+			 F_L2_GDC_PAUSE_OP(GDC_NO_PAUSE);
+		writel_relaxed(regval, data->l2_base + REG_L2_GDC_OP);
+	}
+
+	/* ---- Per-core setup ---- */
+	for (i = 0; i < data->soc->num_cores; i++) {
+		void __iomem *base = data->cores[i].base;
+
+		regval = 0;  /* PFH enabled, walk enabled, cohere disabled */
+		regval |= F_MMU_TF_PROTECT_SEL(2);
+		writel_relaxed(regval, base + REG_MMU_CTRL_REG);
+
+		regval = F_INT_TRANSLATION_FAULT |
+			 F_INT_MAIN_MULTI_HIT_FAULT |
+			 F_INT_INVALID_PA_FAULT |
+			 F_INT_ENTRY_REPLACEMENT_FAULT |
+			 F_INT_TABLE_WALK_FAULT |
+			 F_INT_TLB_MISS_FAULT |
+			 F_INT_PFH_DMA_FIFO_OVERFLOW |
+			 F_INT_MISS_DMA_FIFO_OVERFLOW;
+		writel_relaxed(regval, base + REG_MMU_INT_CONTROL);
+
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	}
+
+	/* Release temporary mappings */
+	iounmap(smi_ext);
+	iounmap(smi_ao);
+
 	return 0;
+
+err_clk:
+	iounmap(smi_ext);
+	iounmap(smi_ao);
+err_iomap:
+	if (smi_ext) iounmap(smi_ext);
+	if (smi_ao) iounmap(smi_ao);
+	return ret;
 }
 
 static const struct iommu_ops mtk_iommu_v1_ops = {
@@ -595,8 +901,39 @@ static const struct iommu_ops mtk_iommu_v1_ops = {
 	}
 };
 
+static const struct mtk_iommu_v1_soc_data mt2701_soc_data = {
+	.compatible = "mediatek,mt2701-m4u",
+	.num_cores = 1,
+	.has_global_base = false,
+	.has_l2_cache = false,
+	.tlb_flush_all = mt2701_tlb_flush_all,
+	.tlb_flush_range = mt2701_tlb_flush_range,
+	.get_fault_larb_port = mt2701_get_fault_larb_port,
+	.hw_init = mt2701_hw_init,
+	.pt_base_reg_offset = REG_MMU_PT_BASE_ADDR,
+	.pt_base_in_global = false,
+	.larb_port_offsets = mt2701_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt2701_m4u_in_larb),
+};
+
+static const struct mtk_iommu_v1_soc_data mt6589_soc_data = {
+	.compatible = "mediatek,mt6589-m4u",
+	.num_cores = 2,
+	.has_global_base = true,
+	.has_l2_cache = true,
+	.tlb_flush_all = mt6589_tlb_flush_all,
+	.tlb_flush_range = mt6589_tlb_flush_range,
+	.get_fault_larb_port = mt6589_get_fault_larb_port,
+	.hw_init = mt6589_hw_init,
+	.pt_base_reg_offset = REG_MMUg_PT_BASE,
+	.pt_base_in_global = true,
+	.larb_port_offsets = mt6589_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt6589_m4u_in_larb),
+};
+
 static const struct of_device_id mtk_iommu_v1_of_ids[] = {
-	{ .compatible = "mediatek,mt2701-m4u", },
+	{ .compatible = "mediatek,mt2701-m4u", .data = &mt2701_soc_data },
+	{ .compatible = "mediatek,mt6589-m4u", .data = &mt6589_soc_data },
 	{}
 };
 MODULE_DEVICE_TABLE(of, mtk_iommu_v1_of_ids);
@@ -610,16 +947,19 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 {
 	struct device			*dev = &pdev->dev;
 	struct mtk_iommu_v1_data	*data;
-	struct resource			*res;
+	const struct mtk_iommu_v1_soc_data *soc;
 	struct component_match		*match = NULL;
 	void				*protect;
 	int				larb_nr, ret, i;
+	struct page			*dummy_page;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
 	data->dev = dev;
+	soc = of_device_get_match_data(dev);
+	data->soc = soc;
 
 	/* Protect memory. HW will access here while translation fault.*/
 	protect = devm_kcalloc(dev, 2, MTK_PROTECT_PA_ALIGN,
@@ -628,26 +968,69 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	data->base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(data->base))
-		return PTR_ERR(data->base);
-
-	data->irq = platform_get_irq(pdev, 0);
-	if (data->irq < 0)
-		return data->irq;
+	if (soc->has_global_base) {
+		data->global_base = devm_platform_ioremap_resource_byname(pdev,
+									  "global");
+		if (IS_ERR(data->global_base))
+			return PTR_ERR(data->global_base);
+	}
+	for (i = 0; i < soc->num_cores; i++) {
+		char name[8];
+		snprintf(name, sizeof(name), "m4u%d", i);
+		data->cores[i].base = devm_platform_ioremap_resource_byname(pdev,
+									    name);
+		if (IS_ERR(data->cores[i].base))
+			return PTR_ERR(data->cores[i].base);
+		data->cores[i].data = data;
+		data->cores[i].id = i;
+	}
+	if (soc->has_l2_cache) {
+		data->l2_base = devm_platform_ioremap_resource_byname(pdev,
+								      "l2cache");
+		if (IS_ERR(data->l2_base))
+			return PTR_ERR(data->l2_base);
+	}
 
 	data->bclk = devm_clk_get(dev, "bclk");
 	if (IS_ERR(data->bclk))
 		return PTR_ERR(data->bclk);
 
+	/* Interrupts - request after hw_init to avoid spurious IRQs? but
+	   we need the core IRQs ready before registering ISR. We'll request
+	   them after hw_init for simplicity. */
+	ret = soc->hw_init(data);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < soc->num_cores; i++) {
+		struct mtk_iommu_v1_core *core = &data->cores[i];
+		char irqname[8];
+		snprintf(irqname, sizeof(irqname), "m4u%d", i);
+		core->irq = platform_get_irq_byname(pdev, irqname);
+		if (core->irq < 0) {
+			ret = core->irq;
+			goto out_clk_unprepare;
+		}
+		ret = devm_request_irq(dev, core->irq, mtk_iommu_v1_isr, 0,
+				       dev_name(dev), core);
+		if (ret) {
+			dev_err(dev, "Failed to request IRQ %d for core%d\n",
+				core->irq, i);
+			goto out_clk_unprepare;
+		}
+	}
+
 	larb_nr = of_count_phandle_with_args(dev->of_node,
 					     "mediatek,larbs", NULL);
-	if (larb_nr < 0)
-		return larb_nr;
+	if (larb_nr < 0) {
+		ret = larb_nr;
+		goto out_clk_unprepare;
+	}
 
-	if (larb_nr > MTK_LARB_NR_MAX)
-		return -EINVAL;
+	if (larb_nr > MTK_LARB_NR_MAX) {
+		ret = -EINVAL;
+		goto out_clk_unprepare;
+	}
 
 	for (i = 0; i < larb_nr; i++) {
 		struct device_node *larbnode;
@@ -682,17 +1065,20 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 					    component_compare_of, larbnode);
 	}
 
+	dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+	if (!dummy_page) {
+		ret = -ENOMEM;
+		goto out_put_larbs;
+	}
+	data->dummy_page = dummy_page;
+
 	platform_set_drvdata(pdev, data);
 
-	ret = mtk_iommu_v1_hw_init(data);
+	ret = iommu_device_sysfs_add(&data->iommu, dev, NULL,
+				     dev_name(dev));
 	if (ret)
 		goto out_put_larbs;
 
-	ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
-				     dev_name(&pdev->dev));
-	if (ret)
-		goto out_clk_unprepare;
-
 	ret = iommu_device_register(&data->iommu, &mtk_iommu_v1_ops, dev);
 	if (ret)
 		goto out_sysfs_remove;
@@ -706,12 +1092,14 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 	iommu_device_unregister(&data->iommu);
 out_sysfs_remove:
 	iommu_device_sysfs_remove(&data->iommu);
-out_clk_unprepare:
-	clk_disable_unprepare(data->bclk);
 out_put_larbs:
 	for (i = 0; i < MTK_LARB_NR_MAX; i++)
-		put_device(data->larb_imu[i].dev);
-
+		if (data->larb_imu[i].dev)
+			put_device(data->larb_imu[i].dev);
+	if (data->dummy_page)
+		__free_page(data->dummy_page);
+out_clk_unprepare:
+	clk_disable_unprepare(data->bclk);
 	return ret;
 }
 
@@ -724,40 +1112,128 @@ static void mtk_iommu_v1_remove(struct platform_device *pdev)
 	iommu_device_unregister(&data->iommu);
 
 	clk_disable_unprepare(data->bclk);
-	devm_free_irq(&pdev->dev, data->irq, data);
+	for (i = 0; i < data->soc->num_cores; i++)
+		devm_free_irq(&pdev->dev, data->cores[i].irq, &data->cores[i]);
 	component_master_del(&pdev->dev, &mtk_iommu_v1_com_ops);
+	__free_page(data->dummy_page);
 
 	for (i = 0; i < MTK_LARB_NR_MAX; i++)
-		put_device(data->larb_imu[i].dev);
+		if (data->larb_imu[i].dev)
+			put_device(data->larb_imu[i].dev);
 }
 
 static int __maybe_unused mtk_iommu_v1_suspend(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
+	void __iomem *base = data->cores[0].base;
 
-	reg->standard_axi_mode = readl_relaxed(base +
-					       REG_MMU_STANDARD_AXI_MODE);
-	reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	/* Common core registers */
 	reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
 	reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL);
+
+	if (data->soc->has_global_base) {
+		reg->mmug_ctrl = readl_relaxed(data->global_base + REG_MMUg_CTRL);
+		reg->mmug_pt_base = readl_relaxed(data->global_base + REG_MMUg_PT_BASE);
+		reg->mmug_l2_sel = readl_relaxed(data->global_base + REG_MMUg_L2_SEL);
+		reg->mmug_dcm = readl_relaxed(data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			reg->l2_gdc_op = readl_relaxed(data->l2_base + REG_L2_GDC_OP);
+
+		/* Save SMI registers (temporary) */
+		void __iomem *smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+		void __iomem *smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+		if (smi_ext && smi_ao) {
+			reg->smi_l1len = readl_relaxed(smi_ext + REG_SMI_L1LEN);
+			reg->smi_bus_sel = readl_relaxed(smi_ext + REG_SMI_BUS_SEL);
+			for (int i = 0; i < 7; i++)
+				reg->smi_secur_con[i] = readl_relaxed(smi_ao + REG_SMI_SECUR_CON(i));
+		}
+		iounmap(smi_ext);
+		iounmap(smi_ao);
+	} else {
+		/* MT2701 specific */
+		base = data->cores[0].base;
+		reg->standard_axi_mode = readl_relaxed(base + REG_MMU_STANDARD_AXI_MODE);
+		reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	}
+
 	return 0;
 }
 
+static void mt6589_restore_pfh_settings(struct mtk_iommu_v1_data *data)
+{
+	const int *offsets = data->soc->larb_port_offsets;
+	int num_larb = data->soc->num_larb;
+	int larb, port;
+
+	for (larb = 0; larb < num_larb; larb++) {
+		int mmu_id = (larb == 0 || larb == 2) ? 0 : 1;
+		void __iomem *base = data->cores[mmu_id].base;
+		int max_port = (larb == num_larb - 1) ? 32 : offsets[larb+1] - offsets[larb];
+		for (port = 0; port < max_port; port++) {
+			int global_port = offsets[larb] + port;
+			m4u_set_field(base, REG_MMU_PFH_DIST(global_port),
+						  F_MMU_PFH_DIST_MASK(global_port),
+						  F_MMU_PFH_DIST_VAL(global_port, 1));
+			m4u_set_field(base, REG_MMU_PFH_DIR(global_port),
+						  1 << (global_port & 0x1f), 0);
+		}
+	}
+}
+
 static int __maybe_unused mtk_iommu_v1_resume(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
-
-	writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR);
-	writel_relaxed(reg->standard_axi_mode,
-		       base + REG_MMU_STANDARD_AXI_MODE);
-	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
-	writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
-	writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
-	writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	void __iomem *base = data->cores[0].base;
+
+	if (data->soc->has_global_base) {
+		writel_relaxed(reg->mmug_ctrl, data->global_base + REG_MMUg_CTRL);
+		writel_relaxed(reg->mmug_pt_base, data->global_base + REG_MMUg_PT_BASE);
+		writel_relaxed(reg->mmug_l2_sel, data->global_base + REG_MMUg_L2_SEL);
+		writel_relaxed(reg->mmug_dcm, data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			writel_relaxed(reg->l2_gdc_op, data->l2_base + REG_L2_GDC_OP);
+
+		/* Restore SMI registers */
+		void __iomem *smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+		void __iomem *smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+		if (smi_ext && smi_ao) {
+			writel_relaxed(reg->smi_l1len, smi_ext + REG_SMI_L1LEN);
+			writel_relaxed(reg->smi_bus_sel, smi_ext + REG_SMI_BUS_SEL);
+			for (int i = 0; i < 7; i++)
+				writel_relaxed(reg->smi_secur_con[i], smi_ao + REG_SMI_SECUR_CON(i));
+		}
+		iounmap(smi_ext);
+		iounmap(smi_ao);
+	}
+
+	/* Per-core restore (common) */
+	for (int i = 0; i < data->soc->num_cores; i++) {
+		base = data->cores[i].base;
+		writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
+		writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	}
+
+	if (!data->soc->has_global_base) {
+		/* MT2701 extra */
+		base = data->cores[0].base;
+		writel_relaxed(reg->standard_axi_mode, base + REG_MMU_STANDARD_AXI_MODE);
+		writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
+	}
+
+	if (data->m4u_dom && data->m4u_dom->pgt_pa) {
+		if (data->soc->pt_base_in_global)
+			writel_relaxed(data->m4u_dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+		else
+			writel_relaxed(data->m4u_dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
+	}
+
+	if (data->soc->has_global_base)
+		mt6589_restore_pfh_settings(data);
+
 	return 0;
 }
 

Assisted-by: DeepSeek:deepseek-v4-pro
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member Author
diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
index 67107e42358b..94f48005fd65 100644
--- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
@@ -72,7 +72,7 @@ properties:
       - enum:
           - mediatek,mt2701-m4u  # generation one
           - mediatek,mt2712-m4u  # generation two
-          - mediatek,mt6589-m4u
+          - mediatek,mt6589-m4u  # generation one (dual core)
           - mediatek,mt6779-m4u  # generation two
           - mediatek,mt6795-m4u  # generation two
           - mediatek,mt6893-iommu-mm         # generation two
@@ -98,10 +98,20 @@ properties:
           - const: mediatek,mt2701-m4u
 
   reg:
-    maxItems: 1
+    minItems: 1
+    maxItems: 4  # MT6589 needs 4 regions (global, m4u0, m4u1, l2cache)
+
+  reg-names:
+    minItems: 1
+    maxItems: 4
 
   interrupts:
-    maxItems: 1
+    minItems: 1
+    maxItems: 2  # MT6589 has one IRQ per core
+
+  interrupt-names:
+    minItems: 1
+    maxItems: 2
 
   clocks:
     items:
@@ -134,6 +144,7 @@ properties:
       dt-binding/memory/mediatek,mt8188-memory-port.h for mt8188,
       dt-binding/memory/mediatek,mt8189-memory-port.h for mt8189,
       dt-binding/memory/mt2701-larb-port.h for mt2701 and mt7623,
+      dt-binding/memory/mt6589-larb-port.h for mt6589,
       dt-binding/memory/mt2712-larb-port.h for mt2712,
       dt-binding/memory/mt6779-larb-port.h for mt6779,
       dt-binding/memory/mt6795-larb-port.h for mt6795,
@@ -156,6 +167,42 @@ required:
   - '#iommu-cells'
 
 allOf:
+  - if:
+      properties:
+        compatible:
+          not:
+            contains:
+              const: mediatek,mt6589-m4u
+    then:
+      properties:
+        reg:
+          maxItems: 1
+        interrupts:
+          maxItems: 1
+      required:
+        - reg
+    else:
+      properties:
+        reg:
+          minItems: 4
+          maxItems: 4
+        interrupts:
+          minItems: 2
+          maxItems: 2
+        reg-names:
+          items:
+            - const: global
+            - const: m4u0
+            - const: m4u1
+            - const: l2cache
+        interrupt-names:
+          items:
+            - const: m4u0
+            - const: m4u1
+      required:
+        - reg-names
+        - interrupt-names
+
   - if:
       properties:
         compatible:
@@ -163,6 +210,7 @@ allOf:
             enum:
               - mediatek,mt2701-m4u
               - mediatek,mt2712-m4u
+              - mediatek,mt6589-m4u
               - mediatek,mt6795-m4u
               - mediatek,mt6893-iommu-mm
               - mediatek,mt8173-m4u
@@ -241,3 +289,23 @@ examples:
                              <&larb3>, <&larb4>, <&larb5>;
             #iommu-cells = <1>;
     };
+
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    iommu@10208000 {
+        compatible = "mediatek,mt6589-m4u";
+        reg = <0x10205000 0x200>,
+              <0x10205200 0x600>,
+              <0x10205800 0x600>,
+              <0x10205100 0x100>;
+        reg-names = "global", "m4u0", "m4u1", "l2cache";
+        interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_LOW>,
+                     <GIC_SPI 142 IRQ_TYPE_LEVEL_LOW>;
+        interrupt-names = "m4u0", "m4u1";
+        clocks = <&infracfg CLK_INFRA_M4U>;
+        clock-names = "bclk";
+        mediatek,larbs = <&larb0>, <&larb1>, <&larb2>,
+                         <&larb3>, <&larb4>, <&larb5>;
+        #iommu-cells = <1>;
+    };
diff --git a/arch/arm/boot/dts/mediatek/mt6589.dtsi b/arch/arm/boot/dts/mediatek/mt6589.dtsi
index 6ea8b76af910..241fa2afbd86 100644
--- a/arch/arm/boot/dts/mediatek/mt6589.dtsi
+++ b/arch/arm/boot/dts/mediatek/mt6589.dtsi
@@ -431,8 +431,9 @@ iommu: iommu@10205000 {
 			compatible = "mediatek,mt6589-m4u";
 			reg = <0x10205000 0x200>,
 			      <0x10205200 0x600>,
-			      <0x10205800 0x600>;
-			reg-names = "global", "m4u0", "m4u1";
+			      <0x10205800 0x600>,
+			      <0x10205100 0x100>;
+			reg-names = "global", "m4u0", "m4u1", "l2cache";
 			interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_LOW>,
 				     <GIC_SPI 106 IRQ_TYPE_LEVEL_LOW>,
 				     <GIC_SPI 107 IRQ_TYPE_LEVEL_LOW>,
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 591ffa66a728..39c3b18dbc23 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -7,6 +7,8 @@
  *
  * Based on driver/iommu/mtk_iommu.c
  */
+#include "linux/types.h"
+#include <linux/array_size.h>
 #include <linux/bug.h>
 #include <linux/clk.h>
 #include <linux/component.h>
@@ -29,6 +31,7 @@
 #include <asm/barrier.h>
 #include <dt-bindings/memory/mtk-memory-port.h>
 #include <dt-bindings/memory/mt2701-larb-port.h>
+#include <dt-bindings/memory/mt6589-larb-port.h>
 #include <soc/mediatek/smi.h>
 
 #if defined(CONFIG_ARM)
@@ -51,6 +54,7 @@ struct dma_iommu_mapping {
 #define F_MMU_FAULT_VA_MSK			0xfffff000
 #define MTK_PROTECT_PA_ALIGN			128
 
+/* -------- Common M4U v1 register definitions (MT2701 and MT6589 core) -------- */
 #define REG_MMU_CTRL_REG			0x210
 #define F_MMU_CTRL_COHERENT_EN			BIT(8)
 #define REG_MMU_IVRP_PADDR			0x214
@@ -71,6 +75,8 @@ struct dma_iommu_mapping {
 #define REG_MMU_FAULT_VA			0x228
 #define REG_MMU_INVLD_PA			0x22C
 #define REG_MMU_INT_ID				0x388
+
+/* MT2701 specific (core space) */
 #define REG_MMU_INVALIDATE			0x5c0
 #define REG_MMU_INVLD_START_A			0x5c4
 #define REG_MMU_INVLD_END_A			0x5c8
@@ -81,14 +87,66 @@ struct dma_iommu_mapping {
 #define REG_MMU_DCM				0x5f0
 #define F_MMU_DCM_ON				BIT(1)
 #define REG_MMU_CPE_DONE			0x60c
+
+/* MT6589 global space registers */
+#define REG_MMUg_CTRL				0x00
+#define F_MMUg_CTRL_INV_EN0			BIT(0)
+#define F_MMUg_CTRL_INV_EN1			BIT(1)
+#define F_MMUg_CTRL_INV_EN2			BIT(2)	/* L2 */
+#define F_MMUg_CTRL_PRE_LOCK(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_CTRL_PRE_EN			BIT(4)
+
+#define REG_MMUg_INVLD				0x04
+#define F_MMUg_INV_ALL				0x2
+#define F_MMUg_INV_RANGE			0x1
+
+#define REG_MMUg_INVLD_SA			0x08
+#define REG_MMUg_INVLD_EA			0x0C
+#define REG_MMUg_PT_BASE			0x10
+#define F_MMUg_PT_VA_MSK			0xffff0000
+
+#define REG_MMUg_L2_SEL				0x18
+#define F_MMUg_L2_SEL_FLUSH_EN(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_L2_SEL_L2_ULTRA(en)		((en) ? BIT(2) : 0)
+#define F_MMUg_L2_SEL_L2_SHARE(en)		((en) ? BIT(1) : 0)
+#define F_MMUg_L2_SEL_L2_BUS_SEL(go_emi)	((go_emi) ? BIT(0) : 0)
+
+#define REG_MMUg_DCM				0x1C
+#define F_MMUg_DCM_ON(on)			((on) ? BIT(0) : 0)
+
+/* L2 cache registers (MT6589) */
+#define REG_L2_GDC_STATE			0x00
+#define F_L2_GDC_ST_EVENT_MSK			GENMASK(7,6)
+#define F_L2_GDC_ST_EVENT_VAL(val)		(((val) & 0x3) << 6)
+
+#define REG_L2_GDC_OP				0x04
+#define F_L2_GDC_BYPASS(en)			((en) ? BIT(10) : 0)
+#define F_L2_GDC_PERF_MASK(msk)			(((msk) & 0x7) << 7)
+#define GDC_PERF_MASK_HIT_MISS			0
+#define F_L2_GDC_LOCK_ALERT_DIS(dis)		((dis) ? BIT(6) : 0)
+#define F_L2_GDC_PERF_EN(en)			((en) ? BIT(5) : 0)
+#define F_L2_GDC_LOCK_TH(th)			(((th) & 0x3) << 2)
+#define F_L2_GDC_PAUSE_OP(op)			((op) & 0x3)
+#define GDC_NO_PAUSE				0
+
+#define REG_L2_GPE_STATUS			0x18
+#define F_L2_GPE_ST_RANGE_INV_DONE		BIT(1)
+#define F_L2_GPE_ST_PREFETCH_DONE		BIT(0)
+
+/* MT6589 core PFH distance / direction registers */
+#define REG_MMU_PFH_DIST(port)			(0x80 + (((port) >> 3) << 2))
+#define F_MMU_PFH_DIST_VAL(port, val)		(((val) & 0xf) << (((port) & 0x7) << 2))
+#define F_MMU_PFH_DIST_MASK(port)		F_MMU_PFH_DIST_VAL(port, 0xf)
+
+#define REG_MMU_PFH_DIR(port)			(((port) < 32) ? 0xF0 : 0xF4)
+#define F_MMU_PFH_DIR(port, val)		((!!(val)) << ((port) & 0x1f))
+
+/* Common page table descriptor bits */
 #define F_DESC_VALID				0x2
 #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)
 /* 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)
-#define MT2701_LARB_NR_MAX			3
 
 /*
  * MTK m4u support 4GB iova address space, and only support 4K page
@@ -96,26 +154,75 @@ struct dma_iommu_mapping {
  */
 #define M2701_IOMMU_PGT_SIZE			SZ_4M
 
+#define MAX_M4U_CORES				2
+
+struct mtk_iommu_v1_data;
+
+struct mtk_iommu_v1_soc_data {
+	const char *compatible;
+	unsigned int num_cores;
+	bool has_global_base;
+	bool has_l2_cache;
+
+	void (*tlb_flush_all)(struct mtk_iommu_v1_data *data);
+	void (*tlb_flush_range)(struct mtk_iommu_v1_data *data,
+				unsigned long iova, size_t size);
+
+	void (*get_fault_larb_port)(u32 int_id, unsigned int *larb,
+				    unsigned int *port);
+
+	int (*hw_init)(struct mtk_iommu_v1_data *data);
+
+	u32 pt_base_reg_offset;
+	bool pt_base_in_global;
+
+	const int *larb_port_offsets;
+	unsigned int num_larb;
+};
+
+struct mtk_iommu_v1_core {
+	void __iomem *base;
+	int irq;
+	struct mtk_iommu_v1_data *data;
+	unsigned int id;
+};
+
 struct mtk_iommu_v1_suspend_reg {
+	/* MT2701 fields */
 	u32			standard_axi_mode;
 	u32			dcm_dis;
 	u32			ctrl_reg;
 	u32			int_control0;
+
+	/* MT6589 additional fields */
+	u32			mmug_ctrl;
+	u32			mmug_pt_base;
+	u32			mmug_l2_sel;
+	u32			mmug_dcm;
+	u32			l2_gdc_op;
+	u32			smi_l1len;
+	u32			smi_bus_sel;
+	u32			smi_secur_con[7];
 };
 
 struct mtk_iommu_v1_data {
-	void __iomem			*base;
-	int				irq;
-	struct device			*dev;
-	struct clk			*bclk;
-	phys_addr_t			protect_base; /* protect memory base */
-	struct mtk_iommu_v1_domain	*m4u_dom;
-
-	struct iommu_device		iommu;
-	struct dma_iommu_mapping	*mapping;
-	struct mtk_smi_larb_iommu	larb_imu[MTK_LARB_NR_MAX];
-
-	struct mtk_iommu_v1_suspend_reg	reg;
+	const struct mtk_iommu_v1_soc_data *soc;
+	struct device *dev;
+
+	struct mtk_iommu_v1_core cores[MAX_M4U_CORES];
+	void __iomem *global_base;
+	void __iomem *l2_base;
+
+	struct clk *bclk;
+	phys_addr_t protect_base;
+	struct mtk_iommu_v1_domain *m4u_dom;
+
+	struct iommu_device iommu;
+	struct dma_iommu_mapping *mapping;
+	struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
+
+	struct mtk_iommu_v1_suspend_reg reg;
+	struct page *dummy_page; /* Physical address of guard dummy page */
 };
 
 struct mtk_iommu_v1_domain {
@@ -150,73 +257,143 @@ static const int mt2701_m4u_in_larb[] = {
 	MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET
 };
 
-static inline int mt2701_m4u_to_larb(int id)
+static const int mt6589_m4u_in_larb[] = {
+	MT6589_LARB0_PORT_OFFSET, MT6589_LARB1_PORT_OFFSET,
+	MT6589_LARB2_PORT_OFFSET, MT6589_LARB3_PORT_OFFSET,
+	MT6589_LARB4_PORT_OFFSET, MT6589_LARB5_PORT_OFFSET
+};
+
+static inline int mtk_iommu_v1_to_larb(struct mtk_iommu_v1_data *data, int id)
 {
+	const int *offsets = data->soc->larb_port_offsets;
+	int num = data->soc->num_larb;
 	int i;
 
-	for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--)
-		if ((id) >= mt2701_m4u_in_larb[i])
+	for (i = num - 1; i >= 0; i--)
+		if (id >= offsets[i])
 			return i;
 
 	return 0;
 }
 
-static inline int mt2701_m4u_to_port(int id)
+static inline int mtk_iommu_v1_to_port(struct mtk_iommu_v1_data *data, int id)
 {
-	int larb = mt2701_m4u_to_larb(id);
+	int larb = mtk_iommu_v1_to_larb(data, id);
+	return id - data->soc->larb_port_offsets[larb];
+}
 
-	return id - mt2701_m4u_in_larb[larb];
+static inline void m4u_set_field(void __iomem *base, u32 reg, u32 mask, u32 val)
+{
+	u32 regval = readl_relaxed(base + reg);
+	regval = (regval & ~mask) | val;
+	writel_relaxed(regval, base + reg);
 }
 
-static void mtk_iommu_v1_tlb_flush_all(struct mtk_iommu_v1_data *data)
+/* MT2701 (single core, no global space) */
+static void mt2701_tlb_flush_all(struct mtk_iommu_v1_data *data)
 {
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-			data->base + REG_MMU_INV_SEL);
-	writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
-	wmb(); /* Make sure the tlb flush all done */
+	void __iomem *base = data->cores[0].base;
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(F_ALL_INVLD, base + REG_MMU_INVALIDATE);
+	wmb();
 }
 
-static void mtk_iommu_v1_tlb_flush_range(struct mtk_iommu_v1_data *data,
-					 unsigned long iova, size_t size)
+static void mt2701_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
 {
-	int ret;
+	void __iomem *base = data->cores[0].base;
 	u32 tmp;
+	int ret;
 
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-		data->base + REG_MMU_INV_SEL);
-	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
-		data->base + REG_MMU_INVLD_START_A);
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK, 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);
+		   base + REG_MMU_INVLD_END_A);
+	writel_relaxed(F_MMU_INV_RANGE, base + REG_MMU_INVALIDATE);
 
-	ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
-				tmp, tmp != 0, 10, 100000);
+	ret = readl_poll_timeout_atomic(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);
+		mt2701_tlb_flush_all(data);
+	}
+	writel_relaxed(0, base + REG_MMU_CPE_DONE);
+}
+
+/* MT6589 (global control, L2) */
+static void mt6589_tlb_flush_all(struct mtk_iommu_v1_data *data)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(F_MMUg_INV_ALL, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 event;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GDC_STATE,
+					 event,
+					 event & F_L2_GDC_ST_EVENT_MSK,
+					 10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GDC_STATE);
 	}
-	/* Clear the CPE status */
-	writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+}
+
+static void mt6589_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_SA);
+	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_EA);
+	writel_relaxed(F_MMUg_INV_RANGE, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 status;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GPE_STATUS,
+					  status,
+					  status & F_L2_GPE_ST_RANGE_INV_DONE,
+					  10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GPE_STATUS);
+	}
+}
+
+static void mt2701_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - ((int_id >> 13) & 0x7);
+	*port = (int_id >> 8) & 0xF;
+}
+
+static void mt6589_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - (int_id >> 12) & 0x7;
+	*port = (int_id >> 8) & 0xF;
 }
 
 static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 {
-	struct mtk_iommu_v1_data *data = dev_id;
+	struct mtk_iommu_v1_core *core = dev_id;
+	struct mtk_iommu_v1_data *data = core->data;
 	struct mtk_iommu_v1_domain *dom = data->m4u_dom;
 	u32 int_state, regval, fault_iova, fault_pa;
 	unsigned int fault_larb, fault_port;
 
 	/* Read error information from registers */
-	int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST);
-	fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
+	int_state = readl_relaxed(core->base + REG_MMU_FAULT_ST);
+	fault_iova = readl_relaxed(core->base + REG_MMU_FAULT_VA) & F_MMU_FAULT_VA_MSK;
+	fault_pa = readl_relaxed(core->base + REG_MMU_INVLD_PA);
+	regval = readl_relaxed(core->base + REG_MMU_INT_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);
+	data->soc->get_fault_larb_port(regval, &fault_larb, &fault_port);
 
 	/*
 	 * MTK v1 iommu HW could not determine whether the fault is read or
@@ -225,16 +402,16 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 	if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
 			IOMMU_FAULT_READ))
 		dev_err_ratelimited(data->dev,
-			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n",
+			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d core=%d\n",
 			int_state, fault_iova, fault_pa,
-			fault_larb, fault_port);
+			fault_larb, fault_port, core->id);
 
 	/* Interrupt clear */
-	regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL);
+	regval = readl_relaxed(core->base + REG_MMU_INT_CONTROL);
 	regval |= F_INT_CLR_BIT;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, core->base + REG_MMU_INT_CONTROL);
 
-	mtk_iommu_v1_tlb_flush_all(data);
+	data->soc->tlb_flush_all(data);
 
 	return IRQ_HANDLED;
 }
@@ -242,14 +419,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 				struct device *dev, bool enable)
 {
-	struct mtk_smi_larb_iommu    *larb_mmu;
-	unsigned int                 larbid, portid;
+	struct mtk_smi_larb_iommu *larb_mmu;
 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	int i;
+	unsigned int larbid, portid, i, mmu_id;
+	void __iomem *base;
 
 	for (i = 0; i < fwspec->num_ids; ++i) {
-		larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
-		portid = mt2701_m4u_to_port(fwspec->ids[i]);
+		larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+		portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
 		larb_mmu = &data->larb_imu[larbid];
 
 		dev_dbg(dev, "%s iommu port: %d\n",
@@ -260,6 +437,26 @@ static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 		else
 			larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid);
 	}
+
+	/* MT6589 specific: set default prefetch distance & direction */
+	if (data->soc->has_global_base) {
+		for (i = 0; i < fwspec->num_ids; i++) {
+			portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
+			larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+
+			/* Determine which M4U core this LARB is connected to */
+			mmu_id = (larbid == 0 || larbid == 2) ? 0 : 1;
+			base = data->cores[mmu_id].base;
+
+			/* Set distance = 1 */
+			m4u_set_field(base, REG_MMU_PFH_DIST(portid),
+				      F_MMU_PFH_DIST_MASK(portid),
+				      F_MMU_PFH_DIST_VAL(portid, 1));
+			/* Set direction = 0 */
+			m4u_set_field(base, REG_MMU_PFH_DIR(portid),
+				      1 << (portid & 0x1f), 0);
+		}
+	}
 }
 
 static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
@@ -273,7 +470,10 @@ static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
 	if (!dom->pgt_va)
 		return -ENOMEM;
 
-	writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR);
+	if (data->soc->pt_base_in_global)
+		writel(dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+	else
+		writel(dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
 
 	dom->data = data;
 
@@ -354,10 +554,12 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 			    int prot, gfp_t gfp, size_t *mapped)
 {
 	struct mtk_iommu_v1_domain *dom = to_mtk_domain(domain);
+	struct mtk_iommu_v1_data *data = dom->data;
 	unsigned long flags;
-	unsigned int i;
-	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT);
+	unsigned int i, guard_pages = (data->soc->has_global_base) ? 4 : 0;
+	u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT);
 	u32 pabase = (u32)paddr;
+	phys_addr_t dummy_pa = page_to_phys(data->dummy_page);
 
 	spin_lock_irqsave(&dom->pgtlock, flags);
 	for (i = 0; i < pgcount; i++) {
@@ -367,10 +569,19 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 		pabase += MT2701_IOMMU_PAGE_SIZE;
 	}
 
+	if (guard_pages && i == pgcount) {
+		for (i = pgcount; i < pgcount + guard_pages; i++) {
+			if ((iova >> MT2701_IOMMU_PAGE_SHIFT) + i >= (M2701_IOMMU_PGT_SIZE / sizeof(u32)))
+				break;
+			if (pgt_base_iova[i] == 0)
+				pgt_base_iova[i] = dummy_pa | F_DESC_VALID | F_DESC_NONSEC;
+		}
+	}
+
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
 	*mapped = i * MT2701_IOMMU_PAGE_SIZE;
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, *mapped);
+	data->soc->tlb_flush_range(data, iova, *mapped);
 
 	return i == pgcount ? 0 : -EEXIST;
 }
@@ -388,7 +599,7 @@ static size_t mtk_iommu_v1_unmap(struct iommu_domain *domain, unsigned long iova
 	memset(pgt_base_iova, 0, pgcount * sizeof(u32));
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, size);
+	dom->data->soc->tlb_flush_range(dom->data, iova, size);
 
 	return size;
 }
@@ -489,12 +700,12 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev)
 	data = dev_iommu_priv_get(dev);
 
 	/* Link the consumer device with the smi-larb device(supplier) */
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
-	if (larbid >= MT2701_LARB_NR_MAX)
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
+	if (larbid >= MTK_LARB_NR_MAX)
 		return ERR_PTR(-EINVAL);
 
 	for (idx = 1; idx < fwspec->num_ids; idx++) {
-		larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]);
+		larbidx = mtk_iommu_v1_to_larb(data, fwspec->ids[idx]);
 		if (larbid != larbidx) {
 			dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
 				larbid, larbidx);
@@ -532,12 +743,12 @@ static void mtk_iommu_v1_release_device(struct device *dev)
 	unsigned int larbid;
 
 	data = dev_iommu_priv_get(dev);
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
 	larbdev = data->larb_imu[larbid].dev;
 	device_link_remove(dev, larbdev);
 }
 
-static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
+static int mt2701_hw_init(struct mtk_iommu_v1_data *data)
 {
 	u32 regval;
 	int ret;
@@ -549,7 +760,7 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 	}
 
 	regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2);
-	writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_CTRL_REG);
 
 	regval = F_INT_TRANSLATION_FAULT |
 		F_INT_MAIN_MULTI_HIT_FAULT |
@@ -559,23 +770,66 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 		F_INT_TLB_MISS_FAULT |
 		F_INT_PFH_DMA_FIFO_OVERFLOW |
 		F_INT_MISS_DMA_FIFO_OVERFLOW;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_INT_CONTROL);
 
-	/* protect memory,hw will write here while translation fault */
-	writel_relaxed(data->protect_base,
-			data->base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(data->protect_base, data->cores[0].base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(F_MMU_DCM_ON, data->cores[0].base + REG_MMU_DCM);
 
-	writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM);
+	return 0;
+}
 
-	if (devm_request_irq(data->dev, data->irq, mtk_iommu_v1_isr, 0,
-			     dev_name(data->dev), (void *)data)) {
-		writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
-		clk_disable_unprepare(data->bclk);
-		dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
-		return -ENODEV;
+static int mt6589_hw_init(struct mtk_iommu_v1_data *data)
+{
+	u32 regval;
+	int i, ret;
+
+	ret = clk_prepare_enable(data->bclk);
+	if (ret) {
+		dev_err(data->dev, "Failed to enable bclk\n");
+		goto err_clk;
+	}
+
+	/* ---- Global registers ---- */
+	writel_relaxed(F_MMUg_L2_SEL_FLUSH_EN(1) | F_MMUg_L2_SEL_L2_ULTRA(1) |
+		   F_MMUg_L2_SEL_L2_SHARE(0) | F_MMUg_L2_SEL_L2_BUS_SEL(1),
+		   data->global_base + REG_MMUg_L2_SEL);
+	writel_relaxed(F_MMUg_DCM_ON(1), data->global_base + REG_MMUg_DCM);
+
+	/* ---- L2 cache ---- */
+	if (data->l2_base) {
+		regval = F_L2_GDC_BYPASS(0) |
+			 F_L2_GDC_PERF_MASK(GDC_PERF_MASK_HIT_MISS) |
+			 F_L2_GDC_LOCK_ALERT_DIS(0) |
+			 F_L2_GDC_LOCK_TH(3) |
+			 F_L2_GDC_PAUSE_OP(GDC_NO_PAUSE);
+		writel_relaxed(regval, data->l2_base + REG_L2_GDC_OP);
+	}
+
+	/* ---- Per-core setup ---- */
+	for (i = 0; i < data->soc->num_cores; i++) {
+		void __iomem *base = data->cores[i].base;
+
+		regval = 0;  /* PFH enabled, walk enabled, cohere disabled */
+		regval |= F_MMU_TF_PROTECT_SEL(2);
+		writel_relaxed(regval, base + REG_MMU_CTRL_REG);
+
+		regval = F_INT_TRANSLATION_FAULT |
+			 F_INT_MAIN_MULTI_HIT_FAULT |
+			 F_INT_INVALID_PA_FAULT |
+			 F_INT_ENTRY_REPLACEMENT_FAULT |
+			 F_INT_TABLE_WALK_FAULT |
+			 F_INT_TLB_MISS_FAULT |
+			 F_INT_PFH_DMA_FIFO_OVERFLOW |
+			 F_INT_MISS_DMA_FIFO_OVERFLOW;
+		writel_relaxed(regval, base + REG_MMU_INT_CONTROL);
+
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
 	}
 
 	return 0;
+
+err_clk:
+	return ret;
 }
 
 static const struct iommu_ops mtk_iommu_v1_ops = {
@@ -595,8 +849,39 @@ static const struct iommu_ops mtk_iommu_v1_ops = {
 	}
 };
 
+static const struct mtk_iommu_v1_soc_data mt2701_soc_data = {
+	.compatible = "mediatek,mt2701-m4u",
+	.num_cores = 1,
+	.has_global_base = false,
+	.has_l2_cache = false,
+	.tlb_flush_all = mt2701_tlb_flush_all,
+	.tlb_flush_range = mt2701_tlb_flush_range,
+	.get_fault_larb_port = mt2701_get_fault_larb_port,
+	.hw_init = mt2701_hw_init,
+	.pt_base_reg_offset = REG_MMU_PT_BASE_ADDR,
+	.pt_base_in_global = false,
+	.larb_port_offsets = mt2701_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt2701_m4u_in_larb),
+};
+
+static const struct mtk_iommu_v1_soc_data mt6589_soc_data = {
+	.compatible = "mediatek,mt6589-m4u",
+	.num_cores = 2,
+	.has_global_base = true,
+	.has_l2_cache = true,
+	.tlb_flush_all = mt6589_tlb_flush_all,
+	.tlb_flush_range = mt6589_tlb_flush_range,
+	.get_fault_larb_port = mt6589_get_fault_larb_port,
+	.hw_init = mt6589_hw_init,
+	.pt_base_reg_offset = REG_MMUg_PT_BASE,
+	.pt_base_in_global = true,
+	.larb_port_offsets = mt6589_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt6589_m4u_in_larb),
+};
+
 static const struct of_device_id mtk_iommu_v1_of_ids[] = {
-	{ .compatible = "mediatek,mt2701-m4u", },
+	{ .compatible = "mediatek,mt2701-m4u", .data = &mt2701_soc_data },
+	{ .compatible = "mediatek,mt6589-m4u", .data = &mt6589_soc_data },
 	{}
 };
 MODULE_DEVICE_TABLE(of, mtk_iommu_v1_of_ids);
@@ -610,16 +895,19 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 {
 	struct device			*dev = &pdev->dev;
 	struct mtk_iommu_v1_data	*data;
-	struct resource			*res;
+	const struct mtk_iommu_v1_soc_data *soc;
 	struct component_match		*match = NULL;
 	void				*protect;
 	int				larb_nr, ret, i;
+	struct page			*dummy_page;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
 	data->dev = dev;
+	soc = of_device_get_match_data(dev);
+	data->soc = soc;
 
 	/* Protect memory. HW will access here while translation fault.*/
 	protect = devm_kcalloc(dev, 2, MTK_PROTECT_PA_ALIGN,
@@ -628,26 +916,69 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	data->base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(data->base))
-		return PTR_ERR(data->base);
-
-	data->irq = platform_get_irq(pdev, 0);
-	if (data->irq < 0)
-		return data->irq;
+	if (soc->has_global_base) {
+		data->global_base = devm_platform_ioremap_resource_byname(pdev,
+									  "global");
+		if (IS_ERR(data->global_base))
+			return PTR_ERR(data->global_base);
+	}
+	for (i = 0; i < soc->num_cores; i++) {
+		char name[8];
+		snprintf(name, sizeof(name), "m4u%d", i);
+		data->cores[i].base = devm_platform_ioremap_resource_byname(pdev,
+									    name);
+		if (IS_ERR(data->cores[i].base))
+			return PTR_ERR(data->cores[i].base);
+		data->cores[i].data = data;
+		data->cores[i].id = i;
+	}
+	if (soc->has_l2_cache) {
+		data->l2_base = devm_platform_ioremap_resource_byname(pdev,
+								      "l2cache");
+		if (IS_ERR(data->l2_base))
+			return PTR_ERR(data->l2_base);
+	}
 
 	data->bclk = devm_clk_get(dev, "bclk");
 	if (IS_ERR(data->bclk))
 		return PTR_ERR(data->bclk);
 
+	/* Interrupts - request after hw_init to avoid spurious IRQs? but
+	   we need the core IRQs ready before registering ISR. We'll request
+	   them after hw_init for simplicity. */
+	ret = soc->hw_init(data);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < soc->num_cores; i++) {
+		struct mtk_iommu_v1_core *core = &data->cores[i];
+		char irqname[8];
+		snprintf(irqname, sizeof(irqname), "m4u%d", i);
+		core->irq = platform_get_irq_byname(pdev, irqname);
+		if (core->irq < 0) {
+			ret = core->irq;
+			goto out_clk_unprepare;
+		}
+		ret = devm_request_irq(dev, core->irq, mtk_iommu_v1_isr, 0,
+				       dev_name(dev), core);
+		if (ret) {
+			dev_err(dev, "Failed to request IRQ %d for core%d\n",
+				core->irq, i);
+			goto out_clk_unprepare;
+		}
+	}
+
 	larb_nr = of_count_phandle_with_args(dev->of_node,
 					     "mediatek,larbs", NULL);
-	if (larb_nr < 0)
-		return larb_nr;
+	if (larb_nr < 0) {
+		ret = larb_nr;
+		goto out_clk_unprepare;
+	}
 
-	if (larb_nr > MTK_LARB_NR_MAX)
-		return -EINVAL;
+	if (larb_nr > MTK_LARB_NR_MAX) {
+		ret = -EINVAL;
+		goto out_clk_unprepare;
+	}
 
 	for (i = 0; i < larb_nr; i++) {
 		struct device_node *larbnode;
@@ -682,17 +1013,20 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 					    component_compare_of, larbnode);
 	}
 
+	dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+	if (!dummy_page) {
+		ret = -ENOMEM;
+		goto out_put_larbs;
+	}
+	data->dummy_page = dummy_page;
+
 	platform_set_drvdata(pdev, data);
 
-	ret = mtk_iommu_v1_hw_init(data);
+	ret = iommu_device_sysfs_add(&data->iommu, dev, NULL,
+				     dev_name(dev));
 	if (ret)
 		goto out_put_larbs;
 
-	ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
-				     dev_name(&pdev->dev));
-	if (ret)
-		goto out_clk_unprepare;
-
 	ret = iommu_device_register(&data->iommu, &mtk_iommu_v1_ops, dev);
 	if (ret)
 		goto out_sysfs_remove;
@@ -706,12 +1040,14 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 	iommu_device_unregister(&data->iommu);
 out_sysfs_remove:
 	iommu_device_sysfs_remove(&data->iommu);
-out_clk_unprepare:
-	clk_disable_unprepare(data->bclk);
 out_put_larbs:
 	for (i = 0; i < MTK_LARB_NR_MAX; i++)
-		put_device(data->larb_imu[i].dev);
-
+		if (data->larb_imu[i].dev)
+			put_device(data->larb_imu[i].dev);
+	if (data->dummy_page)
+		__free_page(data->dummy_page);
+out_clk_unprepare:
+	clk_disable_unprepare(data->bclk);
 	return ret;
 }
 
@@ -724,40 +1060,128 @@ static void mtk_iommu_v1_remove(struct platform_device *pdev)
 	iommu_device_unregister(&data->iommu);
 
 	clk_disable_unprepare(data->bclk);
-	devm_free_irq(&pdev->dev, data->irq, data);
+	for (i = 0; i < data->soc->num_cores; i++)
+		devm_free_irq(&pdev->dev, data->cores[i].irq, &data->cores[i]);
 	component_master_del(&pdev->dev, &mtk_iommu_v1_com_ops);
+	__free_page(data->dummy_page);
 
 	for (i = 0; i < MTK_LARB_NR_MAX; i++)
-		put_device(data->larb_imu[i].dev);
+		if (data->larb_imu[i].dev)
+			put_device(data->larb_imu[i].dev);
 }
 
 static int __maybe_unused mtk_iommu_v1_suspend(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
+	void __iomem *base = data->cores[0].base;
 
-	reg->standard_axi_mode = readl_relaxed(base +
-					       REG_MMU_STANDARD_AXI_MODE);
-	reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	/* Common core registers */
 	reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
 	reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL);
+
+	if (data->soc->has_global_base) {
+		reg->mmug_ctrl = readl_relaxed(data->global_base + REG_MMUg_CTRL);
+		reg->mmug_pt_base = readl_relaxed(data->global_base + REG_MMUg_PT_BASE);
+		reg->mmug_l2_sel = readl_relaxed(data->global_base + REG_MMUg_L2_SEL);
+		reg->mmug_dcm = readl_relaxed(data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			reg->l2_gdc_op = readl_relaxed(data->l2_base + REG_L2_GDC_OP);
+
+		/* Save SMI registers (temporary) */
+		void __iomem *smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+		void __iomem *smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+		if (smi_ext && smi_ao) {
+			reg->smi_l1len = readl_relaxed(smi_ext + REG_SMI_L1LEN);
+			reg->smi_bus_sel = readl_relaxed(smi_ext + REG_SMI_BUS_SEL);
+			for (int i = 0; i < 7; i++)
+				reg->smi_secur_con[i] = readl_relaxed(smi_ao + REG_SMI_SECUR_CON(i));
+		}
+		iounmap(smi_ext);
+		iounmap(smi_ao);
+	} else {
+		/* MT2701 specific */
+		base = data->cores[0].base;
+		reg->standard_axi_mode = readl_relaxed(base + REG_MMU_STANDARD_AXI_MODE);
+		reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	}
+
 	return 0;
 }
 
+static void mt6589_restore_pfh_settings(struct mtk_iommu_v1_data *data)
+{
+	const int *offsets = data->soc->larb_port_offsets;
+	int num_larb = data->soc->num_larb;
+	int larb, port;
+
+	for (larb = 0; larb < num_larb; larb++) {
+		int mmu_id = (larb == 0 || larb == 2) ? 0 : 1;
+		void __iomem *base = data->cores[mmu_id].base;
+		int max_port = (larb == num_larb - 1) ? 32 : offsets[larb+1] - offsets[larb];
+		for (port = 0; port < max_port; port++) {
+			int global_port = offsets[larb] + port;
+			m4u_set_field(base, REG_MMU_PFH_DIST(global_port),
+						  F_MMU_PFH_DIST_MASK(global_port),
+						  F_MMU_PFH_DIST_VAL(global_port, 1));
+			m4u_set_field(base, REG_MMU_PFH_DIR(global_port),
+						  1 << (global_port & 0x1f), 0);
+		}
+	}
+}
+
 static int __maybe_unused mtk_iommu_v1_resume(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
-
-	writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR);
-	writel_relaxed(reg->standard_axi_mode,
-		       base + REG_MMU_STANDARD_AXI_MODE);
-	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
-	writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
-	writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
-	writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	void __iomem *base = data->cores[0].base;
+
+	if (data->soc->has_global_base) {
+		writel_relaxed(reg->mmug_ctrl, data->global_base + REG_MMUg_CTRL);
+		writel_relaxed(reg->mmug_pt_base, data->global_base + REG_MMUg_PT_BASE);
+		writel_relaxed(reg->mmug_l2_sel, data->global_base + REG_MMUg_L2_SEL);
+		writel_relaxed(reg->mmug_dcm, data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			writel_relaxed(reg->l2_gdc_op, data->l2_base + REG_L2_GDC_OP);
+
+		/* Restore SMI registers */
+		void __iomem *smi_ext = ioremap(SMI_COMMON_EXT_BASE, 0x500);
+		void __iomem *smi_ao = ioremap(SMI_COMMON_AO_BASE, 0x1000);
+		if (smi_ext && smi_ao) {
+			writel_relaxed(reg->smi_l1len, smi_ext + REG_SMI_L1LEN);
+			writel_relaxed(reg->smi_bus_sel, smi_ext + REG_SMI_BUS_SEL);
+			for (int i = 0; i < 7; i++)
+				writel_relaxed(reg->smi_secur_con[i], smi_ao + REG_SMI_SECUR_CON(i));
+		}
+		iounmap(smi_ext);
+		iounmap(smi_ao);
+	}
+
+	/* Per-core restore (common) */
+	for (int i = 0; i < data->soc->num_cores; i++) {
+		base = data->cores[i].base;
+		writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
+		writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	}
+
+	if (!data->soc->has_global_base) {
+		/* MT2701 extra */
+		base = data->cores[0].base;
+		writel_relaxed(reg->standard_axi_mode, base + REG_MMU_STANDARD_AXI_MODE);
+		writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
+	}
+
+	if (data->m4u_dom && data->m4u_dom->pgt_pa) {
+		if (data->soc->pt_base_in_global)
+			writel_relaxed(data->m4u_dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+		else
+			writel_relaxed(data->m4u_dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
+	}
+
+	if (data->soc->has_global_base)
+		mt6589_restore_pfh_settings(data);
+
 	return 0;
 }
 

akku1139 added 2 commits July 4, 2026 22:48
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member Author
    //config G3D route to EMI (MCI in default)
    m4uHw_set_field_by_mask(0, REG_SMI_L1LEN, F_SMI_L1LEN_AXROUTE_G3D_EMI(1), F_SMI_L1LEN_AXROUTE_G3D_EMI(1));
    //config AUDIO route to EMI (MCI in default)
    m4uHw_set_field_by_mask(0, REG_SMI_L1LEN, F_SMI_L1LEN_AXROUTE_AUDIO_EMI(1), F_SMI_L1LEN_AXROUTE_AUDIO_EMI(1));

https://github.com/bq/aquaris-5/blob/bc1d0d0e35c969670e2d869001be944ad0c9c5e5/mediatek/platform/mt6589/kernel/drivers/m4u/m4u.c#L4525-L4528

@akku1139

akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member Author
//===============================
// LARB
//===============================
    /* cache coherent operations:

        SMI_SHARE_EN    SMI_ROUTE_ENABLE    coherent_EN+pagetable_coherent  operation
            0               1                       0                       EMI
            0               1                       1                       MCI+snoop
            0               0                       0                       MCI
            0               0                       1                       MCI+snoop
            1               1                       0                       MCI+snoop
            1               1                       1                       MCI+snoop
            1               0                       0                       MCI+snoop
            1               0                       1                       MCI+snoop
    */
    
    {
        int i;
        for(i=0; i<SMI_LARB_NR; i++)
        {
            larb_clock_on(i);
            //set SMI_SHARE_EN to 0
            M4U_WriteReg32(gLarbBaseAddr[i], SMI_SHARE_EN, 0x0);
            //set SMI_ROUTE_SEL to 1
            if(i==2)
                M4U_WriteReg32(gLarbBaseAddr[i], SMI_ROUTE_SEL, 0x1e);
            else if(i==4)
                M4U_WriteReg32(gLarbBaseAddr[i], SMI_ROUTE_SEL, 0xffffffff);
            else
                M4U_WriteReg32(gLarbBaseAddr[i], SMI_ROUTE_SEL, 0x0);

            M4UMSG("larb clock on %d\n", i);

            larb_clock_off(i);
        }

    }

https://github.com/bq/aquaris-5/blob/bc1d0d0e35c969670e2d869001be944ad0c9c5e5/mediatek/platform/mt6589/kernel/drivers/m4u/m4u.c#L4566-L4602

MT8135: FPGA only
https://github.com/amazon-oss/android_kernel_amazon_mt8135/blob/e2b2163a8ec4a7c8d961c89003a15b4ba0f0e371/drivers/misc/mediatek/m4u/mt8135/m4u.c#L5309-L5343

MT8127: partially
https://github.com/bq/aquaris-M10/blob/ab0f5a519edaf314e9b537e448838ec9a4a9a3c8/drivers/misc/mediatek/m4u/mt8127/m4u_priv.c#L4409-L4437

MT6572: no
https://github.com/MTKKernel/android_kernel_mt6572/blob/94e275564da9043a5346bffa9d488ef0ce7c542d/drivers/misc/mediatek/m4u/mt6572/m4u.c#L5131-L5147

MT2601: no
https://github.com/toffyjan/android_Ticwatch_S_mooneye/blob/4f2a73b26f3821afc452cdce660cdbc73daf4e4a/drivers/misc/mediatek/m4u/mt2601/m4u.c#L5187-L5203

MT6571: no
https://github.com/orangepi-xunlong/OrangePi3G-iot_external/blob/b8a66e9bde91d75a8535dc6342d668efb6ab14ee/mediatek/platform/mt6571/kernel/drivers/m4u/m4u.c#L5235-L5251

MT6592: have
https://github.com/xcore995/android_kernel_lg_magna/blob/43a1be80064079af0a2d5e8287a690052fe40e6a/drivers/misc/mediatek/m4u/mt6592/m4u_priv.c#L4242-L4270

MT6589 old: SMI_ROUTE_SEL is always 1
https://github.com/chrmhoffmann/android_kernel_wiko_stairway/blob/d8724c3e5f9c269f790a8a40bb640ccfb653e353/mediatek/platform/mt6589/kernel/drivers/m4u/m4u.c#L4397-L4428

Link: #63 (comment)
Assisted-by: DeepSeek:deepseek-v4-pro
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Jul 4, 2026

Copy link
Copy Markdown
Member Author
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 591ffa66a728..bd2deee19ec5 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/array_size.h>
 #include <linux/bug.h>
 #include <linux/clk.h>
 #include <linux/component.h>
@@ -26,9 +27,11 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/string_choices.h>
+#include <linux/types.h>
 #include <asm/barrier.h>
 #include <dt-bindings/memory/mtk-memory-port.h>
 #include <dt-bindings/memory/mt2701-larb-port.h>
+#include <dt-bindings/memory/mt6589-larb-port.h>
 #include <soc/mediatek/smi.h>
 
 #if defined(CONFIG_ARM)
@@ -51,6 +54,7 @@ struct dma_iommu_mapping {
 #define F_MMU_FAULT_VA_MSK			0xfffff000
 #define MTK_PROTECT_PA_ALIGN			128
 
+/* -------- Common M4U v1 register definitions (MT2701 and MT6589 core) -------- */
 #define REG_MMU_CTRL_REG			0x210
 #define F_MMU_CTRL_COHERENT_EN			BIT(8)
 #define REG_MMU_IVRP_PADDR			0x214
@@ -71,6 +75,8 @@ struct dma_iommu_mapping {
 #define REG_MMU_FAULT_VA			0x228
 #define REG_MMU_INVLD_PA			0x22C
 #define REG_MMU_INT_ID				0x388
+
+/* MT2701 specific (core space) */
 #define REG_MMU_INVALIDATE			0x5c0
 #define REG_MMU_INVLD_START_A			0x5c4
 #define REG_MMU_INVLD_END_A			0x5c8
@@ -81,14 +87,66 @@ struct dma_iommu_mapping {
 #define REG_MMU_DCM				0x5f0
 #define F_MMU_DCM_ON				BIT(1)
 #define REG_MMU_CPE_DONE			0x60c
+
+/* MT6589 global space registers */
+#define REG_MMUg_CTRL				0x00
+#define F_MMUg_CTRL_INV_EN0			BIT(0)
+#define F_MMUg_CTRL_INV_EN1			BIT(1)
+#define F_MMUg_CTRL_INV_EN2			BIT(2)	/* L2 */
+#define F_MMUg_CTRL_PRE_LOCK(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_CTRL_PRE_EN			BIT(4)
+
+#define REG_MMUg_INVLD				0x04
+#define F_MMUg_INV_ALL				0x2
+#define F_MMUg_INV_RANGE			0x1
+
+#define REG_MMUg_INVLD_SA			0x08
+#define REG_MMUg_INVLD_EA			0x0C
+#define REG_MMUg_PT_BASE			0x10
+#define F_MMUg_PT_VA_MSK			0xffff0000
+
+#define REG_MMUg_L2_SEL				0x18
+#define F_MMUg_L2_SEL_FLUSH_EN(en)		((en) ? BIT(3) : 0)
+#define F_MMUg_L2_SEL_L2_ULTRA(en)		((en) ? BIT(2) : 0)
+#define F_MMUg_L2_SEL_L2_SHARE(en)		((en) ? BIT(1) : 0)
+#define F_MMUg_L2_SEL_L2_BUS_SEL(go_emi)	((go_emi) ? BIT(0) : 0)
+
+#define REG_MMUg_DCM				0x1C
+#define F_MMUg_DCM_ON(on)			((on) ? BIT(0) : 0)
+
+/* L2 cache registers (MT6589) */
+#define REG_L2_GDC_STATE			0x00
+#define F_L2_GDC_ST_EVENT_MSK			GENMASK(7,6)
+#define F_L2_GDC_ST_EVENT_VAL(val)		(((val) & 0x3) << 6)
+
+#define REG_L2_GDC_OP				0x04
+#define F_L2_GDC_BYPASS(en)			((en) ? BIT(10) : 0)
+#define F_L2_GDC_PERF_MASK(msk)			(((msk) & 0x7) << 7)
+#define GDC_PERF_MASK_HIT_MISS			0
+#define F_L2_GDC_LOCK_ALERT_DIS(dis)		((dis) ? BIT(6) : 0)
+#define F_L2_GDC_PERF_EN(en)			((en) ? BIT(5) : 0)
+#define F_L2_GDC_LOCK_TH(th)			(((th) & 0x3) << 2)
+#define F_L2_GDC_PAUSE_OP(op)			((op) & 0x3)
+#define GDC_NO_PAUSE				0
+
+#define REG_L2_GPE_STATUS			0x18
+#define F_L2_GPE_ST_RANGE_INV_DONE		BIT(1)
+#define F_L2_GPE_ST_PREFETCH_DONE		BIT(0)
+
+/* MT6589 core PFH distance / direction registers */
+#define REG_MMU_PFH_DIST(port)			(0x80 + (((port) >> 3) << 2))
+#define F_MMU_PFH_DIST_VAL(port, val)		(((val) & 0xf) << (((port) & 0x7) << 2))
+#define F_MMU_PFH_DIST_MASK(port)		F_MMU_PFH_DIST_VAL(port, 0xf)
+
+#define REG_MMU_PFH_DIR(port)			(((port) < 32) ? 0xF0 : 0xF4)
+#define F_MMU_PFH_DIR(port, val)		((!!(val)) << ((port) & 0x1f))
+
+/* Common page table descriptor bits */
 #define F_DESC_VALID				0x2
 #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)
 /* 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)
-#define MT2701_LARB_NR_MAX			3
 
 /*
  * MTK m4u support 4GB iova address space, and only support 4K page
@@ -96,26 +154,72 @@ struct dma_iommu_mapping {
  */
 #define M2701_IOMMU_PGT_SIZE			SZ_4M
 
+#define MAX_M4U_CORES				2
+
+struct mtk_iommu_v1_data;
+
+struct mtk_iommu_v1_soc_data {
+	const char *compatible;
+	unsigned int num_cores;
+	bool has_global_base;
+	bool has_l2_cache;
+
+	void (*tlb_flush_all)(struct mtk_iommu_v1_data *data);
+	void (*tlb_flush_range)(struct mtk_iommu_v1_data *data,
+				unsigned long iova, size_t size);
+
+	void (*get_fault_larb_port)(u32 int_id, unsigned int *larb,
+				    unsigned int *port);
+
+	int (*hw_init)(struct mtk_iommu_v1_data *data);
+
+	u32 pt_base_reg_offset;
+	bool pt_base_in_global;
+
+	const int *larb_port_offsets;
+	unsigned int num_larb;
+};
+
+struct mtk_iommu_v1_core {
+	void __iomem *base;
+	int irq;
+	struct mtk_iommu_v1_data *data;
+	unsigned int id;
+};
+
 struct mtk_iommu_v1_suspend_reg {
+	/* MT2701 fields */
 	u32			standard_axi_mode;
 	u32			dcm_dis;
 	u32			ctrl_reg;
 	u32			int_control0;
+
+	/* MT6589 additional fields */
+	u32			mmug_ctrl;
+	u32			mmug_pt_base;
+	u32			mmug_l2_sel;
+	u32			mmug_dcm;
+	u32			l2_gdc_op;
 };
 
 struct mtk_iommu_v1_data {
-	void __iomem			*base;
-	int				irq;
-	struct device			*dev;
-	struct clk			*bclk;
-	phys_addr_t			protect_base; /* protect memory base */
-	struct mtk_iommu_v1_domain	*m4u_dom;
-
-	struct iommu_device		iommu;
-	struct dma_iommu_mapping	*mapping;
-	struct mtk_smi_larb_iommu	larb_imu[MTK_LARB_NR_MAX];
-
-	struct mtk_iommu_v1_suspend_reg	reg;
+	const struct mtk_iommu_v1_soc_data *soc;
+	struct device *dev;
+
+	struct mtk_iommu_v1_core cores[MAX_M4U_CORES];
+	void __iomem *global_base;
+	void __iomem *l2_base;
+
+	struct clk *bclk;
+	phys_addr_t protect_base;
+	struct mtk_iommu_v1_domain *m4u_dom;
+
+	struct iommu_device iommu;
+	struct dma_iommu_mapping *mapping;
+	struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
+
+	struct mtk_iommu_v1_suspend_reg reg;
+	struct page *dummy_page; /* Physical address of guard dummy page */
 };
 
 struct mtk_iommu_v1_domain {
@@ -150,73 +254,143 @@ static const int mt2701_m4u_in_larb[] = {
 	MT2701_LARB2_PORT_OFFSET, MT2701_LARB3_PORT_OFFSET
 };
 
-static inline int mt2701_m4u_to_larb(int id)
+static const int mt6589_m4u_in_larb[] = {
+	MT6589_LARB0_PORT_OFFSET, MT6589_LARB1_PORT_OFFSET,
+	MT6589_LARB2_PORT_OFFSET, MT6589_LARB3_PORT_OFFSET,
+	MT6589_LARB4_PORT_OFFSET, MT6589_LARB5_PORT_OFFSET
+};
+
+static inline int mtk_iommu_v1_to_larb(struct mtk_iommu_v1_data *data, int id)
 {
+	const int *offsets = data->soc->larb_port_offsets;
+	int num = data->soc->num_larb;
 	int i;
 
-	for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--)
-		if ((id) >= mt2701_m4u_in_larb[i])
+	for (i = num - 1; i >= 0; i--)
+		if (id >= offsets[i])
 			return i;
 
 	return 0;
 }
 
-static inline int mt2701_m4u_to_port(int id)
+static inline int mtk_iommu_v1_to_port(struct mtk_iommu_v1_data *data, int id)
 {
-	int larb = mt2701_m4u_to_larb(id);
+	int larb = mtk_iommu_v1_to_larb(data, id);
+	return id - data->soc->larb_port_offsets[larb];
+}
 
-	return id - mt2701_m4u_in_larb[larb];
+static inline void m4u_set_field(void __iomem *base, u32 reg, u32 mask, u32 val)
+{
+	u32 regval = readl_relaxed(base + reg);
+	regval = (regval & ~mask) | val;
+	writel_relaxed(regval, base + reg);
 }
 
-static void mtk_iommu_v1_tlb_flush_all(struct mtk_iommu_v1_data *data)
+/* MT2701 (single core, no global space) */
+static void mt2701_tlb_flush_all(struct mtk_iommu_v1_data *data)
 {
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-			data->base + REG_MMU_INV_SEL);
-	writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
-	wmb(); /* Make sure the tlb flush all done */
+	void __iomem *base = data->cores[0].base;
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(F_ALL_INVLD, base + REG_MMU_INVALIDATE);
+	wmb();
 }
 
-static void mtk_iommu_v1_tlb_flush_range(struct mtk_iommu_v1_data *data,
-					 unsigned long iova, size_t size)
+static void mt2701_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
 {
-	int ret;
+	void __iomem *base = data->cores[0].base;
 	u32 tmp;
+	int ret;
 
-	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
-		data->base + REG_MMU_INV_SEL);
-	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
-		data->base + REG_MMU_INVLD_START_A);
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, base + REG_MMU_INV_SEL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK, 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);
+		   base + REG_MMU_INVLD_END_A);
+	writel_relaxed(F_MMU_INV_RANGE, base + REG_MMU_INVALIDATE);
 
-	ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
-				tmp, tmp != 0, 10, 100000);
+	ret = readl_poll_timeout_atomic(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);
+		mt2701_tlb_flush_all(data);
+	}
+	writel_relaxed(0, base + REG_MMU_CPE_DONE);
+}
+
+/* MT6589 (global control, L2) */
+static void mt6589_tlb_flush_all(struct mtk_iommu_v1_data *data)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(F_MMUg_INV_ALL, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 event;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GDC_STATE,
+					 event,
+					 event & F_L2_GDC_ST_EVENT_MSK,
+					 10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GDC_STATE);
+	}
+}
+
+static void mt6589_tlb_flush_range(struct mtk_iommu_v1_data *data,
+				   unsigned long iova, size_t size)
+{
+	u32 reg = F_MMUg_CTRL_INV_EN0 | F_MMUg_CTRL_INV_EN1;
+	if (data->l2_base)
+		reg |= F_MMUg_CTRL_INV_EN2;
+
+	writel_relaxed(reg, data->global_base + REG_MMUg_CTRL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_SA);
+	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
+		   data->global_base + REG_MMUg_INVLD_EA);
+	writel_relaxed(F_MMUg_INV_RANGE, data->global_base + REG_MMUg_INVLD);
+
+	if (data->l2_base) {
+		u32 status;
+		readl_poll_timeout_atomic(data->l2_base + REG_L2_GPE_STATUS,
+					  status,
+					  status & F_L2_GPE_ST_RANGE_INV_DONE,
+					  10, 100000);
+		writel_relaxed(0, data->l2_base + REG_L2_GPE_STATUS);
 	}
-	/* Clear the CPE status */
-	writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+}
+
+static void mt2701_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - ((int_id >> 13) & 0x7);
+	*port = (int_id >> 8) & 0xF;
+}
+
+static void mt6589_get_fault_larb_port(u32 int_id, unsigned int *larb,
+				       unsigned int *port)
+{
+	*larb = 6 - (int_id >> 12) & 0x7;
+	*port = (int_id >> 8) & 0xF;
 }
 
 static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 {
-	struct mtk_iommu_v1_data *data = dev_id;
+	struct mtk_iommu_v1_core *core = dev_id;
+	struct mtk_iommu_v1_data *data = core->data;
 	struct mtk_iommu_v1_domain *dom = data->m4u_dom;
 	u32 int_state, regval, fault_iova, fault_pa;
 	unsigned int fault_larb, fault_port;
 
 	/* Read error information from registers */
-	int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST);
-	fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
+	int_state = readl_relaxed(core->base + REG_MMU_FAULT_ST);
+	fault_iova = readl_relaxed(core->base + REG_MMU_FAULT_VA) & F_MMU_FAULT_VA_MSK;
+	fault_pa = readl_relaxed(core->base + REG_MMU_INVLD_PA);
+	regval = readl_relaxed(core->base + REG_MMU_INT_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);
+	data->soc->get_fault_larb_port(regval, &fault_larb, &fault_port);
 
 	/*
 	 * MTK v1 iommu HW could not determine whether the fault is read or
@@ -225,16 +399,16 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 	if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
 			IOMMU_FAULT_READ))
 		dev_err_ratelimited(data->dev,
-			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n",
+			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d core=%d\n",
 			int_state, fault_iova, fault_pa,
-			fault_larb, fault_port);
+			fault_larb, fault_port, core->id);
 
 	/* Interrupt clear */
-	regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL);
+	regval = readl_relaxed(core->base + REG_MMU_INT_CONTROL);
 	regval |= F_INT_CLR_BIT;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, core->base + REG_MMU_INT_CONTROL);
 
-	mtk_iommu_v1_tlb_flush_all(data);
+	data->soc->tlb_flush_all(data);
 
 	return IRQ_HANDLED;
 }
@@ -242,14 +416,14 @@ static irqreturn_t mtk_iommu_v1_isr(int irq, void *dev_id)
 static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 				struct device *dev, bool enable)
 {
-	struct mtk_smi_larb_iommu    *larb_mmu;
-	unsigned int                 larbid, portid;
+	struct mtk_smi_larb_iommu *larb_mmu;
 	struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
-	int i;
+	unsigned int larbid, portid, i, mmu_id;
+	void __iomem *base;
 
 	for (i = 0; i < fwspec->num_ids; ++i) {
-		larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
-		portid = mt2701_m4u_to_port(fwspec->ids[i]);
+		larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+		portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
 		larb_mmu = &data->larb_imu[larbid];
 
 		dev_dbg(dev, "%s iommu port: %d\n",
@@ -260,6 +434,26 @@ static void mtk_iommu_v1_config(struct mtk_iommu_v1_data *data,
 		else
 			larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid);
 	}
+
+	/* MT6589 specific: set default prefetch distance & direction */
+	if (data->soc->has_global_base) {
+		for (i = 0; i < fwspec->num_ids; i++) {
+			portid = mtk_iommu_v1_to_port(data, fwspec->ids[i]);
+			larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[i]);
+
+			/* Determine which M4U core this LARB is connected to */
+			mmu_id = (larbid == 0 || larbid == 2) ? 0 : 1;
+			base = data->cores[mmu_id].base;
+
+			/* Set distance = 1 */
+			m4u_set_field(base, REG_MMU_PFH_DIST(portid),
+				      F_MMU_PFH_DIST_MASK(portid),
+				      F_MMU_PFH_DIST_VAL(portid, 1));
+			/* Set direction = 0 */
+			m4u_set_field(base, REG_MMU_PFH_DIR(portid),
+				      1 << (portid & 0x1f), 0);
+		}
+	}
 }
 
 static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
@@ -273,7 +467,10 @@ static int mtk_iommu_v1_domain_finalise(struct mtk_iommu_v1_data *data)
 	if (!dom->pgt_va)
 		return -ENOMEM;
 
-	writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR);
+	if (data->soc->pt_base_in_global)
+		writel(dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+	else
+		writel(dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
 
 	dom->data = data;
 
@@ -354,10 +551,12 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 			    int prot, gfp_t gfp, size_t *mapped)
 {
 	struct mtk_iommu_v1_domain *dom = to_mtk_domain(domain);
+	struct mtk_iommu_v1_data *data = dom->data;
 	unsigned long flags;
-	unsigned int i;
-	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT);
+	unsigned int i, guard_pages = (data->soc->has_global_base) ? 4 : 0;
+	u32 *pgt_base_iova = dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT);
 	u32 pabase = (u32)paddr;
+	phys_addr_t dummy_pa = page_to_phys(data->dummy_page);
 
 	spin_lock_irqsave(&dom->pgtlock, flags);
 	for (i = 0; i < pgcount; i++) {
@@ -367,10 +566,19 @@ static int mtk_iommu_v1_map(struct iommu_domain *domain, unsigned long iova,
 		pabase += MT2701_IOMMU_PAGE_SIZE;
 	}
 
+	if (guard_pages && i == pgcount) {
+		for (i = pgcount; i < pgcount + guard_pages; i++) {
+			if ((iova >> MT2701_IOMMU_PAGE_SHIFT) + i >= (M2701_IOMMU_PGT_SIZE / sizeof(u32)))
+				break;
+			if (pgt_base_iova[i] == 0)
+				pgt_base_iova[i] = dummy_pa | F_DESC_VALID | F_DESC_NONSEC;
+		}
+	}
+
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
 	*mapped = i * MT2701_IOMMU_PAGE_SIZE;
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, *mapped);
+	data->soc->tlb_flush_range(data, iova, *mapped);
 
 	return i == pgcount ? 0 : -EEXIST;
 }
@@ -388,7 +596,7 @@ static size_t mtk_iommu_v1_unmap(struct iommu_domain *domain, unsigned long iova
 	memset(pgt_base_iova, 0, pgcount * sizeof(u32));
 	spin_unlock_irqrestore(&dom->pgtlock, flags);
 
-	mtk_iommu_v1_tlb_flush_range(dom->data, iova, size);
+	dom->data->soc->tlb_flush_range(dom->data, iova, size);
 
 	return size;
 }
@@ -489,12 +697,12 @@ static struct iommu_device *mtk_iommu_v1_probe_device(struct device *dev)
 	data = dev_iommu_priv_get(dev);
 
 	/* Link the consumer device with the smi-larb device(supplier) */
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
-	if (larbid >= MT2701_LARB_NR_MAX)
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
+	if (larbid >= MTK_LARB_NR_MAX)
 		return ERR_PTR(-EINVAL);
 
 	for (idx = 1; idx < fwspec->num_ids; idx++) {
-		larbidx = mt2701_m4u_to_larb(fwspec->ids[idx]);
+		larbidx = mtk_iommu_v1_to_larb(data, fwspec->ids[idx]);
 		if (larbid != larbidx) {
 			dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
 				larbid, larbidx);
@@ -532,12 +740,12 @@ static void mtk_iommu_v1_release_device(struct device *dev)
 	unsigned int larbid;
 
 	data = dev_iommu_priv_get(dev);
-	larbid = mt2701_m4u_to_larb(fwspec->ids[0]);
+	larbid = mtk_iommu_v1_to_larb(data, fwspec->ids[0]);
 	larbdev = data->larb_imu[larbid].dev;
 	device_link_remove(dev, larbdev);
 }
 
-static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
+static int mt2701_hw_init(struct mtk_iommu_v1_data *data)
 {
 	u32 regval;
 	int ret;
@@ -549,7 +757,7 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 	}
 
 	regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2);
-	writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_CTRL_REG);
 
 	regval = F_INT_TRANSLATION_FAULT |
 		F_INT_MAIN_MULTI_HIT_FAULT |
@@ -559,23 +767,66 @@ static int mtk_iommu_v1_hw_init(const struct mtk_iommu_v1_data *data)
 		F_INT_TLB_MISS_FAULT |
 		F_INT_PFH_DMA_FIFO_OVERFLOW |
 		F_INT_MISS_DMA_FIFO_OVERFLOW;
-	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+	writel_relaxed(regval, data->cores[0].base + REG_MMU_INT_CONTROL);
 
-	/* protect memory,hw will write here while translation fault */
-	writel_relaxed(data->protect_base,
-			data->base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(data->protect_base, data->cores[0].base + REG_MMU_IVRP_PADDR);
+	writel_relaxed(F_MMU_DCM_ON, data->cores[0].base + REG_MMU_DCM);
 
-	writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM);
+	return 0;
+}
 
-	if (devm_request_irq(data->dev, data->irq, mtk_iommu_v1_isr, 0,
-			     dev_name(data->dev), (void *)data)) {
-		writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
-		clk_disable_unprepare(data->bclk);
-		dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
-		return -ENODEV;
+static int mt6589_hw_init(struct mtk_iommu_v1_data *data)
+{
+	u32 regval;
+	int i, ret;
+
+	ret = clk_prepare_enable(data->bclk);
+	if (ret) {
+		dev_err(data->dev, "Failed to enable bclk\n");
+		goto err_clk;
+	}
+
+	/* ---- Global registers ---- */
+	writel_relaxed(F_MMUg_L2_SEL_FLUSH_EN(1) | F_MMUg_L2_SEL_L2_ULTRA(1) |
+		   F_MMUg_L2_SEL_L2_SHARE(0) | F_MMUg_L2_SEL_L2_BUS_SEL(1),
+		   data->global_base + REG_MMUg_L2_SEL);
+	writel_relaxed(F_MMUg_DCM_ON(1), data->global_base + REG_MMUg_DCM);
+
+	/* ---- L2 cache ---- */
+	if (data->l2_base) {
+		regval = F_L2_GDC_BYPASS(0) |
+			 F_L2_GDC_PERF_MASK(GDC_PERF_MASK_HIT_MISS) |
+			 F_L2_GDC_LOCK_ALERT_DIS(0) |
+			 F_L2_GDC_LOCK_TH(3) |
+			 F_L2_GDC_PAUSE_OP(GDC_NO_PAUSE);
+		writel_relaxed(regval, data->l2_base + REG_L2_GDC_OP);
+	}
+
+	/* ---- Per-core setup ---- */
+	for (i = 0; i < data->soc->num_cores; i++) {
+		void __iomem *base = data->cores[i].base;
+
+		regval = 0;  /* PFH enabled, walk enabled, cohere disabled */
+		regval |= F_MMU_TF_PROTECT_SEL(2);
+		writel_relaxed(regval, base + REG_MMU_CTRL_REG);
+
+		regval = F_INT_TRANSLATION_FAULT |
+			 F_INT_MAIN_MULTI_HIT_FAULT |
+			 F_INT_INVALID_PA_FAULT |
+			 F_INT_ENTRY_REPLACEMENT_FAULT |
+			 F_INT_TABLE_WALK_FAULT |
+			 F_INT_TLB_MISS_FAULT |
+			 F_INT_PFH_DMA_FIFO_OVERFLOW |
+			 F_INT_MISS_DMA_FIFO_OVERFLOW;
+		writel_relaxed(regval, base + REG_MMU_INT_CONTROL);
+
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
 	}
 
 	return 0;
+
+err_clk:
+	return ret;
 }
 
 static const struct iommu_ops mtk_iommu_v1_ops = {
@@ -595,8 +846,39 @@ static const struct iommu_ops mtk_iommu_v1_ops = {
 	}
 };
 
+static const struct mtk_iommu_v1_soc_data mt2701_soc_data = {
+	.compatible = "mediatek,mt2701-m4u",
+	.num_cores = 1,
+	.has_global_base = false,
+	.has_l2_cache = false,
+	.tlb_flush_all = mt2701_tlb_flush_all,
+	.tlb_flush_range = mt2701_tlb_flush_range,
+	.get_fault_larb_port = mt2701_get_fault_larb_port,
+	.hw_init = mt2701_hw_init,
+	.pt_base_reg_offset = REG_MMU_PT_BASE_ADDR,
+	.pt_base_in_global = false,
+	.larb_port_offsets = mt2701_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt2701_m4u_in_larb),
+};
+
+static const struct mtk_iommu_v1_soc_data mt6589_soc_data = {
+	.compatible = "mediatek,mt6589-m4u",
+	.num_cores = 2,
+	.has_global_base = true,
+	.has_l2_cache = true,
+	.tlb_flush_all = mt6589_tlb_flush_all,
+	.tlb_flush_range = mt6589_tlb_flush_range,
+	.get_fault_larb_port = mt6589_get_fault_larb_port,
+	.hw_init = mt6589_hw_init,
+	.pt_base_reg_offset = REG_MMUg_PT_BASE,
+	.pt_base_in_global = true,
+	.larb_port_offsets = mt6589_m4u_in_larb,
+	.num_larb = ARRAY_SIZE(mt6589_m4u_in_larb),
+};
+
 static const struct of_device_id mtk_iommu_v1_of_ids[] = {
-	{ .compatible = "mediatek,mt2701-m4u", },
+	{ .compatible = "mediatek,mt2701-m4u", .data = &mt2701_soc_data },
+	{ .compatible = "mediatek,mt6589-m4u", .data = &mt6589_soc_data },
 	{}
 };
 MODULE_DEVICE_TABLE(of, mtk_iommu_v1_of_ids);
@@ -610,16 +892,19 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 {
 	struct device			*dev = &pdev->dev;
 	struct mtk_iommu_v1_data	*data;
-	struct resource			*res;
+	const struct mtk_iommu_v1_soc_data *soc;
 	struct component_match		*match = NULL;
 	void				*protect;
 	int				larb_nr, ret, i;
+	struct page			*dummy_page;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
 
 	data->dev = dev;
+	soc = of_device_get_match_data(dev);
+	data->soc = soc;
 
 	/* Protect memory. HW will access here while translation fault.*/
 	protect = devm_kcalloc(dev, 2, MTK_PROTECT_PA_ALIGN,
@@ -628,26 +913,69 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	data->base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(data->base))
-		return PTR_ERR(data->base);
-
-	data->irq = platform_get_irq(pdev, 0);
-	if (data->irq < 0)
-		return data->irq;
+	if (soc->has_global_base) {
+		data->global_base = devm_platform_ioremap_resource_byname(pdev,
+									  "global");
+		if (IS_ERR(data->global_base))
+			return PTR_ERR(data->global_base);
+	}
+	for (i = 0; i < soc->num_cores; i++) {
+		char name[8];
+		snprintf(name, sizeof(name), "m4u%d", i);
+		data->cores[i].base = devm_platform_ioremap_resource_byname(pdev,
+									    name);
+		if (IS_ERR(data->cores[i].base))
+			return PTR_ERR(data->cores[i].base);
+		data->cores[i].data = data;
+		data->cores[i].id = i;
+	}
+	if (soc->has_l2_cache) {
+		data->l2_base = devm_platform_ioremap_resource_byname(pdev,
+								      "l2cache");
+		if (IS_ERR(data->l2_base))
+			return PTR_ERR(data->l2_base);
+	}
 
 	data->bclk = devm_clk_get(dev, "bclk");
 	if (IS_ERR(data->bclk))
 		return PTR_ERR(data->bclk);
 
+	/* Interrupts - request after hw_init to avoid spurious IRQs? but
+	   we need the core IRQs ready before registering ISR. We'll request
+	   them after hw_init for simplicity. */
+	ret = soc->hw_init(data);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < soc->num_cores; i++) {
+		struct mtk_iommu_v1_core *core = &data->cores[i];
+		char irqname[8];
+		snprintf(irqname, sizeof(irqname), "m4u%d", i);
+		core->irq = platform_get_irq_byname(pdev, irqname);
+		if (core->irq < 0) {
+			ret = core->irq;
+			goto out_clk_unprepare;
+		}
+		ret = devm_request_irq(dev, core->irq, mtk_iommu_v1_isr, 0,
+				       dev_name(dev), core);
+		if (ret) {
+			dev_err(dev, "Failed to request IRQ %d for core%d\n",
+				core->irq, i);
+			goto out_clk_unprepare;
+		}
+	}
+
 	larb_nr = of_count_phandle_with_args(dev->of_node,
 					     "mediatek,larbs", NULL);
-	if (larb_nr < 0)
-		return larb_nr;
+	if (larb_nr < 0) {
+		ret = larb_nr;
+		goto out_clk_unprepare;
+	}
 
-	if (larb_nr > MTK_LARB_NR_MAX)
-		return -EINVAL;
+	if (larb_nr > MTK_LARB_NR_MAX) {
+		ret = -EINVAL;
+		goto out_clk_unprepare;
+	}
 
 	for (i = 0; i < larb_nr; i++) {
 		struct device_node *larbnode;
@@ -682,17 +1010,20 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 					    component_compare_of, larbnode);
 	}
 
+	dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+	if (!dummy_page) {
+		ret = -ENOMEM;
+		goto out_put_larbs;
+	}
+	data->dummy_page = dummy_page;
+
 	platform_set_drvdata(pdev, data);
 
-	ret = mtk_iommu_v1_hw_init(data);
+	ret = iommu_device_sysfs_add(&data->iommu, dev, NULL,
+				     dev_name(dev));
 	if (ret)
 		goto out_put_larbs;
 
-	ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
-				     dev_name(&pdev->dev));
-	if (ret)
-		goto out_clk_unprepare;
-
 	ret = iommu_device_register(&data->iommu, &mtk_iommu_v1_ops, dev);
 	if (ret)
 		goto out_sysfs_remove;
@@ -706,12 +1037,14 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
 	iommu_device_unregister(&data->iommu);
 out_sysfs_remove:
 	iommu_device_sysfs_remove(&data->iommu);
-out_clk_unprepare:
-	clk_disable_unprepare(data->bclk);
 out_put_larbs:
 	for (i = 0; i < MTK_LARB_NR_MAX; i++)
-		put_device(data->larb_imu[i].dev);
-
+		if (data->larb_imu[i].dev)
+			put_device(data->larb_imu[i].dev);
+	if (data->dummy_page)
+		__free_page(data->dummy_page);
+out_clk_unprepare:
+	clk_disable_unprepare(data->bclk);
 	return ret;
 }
 
@@ -724,40 +1057,104 @@ static void mtk_iommu_v1_remove(struct platform_device *pdev)
 	iommu_device_unregister(&data->iommu);
 
 	clk_disable_unprepare(data->bclk);
-	devm_free_irq(&pdev->dev, data->irq, data);
+	for (i = 0; i < data->soc->num_cores; i++)
+		devm_free_irq(&pdev->dev, data->cores[i].irq, &data->cores[i]);
 	component_master_del(&pdev->dev, &mtk_iommu_v1_com_ops);
+	__free_page(data->dummy_page);
 
 	for (i = 0; i < MTK_LARB_NR_MAX; i++)
-		put_device(data->larb_imu[i].dev);
+		if (data->larb_imu[i].dev)
+			put_device(data->larb_imu[i].dev);
 }
 
 static int __maybe_unused mtk_iommu_v1_suspend(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
+	void __iomem *base = data->cores[0].base;
 
-	reg->standard_axi_mode = readl_relaxed(base +
-					       REG_MMU_STANDARD_AXI_MODE);
-	reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	/* Common core registers */
 	reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
 	reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL);
+
+	if (data->soc->has_global_base) {
+		reg->mmug_ctrl = readl_relaxed(data->global_base + REG_MMUg_CTRL);
+		reg->mmug_pt_base = readl_relaxed(data->global_base + REG_MMUg_PT_BASE);
+		reg->mmug_l2_sel = readl_relaxed(data->global_base + REG_MMUg_L2_SEL);
+		reg->mmug_dcm = readl_relaxed(data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			reg->l2_gdc_op = readl_relaxed(data->l2_base + REG_L2_GDC_OP);
+	} else {
+		/* MT2701 specific */
+		base = data->cores[0].base;
+		reg->standard_axi_mode = readl_relaxed(base + REG_MMU_STANDARD_AXI_MODE);
+		reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	}
+
 	return 0;
 }
 
+static void mt6589_restore_pfh_settings(struct mtk_iommu_v1_data *data)
+{
+	const int *offsets = data->soc->larb_port_offsets;
+	int num_larb = data->soc->num_larb;
+	int larb, port;
+
+	for (larb = 0; larb < num_larb; larb++) {
+		int mmu_id = (larb == 0 || larb == 2) ? 0 : 1;
+		void __iomem *base = data->cores[mmu_id].base;
+		int max_port = (larb == num_larb - 1) ? 32 : offsets[larb+1] - offsets[larb];
+		for (port = 0; port < max_port; port++) {
+			int global_port = offsets[larb] + port;
+			m4u_set_field(base, REG_MMU_PFH_DIST(global_port),
+						  F_MMU_PFH_DIST_MASK(global_port),
+						  F_MMU_PFH_DIST_VAL(global_port, 1));
+			m4u_set_field(base, REG_MMU_PFH_DIR(global_port),
+						  1 << (global_port & 0x1f), 0);
+		}
+	}
+}
+
 static int __maybe_unused mtk_iommu_v1_resume(struct device *dev)
 {
 	struct mtk_iommu_v1_data *data = dev_get_drvdata(dev);
 	struct mtk_iommu_v1_suspend_reg *reg = &data->reg;
-	void __iomem *base = data->base;
-
-	writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR);
-	writel_relaxed(reg->standard_axi_mode,
-		       base + REG_MMU_STANDARD_AXI_MODE);
-	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
-	writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
-	writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
-	writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	void __iomem *base = data->cores[0].base;
+
+	if (data->soc->has_global_base) {
+		writel_relaxed(reg->mmug_ctrl, data->global_base + REG_MMUg_CTRL);
+		writel_relaxed(reg->mmug_pt_base, data->global_base + REG_MMUg_PT_BASE);
+		writel_relaxed(reg->mmug_l2_sel, data->global_base + REG_MMUg_L2_SEL);
+		writel_relaxed(reg->mmug_dcm, data->global_base + REG_MMUg_DCM);
+		if (data->l2_base)
+			writel_relaxed(reg->l2_gdc_op, data->l2_base + REG_L2_GDC_OP);
+	}
+
+	/* Per-core restore (common) */
+	for (int i = 0; i < data->soc->num_cores; i++) {
+		base = data->cores[i].base;
+		writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
+		writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
+		writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	}
+
+	if (!data->soc->has_global_base) {
+		/* MT2701 extra */
+		base = data->cores[0].base;
+		writel_relaxed(reg->standard_axi_mode, base + REG_MMU_STANDARD_AXI_MODE);
+		writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
+	}
+
+	if (data->m4u_dom && data->m4u_dom->pgt_pa) {
+		if (data->soc->pt_base_in_global)
+			writel_relaxed(data->m4u_dom->pgt_pa, data->global_base + data->soc->pt_base_reg_offset);
+		else
+			writel_relaxed(data->m4u_dom->pgt_pa, data->cores[0].base + data->soc->pt_base_reg_offset);
+	}
+
+	if (data->soc->has_global_base)
+		mt6589_restore_pfh_settings(data);
+
 	return 0;
 }
 

akku1139 added 5 commits July 5, 2026 02:21
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Assited-by: DeepSeek:deepseek-v4-pro
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
@akku1139

akku1139 commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

@akku1139 akku1139 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • add rdma driver

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant