All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
@ 2011-07-04  1:41 ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim

This patch set includes the following patches:

- [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2
  The previous driver defines System MMUs for 2 MDMAs each.
  Since one of the MDMAs is not used any more, it is removed.
  SYSMMU_MDMA2 is renamed to SYSMMU_MDMA and it now represents number 10.
 
- [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMUof SSS
  System MMU of SSS was not complete to use because previous driver did not
  prepare anything for its clock gating feature. This patch enables clock
  gating for System MMU of SSS.
  
- [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition
  As the suggestion of Marek Szyprowski with his patch set, this patch
  separates the single definition of platform device of System MMU into
  15 different platform devices as well as structured resource management.

- [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support clkdev.
  This patch adds 'devname' fields into the clokc definitions of System MMU.
  The pointer to clk structure for System MMU can be found with kobj name
  dynamically.

- [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE
  This patch adds another System MMU ID(ips), "SYSMMU_NONE" which means
  invalid system MMU ID.

- [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU device driver.
  The previous driver implements clock gating functions in the mach
  directory because it is mach dependent. It is now removed and
  clock gating functions are moved to System MMU driver because clkdev is
  supported.

- [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
  This patch moves the existing System MMU driver to drivers/iommu
  directory that is in Joerg Roedel's git.
  git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
  This patch also adds implementation of IOMMU API for Exynos4's System MMU.

The above patches must be applied in the order of their presence.

The last patch is not applicable to any kernel version except Joerg
Roedel's git tree.

This patch set does not improve System MMU driver completely.
The rest of enhancements will be submitted soon.

Regards,

KyongHo Cho.


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
@ 2011-07-04  1:41 ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

This patch set includes the following patches:

- [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2
  The previous driver defines System MMUs for 2 MDMAs each.
  Since one of the MDMAs is not used any more, it is removed.
  SYSMMU_MDMA2 is renamed to SYSMMU_MDMA and it now represents number 10.
 
- [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMUof SSS
  System MMU of SSS was not complete to use because previous driver did not
  prepare anything for its clock gating feature. This patch enables clock
  gating for System MMU of SSS.
  
- [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition
  As the suggestion of Marek Szyprowski with his patch set, this patch
  separates the single definition of platform device of System MMU into
  15 different platform devices as well as structured resource management.

- [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support clkdev.
  This patch adds 'devname' fields into the clokc definitions of System MMU.
  The pointer to clk structure for System MMU can be found with kobj name
  dynamically.

- [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE
  This patch adds another System MMU ID(ips), "SYSMMU_NONE" which means
  invalid system MMU ID.

- [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU device driver.
  The previous driver implements clock gating functions in the mach
  directory because it is mach dependent. It is now removed and
  clock gating functions are moved to System MMU driver because clkdev is
  supported.

- [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
  This patch moves the existing System MMU driver to drivers/iommu
  directory that is in Joerg Roedel's git.
  git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
  This patch also adds implementation of IOMMU API for Exynos4's System MMU.

The above patches must be applied in the order of their presence.

The last patch is not applicable to any kernel version except Joerg
Roedel's git tree.

This patch set does not improve System MMU driver completely.
The rest of enhancements will be submitted soon.

Regards,

KyongHo Cho.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho

EXYNOS4210 has 2 MDMA(MDMA0 and MDMA1) and thus 2 System MMUs
for each MDMAs.

The current definition of sysmmu_ips represented like below:
SYSMMU_MDMA -> System MMU of MDM0
SYSMMU_MDMA2 -> System MMU of MDMA1

Since MDMA0 is not used anymore, it is removed and the definition
of sysmmu_ips is changed as following:
SYSMMU_MDMA -> System MMU of MDMA1
SYSMMU_MDMA2 -> removed.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/clock.c               |    8 ++--
 arch/arm/mach-exynos4/dev-sysmmu.c          |   73 +++++++++++---------------
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    3 +-
 3 files changed, 36 insertions(+), 48 deletions(-)

diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index 5091e13..256b46b 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -622,10 +622,6 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 13),
 	}, {
-		.name		= "SYSMMU_MDMA",
-		.enable		= exynos4_clk_ip_image_ctrl,
-		.ctrlbit	= (1 << 5),
-	}, {
 		.name		= "SYSMMU_FIMC0",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 7),
@@ -666,6 +662,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
+		.name		= "SYSMMU_MDMA",
+		.enable		= exynos4_clk_ip_image_ctrl,
+		.ctrlbit	= (1 << 5),
+	}, {
 		.name		= "SYSMMU_TV",
 		.enable		= exynos4_clk_ip_tv_ctrl,
 		.ctrlbit	= (1 << 4),
diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index 3b7cae0..af1110e 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -20,7 +20,6 @@
 
 /* These names must be equal to the clock names in mach-exynos4/clock.c */
 const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
-	"SYSMMU_MDMA"	,
 	"SYSMMU_SSS"	,
 	"SYSMMU_FIMC0"	,
 	"SYSMMU_FIMC1"	,
@@ -32,7 +31,7 @@ const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
 	"SYSMMU_PCIe"	,
 	"SYSMMU_G2D"	,
 	"SYSMMU_ROTATOR",
-	"SYSMMU_MDMA2"	,
+	"SYSMMU_MDMA"	,
 	"SYSMMU_TV"	,
 	"SYSMMU_MFC_L"	,
 	"SYSMMU_MFC_R"	,
@@ -40,161 +39,151 @@ const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
 
 static struct resource exynos4_sysmmu_resource[] = {
 	[0] = {
-		.start	= EXYNOS4_PA_SYSMMU_MDMA,
-		.end	= EXYNOS4_PA_SYSMMU_MDMA + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[1] = {
-		.start	= IRQ_SYSMMU_MDMA0_0,
-		.end	= IRQ_SYSMMU_MDMA0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[2] = {
 		.start	= EXYNOS4_PA_SYSMMU_SSS,
 		.end	= EXYNOS4_PA_SYSMMU_SSS + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[3] = {
+	[1] = {
 		.start	= IRQ_SYSMMU_SSS_0,
 		.end	= IRQ_SYSMMU_SSS_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[4] = {
+	[2] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC0,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC0 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[5] = {
+	[3] = {
 		.start	= IRQ_SYSMMU_FIMC0_0,
 		.end	= IRQ_SYSMMU_FIMC0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[6] = {
+	[4] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC1,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC1 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[7] = {
+	[5] = {
 		.start	= IRQ_SYSMMU_FIMC1_0,
 		.end	= IRQ_SYSMMU_FIMC1_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[8] = {
+	[6] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC2,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC2 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[9] = {
+	[7] = {
 		.start	= IRQ_SYSMMU_FIMC2_0,
 		.end	= IRQ_SYSMMU_FIMC2_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[10] = {
+	[8] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC3,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC3 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[11] = {
+	[9] = {
 		.start	= IRQ_SYSMMU_FIMC3_0,
 		.end	= IRQ_SYSMMU_FIMC3_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[12] = {
+	[10] = {
 		.start	= EXYNOS4_PA_SYSMMU_JPEG,
 		.end	= EXYNOS4_PA_SYSMMU_JPEG + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[13] = {
+	[11] = {
 		.start	= IRQ_SYSMMU_JPEG_0,
 		.end	= IRQ_SYSMMU_JPEG_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[14] = {
+	[12] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMD0,
 		.end	= EXYNOS4_PA_SYSMMU_FIMD0 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[15] = {
+	[13] = {
 		.start	= IRQ_SYSMMU_LCD0_M0_0,
 		.end	= IRQ_SYSMMU_LCD0_M0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[16] = {
+	[14] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMD1,
 		.end	= EXYNOS4_PA_SYSMMU_FIMD1 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[17] = {
+	[15] = {
 		.start	= IRQ_SYSMMU_LCD1_M1_0,
 		.end	= IRQ_SYSMMU_LCD1_M1_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[18] = {
+	[16] = {
 		.start	= EXYNOS4_PA_SYSMMU_PCIe,
 		.end	= EXYNOS4_PA_SYSMMU_PCIe + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[19] = {
+	[17] = {
 		.start	= IRQ_SYSMMU_PCIE_0,
 		.end	= IRQ_SYSMMU_PCIE_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[20] = {
+	[18] = {
 		.start	= EXYNOS4_PA_SYSMMU_G2D,
 		.end	= EXYNOS4_PA_SYSMMU_G2D + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[21] = {
+	[19] = {
 		.start	= IRQ_SYSMMU_2D_0,
 		.end	= IRQ_SYSMMU_2D_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[22] = {
+	[20] = {
 		.start	= EXYNOS4_PA_SYSMMU_ROTATOR,
 		.end	= EXYNOS4_PA_SYSMMU_ROTATOR + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[23] = {
+	[21] = {
 		.start	= IRQ_SYSMMU_ROTATOR_0,
 		.end	= IRQ_SYSMMU_ROTATOR_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[24] = {
+	[22] = {
 		.start	= EXYNOS4_PA_SYSMMU_MDMA2,
 		.end	= EXYNOS4_PA_SYSMMU_MDMA2 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[25] = {
+	[23] = {
 		.start	= IRQ_SYSMMU_MDMA1_0,
 		.end	= IRQ_SYSMMU_MDMA1_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[26] = {
+	[24] = {
 		.start	= EXYNOS4_PA_SYSMMU_TV,
 		.end	= EXYNOS4_PA_SYSMMU_TV + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[27] = {
+	[25] = {
 		.start	= IRQ_SYSMMU_TV_M0_0,
 		.end	= IRQ_SYSMMU_TV_M0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[28] = {
+	[26] = {
 		.start	= EXYNOS4_PA_SYSMMU_MFC_L,
 		.end	= EXYNOS4_PA_SYSMMU_MFC_L + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[29] = {
+	[27] = {
 		.start	= IRQ_SYSMMU_MFC_M0_0,
 		.end	= IRQ_SYSMMU_MFC_M0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[30] = {
+	[28] = {
 		.start	= EXYNOS4_PA_SYSMMU_MFC_R,
 		.end	= EXYNOS4_PA_SYSMMU_MFC_R + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[31] = {
+	[29] = {
 		.start	= IRQ_SYSMMU_MFC_M1_0,
 		.end	= IRQ_SYSMMU_MFC_M1_0,
 		.flags	= IORESOURCE_IRQ,
@@ -203,7 +192,7 @@ static struct resource exynos4_sysmmu_resource[] = {
 
 struct platform_device exynos4_device_sysmmu = {
 	.name		= "s5p-sysmmu",
-	.id		= 32,
+	.id		= 30,
 	.num_resources	= ARRAY_SIZE(exynos4_sysmmu_resource),
 	.resource	= exynos4_sysmmu_resource,
 };
diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 6a5fbb5..2d5d78b 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -14,7 +14,6 @@
 #define __ASM_ARM_ARCH_SYSMMU_H __FILE__
 
 enum exynos4_sysmmu_ips {
-	SYSMMU_MDMA,
 	SYSMMU_SSS,
 	SYSMMU_FIMC0,
 	SYSMMU_FIMC1,
@@ -26,7 +25,7 @@ enum exynos4_sysmmu_ips {
 	SYSMMU_PCIe,
 	SYSMMU_G2D,
 	SYSMMU_ROTATOR,
-	SYSMMU_MDMA2,
+	SYSMMU_MDMA,
 	SYSMMU_TV,
 	SYSMMU_MFC_L,
 	SYSMMU_MFC_R,
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

EXYNOS4210 has 2 MDMA(MDMA0 and MDMA1) and thus 2 System MMUs
for each MDMAs.

The current definition of sysmmu_ips represented like below:
SYSMMU_MDMA -> System MMU of MDM0
SYSMMU_MDMA2 -> System MMU of MDMA1

Since MDMA0 is not used anymore, it is removed and the definition
of sysmmu_ips is changed as following:
SYSMMU_MDMA -> System MMU of MDMA1
SYSMMU_MDMA2 -> removed.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/clock.c               |    8 ++--
 arch/arm/mach-exynos4/dev-sysmmu.c          |   73 +++++++++++---------------
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    3 +-
 3 files changed, 36 insertions(+), 48 deletions(-)

diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index 5091e13..256b46b 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -622,10 +622,6 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 13),
 	}, {
-		.name		= "SYSMMU_MDMA",
-		.enable		= exynos4_clk_ip_image_ctrl,
-		.ctrlbit	= (1 << 5),
-	}, {
 		.name		= "SYSMMU_FIMC0",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 7),
@@ -666,6 +662,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
+		.name		= "SYSMMU_MDMA",
+		.enable		= exynos4_clk_ip_image_ctrl,
+		.ctrlbit	= (1 << 5),
+	}, {
 		.name		= "SYSMMU_TV",
 		.enable		= exynos4_clk_ip_tv_ctrl,
 		.ctrlbit	= (1 << 4),
diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index 3b7cae0..af1110e 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -20,7 +20,6 @@
 
 /* These names must be equal to the clock names in mach-exynos4/clock.c */
 const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
-	"SYSMMU_MDMA"	,
 	"SYSMMU_SSS"	,
 	"SYSMMU_FIMC0"	,
 	"SYSMMU_FIMC1"	,
@@ -32,7 +31,7 @@ const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
 	"SYSMMU_PCIe"	,
 	"SYSMMU_G2D"	,
 	"SYSMMU_ROTATOR",
-	"SYSMMU_MDMA2"	,
+	"SYSMMU_MDMA"	,
 	"SYSMMU_TV"	,
 	"SYSMMU_MFC_L"	,
 	"SYSMMU_MFC_R"	,
@@ -40,161 +39,151 @@ const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
 
 static struct resource exynos4_sysmmu_resource[] = {
 	[0] = {
-		.start	= EXYNOS4_PA_SYSMMU_MDMA,
-		.end	= EXYNOS4_PA_SYSMMU_MDMA + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[1] = {
-		.start	= IRQ_SYSMMU_MDMA0_0,
-		.end	= IRQ_SYSMMU_MDMA0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[2] = {
 		.start	= EXYNOS4_PA_SYSMMU_SSS,
 		.end	= EXYNOS4_PA_SYSMMU_SSS + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[3] = {
+	[1] = {
 		.start	= IRQ_SYSMMU_SSS_0,
 		.end	= IRQ_SYSMMU_SSS_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[4] = {
+	[2] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC0,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC0 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[5] = {
+	[3] = {
 		.start	= IRQ_SYSMMU_FIMC0_0,
 		.end	= IRQ_SYSMMU_FIMC0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[6] = {
+	[4] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC1,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC1 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[7] = {
+	[5] = {
 		.start	= IRQ_SYSMMU_FIMC1_0,
 		.end	= IRQ_SYSMMU_FIMC1_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[8] = {
+	[6] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC2,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC2 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[9] = {
+	[7] = {
 		.start	= IRQ_SYSMMU_FIMC2_0,
 		.end	= IRQ_SYSMMU_FIMC2_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[10] = {
+	[8] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMC3,
 		.end	= EXYNOS4_PA_SYSMMU_FIMC3 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[11] = {
+	[9] = {
 		.start	= IRQ_SYSMMU_FIMC3_0,
 		.end	= IRQ_SYSMMU_FIMC3_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[12] = {
+	[10] = {
 		.start	= EXYNOS4_PA_SYSMMU_JPEG,
 		.end	= EXYNOS4_PA_SYSMMU_JPEG + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[13] = {
+	[11] = {
 		.start	= IRQ_SYSMMU_JPEG_0,
 		.end	= IRQ_SYSMMU_JPEG_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[14] = {
+	[12] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMD0,
 		.end	= EXYNOS4_PA_SYSMMU_FIMD0 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[15] = {
+	[13] = {
 		.start	= IRQ_SYSMMU_LCD0_M0_0,
 		.end	= IRQ_SYSMMU_LCD0_M0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[16] = {
+	[14] = {
 		.start	= EXYNOS4_PA_SYSMMU_FIMD1,
 		.end	= EXYNOS4_PA_SYSMMU_FIMD1 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[17] = {
+	[15] = {
 		.start	= IRQ_SYSMMU_LCD1_M1_0,
 		.end	= IRQ_SYSMMU_LCD1_M1_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[18] = {
+	[16] = {
 		.start	= EXYNOS4_PA_SYSMMU_PCIe,
 		.end	= EXYNOS4_PA_SYSMMU_PCIe + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[19] = {
+	[17] = {
 		.start	= IRQ_SYSMMU_PCIE_0,
 		.end	= IRQ_SYSMMU_PCIE_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[20] = {
+	[18] = {
 		.start	= EXYNOS4_PA_SYSMMU_G2D,
 		.end	= EXYNOS4_PA_SYSMMU_G2D + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[21] = {
+	[19] = {
 		.start	= IRQ_SYSMMU_2D_0,
 		.end	= IRQ_SYSMMU_2D_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[22] = {
+	[20] = {
 		.start	= EXYNOS4_PA_SYSMMU_ROTATOR,
 		.end	= EXYNOS4_PA_SYSMMU_ROTATOR + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[23] = {
+	[21] = {
 		.start	= IRQ_SYSMMU_ROTATOR_0,
 		.end	= IRQ_SYSMMU_ROTATOR_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[24] = {
+	[22] = {
 		.start	= EXYNOS4_PA_SYSMMU_MDMA2,
 		.end	= EXYNOS4_PA_SYSMMU_MDMA2 + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[25] = {
+	[23] = {
 		.start	= IRQ_SYSMMU_MDMA1_0,
 		.end	= IRQ_SYSMMU_MDMA1_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[26] = {
+	[24] = {
 		.start	= EXYNOS4_PA_SYSMMU_TV,
 		.end	= EXYNOS4_PA_SYSMMU_TV + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[27] = {
+	[25] = {
 		.start	= IRQ_SYSMMU_TV_M0_0,
 		.end	= IRQ_SYSMMU_TV_M0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[28] = {
+	[26] = {
 		.start	= EXYNOS4_PA_SYSMMU_MFC_L,
 		.end	= EXYNOS4_PA_SYSMMU_MFC_L + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[29] = {
+	[27] = {
 		.start	= IRQ_SYSMMU_MFC_M0_0,
 		.end	= IRQ_SYSMMU_MFC_M0_0,
 		.flags	= IORESOURCE_IRQ,
 	},
-	[30] = {
+	[28] = {
 		.start	= EXYNOS4_PA_SYSMMU_MFC_R,
 		.end	= EXYNOS4_PA_SYSMMU_MFC_R + SZ_64K - 1,
 		.flags	= IORESOURCE_MEM,
 	},
-	[31] = {
+	[29] = {
 		.start	= IRQ_SYSMMU_MFC_M1_0,
 		.end	= IRQ_SYSMMU_MFC_M1_0,
 		.flags	= IORESOURCE_IRQ,
@@ -203,7 +192,7 @@ static struct resource exynos4_sysmmu_resource[] = {
 
 struct platform_device exynos4_device_sysmmu = {
 	.name		= "s5p-sysmmu",
-	.id		= 32,
+	.id		= 30,
 	.num_resources	= ARRAY_SIZE(exynos4_sysmmu_resource),
 	.resource	= exynos4_sysmmu_resource,
 };
diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 6a5fbb5..2d5d78b 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -14,7 +14,6 @@
 #define __ASM_ARM_ARCH_SYSMMU_H __FILE__
 
 enum exynos4_sysmmu_ips {
-	SYSMMU_MDMA,
 	SYSMMU_SSS,
 	SYSMMU_FIMC0,
 	SYSMMU_FIMC1,
@@ -26,7 +25,7 @@ enum exynos4_sysmmu_ips {
 	SYSMMU_PCIe,
 	SYSMMU_G2D,
 	SYSMMU_ROTATOR,
-	SYSMMU_MDMA2,
+	SYSMMU_MDMA,
 	SYSMMU_TV,
 	SYSMMU_MFC_L,
 	SYSMMU_MFC_R,
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMU of SSS
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho

clock gating of SYSMMU_SSS that was missing is added.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/clock.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index 256b46b..ecbe87b 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -154,6 +154,11 @@ static int exynos4_clk_audss_ctrl(struct clk *clk, int enable)
 	return s5p_gatectrl(S5P_CLKGATE_AUDSS, clk, enable);
 }
 
+static int exynos4_clk_ip_dmc_ctrl(struct clk *clk, int enable)
+{
+	return s5p_gatectrl(S5P_CLKGATE_IP_DMC, clk, enable);
+}
+
 /* Core list of CMU_CPU side */
 
 static struct clksrc_clk clk_mout_apll = {
@@ -622,6 +627,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 13),
 	}, {
+		.name		= "SYSMMU_SSS",
+		.enable		= exynos4_clk_ip_dmc_ctrl,
+		.ctrlbit	= (1 << 12),
+	}, {
 		.name		= "SYSMMU_FIMC0",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 7),
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMU of SSS
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

clock gating of SYSMMU_SSS that was missing is added.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/clock.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index 256b46b..ecbe87b 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -154,6 +154,11 @@ static int exynos4_clk_audss_ctrl(struct clk *clk, int enable)
 	return s5p_gatectrl(S5P_CLKGATE_AUDSS, clk, enable);
 }
 
+static int exynos4_clk_ip_dmc_ctrl(struct clk *clk, int enable)
+{
+	return s5p_gatectrl(S5P_CLKGATE_IP_DMC, clk, enable);
+}
+
 /* Core list of CMU_CPU side */
 
 static struct clksrc_clk clk_mout_apll = {
@@ -622,6 +627,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 13),
 	}, {
+		.name		= "SYSMMU_SSS",
+		.enable		= exynos4_clk_ip_dmc_ctrl,
+		.ctrlbit	= (1 << 12),
+	}, {
 		.name		= "SYSMMU_FIMC0",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 7),
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho, Marek Szyprowski

This patch is a bit enhancement of Marek and Andrzej's patch
that includes the suggestion of System MMU's resource management.
https://patchwork.kernel.org/patch/714601/

Resource and platform device definitions of all system MMUs are very
similar, the definitions are simplified with a macro definition.

Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/dev-sysmmu.c |  216 ++++++++++--------------------------
 1 files changed, 59 insertions(+), 157 deletions(-)

diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index af1110e..5da8a4c 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -37,166 +37,68 @@ const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
 	"SYSMMU_MFC_R"	,
 };
 
-static struct resource exynos4_sysmmu_resource[] = {
-	[0] = {
-		.start	= EXYNOS4_PA_SYSMMU_SSS,
-		.end	= EXYNOS4_PA_SYSMMU_SSS + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[1] = {
-		.start	= IRQ_SYSMMU_SSS_0,
-		.end	= IRQ_SYSMMU_SSS_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[2] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC0,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC0 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[3] = {
-		.start	= IRQ_SYSMMU_FIMC0_0,
-		.end	= IRQ_SYSMMU_FIMC0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[4] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC1,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC1 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[5] = {
-		.start	= IRQ_SYSMMU_FIMC1_0,
-		.end	= IRQ_SYSMMU_FIMC1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[6] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC2,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC2 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[7] = {
-		.start	= IRQ_SYSMMU_FIMC2_0,
-		.end	= IRQ_SYSMMU_FIMC2_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[8] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC3,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC3 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[9] = {
-		.start	= IRQ_SYSMMU_FIMC3_0,
-		.end	= IRQ_SYSMMU_FIMC3_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[10] = {
-		.start	= EXYNOS4_PA_SYSMMU_JPEG,
-		.end	= EXYNOS4_PA_SYSMMU_JPEG + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[11] = {
-		.start	= IRQ_SYSMMU_JPEG_0,
-		.end	= IRQ_SYSMMU_JPEG_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[12] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMD0,
-		.end	= EXYNOS4_PA_SYSMMU_FIMD0 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[13] = {
-		.start	= IRQ_SYSMMU_LCD0_M0_0,
-		.end	= IRQ_SYSMMU_LCD0_M0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[14] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMD1,
-		.end	= EXYNOS4_PA_SYSMMU_FIMD1 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[15] = {
-		.start	= IRQ_SYSMMU_LCD1_M1_0,
-		.end	= IRQ_SYSMMU_LCD1_M1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[16] = {
-		.start	= EXYNOS4_PA_SYSMMU_PCIe,
-		.end	= EXYNOS4_PA_SYSMMU_PCIe + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[17] = {
-		.start	= IRQ_SYSMMU_PCIE_0,
-		.end	= IRQ_SYSMMU_PCIE_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[18] = {
-		.start	= EXYNOS4_PA_SYSMMU_G2D,
-		.end	= EXYNOS4_PA_SYSMMU_G2D + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[19] = {
-		.start	= IRQ_SYSMMU_2D_0,
-		.end	= IRQ_SYSMMU_2D_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[20] = {
-		.start	= EXYNOS4_PA_SYSMMU_ROTATOR,
-		.end	= EXYNOS4_PA_SYSMMU_ROTATOR + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[21] = {
-		.start	= IRQ_SYSMMU_ROTATOR_0,
-		.end	= IRQ_SYSMMU_ROTATOR_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[22] = {
-		.start	= EXYNOS4_PA_SYSMMU_MDMA2,
-		.end	= EXYNOS4_PA_SYSMMU_MDMA2 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[23] = {
-		.start	= IRQ_SYSMMU_MDMA1_0,
-		.end	= IRQ_SYSMMU_MDMA1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[24] = {
-		.start	= EXYNOS4_PA_SYSMMU_TV,
-		.end	= EXYNOS4_PA_SYSMMU_TV + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[25] = {
-		.start	= IRQ_SYSMMU_TV_M0_0,
-		.end	= IRQ_SYSMMU_TV_M0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[26] = {
-		.start	= EXYNOS4_PA_SYSMMU_MFC_L,
-		.end	= EXYNOS4_PA_SYSMMU_MFC_L + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[27] = {
-		.start	= IRQ_SYSMMU_MFC_M0_0,
-		.end	= IRQ_SYSMMU_MFC_M0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[28] = {
-		.start	= EXYNOS4_PA_SYSMMU_MFC_R,
-		.end	= EXYNOS4_PA_SYSMMU_MFC_R + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[29] = {
-		.start	= IRQ_SYSMMU_MFC_M1_0,
-		.end	= IRQ_SYSMMU_MFC_M1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
+#define SYSMMU_RESOURCE(name, irq) [SYSMMU_##name] = {\
+		[0] = {\
+			.start	= EXYNOS4_PA_SYSMMU_##name,\
+			.end	= EXYNOS4_PA_SYSMMU_##name + SZ_4K - 1,\
+			.flags	= IORESOURCE_MEM,\
+		},\
+		[1] = {\
+			.start	= IRQ_SYSMMU_##irq##_0,\
+			.end	= IRQ_SYSMMU_##irq##_0,\
+			.flags	= IORESOURCE_IRQ,\
+		},\
+	}
+
+static u64 exynos4_sysmmu_dma_mask = DMA_BIT_MASK(32);
+
+#define SYSMMU_PLATFORM_DEVICE(ips) [SYSMMU_##ips] = {\
+	.name		= "s5p-sysmmu",\
+	.id		= SYSMMU_##ips,\
+	.num_resources	= ARRAY_SIZE(\
+				exynos4_sysmmu_resource[SYSMMU_##ips]),\
+	.resource	= exynos4_sysmmu_resource[SYSMMU_##ips],\
+	.dev		= {\
+		.dma_mask		= &exynos4_sysmmu_dma_mask,\
+		.coherent_dma_mask	= DMA_BIT_MASK(32),\
+	},\
+}
+
+static struct resource exynos4_sysmmu_resource[S5P_SYSMMU_TOTAL_IPNUM][2] = {
+	SYSMMU_RESOURCE(SSS, SSS),
+	SYSMMU_RESOURCE(FIMC0, FIMC0),
+	SYSMMU_RESOURCE(FIMC1, FIMC1),
+	SYSMMU_RESOURCE(FIMC2, FIMC2),
+	SYSMMU_RESOURCE(FIMC3, FIMC3),
+	SYSMMU_RESOURCE(JPEG, JPEG),
+	SYSMMU_RESOURCE(FIMD0, LCD0_M0),
+	SYSMMU_RESOURCE(FIMD1, LCD1_M1),
+	SYSMMU_RESOURCE(PCIe, PCIE),
+	SYSMMU_RESOURCE(G2D, 2D),
+	SYSMMU_RESOURCE(ROTATOR, ROTATOR),
+	SYSMMU_RESOURCE(MDMA, MDMA1),
+	SYSMMU_RESOURCE(TV, TV_M0),
+	SYSMMU_RESOURCE(MFC_L, MFC_M0),
+	SYSMMU_RESOURCE(MFC_R, MFC_M1),
 };
 
-struct platform_device exynos4_device_sysmmu = {
-	.name		= "s5p-sysmmu",
-	.id		= 30,
-	.num_resources	= ARRAY_SIZE(exynos4_sysmmu_resource),
-	.resource	= exynos4_sysmmu_resource,
+struct platform_device exynos4_device_sysmmu[S5P_SYSMMU_TOTAL_IPNUM] = {
+	SYSMMU_PLATFORM_DEVICE(SSS),
+	SYSMMU_PLATFORM_DEVICE(FIMC0),
+	SYSMMU_PLATFORM_DEVICE(FIMC1),
+	SYSMMU_PLATFORM_DEVICE(FIMC2),
+	SYSMMU_PLATFORM_DEVICE(FIMC3),
+	SYSMMU_PLATFORM_DEVICE(JPEG),
+	SYSMMU_PLATFORM_DEVICE(FIMD0),
+	SYSMMU_PLATFORM_DEVICE(FIMD1),
+	SYSMMU_PLATFORM_DEVICE(PCIe),
+	SYSMMU_PLATFORM_DEVICE(G2D),
+	SYSMMU_PLATFORM_DEVICE(ROTATOR),
+	SYSMMU_PLATFORM_DEVICE(MDMA),
+	SYSMMU_PLATFORM_DEVICE(TV),
+	SYSMMU_PLATFORM_DEVICE(MFC_L),
+	SYSMMU_PLATFORM_DEVICE(MFC_R),
 };
-EXPORT_SYMBOL(exynos4_device_sysmmu);
 
 static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
 void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

This patch is a bit enhancement of Marek and Andrzej's patch
that includes the suggestion of System MMU's resource management.
https://patchwork.kernel.org/patch/714601/

Resource and platform device definitions of all system MMUs are very
similar, the definitions are simplified with a macro definition.

Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/dev-sysmmu.c |  216 ++++++++++--------------------------
 1 files changed, 59 insertions(+), 157 deletions(-)

diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index af1110e..5da8a4c 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -37,166 +37,68 @@ const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
 	"SYSMMU_MFC_R"	,
 };
 
-static struct resource exynos4_sysmmu_resource[] = {
-	[0] = {
-		.start	= EXYNOS4_PA_SYSMMU_SSS,
-		.end	= EXYNOS4_PA_SYSMMU_SSS + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[1] = {
-		.start	= IRQ_SYSMMU_SSS_0,
-		.end	= IRQ_SYSMMU_SSS_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[2] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC0,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC0 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[3] = {
-		.start	= IRQ_SYSMMU_FIMC0_0,
-		.end	= IRQ_SYSMMU_FIMC0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[4] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC1,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC1 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[5] = {
-		.start	= IRQ_SYSMMU_FIMC1_0,
-		.end	= IRQ_SYSMMU_FIMC1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[6] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC2,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC2 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[7] = {
-		.start	= IRQ_SYSMMU_FIMC2_0,
-		.end	= IRQ_SYSMMU_FIMC2_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[8] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMC3,
-		.end	= EXYNOS4_PA_SYSMMU_FIMC3 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[9] = {
-		.start	= IRQ_SYSMMU_FIMC3_0,
-		.end	= IRQ_SYSMMU_FIMC3_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[10] = {
-		.start	= EXYNOS4_PA_SYSMMU_JPEG,
-		.end	= EXYNOS4_PA_SYSMMU_JPEG + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[11] = {
-		.start	= IRQ_SYSMMU_JPEG_0,
-		.end	= IRQ_SYSMMU_JPEG_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[12] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMD0,
-		.end	= EXYNOS4_PA_SYSMMU_FIMD0 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[13] = {
-		.start	= IRQ_SYSMMU_LCD0_M0_0,
-		.end	= IRQ_SYSMMU_LCD0_M0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[14] = {
-		.start	= EXYNOS4_PA_SYSMMU_FIMD1,
-		.end	= EXYNOS4_PA_SYSMMU_FIMD1 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[15] = {
-		.start	= IRQ_SYSMMU_LCD1_M1_0,
-		.end	= IRQ_SYSMMU_LCD1_M1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[16] = {
-		.start	= EXYNOS4_PA_SYSMMU_PCIe,
-		.end	= EXYNOS4_PA_SYSMMU_PCIe + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[17] = {
-		.start	= IRQ_SYSMMU_PCIE_0,
-		.end	= IRQ_SYSMMU_PCIE_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[18] = {
-		.start	= EXYNOS4_PA_SYSMMU_G2D,
-		.end	= EXYNOS4_PA_SYSMMU_G2D + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[19] = {
-		.start	= IRQ_SYSMMU_2D_0,
-		.end	= IRQ_SYSMMU_2D_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[20] = {
-		.start	= EXYNOS4_PA_SYSMMU_ROTATOR,
-		.end	= EXYNOS4_PA_SYSMMU_ROTATOR + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[21] = {
-		.start	= IRQ_SYSMMU_ROTATOR_0,
-		.end	= IRQ_SYSMMU_ROTATOR_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[22] = {
-		.start	= EXYNOS4_PA_SYSMMU_MDMA2,
-		.end	= EXYNOS4_PA_SYSMMU_MDMA2 + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[23] = {
-		.start	= IRQ_SYSMMU_MDMA1_0,
-		.end	= IRQ_SYSMMU_MDMA1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[24] = {
-		.start	= EXYNOS4_PA_SYSMMU_TV,
-		.end	= EXYNOS4_PA_SYSMMU_TV + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[25] = {
-		.start	= IRQ_SYSMMU_TV_M0_0,
-		.end	= IRQ_SYSMMU_TV_M0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[26] = {
-		.start	= EXYNOS4_PA_SYSMMU_MFC_L,
-		.end	= EXYNOS4_PA_SYSMMU_MFC_L + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[27] = {
-		.start	= IRQ_SYSMMU_MFC_M0_0,
-		.end	= IRQ_SYSMMU_MFC_M0_0,
-		.flags	= IORESOURCE_IRQ,
-	},
-	[28] = {
-		.start	= EXYNOS4_PA_SYSMMU_MFC_R,
-		.end	= EXYNOS4_PA_SYSMMU_MFC_R + SZ_64K - 1,
-		.flags	= IORESOURCE_MEM,
-	},
-	[29] = {
-		.start	= IRQ_SYSMMU_MFC_M1_0,
-		.end	= IRQ_SYSMMU_MFC_M1_0,
-		.flags	= IORESOURCE_IRQ,
-	},
+#define SYSMMU_RESOURCE(name, irq) [SYSMMU_##name] = {\
+		[0] = {\
+			.start	= EXYNOS4_PA_SYSMMU_##name,\
+			.end	= EXYNOS4_PA_SYSMMU_##name + SZ_4K - 1,\
+			.flags	= IORESOURCE_MEM,\
+		},\
+		[1] = {\
+			.start	= IRQ_SYSMMU_##irq##_0,\
+			.end	= IRQ_SYSMMU_##irq##_0,\
+			.flags	= IORESOURCE_IRQ,\
+		},\
+	}
+
+static u64 exynos4_sysmmu_dma_mask = DMA_BIT_MASK(32);
+
+#define SYSMMU_PLATFORM_DEVICE(ips) [SYSMMU_##ips] = {\
+	.name		= "s5p-sysmmu",\
+	.id		= SYSMMU_##ips,\
+	.num_resources	= ARRAY_SIZE(\
+				exynos4_sysmmu_resource[SYSMMU_##ips]),\
+	.resource	= exynos4_sysmmu_resource[SYSMMU_##ips],\
+	.dev		= {\
+		.dma_mask		= &exynos4_sysmmu_dma_mask,\
+		.coherent_dma_mask	= DMA_BIT_MASK(32),\
+	},\
+}
+
+static struct resource exynos4_sysmmu_resource[S5P_SYSMMU_TOTAL_IPNUM][2] = {
+	SYSMMU_RESOURCE(SSS, SSS),
+	SYSMMU_RESOURCE(FIMC0, FIMC0),
+	SYSMMU_RESOURCE(FIMC1, FIMC1),
+	SYSMMU_RESOURCE(FIMC2, FIMC2),
+	SYSMMU_RESOURCE(FIMC3, FIMC3),
+	SYSMMU_RESOURCE(JPEG, JPEG),
+	SYSMMU_RESOURCE(FIMD0, LCD0_M0),
+	SYSMMU_RESOURCE(FIMD1, LCD1_M1),
+	SYSMMU_RESOURCE(PCIe, PCIE),
+	SYSMMU_RESOURCE(G2D, 2D),
+	SYSMMU_RESOURCE(ROTATOR, ROTATOR),
+	SYSMMU_RESOURCE(MDMA, MDMA1),
+	SYSMMU_RESOURCE(TV, TV_M0),
+	SYSMMU_RESOURCE(MFC_L, MFC_M0),
+	SYSMMU_RESOURCE(MFC_R, MFC_M1),
 };
 
-struct platform_device exynos4_device_sysmmu = {
-	.name		= "s5p-sysmmu",
-	.id		= 30,
-	.num_resources	= ARRAY_SIZE(exynos4_sysmmu_resource),
-	.resource	= exynos4_sysmmu_resource,
+struct platform_device exynos4_device_sysmmu[S5P_SYSMMU_TOTAL_IPNUM] = {
+	SYSMMU_PLATFORM_DEVICE(SSS),
+	SYSMMU_PLATFORM_DEVICE(FIMC0),
+	SYSMMU_PLATFORM_DEVICE(FIMC1),
+	SYSMMU_PLATFORM_DEVICE(FIMC2),
+	SYSMMU_PLATFORM_DEVICE(FIMC3),
+	SYSMMU_PLATFORM_DEVICE(JPEG),
+	SYSMMU_PLATFORM_DEVICE(FIMD0),
+	SYSMMU_PLATFORM_DEVICE(FIMD1),
+	SYSMMU_PLATFORM_DEVICE(PCIe),
+	SYSMMU_PLATFORM_DEVICE(G2D),
+	SYSMMU_PLATFORM_DEVICE(ROTATOR),
+	SYSMMU_PLATFORM_DEVICE(MDMA),
+	SYSMMU_PLATFORM_DEVICE(TV),
+	SYSMMU_PLATFORM_DEVICE(MFC_L),
+	SYSMMU_PLATFORM_DEVICE(MFC_R),
 };
-EXPORT_SYMBOL(exynos4_device_sysmmu);
 
 static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
 void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support clkdev.
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho

This patch adds 'devname' memeber to clk structure definition for
System MMUs. Use of 'name' member of clk structure is obsolete anymore.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/clock.c      |   15 +++++++++++++++
 arch/arm/mach-exynos4/dev-sysmmu.c |    2 +-
 2 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index ecbe87b..7d9fbeb 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -628,62 +628,77 @@ static struct clk init_clocks_off[] = {
 		.ctrlbit	= (1 << 13),
 	}, {
 		.name		= "SYSMMU_SSS",
+		.devname	= "s5p-sysmmu.0",
 		.enable		= exynos4_clk_ip_dmc_ctrl,
 		.ctrlbit	= (1 << 12),
 	}, {
 		.name		= "SYSMMU_FIMC0",
+		.devname	= "s5p-sysmmu.1",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 7),
 	}, {
 		.name		= "SYSMMU_FIMC1",
+		.devname	= "s5p-sysmmu.2",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 8),
 	}, {
 		.name		= "SYSMMU_FIMC2",
+		.devname	= "s5p-sysmmu.3",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 9),
 	}, {
 		.name		= "SYSMMU_FIMC3",
+		.devname	= "s5p-sysmmu.4",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 10),
 	}, {
 		.name		= "SYSMMU_JPEG",
+		.devname	= "s5p-sysmmu.5",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 11),
 	}, {
 		.name		= "SYSMMU_FIMD0",
+		.devname	= "s5p-sysmmu.6",
 		.enable		= exynos4_clk_ip_lcd0_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_FIMD1",
+		.devname	= "s5p-sysmmu.7",
 		.enable		= exynos4_clk_ip_lcd1_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_PCIe",
+		.devname	= "s5p-sysmmu.8",
 		.enable		= exynos4_clk_ip_fsys_ctrl,
 		.ctrlbit	= (1 << 18),
 	}, {
 		.name		= "SYSMMU_G2D",
+		.devname	= "s5p-sysmmu.9",
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 3),
 	}, {
 		.name		= "SYSMMU_ROTATOR",
+		.devname	= "s5p-sysmmu.10",
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_MDMA",
+		.devname	= "s5p-sysmmu.11",
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 5),
 	}, {
 		.name		= "SYSMMU_TV",
+		.devname	= "s5p-sysmmu.12",
 		.enable		= exynos4_clk_ip_tv_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_MFC_L",
+		.devname	= "s5p-sysmmu.13",
 		.enable		= exynos4_clk_ip_mfc_ctrl,
 		.ctrlbit	= (1 << 1),
 	}, {
 		.name		= "SYSMMU_MFC_R",
+		.devname	= "s5p-sysmmu.14",
 		.enable		= exynos4_clk_ip_mfc_ctrl,
 		.ctrlbit	= (1 << 2),
 	}
diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index 5da8a4c..824a079 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -103,7 +103,7 @@ struct platform_device exynos4_device_sysmmu[S5P_SYSMMU_TOTAL_IPNUM] = {
 static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
 void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
 {
-	sysmmu_clk[ips] = clk_get(dev, sysmmu_ips_name[ips]);
+	sysmmu_clk[ips] = clk_get(dev, NULL);
 	if (IS_ERR(sysmmu_clk[ips]))
 		sysmmu_clk[ips] = NULL;
 	else
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support clkdev.
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds 'devname' memeber to clk structure definition for
System MMUs. Use of 'name' member of clk structure is obsolete anymore.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/clock.c      |   15 +++++++++++++++
 arch/arm/mach-exynos4/dev-sysmmu.c |    2 +-
 2 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c
index ecbe87b..7d9fbeb 100644
--- a/arch/arm/mach-exynos4/clock.c
+++ b/arch/arm/mach-exynos4/clock.c
@@ -628,62 +628,77 @@ static struct clk init_clocks_off[] = {
 		.ctrlbit	= (1 << 13),
 	}, {
 		.name		= "SYSMMU_SSS",
+		.devname	= "s5p-sysmmu.0",
 		.enable		= exynos4_clk_ip_dmc_ctrl,
 		.ctrlbit	= (1 << 12),
 	}, {
 		.name		= "SYSMMU_FIMC0",
+		.devname	= "s5p-sysmmu.1",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 7),
 	}, {
 		.name		= "SYSMMU_FIMC1",
+		.devname	= "s5p-sysmmu.2",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 8),
 	}, {
 		.name		= "SYSMMU_FIMC2",
+		.devname	= "s5p-sysmmu.3",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 9),
 	}, {
 		.name		= "SYSMMU_FIMC3",
+		.devname	= "s5p-sysmmu.4",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 10),
 	}, {
 		.name		= "SYSMMU_JPEG",
+		.devname	= "s5p-sysmmu.5",
 		.enable		= exynos4_clk_ip_cam_ctrl,
 		.ctrlbit	= (1 << 11),
 	}, {
 		.name		= "SYSMMU_FIMD0",
+		.devname	= "s5p-sysmmu.6",
 		.enable		= exynos4_clk_ip_lcd0_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_FIMD1",
+		.devname	= "s5p-sysmmu.7",
 		.enable		= exynos4_clk_ip_lcd1_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_PCIe",
+		.devname	= "s5p-sysmmu.8",
 		.enable		= exynos4_clk_ip_fsys_ctrl,
 		.ctrlbit	= (1 << 18),
 	}, {
 		.name		= "SYSMMU_G2D",
+		.devname	= "s5p-sysmmu.9",
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 3),
 	}, {
 		.name		= "SYSMMU_ROTATOR",
+		.devname	= "s5p-sysmmu.10",
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_MDMA",
+		.devname	= "s5p-sysmmu.11",
 		.enable		= exynos4_clk_ip_image_ctrl,
 		.ctrlbit	= (1 << 5),
 	}, {
 		.name		= "SYSMMU_TV",
+		.devname	= "s5p-sysmmu.12",
 		.enable		= exynos4_clk_ip_tv_ctrl,
 		.ctrlbit	= (1 << 4),
 	}, {
 		.name		= "SYSMMU_MFC_L",
+		.devname	= "s5p-sysmmu.13",
 		.enable		= exynos4_clk_ip_mfc_ctrl,
 		.ctrlbit	= (1 << 1),
 	}, {
 		.name		= "SYSMMU_MFC_R",
+		.devname	= "s5p-sysmmu.14",
 		.enable		= exynos4_clk_ip_mfc_ctrl,
 		.ctrlbit	= (1 << 2),
 	}
diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index 5da8a4c..824a079 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -103,7 +103,7 @@ struct platform_device exynos4_device_sysmmu[S5P_SYSMMU_TOTAL_IPNUM] = {
 static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
 void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
 {
-	sysmmu_clk[ips] = clk_get(dev, sysmmu_ips_name[ips]);
+	sysmmu_clk[ips] = clk_get(dev, NULL);
 	if (IS_ERR(sysmmu_clk[ips]))
 		sysmmu_clk[ips] = NULL;
 	else
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho

Added new value 'SYSMMU_NONE' to sysmmu_ips.
This value represents invalid System MMU.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 2d5d78b..05e282c 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -33,6 +33,7 @@ enum exynos4_sysmmu_ips {
 };
 
 #define S5P_SYSMMU_TOTAL_IPNUM		EXYNOS4_SYSMMU_TOTAL_IPNUM
+#define SYSMMU_NONE			S5P_SYSMMU_TOTAL_IPNUM
 
 extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

Added new value 'SYSMMU_NONE' to sysmmu_ips.
This value represents invalid System MMU.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 2d5d78b..05e282c 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -33,6 +33,7 @@ enum exynos4_sysmmu_ips {
 };
 
 #define S5P_SYSMMU_TOTAL_IPNUM		EXYNOS4_SYSMMU_TOTAL_IPNUM
+#define SYSMMU_NONE			S5P_SYSMMU_TOTAL_IPNUM
 
 extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU device driver.
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho

Clock gating functions that was defined in mach/dev-sysmmu.c.
As with clkdev, clock gating is not dependent upon a specific platform,
thus, it is moved to system MMU driver.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/dev-sysmmu.c          |   42 ---------------------------
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    8 +----
 arch/arm/plat-s5p/sysmmu.c                  |   42 +++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 48 deletions(-)

diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index 824a079..71be6bf 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -16,26 +16,6 @@
 #include <mach/map.h>
 #include <mach/irqs.h>
 #include <mach/sysmmu.h>
-#include <plat/s5p-clock.h>
-
-/* These names must be equal to the clock names in mach-exynos4/clock.c */
-const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
-	"SYSMMU_SSS"	,
-	"SYSMMU_FIMC0"	,
-	"SYSMMU_FIMC1"	,
-	"SYSMMU_FIMC2"	,
-	"SYSMMU_FIMC3"	,
-	"SYSMMU_JPEG"	,
-	"SYSMMU_FIMD0"	,
-	"SYSMMU_FIMD1"	,
-	"SYSMMU_PCIe"	,
-	"SYSMMU_G2D"	,
-	"SYSMMU_ROTATOR",
-	"SYSMMU_MDMA"	,
-	"SYSMMU_TV"	,
-	"SYSMMU_MFC_L"	,
-	"SYSMMU_MFC_R"	,
-};
 
 #define SYSMMU_RESOURCE(name, irq) [SYSMMU_##name] = {\
 		[0] = {\
@@ -99,25 +79,3 @@ struct platform_device exynos4_device_sysmmu[S5P_SYSMMU_TOTAL_IPNUM] = {
 	SYSMMU_PLATFORM_DEVICE(MFC_L),
 	SYSMMU_PLATFORM_DEVICE(MFC_R),
 };
-
-static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
-void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
-{
-	sysmmu_clk[ips] = clk_get(dev, NULL);
-	if (IS_ERR(sysmmu_clk[ips]))
-		sysmmu_clk[ips] = NULL;
-	else
-		clk_put(sysmmu_clk[ips]);
-}
-
-void sysmmu_clk_enable(sysmmu_ips ips)
-{
-	if (sysmmu_clk[ips])
-		clk_enable(sysmmu_clk[ips]);
-}
-
-void sysmmu_clk_disable(sysmmu_ips ips)
-{
-	if (sysmmu_clk[ips])
-		clk_disable(sysmmu_clk[ips]);
-}
diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 05e282c..a973385 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -35,12 +35,8 @@ enum exynos4_sysmmu_ips {
 #define S5P_SYSMMU_TOTAL_IPNUM		EXYNOS4_SYSMMU_TOTAL_IPNUM
 #define SYSMMU_NONE			S5P_SYSMMU_TOTAL_IPNUM
 
-extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
-
 typedef enum exynos4_sysmmu_ips sysmmu_ips;
 
-void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
-void sysmmu_clk_enable(sysmmu_ips ips);
-void sysmmu_clk_disable(sysmmu_ips ips);
-
+#else /*__ASM_ARM_ARCH_SYSMMU_H */
+#error mach/sysmmu.h must not be included by device drivers
 #endif /* __ASM_ARM_ARCH_SYSMMU_H */
diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c
index 54f5edd..b3ceec9 100644
--- a/arch/arm/plat-s5p/sysmmu.c
+++ b/arch/arm/plat-s5p/sysmmu.c
@@ -11,6 +11,8 @@
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 
 #include <asm/pgtable.h>
 
@@ -24,6 +26,46 @@
 
 static struct device *dev;
 
+const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
+	"SYSMMU_SSS"	,
+	"SYSMMU_FIMC0"	,
+	"SYSMMU_FIMC1"	,
+	"SYSMMU_FIMC2"	,
+	"SYSMMU_FIMC3"	,
+	"SYSMMU_JPEG"	,
+	"SYSMMU_FIMD0"	,
+	"SYSMMU_FIMD1"	,
+	"SYSMMU_PCIe"	,
+	"SYSMMU_G2D"	,
+	"SYSMMU_ROTATOR",
+	"SYSMMU_MDMA"	,
+	"SYSMMU_TV"	,
+	"SYSMMU_MFC_L"	,
+	"SYSMMU_MFC_R"	,
+};
+
+static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
+void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
+{
+	sysmmu_clk[ips] = clk_get(dev, NULL);
+	if (IS_ERR(sysmmu_clk[ips]))
+		sysmmu_clk[ips] = NULL;
+	else
+		clk_put(sysmmu_clk[ips]);
+}
+
+void sysmmu_clk_enable(sysmmu_ips ips)
+{
+	if (sysmmu_clk[ips])
+		clk_enable(sysmmu_clk[ips]);
+}
+
+void sysmmu_clk_disable(sysmmu_ips ips)
+{
+	if (sysmmu_clk[ips])
+		clk_disable(sysmmu_clk[ips]);
+}
+
 static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
 	S5P_PAGE_FAULT_ADDR,
 	S5P_AR_FAULT_ADDR,
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU device driver.
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

Clock gating functions that was defined in mach/dev-sysmmu.c.
As with clkdev, clock gating is not dependent upon a specific platform,
thus, it is moved to system MMU driver.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/dev-sysmmu.c          |   42 ---------------------------
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    8 +----
 arch/arm/plat-s5p/sysmmu.c                  |   42 +++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 48 deletions(-)

diff --git a/arch/arm/mach-exynos4/dev-sysmmu.c b/arch/arm/mach-exynos4/dev-sysmmu.c
index 824a079..71be6bf 100644
--- a/arch/arm/mach-exynos4/dev-sysmmu.c
+++ b/arch/arm/mach-exynos4/dev-sysmmu.c
@@ -16,26 +16,6 @@
 #include <mach/map.h>
 #include <mach/irqs.h>
 #include <mach/sysmmu.h>
-#include <plat/s5p-clock.h>
-
-/* These names must be equal to the clock names in mach-exynos4/clock.c */
-const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
-	"SYSMMU_SSS"	,
-	"SYSMMU_FIMC0"	,
-	"SYSMMU_FIMC1"	,
-	"SYSMMU_FIMC2"	,
-	"SYSMMU_FIMC3"	,
-	"SYSMMU_JPEG"	,
-	"SYSMMU_FIMD0"	,
-	"SYSMMU_FIMD1"	,
-	"SYSMMU_PCIe"	,
-	"SYSMMU_G2D"	,
-	"SYSMMU_ROTATOR",
-	"SYSMMU_MDMA"	,
-	"SYSMMU_TV"	,
-	"SYSMMU_MFC_L"	,
-	"SYSMMU_MFC_R"	,
-};
 
 #define SYSMMU_RESOURCE(name, irq) [SYSMMU_##name] = {\
 		[0] = {\
@@ -99,25 +79,3 @@ struct platform_device exynos4_device_sysmmu[S5P_SYSMMU_TOTAL_IPNUM] = {
 	SYSMMU_PLATFORM_DEVICE(MFC_L),
 	SYSMMU_PLATFORM_DEVICE(MFC_R),
 };
-
-static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
-void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
-{
-	sysmmu_clk[ips] = clk_get(dev, NULL);
-	if (IS_ERR(sysmmu_clk[ips]))
-		sysmmu_clk[ips] = NULL;
-	else
-		clk_put(sysmmu_clk[ips]);
-}
-
-void sysmmu_clk_enable(sysmmu_ips ips)
-{
-	if (sysmmu_clk[ips])
-		clk_enable(sysmmu_clk[ips]);
-}
-
-void sysmmu_clk_disable(sysmmu_ips ips)
-{
-	if (sysmmu_clk[ips])
-		clk_disable(sysmmu_clk[ips]);
-}
diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 05e282c..a973385 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -35,12 +35,8 @@ enum exynos4_sysmmu_ips {
 #define S5P_SYSMMU_TOTAL_IPNUM		EXYNOS4_SYSMMU_TOTAL_IPNUM
 #define SYSMMU_NONE			S5P_SYSMMU_TOTAL_IPNUM
 
-extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
-
 typedef enum exynos4_sysmmu_ips sysmmu_ips;
 
-void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
-void sysmmu_clk_enable(sysmmu_ips ips);
-void sysmmu_clk_disable(sysmmu_ips ips);
-
+#else /*__ASM_ARM_ARCH_SYSMMU_H */
+#error mach/sysmmu.h must not be included by device drivers
 #endif /* __ASM_ARM_ARCH_SYSMMU_H */
diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c
index 54f5edd..b3ceec9 100644
--- a/arch/arm/plat-s5p/sysmmu.c
+++ b/arch/arm/plat-s5p/sysmmu.c
@@ -11,6 +11,8 @@
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
 
 #include <asm/pgtable.h>
 
@@ -24,6 +26,46 @@
 
 static struct device *dev;
 
+const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = {
+	"SYSMMU_SSS"	,
+	"SYSMMU_FIMC0"	,
+	"SYSMMU_FIMC1"	,
+	"SYSMMU_FIMC2"	,
+	"SYSMMU_FIMC3"	,
+	"SYSMMU_JPEG"	,
+	"SYSMMU_FIMD0"	,
+	"SYSMMU_FIMD1"	,
+	"SYSMMU_PCIe"	,
+	"SYSMMU_G2D"	,
+	"SYSMMU_ROTATOR",
+	"SYSMMU_MDMA"	,
+	"SYSMMU_TV"	,
+	"SYSMMU_MFC_L"	,
+	"SYSMMU_MFC_R"	,
+};
+
+static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM];
+void sysmmu_clk_init(struct device *dev, sysmmu_ips ips)
+{
+	sysmmu_clk[ips] = clk_get(dev, NULL);
+	if (IS_ERR(sysmmu_clk[ips]))
+		sysmmu_clk[ips] = NULL;
+	else
+		clk_put(sysmmu_clk[ips]);
+}
+
+void sysmmu_clk_enable(sysmmu_ips ips)
+{
+	if (sysmmu_clk[ips])
+		clk_enable(sysmmu_clk[ips]);
+}
+
+void sysmmu_clk_disable(sysmmu_ips ips)
+{
+	if (sysmmu_clk[ips])
+		clk_disable(sysmmu_clk[ips]);
+}
+
 static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
 	S5P_PAGE_FAULT_ADDR,
 	S5P_AR_FAULT_ADDR,
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  1:41   ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: Joerg Roedel, Sanghyun Lee, Kwanghyun La, Ilho Lee, Kukjin Kim,
	KyongHo Cho

Implemented IOMMU API for Exynos4 platform that has IOMMU(System MMU).
The previous System MMU driver exposed its own functions and was lack
of page table management.

This patch includes complete implementation of IOMMU API
and it is capable of mapping and unmapping any number of orders.

iommu_map() for Exynos4 does not map 16MiB page because it is not
practical now. 1MiB page is sufficient for larger physically contiguous
memory mapping. Performance degradation due to TLB miss is not such a
big problem with 1MiB page.

Since archdata field of structure 'device' contains nothing in ARM,
I've used linked list to manage the relation between domain and device.

Marek is trying to add domain and dma_map_ops fields into dev_archdata.
This way of relation management will be changed after the Marek's work
is merged into the mainline.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    9 +-
 drivers/iommu/Kconfig                       |   11 +
 drivers/iommu/Makefile                      |    1 +
 drivers/iommu/exynos4_sysmmu.c              |  343 ++++++++++++++++++
 drivers/iommu/exynos4_sysmmu.h              |   18 +
 drivers/iommu/exynos_iommu.c                |  496 +++++++++++++++++++++++++++
 6 files changed, 872 insertions(+), 6 deletions(-)
 create mode 100644 drivers/iommu/exynos4_sysmmu.c
 create mode 100644 drivers/iommu/exynos4_sysmmu.h
 create mode 100644 drivers/iommu/exynos_iommu.c

diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 6a5fbb5..2be20c5 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -34,13 +34,10 @@ enum exynos4_sysmmu_ips {
 };
 
 #define S5P_SYSMMU_TOTAL_IPNUM		EXYNOS4_SYSMMU_TOTAL_IPNUM
-
-extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
+#define SYSMMU_NONE			S5P_SYSMMU_TOTAL_IPNUM
 
 typedef enum exynos4_sysmmu_ips sysmmu_ips;
 
-void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
-void sysmmu_clk_enable(sysmmu_ips ips);
-void sysmmu_clk_disable(sysmmu_ips ips);
-
+#else /* __ASM_ARM_ARCH_SYSMMU_H */
+#error mach/sysmmu.h must not be included by device drivers
 #endif /* __ASM_ARM_ARCH_SYSMMU_H */
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index b57b3fa..a9fcc79 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -107,4 +107,15 @@ config INTR_REMAP
 	  To use x2apic mode in the CPU's which support x2APIC enhancements or
 	  to support platforms with CPU's having > 8 bit APIC ID, say Y.
 
+# EXYNOS IOMMU support
+config EXYNOS_IOMMU
+	bool "Exynos4 IOMMU(System MMU) Support"
+	depends on ARCH_EXYNOS4
+	select IOMMU_API
+	help
+	  Support for the System MMU embedded in Samsung Exynos4 SOCs.
+	  These IOMMUs allow virtualization of the address space used by most
+	  cores within the multimedia subsystem.
+
+	  If unsure, say N here.
 endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 4d4d77d..446bbdd 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
 obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
+obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o exynos4_sysmmu.o
diff --git a/drivers/iommu/exynos4_sysmmu.c b/drivers/iommu/exynos4_sysmmu.c
new file mode 100644
index 0000000..01e0966
--- /dev/null
+++ b/drivers/iommu/exynos4_sysmmu.c
@@ -0,0 +1,343 @@
+/* linux/drivers/iommu/exynos4_sysmmu.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/pgtable.h>
+
+#include <mach/map.h>
+#include <mach/regs-sysmmu.h>
+
+#include "exynos4_sysmmu.h"
+
+#define CTRL_ENABLE	0x5
+#define CTRL_BLOCK	0x7
+#define CTRL_DISABLE	0x0
+
+static struct device *dev_sysmmu[S5P_SYSMMU_TOTAL_IPNUM];
+
+static void sysmmu_clk_enable(sysmmu_ips ips)
+{
+	struct clk *clk;
+
+	clk = clk_get(dev_sysmmu[ips], NULL);
+	if (clk)
+		clk_enable(clk);
+}
+
+static void sysmmu_clk_disable(sysmmu_ips ips)
+{
+	struct clk *clk;
+
+	clk = clk_get(dev_sysmmu[ips], NULL);
+	if (clk)
+		clk_disable(clk);
+}
+
+enum S5P_SYSMMU_INTERRUPT_TYPE {
+	SYSMMU_PAGEFAULT,
+	SYSMMU_AR_MULTIHIT,
+	SYSMMU_AW_MULTIHIT,
+	SYSMMU_BUSERROR,
+	SYSMMU_AR_SECURITY,
+	SYSMMU_AR_ACCESS,
+	SYSMMU_AW_SECURITY,
+	SYSMMU_AW_PROTECTION, /* 7 */
+	SYSMMU_FAULTS_NUM
+};
+
+static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
+	S5P_PAGE_FAULT_ADDR,
+	S5P_AR_FAULT_ADDR,
+	S5P_AW_FAULT_ADDR,
+	S5P_DEFAULT_SLAVE_ADDR,
+	S5P_AR_FAULT_ADDR,
+	S5P_AR_FAULT_ADDR,
+	S5P_AW_FAULT_ADDR,
+	S5P_AW_FAULT_ADDR
+};
+
+static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
+	"PAGE FAULT",
+	"AR MULTI-HIT FAULT",
+	"AW MULTI-HIT FAULT",
+	"BUS ERROR",
+	"AR SECURITY PROTECTION FAULT",
+	"AR ACCESS PROTECTION FAULT",
+	"AW SECURITY PROTECTION FAULT",
+	"AW ACCESS PROTECTION FAULT"
+};
+
+static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])(
+		enum S5P_SYSMMU_INTERRUPT_TYPE itype,
+		unsigned long pgtable_base,
+		unsigned long fault_addr);
+
+/*
+ * If adjacent 2 bits are true, the system MMU is enabled.
+ * The system MMU is disabled, otherwise.
+ */
+static unsigned long sysmmu_states;
+
+static inline int set_sysmmu_active(sysmmu_ips ips)
+{
+	/* return true if it is not set */
+	return !test_and_set_bit(ips, &sysmmu_states);
+}
+
+static inline int set_sysmmu_inactive(sysmmu_ips ips)
+{
+	/* return true if it is set */
+	return test_and_clear_bit(ips, &sysmmu_states);
+}
+
+static inline int is_sysmmu_active(sysmmu_ips ips)
+{
+	/* BUG_ON(ips >= S5P_SYSMMU_TOTAL_IPNUM); */
+	return sysmmu_states & (1 << ips);
+}
+
+static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM];
+
+static inline void sysmmu_block(sysmmu_ips ips)
+{
+	__raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
+	dev_dbg(dev_sysmmu[ips], "blocked.\n");
+}
+
+static inline void sysmmu_unblock(sysmmu_ips ips)
+{
+	__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
+	dev_dbg(dev_sysmmu[ips], "unblocked.\n");
+}
+
+static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
+{
+	__raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
+	dev_dbg(dev_sysmmu[ips], "TLB is invalidated.\n");
+}
+
+static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
+{
+	if (unlikely(pgd == 0)) {
+		pgd = (unsigned long)ZERO_PAGE(0);
+		__raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */
+	} else {
+		__raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */
+	}
+
+	__raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
+
+	dev_dbg(dev_sysmmu[ips], "Page table base initialized with 0x%08lX.\n",
+									pgd);
+	__sysmmu_tlb_invalidate(ips);
+}
+
+static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
+{
+	/* SYSMMU is in blocked when interrupt occurred. */
+	unsigned long base = 0;
+	unsigned long addr;
+	sysmmu_ips ips = (sysmmu_ips)dev_id;
+	enum S5P_SYSMMU_INTERRUPT_TYPE itype;
+
+	BUG_ON(!is_sysmmu_active(ips));
+
+	itype = (enum S5P_SYSMMU_INTERRUPT_TYPE)
+		__ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
+
+	BUG_ON(!((itype >= 0) && (itype < 8)));
+
+	base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
+	addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
+
+	dev_alert(dev_sysmmu[ips], "%s occurred at %08lx (PT_BASE_ADDR: %08lx)."
+		"\n", sysmmu_fault_name[itype], addr, base);
+
+	if (fault_handlers[ips]) {
+
+
+		if (fault_handlers[ips](itype, base, addr)) {
+			__raw_writel(1 << itype,
+					sysmmusfrs[ips] + S5P_INT_CLEAR);
+			dev_notice(dev_sysmmu[ips], "%s is resolved. Retrying"
+				" translation.\n", sysmmu_fault_name[itype]);
+		} else {
+			base = 0;
+		}
+	}
+
+	sysmmu_unblock(ips);
+
+	if (!base)
+		dev_notice(dev_sysmmu[ips], "%s is not handled.\n",
+						sysmmu_fault_name[itype]);
+
+	return IRQ_HANDLED;
+}
+
+void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
+{
+	if (is_sysmmu_active(ips)) {
+		sysmmu_block(ips);
+		__sysmmu_set_ptbase(ips, pgd);
+		sysmmu_unblock(ips);
+	} else {
+		dev_dbg(dev_sysmmu[ips], "disabled. Skipping initializing page"
+				" table base...\n");
+	}
+}
+
+void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd)
+{
+	if (set_sysmmu_active(ips)) {
+		sysmmu_clk_enable(ips);
+
+		__sysmmu_set_ptbase(ips, pgd);
+
+		__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
+
+		set_sysmmu_active(ips);
+		dev_dbg(dev_sysmmu[ips], "enabled.\n");
+	} else {
+		dev_dbg(dev_sysmmu[ips], "already enabled."
+					" Skipping initialization...\n");
+	}
+}
+
+void s5p_sysmmu_disable(sysmmu_ips ips)
+{
+	if (set_sysmmu_inactive(ips)) {
+		__raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
+		sysmmu_clk_disable(ips);
+		dev_dbg(dev_sysmmu[ips], "disabled.\n");
+	} else {
+		dev_dbg(dev_sysmmu[ips], "already disabled."
+					" Skipping deinitialization...\n");
+	}
+}
+
+void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
+{
+	if (is_sysmmu_active(ips)) {
+		sysmmu_block(ips);
+		__sysmmu_tlb_invalidate(ips);
+		sysmmu_unblock(ips);
+	} else {
+		dev_dbg(dev_sysmmu[ips], "is disabled. "
+			"Skipping invalidating TLB...\n");
+	}
+}
+
+static int s5p_sysmmu_probe(struct platform_device *pdev)
+{
+	sysmmu_ips id;
+	struct resource *res, *ioarea;
+	int ret;
+	int irq;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to get resource.");
+		return -ENOENT;
+	}
+
+	id = (sysmmu_ips)pdev->id;
+
+	if (id >= S5P_SYSMMU_TOTAL_IPNUM) {
+		dev_err(&pdev->dev, "Unknown System MMU ID %d.", id);
+		return -ENOENT;
+	}
+
+	ioarea = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (ioarea == NULL) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+					"failed to request memory region.");
+		return -ENOMEM;
+	}
+
+	sysmmusfrs[id] = ioremap(res->start, resource_size(res));
+	if (!sysmmusfrs[id]) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to call ioremap().");
+		ret = -ENOENT;
+		goto err_ioremap;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to get irq resource.");
+		ret = irq;
+		goto err_irq;
+	}
+
+	if (request_irq(irq, s5p_sysmmu_irq, 0, dev_name(&pdev->dev),
+								(void *)id)) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to request irq.");
+		ret = -ENOENT;
+		goto err_irq;
+	}
+
+	dev_sysmmu[id] = &pdev->dev;
+
+	dev_dbg(&pdev->dev, "Probing system MMU succeeded.");
+	return 0;
+
+err_irq:
+	iounmap(sysmmusfrs[id]);
+err_ioremap:
+	release_resource(ioarea);
+	kfree(ioarea);
+	dev_err(&pdev->dev, "Probing system MMU failed.");
+	return ret;
+}
+
+static int s5p_sysmmu_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+static int s5p_sysmmu_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int s5p_sysmmu_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops s5p_sysmmu_pm_ops = {
+	.runtime_suspend	= s5p_sysmmu_runtime_suspend,
+	.runtime_resume		= s5p_sysmmu_runtime_resume,
+};
+
+static struct platform_driver s5p_sysmmu_driver = {
+	.probe		= s5p_sysmmu_probe,
+	.remove		= s5p_sysmmu_remove,
+	.driver		= {
+		.owner		= THIS_MODULE,
+		.name		= "s5p-sysmmu",
+		.pm		= &s5p_sysmmu_pm_ops,
+	}
+};
+
+static int __init s5p_sysmmu_init(void)
+{
+	return platform_driver_register(&s5p_sysmmu_driver);
+}
+arch_initcall(s5p_sysmmu_init);
diff --git a/drivers/iommu/exynos4_sysmmu.h b/drivers/iommu/exynos4_sysmmu.h
new file mode 100644
index 0000000..f739240
--- /dev/null
+++ b/drivers/iommu/exynos4_sysmmu.h
@@ -0,0 +1,18 @@
+/* linux/drivers/iommu/exynos4_sysmmu.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Samsung System MMU driver for Exynos4 platforms
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <mach/sysmmu.h>
+
+void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd);
+void s5p_sysmmu_disable(sysmmu_ips ips);
+void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd);
+void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips);
diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c
new file mode 100644
index 0000000..cbb94df
--- /dev/null
+++ b/drivers/iommu/exynos_iommu.c
@@ -0,0 +1,496 @@
+/* linux/drivers/iommu/exynos_iommu.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <asm/cacheflush.h>
+
+#include "exynos4_sysmmu.h"
+
+#ifdef CONFIG_S5P_SYSTEM_MMU_DEBUG
+#define DEBUG /* for dev_dbg() */
+#endif
+
+/* We does not consider super section mapping (16MB) */
+#define S5P_SPAGE_SHIFT		12
+#define S5P_LPAGE_SHIFT		16
+#define S5P_SECTION_SHIFT	20
+
+#define S5P_SPAGE_SIZE		(1 << S5P_SPAGE_SHIFT)
+#define S5P_LPAGE_SIZE		(1 << S5P_LPAGE_SHIFT)
+#define S5P_SECTION_SIZE	(1 << S5P_SECTION_SHIFT)
+
+#define S5P_SPAGE_MASK		(~(S5P_SPAGE_SIZE - 1))
+#define S5P_LPAGE_MASK		(~(S5P_LPAGE_SIZE - 1))
+#define S5P_SECTION_MASK	(~(S5P_SECTION_SIZE - 1))
+
+#define S5P_SPAGE_ORDER		(S5P_SPAGE_SHIFT - PAGE_SHIFT)
+#define S5P_LPAGE_ORDER		(S5P_LPAGE_SHIFT - S5P_SPAGE_SHIFT)
+#define S5P_SECTION_ORDER	(S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT)
+
+#define S5P_LV1TABLE_ENTRIES	(1 << (BITS_PER_LONG - S5P_SECTION_SHIFT))
+
+#define S5P_LV2TABLE_ENTRIES	(1 << S5P_SECTION_ORDER)
+#define S5P_LV2TABLE_SIZE	(S5P_LV2TABLE_ENTRIES * sizeof(long))
+#define S5P_LV2TABLE_MASK	(~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */
+
+#define S5P_SECTION_LV1_ENTRY(entry)	((entry & 0x40003) == 2)
+#define S5P_SUPSECT_LV1_ENTRY(entry)	((entry & 0x40003) == 0x40002)
+#define S5P_PAGE_LV1_ENTRY(entry)	((entry & 3) == 1)
+#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) == 3)
+
+#define S5P_LPAGE_LV2_ENTRY(entry)	((entry & 3) == 1)
+#define S5P_SPAGE_LV2_ENTRY(entry)	((entry & 2) == 2)
+#define S5P_FAULT_LV2_ENTRY(entry)	((entry & 3) == 0)
+
+#define MAKE_FAULT_ENTRY(entry)		do { entry = 0; } while (0)
+#define MAKE_SECTION_ENTRY(entry, pa)	do { entry = pa | 2; } while (0)
+#define MAKE_SUPSECT_ENTRY(entry, pa)	do { entry = pa | 0x40002; } while (0)
+#define MAKE_LV2TABLE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
+
+#define MAKE_LPAGE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
+#define MAKE_SPAGE_ENTRY(entry, pa)	do { entry = pa | 3; } while (0)
+
+#define GET_LV2ENTRY(entry, iova) (\
+	(unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\
+	((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT))
+
+struct s5p_iommu_domain {
+	struct device *dev;
+	sysmmu_ips ips;
+	unsigned long *pgtable;
+};
+
+struct s5p_finddev_struct {
+	char *name;
+	int id;
+};
+/* Shared page table implementation */
+static unsigned long *pgtable;
+
+static struct s5p_finddev_struct pdev_names[S5P_SYSMMU_TOTAL_IPNUM] = {
+	{"s3c-pl330", 0},
+	{"s5p-sss", -1},
+	{"s3c-fimc", 0},
+	{"s3c-fimc", 1},
+	{"s3c-fimc", 2},
+	{"s3c-fimc", 3},
+	{"s5p-jpeg", -1},
+	{"s5p-fb", 0},
+	{"s5p-fb", 1},
+	{"s5p-pcie", -1},
+	{"s5p-fimg2d", -1},
+	{"s5p-rotator", -1},
+	{"s5p-mdma2", -1},
+	{"s5p-mixer", -1},
+	{"mfc", -1}, /* SYSMMU_MFC_L */
+};
+/* slab cache for level 2 page tables */
+static struct kmem_cache *l2table_cachep;
+
+LIST_HEAD(dev_lookup_list);
+
+struct dev_dom {
+	struct list_head node;
+	struct s5p_iommu_domain *dom;
+	struct device *dev;
+};
+
+static inline struct dev_dom *lookup_dev(struct device *dev)
+{
+	struct list_head *pos;
+	struct dev_dom *rel = NULL;
+
+	list_for_each(pos, &dev_lookup_list) {
+		rel = list_entry(pos, struct dev_dom, node);
+		if (rel->dev == dev)
+			return rel;
+	}
+
+	return NULL;
+}
+
+static inline int bind_dev(struct s5p_iommu_domain *dom, struct device *dev)
+{
+	struct dev_dom *rel;
+
+	rel = kmalloc(sizeof(*rel), GFP_KERNEL);
+	if (!rel)
+		return -ENOMEM;
+
+	if (!lookup_dev(dev)) {
+		rel->dom = dom;
+		rel->dev = dev;
+		list_add(&rel->node, &dev_lookup_list);
+	}
+
+	return 0;
+}
+
+static inline void unbind_dev(struct device *dev)
+{
+	struct dev_dom *rel;
+
+	rel = lookup_dev(dev);
+	if (rel)
+		list_del(&rel->node);
+}
+
+static inline void pgtable_flush(void *vastart, void *vaend)
+{
+	dmac_flush_range(vastart, vaend);
+	outer_flush_range(virt_to_phys(vastart),
+				virt_to_phys(vaend));
+}
+
+static int s5p_iommu_domain_init(struct iommu_domain *domain)
+{
+	struct s5p_iommu_domain *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
+		(S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >> PAGE_SHIFT);
+	if (!priv->pgtable) {
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	memset(pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long));
+	pgtable_flush(pgtable, pgtable + S5P_LV1TABLE_ENTRIES);
+
+	domain->priv = priv;
+	return 0;
+}
+
+static void s5p_iommu_domain_destroy(struct iommu_domain *domain)
+{
+	struct s5p_iommu_domain *priv = domain->priv;
+
+	free_pages((unsigned long)priv->pgtable, 2);
+	kfree(domain->priv);
+	domain->priv = NULL;
+}
+
+static sysmmu_ips get_sysmmu_id(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++)
+		if ((strcmp(pdev->name, pdev_names[i].name) == 0) &&
+				(pdev->id == pdev_names[i].id))
+			break;
+
+	return (sysmmu_ips)i;
+}
+
+static int s5p_iommu_attach_device(struct iommu_domain *domain,
+				   struct device *dev)
+{
+	sysmmu_ips ips;
+	int ret;
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	struct platform_device *pdev =
+				container_of(dev, struct platform_device, dev);
+
+	ips = get_sysmmu_id(pdev);
+	if (ips == SYSMMU_NONE)
+		return -ENODEV;
+
+	s5p_domain->ips = ips;
+
+	s5p_sysmmu_enable(ips, (unsigned long)s5p_domain->pgtable);
+	if (ips == SYSMMU_MFC_L)
+		s5p_sysmmu_enable(ips + 1, (unsigned long)s5p_domain->pgtable);
+
+	s5p_domain->dev = dev;
+
+	ret = bind_dev(s5p_domain, dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void s5p_iommu_detach_device(struct iommu_domain *domain,
+				    struct device *dev)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+
+	if (s5p_domain->dev == dev) {
+		s5p_sysmmu_disable(s5p_domain->ips);
+		if (s5p_domain->ips == SYSMMU_MFC_L)
+			s5p_sysmmu_disable(s5p_domain->ips + 1);
+		unbind_dev(dev);
+	}
+}
+
+static bool section_available(struct iommu_domain *domain,
+			      unsigned long *lv1entry)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+
+	if (S5P_SECTION_LV1_ENTRY(*lv1entry)) {
+		dev_err(s5p_domain->dev,
+				"1MB entry alread exists at 0x%08x\n",
+				(lv1entry - s5p_domain->pgtable) * SZ_1M);
+		return false;
+	}
+
+	if (S5P_PAGE_LV1_ENTRY(*lv1entry)) {
+		unsigned long *lv2end, *lv2base;
+
+		lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK);
+		lv2end = lv2base + S5P_LV2TABLE_ENTRIES;
+		while (lv2base != lv2end) {
+			if (S5P_FAULT_LV2_ENTRY(*lv2base)) {
+				dev_err(s5p_domain->dev,
+					"Failed to free L2 page table for"
+					"section mapping.\n");
+				return false;
+			}
+			lv2base++;
+		}
+
+		kmem_cache_free(l2table_cachep,
+				phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK));
+
+		MAKE_FAULT_ENTRY(*lv1entry);
+	}
+
+	return true;
+}
+
+static bool write_lpage(unsigned long *head_entry, unsigned long phys_addr)
+{
+	unsigned long *entry, *end;
+
+	entry = head_entry;
+	end = entry + (1 << S5P_LPAGE_ORDER);
+
+	while (entry != end) {
+		if (!S5P_FAULT_LV2_ENTRY(*entry))
+			break;
+
+		MAKE_LPAGE_ENTRY(*entry, phys_addr);
+
+		entry++;
+	}
+
+	if (entry != end) {
+		end = entry;
+		while (entry != head_entry)
+			MAKE_FAULT_ENTRY(*(--entry));
+
+		return false;
+	}
+
+	return true;
+}
+
+int s5p_iommu_map(struct iommu_domain *domain, unsigned long iova,
+			 phys_addr_t paddr, int gfp_order, int prot)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	unsigned long *start_entry, *entry, *end_entry;
+	int num_entry;
+
+	BUG_ON(s5p_domain->dev == NULL);
+
+	start_entry = entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
+
+	if (gfp_order >= S5P_SECTION_ORDER) {
+		BUG_ON((paddr | iova) & ~S5P_SECTION_MASK);
+		/* 1MiB mapping */
+
+		num_entry = 1 << (gfp_order - S5P_SECTION_ORDER);
+		end_entry = entry + num_entry;
+
+		while (entry != end_entry) {
+			if (!section_available(domain, entry))
+				break;
+
+			MAKE_SECTION_ENTRY(*entry, paddr);
+
+			paddr += S5P_SECTION_SIZE;
+			entry++;
+		}
+
+		if (entry != end_entry)
+			goto mapping_error;
+
+		return 0;
+	}
+
+	if (S5P_FAULT_LV1_ENTRY(*entry)) {
+		unsigned long *l2table;
+
+		l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL);
+		if (!l2table)
+			return -ENOMEM;
+
+		pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES);
+
+		MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table));
+		pgtable_flush(entry, entry + 1);
+	}
+
+	/* 'entry' points level 2 entries, hereafter */
+	entry = GET_LV2ENTRY(*entry, iova);
+
+	start_entry = entry;
+	num_entry = 1 << gfp_order;
+	end_entry = entry + num_entry;
+
+	if (gfp_order >= S5P_LPAGE_ORDER) {
+		/* large page(64KiB) mapping */
+		BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK);
+
+		while (entry != end_entry) {
+			if (!write_lpage(entry, paddr)) {
+				dev_err(s5p_domain->dev,
+					"Failed to allocate large page entry.\n"
+					);
+				break;
+			}
+
+			paddr += S5P_LPAGE_SIZE;
+			entry += (1 << S5P_LPAGE_ORDER);
+		}
+
+		if (entry != end_entry) {
+			entry -= 1 << S5P_LPAGE_ORDER;
+			goto mapping_error;
+		}
+
+		return 0;
+	}
+
+	/* page (4KiB) mapping */
+	while (entry != end_entry && !S5P_FAULT_LV2_ENTRY(*entry)) {
+
+		MAKE_SPAGE_ENTRY(*entry, paddr);
+
+		entry++;
+		paddr += S5P_SPAGE_SIZE;
+	}
+
+	if (entry != end_entry) {
+		dev_err(s5p_domain->dev,
+			"Failed to allocate small page entry.\n");
+		goto mapping_error;
+	}
+
+	pgtable_flush(start_entry, entry);
+mapping_error:
+	if (entry != end_entry) {
+		while (entry != start_entry)
+			MAKE_FAULT_ENTRY(*(--entry));
+		return -EADDRINUSE;
+	}
+
+	return 0;
+}
+
+int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+			   int gfp_order)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	unsigned long *entry;
+	int num_entry;
+
+	BUG_ON(s5p_domain->dev == NULL);
+
+	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
+
+	if (gfp_order >= S5P_SECTION_ORDER)
+		gfp_order -= S5P_SECTION_ORDER;
+	else
+		entry = GET_LV2ENTRY(*entry, iova);
+
+	BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && (gfp_order < S5P_LPAGE_ORDER));
+
+	num_entry = 1 << gfp_order;
+
+	while (num_entry-- > 0) {
+		MAKE_FAULT_ENTRY(*entry);
+		entry++;
+	}
+
+	s5p_sysmmu_tlb_invalidate(s5p_domain->ips);
+	if (s5p_domain->ips == SYSMMU_MFC_L)
+		s5p_sysmmu_tlb_invalidate(s5p_domain->ips + 1);
+	return 0;
+}
+
+phys_addr_t s5p_iommu_iova_to_phys(struct iommu_domain *domain,
+					  unsigned long iova)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	unsigned long *entry;
+	unsigned long offset;
+
+	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
+
+	if (S5P_FAULT_LV1_ENTRY(*entry))
+		return 0;
+
+	offset = iova & ~S5P_SECTION_MASK;
+
+	if (S5P_SECTION_LV1_ENTRY(*entry))
+		return (*entry & S5P_SECTION_MASK) + offset;
+
+	entry = GET_LV2ENTRY(*entry, iova);
+
+	if (S5P_SPAGE_LV2_ENTRY(*entry))
+		return (*entry & S5P_SPAGE_MASK) + (iova & ~S5P_SPAGE_MASK);
+
+	if (S5P_LPAGE_LV2_ENTRY(*entry))
+		return (*entry & S5P_LPAGE_MASK) + (iova & ~S5P_LPAGE_MASK);
+
+	return 0;
+}
+
+static int s5p_iommu_domain_has_cap(struct iommu_domain *domain,
+				    unsigned long cap)
+{
+	return 0;
+}
+
+static struct iommu_ops s5p_iommu_ops = {
+	.domain_init = &s5p_iommu_domain_init,
+	.domain_destroy = &s5p_iommu_domain_destroy,
+	.attach_dev = &s5p_iommu_attach_device,
+	.detach_dev = &s5p_iommu_detach_device,
+	.map = &s5p_iommu_map,
+	.unmap = &s5p_iommu_unmap,
+	.iova_to_phys = &s5p_iommu_iova_to_phys,
+	.domain_has_cap = &s5p_iommu_domain_has_cap
+};
+
+static int __init s5p_iommu_init(void)
+{
+	l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables",
+				S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE, 0, NULL);
+	if (!l2table_cachep)
+		return -ENOMEM;
+
+	register_iommu(&s5p_iommu_ops);
+	return 0;
+}
+arch_initcall(s5p_iommu_init);
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
@ 2011-07-04  1:41   ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04  1:41 UTC (permalink / raw)
  To: linux-arm-kernel

Implemented IOMMU API for Exynos4 platform that has IOMMU(System MMU).
The previous System MMU driver exposed its own functions and was lack
of page table management.

This patch includes complete implementation of IOMMU API
and it is capable of mapping and unmapping any number of orders.

iommu_map() for Exynos4 does not map 16MiB page because it is not
practical now. 1MiB page is sufficient for larger physically contiguous
memory mapping. Performance degradation due to TLB miss is not such a
big problem with 1MiB page.

Since archdata field of structure 'device' contains nothing in ARM,
I've used linked list to manage the relation between domain and device.

Marek is trying to add domain and dma_map_ops fields into dev_archdata.
This way of relation management will be changed after the Marek's work
is merged into the mainline.

Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/sysmmu.h |    9 +-
 drivers/iommu/Kconfig                       |   11 +
 drivers/iommu/Makefile                      |    1 +
 drivers/iommu/exynos4_sysmmu.c              |  343 ++++++++++++++++++
 drivers/iommu/exynos4_sysmmu.h              |   18 +
 drivers/iommu/exynos_iommu.c                |  496 +++++++++++++++++++++++++++
 6 files changed, 872 insertions(+), 6 deletions(-)
 create mode 100644 drivers/iommu/exynos4_sysmmu.c
 create mode 100644 drivers/iommu/exynos4_sysmmu.h
 create mode 100644 drivers/iommu/exynos_iommu.c

diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-exynos4/include/mach/sysmmu.h
index 6a5fbb5..2be20c5 100644
--- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
@@ -34,13 +34,10 @@ enum exynos4_sysmmu_ips {
 };
 
 #define S5P_SYSMMU_TOTAL_IPNUM		EXYNOS4_SYSMMU_TOTAL_IPNUM
-
-extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
+#define SYSMMU_NONE			S5P_SYSMMU_TOTAL_IPNUM
 
 typedef enum exynos4_sysmmu_ips sysmmu_ips;
 
-void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
-void sysmmu_clk_enable(sysmmu_ips ips);
-void sysmmu_clk_disable(sysmmu_ips ips);
-
+#else /* __ASM_ARM_ARCH_SYSMMU_H */
+#error mach/sysmmu.h must not be included by device drivers
 #endif /* __ASM_ARM_ARCH_SYSMMU_H */
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index b57b3fa..a9fcc79 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -107,4 +107,15 @@ config INTR_REMAP
 	  To use x2apic mode in the CPU's which support x2APIC enhancements or
 	  to support platforms with CPU's having > 8 bit APIC ID, say Y.
 
+# EXYNOS IOMMU support
+config EXYNOS_IOMMU
+	bool "Exynos4 IOMMU(System MMU) Support"
+	depends on ARCH_EXYNOS4
+	select IOMMU_API
+	help
+	  Support for the System MMU embedded in Samsung Exynos4 SOCs.
+	  These IOMMUs allow virtualization of the address space used by most
+	  cores within the multimedia subsystem.
+
+	  If unsure, say N here.
 endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 4d4d77d..446bbdd 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
 obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
+obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o exynos4_sysmmu.o
diff --git a/drivers/iommu/exynos4_sysmmu.c b/drivers/iommu/exynos4_sysmmu.c
new file mode 100644
index 0000000..01e0966
--- /dev/null
+++ b/drivers/iommu/exynos4_sysmmu.c
@@ -0,0 +1,343 @@
+/* linux/drivers/iommu/exynos4_sysmmu.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/pgtable.h>
+
+#include <mach/map.h>
+#include <mach/regs-sysmmu.h>
+
+#include "exynos4_sysmmu.h"
+
+#define CTRL_ENABLE	0x5
+#define CTRL_BLOCK	0x7
+#define CTRL_DISABLE	0x0
+
+static struct device *dev_sysmmu[S5P_SYSMMU_TOTAL_IPNUM];
+
+static void sysmmu_clk_enable(sysmmu_ips ips)
+{
+	struct clk *clk;
+
+	clk = clk_get(dev_sysmmu[ips], NULL);
+	if (clk)
+		clk_enable(clk);
+}
+
+static void sysmmu_clk_disable(sysmmu_ips ips)
+{
+	struct clk *clk;
+
+	clk = clk_get(dev_sysmmu[ips], NULL);
+	if (clk)
+		clk_disable(clk);
+}
+
+enum S5P_SYSMMU_INTERRUPT_TYPE {
+	SYSMMU_PAGEFAULT,
+	SYSMMU_AR_MULTIHIT,
+	SYSMMU_AW_MULTIHIT,
+	SYSMMU_BUSERROR,
+	SYSMMU_AR_SECURITY,
+	SYSMMU_AR_ACCESS,
+	SYSMMU_AW_SECURITY,
+	SYSMMU_AW_PROTECTION, /* 7 */
+	SYSMMU_FAULTS_NUM
+};
+
+static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
+	S5P_PAGE_FAULT_ADDR,
+	S5P_AR_FAULT_ADDR,
+	S5P_AW_FAULT_ADDR,
+	S5P_DEFAULT_SLAVE_ADDR,
+	S5P_AR_FAULT_ADDR,
+	S5P_AR_FAULT_ADDR,
+	S5P_AW_FAULT_ADDR,
+	S5P_AW_FAULT_ADDR
+};
+
+static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
+	"PAGE FAULT",
+	"AR MULTI-HIT FAULT",
+	"AW MULTI-HIT FAULT",
+	"BUS ERROR",
+	"AR SECURITY PROTECTION FAULT",
+	"AR ACCESS PROTECTION FAULT",
+	"AW SECURITY PROTECTION FAULT",
+	"AW ACCESS PROTECTION FAULT"
+};
+
+static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])(
+		enum S5P_SYSMMU_INTERRUPT_TYPE itype,
+		unsigned long pgtable_base,
+		unsigned long fault_addr);
+
+/*
+ * If adjacent 2 bits are true, the system MMU is enabled.
+ * The system MMU is disabled, otherwise.
+ */
+static unsigned long sysmmu_states;
+
+static inline int set_sysmmu_active(sysmmu_ips ips)
+{
+	/* return true if it is not set */
+	return !test_and_set_bit(ips, &sysmmu_states);
+}
+
+static inline int set_sysmmu_inactive(sysmmu_ips ips)
+{
+	/* return true if it is set */
+	return test_and_clear_bit(ips, &sysmmu_states);
+}
+
+static inline int is_sysmmu_active(sysmmu_ips ips)
+{
+	/* BUG_ON(ips >= S5P_SYSMMU_TOTAL_IPNUM); */
+	return sysmmu_states & (1 << ips);
+}
+
+static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM];
+
+static inline void sysmmu_block(sysmmu_ips ips)
+{
+	__raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
+	dev_dbg(dev_sysmmu[ips], "blocked.\n");
+}
+
+static inline void sysmmu_unblock(sysmmu_ips ips)
+{
+	__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
+	dev_dbg(dev_sysmmu[ips], "unblocked.\n");
+}
+
+static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
+{
+	__raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
+	dev_dbg(dev_sysmmu[ips], "TLB is invalidated.\n");
+}
+
+static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
+{
+	if (unlikely(pgd == 0)) {
+		pgd = (unsigned long)ZERO_PAGE(0);
+		__raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */
+	} else {
+		__raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */
+	}
+
+	__raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
+
+	dev_dbg(dev_sysmmu[ips], "Page table base initialized with 0x%08lX.\n",
+									pgd);
+	__sysmmu_tlb_invalidate(ips);
+}
+
+static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
+{
+	/* SYSMMU is in blocked when interrupt occurred. */
+	unsigned long base = 0;
+	unsigned long addr;
+	sysmmu_ips ips = (sysmmu_ips)dev_id;
+	enum S5P_SYSMMU_INTERRUPT_TYPE itype;
+
+	BUG_ON(!is_sysmmu_active(ips));
+
+	itype = (enum S5P_SYSMMU_INTERRUPT_TYPE)
+		__ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
+
+	BUG_ON(!((itype >= 0) && (itype < 8)));
+
+	base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
+	addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
+
+	dev_alert(dev_sysmmu[ips], "%s occurred@%08lx (PT_BASE_ADDR: %08lx)."
+		"\n", sysmmu_fault_name[itype], addr, base);
+
+	if (fault_handlers[ips]) {
+
+
+		if (fault_handlers[ips](itype, base, addr)) {
+			__raw_writel(1 << itype,
+					sysmmusfrs[ips] + S5P_INT_CLEAR);
+			dev_notice(dev_sysmmu[ips], "%s is resolved. Retrying"
+				" translation.\n", sysmmu_fault_name[itype]);
+		} else {
+			base = 0;
+		}
+	}
+
+	sysmmu_unblock(ips);
+
+	if (!base)
+		dev_notice(dev_sysmmu[ips], "%s is not handled.\n",
+						sysmmu_fault_name[itype]);
+
+	return IRQ_HANDLED;
+}
+
+void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
+{
+	if (is_sysmmu_active(ips)) {
+		sysmmu_block(ips);
+		__sysmmu_set_ptbase(ips, pgd);
+		sysmmu_unblock(ips);
+	} else {
+		dev_dbg(dev_sysmmu[ips], "disabled. Skipping initializing page"
+				" table base...\n");
+	}
+}
+
+void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd)
+{
+	if (set_sysmmu_active(ips)) {
+		sysmmu_clk_enable(ips);
+
+		__sysmmu_set_ptbase(ips, pgd);
+
+		__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
+
+		set_sysmmu_active(ips);
+		dev_dbg(dev_sysmmu[ips], "enabled.\n");
+	} else {
+		dev_dbg(dev_sysmmu[ips], "already enabled."
+					" Skipping initialization...\n");
+	}
+}
+
+void s5p_sysmmu_disable(sysmmu_ips ips)
+{
+	if (set_sysmmu_inactive(ips)) {
+		__raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
+		sysmmu_clk_disable(ips);
+		dev_dbg(dev_sysmmu[ips], "disabled.\n");
+	} else {
+		dev_dbg(dev_sysmmu[ips], "already disabled."
+					" Skipping deinitialization...\n");
+	}
+}
+
+void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
+{
+	if (is_sysmmu_active(ips)) {
+		sysmmu_block(ips);
+		__sysmmu_tlb_invalidate(ips);
+		sysmmu_unblock(ips);
+	} else {
+		dev_dbg(dev_sysmmu[ips], "is disabled. "
+			"Skipping invalidating TLB...\n");
+	}
+}
+
+static int s5p_sysmmu_probe(struct platform_device *pdev)
+{
+	sysmmu_ips id;
+	struct resource *res, *ioarea;
+	int ret;
+	int irq;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to get resource.");
+		return -ENOENT;
+	}
+
+	id = (sysmmu_ips)pdev->id;
+
+	if (id >= S5P_SYSMMU_TOTAL_IPNUM) {
+		dev_err(&pdev->dev, "Unknown System MMU ID %d.", id);
+		return -ENOENT;
+	}
+
+	ioarea = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (ioarea == NULL) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+					"failed to request memory region.");
+		return -ENOMEM;
+	}
+
+	sysmmusfrs[id] = ioremap(res->start, resource_size(res));
+	if (!sysmmusfrs[id]) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to call ioremap().");
+		ret = -ENOENT;
+		goto err_ioremap;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to get irq resource.");
+		ret = irq;
+		goto err_irq;
+	}
+
+	if (request_irq(irq, s5p_sysmmu_irq, 0, dev_name(&pdev->dev),
+								(void *)id)) {
+		dev_err(&pdev->dev, "Failed probing system MMU: "
+						"failed to request irq.");
+		ret = -ENOENT;
+		goto err_irq;
+	}
+
+	dev_sysmmu[id] = &pdev->dev;
+
+	dev_dbg(&pdev->dev, "Probing system MMU succeeded.");
+	return 0;
+
+err_irq:
+	iounmap(sysmmusfrs[id]);
+err_ioremap:
+	release_resource(ioarea);
+	kfree(ioarea);
+	dev_err(&pdev->dev, "Probing system MMU failed.");
+	return ret;
+}
+
+static int s5p_sysmmu_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+static int s5p_sysmmu_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int s5p_sysmmu_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops s5p_sysmmu_pm_ops = {
+	.runtime_suspend	= s5p_sysmmu_runtime_suspend,
+	.runtime_resume		= s5p_sysmmu_runtime_resume,
+};
+
+static struct platform_driver s5p_sysmmu_driver = {
+	.probe		= s5p_sysmmu_probe,
+	.remove		= s5p_sysmmu_remove,
+	.driver		= {
+		.owner		= THIS_MODULE,
+		.name		= "s5p-sysmmu",
+		.pm		= &s5p_sysmmu_pm_ops,
+	}
+};
+
+static int __init s5p_sysmmu_init(void)
+{
+	return platform_driver_register(&s5p_sysmmu_driver);
+}
+arch_initcall(s5p_sysmmu_init);
diff --git a/drivers/iommu/exynos4_sysmmu.h b/drivers/iommu/exynos4_sysmmu.h
new file mode 100644
index 0000000..f739240
--- /dev/null
+++ b/drivers/iommu/exynos4_sysmmu.h
@@ -0,0 +1,18 @@
+/* linux/drivers/iommu/exynos4_sysmmu.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Samsung System MMU driver for Exynos4 platforms
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <mach/sysmmu.h>
+
+void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd);
+void s5p_sysmmu_disable(sysmmu_ips ips);
+void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd);
+void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips);
diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c
new file mode 100644
index 0000000..cbb94df
--- /dev/null
+++ b/drivers/iommu/exynos_iommu.c
@@ -0,0 +1,496 @@
+/* linux/drivers/iommu/exynos_iommu.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+
+#include <asm/cacheflush.h>
+
+#include "exynos4_sysmmu.h"
+
+#ifdef CONFIG_S5P_SYSTEM_MMU_DEBUG
+#define DEBUG /* for dev_dbg() */
+#endif
+
+/* We does not consider super section mapping (16MB) */
+#define S5P_SPAGE_SHIFT		12
+#define S5P_LPAGE_SHIFT		16
+#define S5P_SECTION_SHIFT	20
+
+#define S5P_SPAGE_SIZE		(1 << S5P_SPAGE_SHIFT)
+#define S5P_LPAGE_SIZE		(1 << S5P_LPAGE_SHIFT)
+#define S5P_SECTION_SIZE	(1 << S5P_SECTION_SHIFT)
+
+#define S5P_SPAGE_MASK		(~(S5P_SPAGE_SIZE - 1))
+#define S5P_LPAGE_MASK		(~(S5P_LPAGE_SIZE - 1))
+#define S5P_SECTION_MASK	(~(S5P_SECTION_SIZE - 1))
+
+#define S5P_SPAGE_ORDER		(S5P_SPAGE_SHIFT - PAGE_SHIFT)
+#define S5P_LPAGE_ORDER		(S5P_LPAGE_SHIFT - S5P_SPAGE_SHIFT)
+#define S5P_SECTION_ORDER	(S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT)
+
+#define S5P_LV1TABLE_ENTRIES	(1 << (BITS_PER_LONG - S5P_SECTION_SHIFT))
+
+#define S5P_LV2TABLE_ENTRIES	(1 << S5P_SECTION_ORDER)
+#define S5P_LV2TABLE_SIZE	(S5P_LV2TABLE_ENTRIES * sizeof(long))
+#define S5P_LV2TABLE_MASK	(~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */
+
+#define S5P_SECTION_LV1_ENTRY(entry)	((entry & 0x40003) == 2)
+#define S5P_SUPSECT_LV1_ENTRY(entry)	((entry & 0x40003) == 0x40002)
+#define S5P_PAGE_LV1_ENTRY(entry)	((entry & 3) == 1)
+#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) == 3)
+
+#define S5P_LPAGE_LV2_ENTRY(entry)	((entry & 3) == 1)
+#define S5P_SPAGE_LV2_ENTRY(entry)	((entry & 2) == 2)
+#define S5P_FAULT_LV2_ENTRY(entry)	((entry & 3) == 0)
+
+#define MAKE_FAULT_ENTRY(entry)		do { entry = 0; } while (0)
+#define MAKE_SECTION_ENTRY(entry, pa)	do { entry = pa | 2; } while (0)
+#define MAKE_SUPSECT_ENTRY(entry, pa)	do { entry = pa | 0x40002; } while (0)
+#define MAKE_LV2TABLE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
+
+#define MAKE_LPAGE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
+#define MAKE_SPAGE_ENTRY(entry, pa)	do { entry = pa | 3; } while (0)
+
+#define GET_LV2ENTRY(entry, iova) (\
+	(unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\
+	((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT))
+
+struct s5p_iommu_domain {
+	struct device *dev;
+	sysmmu_ips ips;
+	unsigned long *pgtable;
+};
+
+struct s5p_finddev_struct {
+	char *name;
+	int id;
+};
+/* Shared page table implementation */
+static unsigned long *pgtable;
+
+static struct s5p_finddev_struct pdev_names[S5P_SYSMMU_TOTAL_IPNUM] = {
+	{"s3c-pl330", 0},
+	{"s5p-sss", -1},
+	{"s3c-fimc", 0},
+	{"s3c-fimc", 1},
+	{"s3c-fimc", 2},
+	{"s3c-fimc", 3},
+	{"s5p-jpeg", -1},
+	{"s5p-fb", 0},
+	{"s5p-fb", 1},
+	{"s5p-pcie", -1},
+	{"s5p-fimg2d", -1},
+	{"s5p-rotator", -1},
+	{"s5p-mdma2", -1},
+	{"s5p-mixer", -1},
+	{"mfc", -1}, /* SYSMMU_MFC_L */
+};
+/* slab cache for level 2 page tables */
+static struct kmem_cache *l2table_cachep;
+
+LIST_HEAD(dev_lookup_list);
+
+struct dev_dom {
+	struct list_head node;
+	struct s5p_iommu_domain *dom;
+	struct device *dev;
+};
+
+static inline struct dev_dom *lookup_dev(struct device *dev)
+{
+	struct list_head *pos;
+	struct dev_dom *rel = NULL;
+
+	list_for_each(pos, &dev_lookup_list) {
+		rel = list_entry(pos, struct dev_dom, node);
+		if (rel->dev == dev)
+			return rel;
+	}
+
+	return NULL;
+}
+
+static inline int bind_dev(struct s5p_iommu_domain *dom, struct device *dev)
+{
+	struct dev_dom *rel;
+
+	rel = kmalloc(sizeof(*rel), GFP_KERNEL);
+	if (!rel)
+		return -ENOMEM;
+
+	if (!lookup_dev(dev)) {
+		rel->dom = dom;
+		rel->dev = dev;
+		list_add(&rel->node, &dev_lookup_list);
+	}
+
+	return 0;
+}
+
+static inline void unbind_dev(struct device *dev)
+{
+	struct dev_dom *rel;
+
+	rel = lookup_dev(dev);
+	if (rel)
+		list_del(&rel->node);
+}
+
+static inline void pgtable_flush(void *vastart, void *vaend)
+{
+	dmac_flush_range(vastart, vaend);
+	outer_flush_range(virt_to_phys(vastart),
+				virt_to_phys(vaend));
+}
+
+static int s5p_iommu_domain_init(struct iommu_domain *domain)
+{
+	struct s5p_iommu_domain *priv;
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
+		(S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >> PAGE_SHIFT);
+	if (!priv->pgtable) {
+		kfree(priv);
+		return -ENOMEM;
+	}
+
+	memset(pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long));
+	pgtable_flush(pgtable, pgtable + S5P_LV1TABLE_ENTRIES);
+
+	domain->priv = priv;
+	return 0;
+}
+
+static void s5p_iommu_domain_destroy(struct iommu_domain *domain)
+{
+	struct s5p_iommu_domain *priv = domain->priv;
+
+	free_pages((unsigned long)priv->pgtable, 2);
+	kfree(domain->priv);
+	domain->priv = NULL;
+}
+
+static sysmmu_ips get_sysmmu_id(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++)
+		if ((strcmp(pdev->name, pdev_names[i].name) == 0) &&
+				(pdev->id == pdev_names[i].id))
+			break;
+
+	return (sysmmu_ips)i;
+}
+
+static int s5p_iommu_attach_device(struct iommu_domain *domain,
+				   struct device *dev)
+{
+	sysmmu_ips ips;
+	int ret;
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	struct platform_device *pdev =
+				container_of(dev, struct platform_device, dev);
+
+	ips = get_sysmmu_id(pdev);
+	if (ips == SYSMMU_NONE)
+		return -ENODEV;
+
+	s5p_domain->ips = ips;
+
+	s5p_sysmmu_enable(ips, (unsigned long)s5p_domain->pgtable);
+	if (ips == SYSMMU_MFC_L)
+		s5p_sysmmu_enable(ips + 1, (unsigned long)s5p_domain->pgtable);
+
+	s5p_domain->dev = dev;
+
+	ret = bind_dev(s5p_domain, dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void s5p_iommu_detach_device(struct iommu_domain *domain,
+				    struct device *dev)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+
+	if (s5p_domain->dev == dev) {
+		s5p_sysmmu_disable(s5p_domain->ips);
+		if (s5p_domain->ips == SYSMMU_MFC_L)
+			s5p_sysmmu_disable(s5p_domain->ips + 1);
+		unbind_dev(dev);
+	}
+}
+
+static bool section_available(struct iommu_domain *domain,
+			      unsigned long *lv1entry)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+
+	if (S5P_SECTION_LV1_ENTRY(*lv1entry)) {
+		dev_err(s5p_domain->dev,
+				"1MB entry alread exists at 0x%08x\n",
+				(lv1entry - s5p_domain->pgtable) * SZ_1M);
+		return false;
+	}
+
+	if (S5P_PAGE_LV1_ENTRY(*lv1entry)) {
+		unsigned long *lv2end, *lv2base;
+
+		lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK);
+		lv2end = lv2base + S5P_LV2TABLE_ENTRIES;
+		while (lv2base != lv2end) {
+			if (S5P_FAULT_LV2_ENTRY(*lv2base)) {
+				dev_err(s5p_domain->dev,
+					"Failed to free L2 page table for"
+					"section mapping.\n");
+				return false;
+			}
+			lv2base++;
+		}
+
+		kmem_cache_free(l2table_cachep,
+				phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK));
+
+		MAKE_FAULT_ENTRY(*lv1entry);
+	}
+
+	return true;
+}
+
+static bool write_lpage(unsigned long *head_entry, unsigned long phys_addr)
+{
+	unsigned long *entry, *end;
+
+	entry = head_entry;
+	end = entry + (1 << S5P_LPAGE_ORDER);
+
+	while (entry != end) {
+		if (!S5P_FAULT_LV2_ENTRY(*entry))
+			break;
+
+		MAKE_LPAGE_ENTRY(*entry, phys_addr);
+
+		entry++;
+	}
+
+	if (entry != end) {
+		end = entry;
+		while (entry != head_entry)
+			MAKE_FAULT_ENTRY(*(--entry));
+
+		return false;
+	}
+
+	return true;
+}
+
+int s5p_iommu_map(struct iommu_domain *domain, unsigned long iova,
+			 phys_addr_t paddr, int gfp_order, int prot)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	unsigned long *start_entry, *entry, *end_entry;
+	int num_entry;
+
+	BUG_ON(s5p_domain->dev == NULL);
+
+	start_entry = entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
+
+	if (gfp_order >= S5P_SECTION_ORDER) {
+		BUG_ON((paddr | iova) & ~S5P_SECTION_MASK);
+		/* 1MiB mapping */
+
+		num_entry = 1 << (gfp_order - S5P_SECTION_ORDER);
+		end_entry = entry + num_entry;
+
+		while (entry != end_entry) {
+			if (!section_available(domain, entry))
+				break;
+
+			MAKE_SECTION_ENTRY(*entry, paddr);
+
+			paddr += S5P_SECTION_SIZE;
+			entry++;
+		}
+
+		if (entry != end_entry)
+			goto mapping_error;
+
+		return 0;
+	}
+
+	if (S5P_FAULT_LV1_ENTRY(*entry)) {
+		unsigned long *l2table;
+
+		l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL);
+		if (!l2table)
+			return -ENOMEM;
+
+		pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES);
+
+		MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table));
+		pgtable_flush(entry, entry + 1);
+	}
+
+	/* 'entry' points level 2 entries, hereafter */
+	entry = GET_LV2ENTRY(*entry, iova);
+
+	start_entry = entry;
+	num_entry = 1 << gfp_order;
+	end_entry = entry + num_entry;
+
+	if (gfp_order >= S5P_LPAGE_ORDER) {
+		/* large page(64KiB) mapping */
+		BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK);
+
+		while (entry != end_entry) {
+			if (!write_lpage(entry, paddr)) {
+				dev_err(s5p_domain->dev,
+					"Failed to allocate large page entry.\n"
+					);
+				break;
+			}
+
+			paddr += S5P_LPAGE_SIZE;
+			entry += (1 << S5P_LPAGE_ORDER);
+		}
+
+		if (entry != end_entry) {
+			entry -= 1 << S5P_LPAGE_ORDER;
+			goto mapping_error;
+		}
+
+		return 0;
+	}
+
+	/* page (4KiB) mapping */
+	while (entry != end_entry && !S5P_FAULT_LV2_ENTRY(*entry)) {
+
+		MAKE_SPAGE_ENTRY(*entry, paddr);
+
+		entry++;
+		paddr += S5P_SPAGE_SIZE;
+	}
+
+	if (entry != end_entry) {
+		dev_err(s5p_domain->dev,
+			"Failed to allocate small page entry.\n");
+		goto mapping_error;
+	}
+
+	pgtable_flush(start_entry, entry);
+mapping_error:
+	if (entry != end_entry) {
+		while (entry != start_entry)
+			MAKE_FAULT_ENTRY(*(--entry));
+		return -EADDRINUSE;
+	}
+
+	return 0;
+}
+
+int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+			   int gfp_order)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	unsigned long *entry;
+	int num_entry;
+
+	BUG_ON(s5p_domain->dev == NULL);
+
+	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
+
+	if (gfp_order >= S5P_SECTION_ORDER)
+		gfp_order -= S5P_SECTION_ORDER;
+	else
+		entry = GET_LV2ENTRY(*entry, iova);
+
+	BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && (gfp_order < S5P_LPAGE_ORDER));
+
+	num_entry = 1 << gfp_order;
+
+	while (num_entry-- > 0) {
+		MAKE_FAULT_ENTRY(*entry);
+		entry++;
+	}
+
+	s5p_sysmmu_tlb_invalidate(s5p_domain->ips);
+	if (s5p_domain->ips == SYSMMU_MFC_L)
+		s5p_sysmmu_tlb_invalidate(s5p_domain->ips + 1);
+	return 0;
+}
+
+phys_addr_t s5p_iommu_iova_to_phys(struct iommu_domain *domain,
+					  unsigned long iova)
+{
+	struct s5p_iommu_domain *s5p_domain = domain->priv;
+	unsigned long *entry;
+	unsigned long offset;
+
+	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
+
+	if (S5P_FAULT_LV1_ENTRY(*entry))
+		return 0;
+
+	offset = iova & ~S5P_SECTION_MASK;
+
+	if (S5P_SECTION_LV1_ENTRY(*entry))
+		return (*entry & S5P_SECTION_MASK) + offset;
+
+	entry = GET_LV2ENTRY(*entry, iova);
+
+	if (S5P_SPAGE_LV2_ENTRY(*entry))
+		return (*entry & S5P_SPAGE_MASK) + (iova & ~S5P_SPAGE_MASK);
+
+	if (S5P_LPAGE_LV2_ENTRY(*entry))
+		return (*entry & S5P_LPAGE_MASK) + (iova & ~S5P_LPAGE_MASK);
+
+	return 0;
+}
+
+static int s5p_iommu_domain_has_cap(struct iommu_domain *domain,
+				    unsigned long cap)
+{
+	return 0;
+}
+
+static struct iommu_ops s5p_iommu_ops = {
+	.domain_init = &s5p_iommu_domain_init,
+	.domain_destroy = &s5p_iommu_domain_destroy,
+	.attach_dev = &s5p_iommu_attach_device,
+	.detach_dev = &s5p_iommu_detach_device,
+	.map = &s5p_iommu_map,
+	.unmap = &s5p_iommu_unmap,
+	.iova_to_phys = &s5p_iommu_iova_to_phys,
+	.domain_has_cap = &s5p_iommu_domain_has_cap
+};
+
+static int __init s5p_iommu_init(void)
+{
+	l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables",
+				S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE, 0, NULL);
+	if (!l2table_cachep)
+		return -ENOMEM;
+
+	register_iommu(&s5p_iommu_ops);
+	return 0;
+}
+arch_initcall(s5p_iommu_init);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* RE: [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
  2011-07-04  1:41 ` KyongHo Cho
@ 2011-07-04  6:47   ` Marek Szyprowski
  -1 siblings, 0 replies; 27+ messages in thread
From: Marek Szyprowski @ 2011-07-04  6:47 UTC (permalink / raw)
  To: 'KyongHo Cho',
	linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: 'Joerg Roedel', 'Kukjin Kim',
	'Sanghyun Lee', 'Ilho Lee',
	'Kwanghyun La', 'Kyungmin Park',
	Andrzej Pietrasiewicz

Hello,

On Monday, July 04, 2011 3:42 AM KyongHo Cho wrote:

> This patch set includes the following patches:
> 
> - [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2
>   The previous driver defines System MMUs for 2 MDMAs each.
>   Since one of the MDMAs is not used any more, it is removed.
>   SYSMMU_MDMA2 is renamed to SYSMMU_MDMA and it now represents number 10.
> 
> - [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMUof
> SSS
>   System MMU of SSS was not complete to use because previous driver did not
>   prepare anything for its clock gating feature. This patch enables clock
>   gating for System MMU of SSS.
> 
> - [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition
>   As the suggestion of Marek Szyprowski with his patch set, this patch
>   separates the single definition of platform device of System MMU into
>   15 different platform devices as well as structured resource management.
> 
> - [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support
> clkdev.
>   This patch adds 'devname' fields into the clokc definitions of System MMU.
>   The pointer to clk structure for System MMU can be found with kobj name
>   dynamically.
> 
> - [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE
>   This patch adds another System MMU ID(ips), "SYSMMU_NONE" which means
>   invalid system MMU ID.
> 
> - [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU
> device driver.
>   The previous driver implements clock gating functions in the mach
>   directory because it is mach dependent. It is now removed and
>   clock gating functions are moved to System MMU driver because clkdev is
>   supported.
> 
> - [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
>   This patch moves the existing System MMU driver to drivers/iommu
>   directory that is in Joerg Roedel's git.
>   git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
>   This patch also adds implementation of IOMMU API for Exynos4's System MMU.
> 
> The above patches must be applied in the order of their presence.
> 
> The last patch is not applicable to any kernel version except Joerg
> Roedel's git tree.
> 
> This patch set does not improve System MMU driver completely.
> The rest of enhancements will be submitted soon.

I really don't get why do You persist in creating one monster platform
device with resources for ALL SYSMMU/IOMMU controllers that are available
on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
a clean solution for making the SYSMMU controllers independent of each
other as well as making the main SYSMMU driver simpler and more
independent of the particular Exynos4 platform definition. In Linux
device model if the device/controller exist in the system in more than
one instance, there should by one generic driver for it and a set of
platform definitions for each instance. 

Here is the link to our SYSMMU updated driver (implementing common 
iommu API):
http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
The driver still needs some cleanup, but this has been delayed until
the dma-mapping framework update is finished.

I also don't get why do you want to keep the completely custom sysmmu 
driver and implement iommu API on top of it. Is it really required for
anything? Mainline kernel is not a place for custom APIs if there is
already a common, generic one.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
@ 2011-07-04  6:47   ` Marek Szyprowski
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Szyprowski @ 2011-07-04  6:47 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Monday, July 04, 2011 3:42 AM KyongHo Cho wrote:

> This patch set includes the following patches:
> 
> - [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2
>   The previous driver defines System MMUs for 2 MDMAs each.
>   Since one of the MDMAs is not used any more, it is removed.
>   SYSMMU_MDMA2 is renamed to SYSMMU_MDMA and it now represents number 10.
> 
> - [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMUof
> SSS
>   System MMU of SSS was not complete to use because previous driver did not
>   prepare anything for its clock gating feature. This patch enables clock
>   gating for System MMU of SSS.
> 
> - [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition
>   As the suggestion of Marek Szyprowski with his patch set, this patch
>   separates the single definition of platform device of System MMU into
>   15 different platform devices as well as structured resource management.
> 
> - [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support
> clkdev.
>   This patch adds 'devname' fields into the clokc definitions of System MMU.
>   The pointer to clk structure for System MMU can be found with kobj name
>   dynamically.
> 
> - [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE
>   This patch adds another System MMU ID(ips), "SYSMMU_NONE" which means
>   invalid system MMU ID.
> 
> - [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU
> device driver.
>   The previous driver implements clock gating functions in the mach
>   directory because it is mach dependent. It is now removed and
>   clock gating functions are moved to System MMU driver because clkdev is
>   supported.
> 
> - [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
>   This patch moves the existing System MMU driver to drivers/iommu
>   directory that is in Joerg Roedel's git.
>   git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
>   This patch also adds implementation of IOMMU API for Exynos4's System MMU.
> 
> The above patches must be applied in the order of their presence.
> 
> The last patch is not applicable to any kernel version except Joerg
> Roedel's git tree.
> 
> This patch set does not improve System MMU driver completely.
> The rest of enhancements will be submitted soon.

I really don't get why do You persist in creating one monster platform
device with resources for ALL SYSMMU/IOMMU controllers that are available
on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
a clean solution for making the SYSMMU controllers independent of each
other as well as making the main SYSMMU driver simpler and more
independent of the particular Exynos4 platform definition. In Linux
device model if the device/controller exist in the system in more than
one instance, there should by one generic driver for it and a set of
platform definitions for each instance. 

Here is the link to our SYSMMU updated driver (implementing common 
iommu API):
http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
The driver still needs some cleanup, but this has been delayed until
the dma-mapping framework update is finished.

I also don't get why do you want to keep the completely custom sysmmu 
driver and implement iommu API on top of it. Is it really required for
anything? Mainline kernel is not a place for custom APIs if there is
already a common, generic one.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
  2011-07-04  6:47   ` Marek Szyprowski
@ 2011-07-04 23:38     ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04 23:38 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel,
	Joerg Roedel, Kukjin Kim, Sanghyun Lee, Ilho Lee, Kwanghyun La,
	Kyungmin Park, Andrzej Pietrasiewicz

Hi.
On Mon, Jul 4, 2011 at 3:47 PM, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> Hello,
>
> I really don't get why do You persist in creating one monster platform
> device with resources for ALL SYSMMU/IOMMU controllers that are available
> on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
> a clean solution for making the SYSMMU controllers independent of each
> other as well as making the main SYSMMU driver simpler and more
> independent of the particular Exynos4 platform definition. In Linux
> device model if the device/controller exist in the system in more than
> one instance, there should by one generic driver for it and a set of
> platform definitions for each instance.
>
Did you find this patch? "[PATCH 3/6] ARM: EXYNOS4: SYSMMU"
I Cced you because it includes your suggestion.

> Here is the link to our SYSMMU updated driver (implementing common
> iommu API):
> http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
> The driver still needs some cleanup, but this has been delayed until
> the dma-mapping framework update is finished.
>
> I also don't get why do you want to keep the completely custom sysmmu
> driver and implement iommu API on top of it. Is it really required for
> anything? Mainline kernel is not a place for custom APIs if there is
> already a common, generic one.

I did not exposed any custom API.
The global function in the last patch is just for IOMMU API implemenation.
The header file in the last patch is not for the device drivers but
for the IOMMU API implementation.
Actually, they can be merged into one single file.
But I just wanted to change the kernel code stepwise.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
@ 2011-07-04 23:38     ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-04 23:38 UTC (permalink / raw)
  To: linux-arm-kernel

Hi.
On Mon, Jul 4, 2011 at 3:47 PM, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> Hello,
>
> I really don't get why do You persist in creating one monster platform
> device with resources for ALL SYSMMU/IOMMU controllers that are available
> on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
> a clean solution for making the SYSMMU controllers independent of each
> other as well as making the main SYSMMU driver simpler and more
> independent of the particular Exynos4 platform definition. In Linux
> device model if the device/controller exist in the system in more than
> one instance, there should by one generic driver for it and a set of
> platform definitions for each instance.
>
Did you find this patch? "[PATCH 3/6] ARM: EXYNOS4: SYSMMU"
I Cced you because it includes your suggestion.

> Here is the link to our SYSMMU updated driver (implementing common
> iommu API):
> http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
> The driver still needs some cleanup, but this has been delayed until
> the dma-mapping framework update is finished.
>
> I also don't get why do you want to keep the completely custom sysmmu
> driver and implement iommu API on top of it. Is it really required for
> anything? Mainline kernel is not a place for custom APIs if there is
> already a common, generic one.

I did not exposed any custom API.
The global function in the last patch is just for IOMMU API implemenation.
The header file in the last patch is not for the device drivers but
for the IOMMU API implementation.
Actually, they can be merged into one single file.
But I just wanted to change the kernel code stepwise.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* RE: [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
  2011-07-04 23:38     ` KyongHo Cho
@ 2011-07-05 11:14       ` Marek Szyprowski
  -1 siblings, 0 replies; 27+ messages in thread
From: Marek Szyprowski @ 2011-07-05 11:14 UTC (permalink / raw)
  To: 'KyongHo Cho'
  Cc: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel,
	'Joerg Roedel', 'Kukjin Kim',
	'Sanghyun Lee', 'Ilho Lee',
	'Kwanghyun La', 'Kyungmin Park',
	Andrzej Pietrasiewicz

Hello,

On Tuesday, July 05, 2011 1:38 AM KyongHo Cho wrote:

> On Mon, Jul 4, 2011 at 3:47 PM, Marek Szyprowski
> <m.szyprowski@samsung.com> wrote:
> > Hello,
> >
> > I really don't get why do You persist in creating one monster platform
> > device with resources for ALL SYSMMU/IOMMU controllers that are available
> > on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
> > a clean solution for making the SYSMMU controllers independent of each
> > other as well as making the main SYSMMU driver simpler and more
> > independent of the particular Exynos4 platform definition. In Linux
> > device model if the device/controller exist in the system in more than
> > one instance, there should by one generic driver for it and a set of
> > platform definitions for each instance.
> >
> Did you find this patch? "[PATCH 3/6] ARM: EXYNOS4: SYSMMU"
> I Cced you because it includes your suggestion.

Right, I'm really sorry. It looks that I'm too busy and misunderstood your
patches in a brief look.

> > Here is the link to our SYSMMU updated driver (implementing common
> > iommu API):
> > http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
> > The driver still needs some cleanup, but this has been delayed until
> > the dma-mapping framework update is finished.
> >
> > I also don't get why do you want to keep the completely custom sysmmu
> > driver and implement iommu API on top of it. Is it really required for
> > anything? Mainline kernel is not a place for custom APIs if there is
> > already a common, generic one.
> 
> I did not exposed any custom API.
> The global function in the last patch is just for IOMMU API implemenation.
> The header file in the last patch is not for the device drivers but
> for the IOMMU API implementation.
> Actually, they can be merged into one single file.
> But I just wanted to change the kernel code stepwise.

What about arch/arm/plat-s5p/sysmmu.c ? I didn't notice any commit which 
removes this file. Also both drivers/iommu/exynos4_sysmmu.c and 
drivers/iommu/exynos_iommu.c should be merged together.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
@ 2011-07-05 11:14       ` Marek Szyprowski
  0 siblings, 0 replies; 27+ messages in thread
From: Marek Szyprowski @ 2011-07-05 11:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

On Tuesday, July 05, 2011 1:38 AM KyongHo Cho wrote:

> On Mon, Jul 4, 2011 at 3:47 PM, Marek Szyprowski
> <m.szyprowski@samsung.com> wrote:
> > Hello,
> >
> > I really don't get why do You persist in creating one monster platform
> > device with resources for ALL SYSMMU/IOMMU controllers that are available
> > on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
> > a clean solution for making the SYSMMU controllers independent of each
> > other as well as making the main SYSMMU driver simpler and more
> > independent of the particular Exynos4 platform definition. In Linux
> > device model if the device/controller exist in the system in more than
> > one instance, there should by one generic driver for it and a set of
> > platform definitions for each instance.
> >
> Did you find this patch? "[PATCH 3/6] ARM: EXYNOS4: SYSMMU"
> I Cced you because it includes your suggestion.

Right, I'm really sorry. It looks that I'm too busy and misunderstood your
patches in a brief look.

> > Here is the link to our SYSMMU updated driver (implementing common
> > iommu API):
> > http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
> > The driver still needs some cleanup, but this has been delayed until
> > the dma-mapping framework update is finished.
> >
> > I also don't get why do you want to keep the completely custom sysmmu
> > driver and implement iommu API on top of it. Is it really required for
> > anything? Mainline kernel is not a place for custom APIs if there is
> > already a common, generic one.
> 
> I did not exposed any custom API.
> The global function in the last patch is just for IOMMU API implemenation.
> The header file in the last patch is not for the device drivers but
> for the IOMMU API implementation.
> Actually, they can be merged into one single file.
> But I just wanted to change the kernel code stepwise.

What about arch/arm/plat-s5p/sysmmu.c ? I didn't notice any commit which 
removes this file. Also both drivers/iommu/exynos4_sysmmu.c and 
drivers/iommu/exynos_iommu.c should be merged together.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
  2011-07-05 11:14       ` Marek Szyprowski
@ 2011-07-05 23:51         ` KyongHo Cho
  -1 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-05 23:51 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel,
	Joerg Roedel, Kukjin Kim, Sanghyun Lee, Ilho Lee, Kwanghyun La,
	Kyungmin Park, Andrzej Pietrasiewicz

On Tue, Jul 5, 2011 at 8:14 PM, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> Hello,
>
> On Tuesday, July 05, 2011 1:38 AM KyongHo Cho wrote:
>
>> On Mon, Jul 4, 2011 at 3:47 PM, Marek Szyprowski
>> <m.szyprowski@samsung.com> wrote:
>> > Hello,
>> >
>> > I really don't get why do You persist in creating one monster platform
>> > device with resources for ALL SYSMMU/IOMMU controllers that are available
>> > on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
>> > a clean solution for making the SYSMMU controllers independent of each
>> > other as well as making the main SYSMMU driver simpler and more
>> > independent of the particular Exynos4 platform definition. In Linux
>> > device model if the device/controller exist in the system in more than
>> > one instance, there should by one generic driver for it and a set of
>> > platform definitions for each instance.
>> >
>> Did you find this patch? "[PATCH 3/6] ARM: EXYNOS4: SYSMMU"
>> I Cced you because it includes your suggestion.
>
> Right, I'm really sorry. It looks that I'm too busy and misunderstood your
> patches in a brief look.

No problem with that :)

>> > Here is the link to our SYSMMU updated driver (implementing common
>> > iommu API):
>> > http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
>> > The driver still needs some cleanup, but this has been delayed until
>> > the dma-mapping framework update is finished.
>> >
>> > I also don't get why do you want to keep the completely custom sysmmu
>> > driver and implement iommu API on top of it. Is it really required for
>> > anything? Mainline kernel is not a place for custom APIs if there is
>> > already a common, generic one.
>>
>> I did not exposed any custom API.
>> The global function in the last patch is just for IOMMU API implemenation.
>> The header file in the last patch is not for the device drivers but
>> for the IOMMU API implementation.
>> Actually, they can be merged into one single file.
>> But I just wanted to change the kernel code stepwise.
>
> What about arch/arm/plat-s5p/sysmmu.c ? I didn't notice any commit which
> removes this file. Also both drivers/iommu/exynos4_sysmmu.c and
> drivers/iommu/exynos_iommu.c should be merged together.

It will be removed.
And.. omitting removal of that file is my mistake :(
Since clkdev for Exynos4 is not applied to of Joerg Roedel's iommu git
and drivers/iommu is not created in Samsung SOC git,
it is not simple to apply patch for both.

Thanks for pointing out it and I'll send a patch to remove those files
immediately.

On the other hands, merging those files will be also considered.

Regards,
KyongHo Cho.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver
@ 2011-07-05 23:51         ` KyongHo Cho
  0 siblings, 0 replies; 27+ messages in thread
From: KyongHo Cho @ 2011-07-05 23:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jul 5, 2011 at 8:14 PM, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> Hello,
>
> On Tuesday, July 05, 2011 1:38 AM KyongHo Cho wrote:
>
>> On Mon, Jul 4, 2011 at 3:47 PM, Marek Szyprowski
>> <m.szyprowski@samsung.com> wrote:
>> > Hello,
>> >
>> > I really don't get why do You persist in creating one monster platform
>> > device with resources for ALL SYSMMU/IOMMU controllers that are available
>> > on Exynos4 CPU. We (SPRC, see Andrzej's SYSMMU patches) already proposed
>> > a clean solution for making the SYSMMU controllers independent of each
>> > other as well as making the main SYSMMU driver simpler and more
>> > independent of the particular Exynos4 platform definition. In Linux
>> > device model if the device/controller exist in the system in more than
>> > one instance, there should by one generic driver for it and a set of
>> > platform definitions for each instance.
>> >
>> Did you find this patch? "[PATCH 3/6] ARM: EXYNOS4: SYSMMU"
>> I Cced you because it includes your suggestion.
>
> Right, I'm really sorry. It looks that I'm too busy and misunderstood your
> patches in a brief look.

No problem with that :)

>> > Here is the link to our SYSMMU updated driver (implementing common
>> > iommu API):
>> > http://www.spinics.net/lists/linux-samsung-soc/msg04508.html
>> > The driver still needs some cleanup, but this has been delayed until
>> > the dma-mapping framework update is finished.
>> >
>> > I also don't get why do you want to keep the completely custom sysmmu
>> > driver and implement iommu API on top of it. Is it really required for
>> > anything? Mainline kernel is not a place for custom APIs if there is
>> > already a common, generic one.
>>
>> I did not exposed any custom API.
>> The global function in the last patch is just for IOMMU API implemenation.
>> The header file in the last patch is not for the device drivers but
>> for the IOMMU API implementation.
>> Actually, they can be merged into one single file.
>> But I just wanted to change the kernel code stepwise.
>
> What about arch/arm/plat-s5p/sysmmu.c ? I didn't notice any commit which
> removes this file. Also both drivers/iommu/exynos4_sysmmu.c and
> drivers/iommu/exynos_iommu.c should be merged together.

It will be removed.
And.. omitting removal of that file is my mistake :(
Since clkdev for Exynos4 is not applied to of Joerg Roedel's iommu git
and drivers/iommu is not created in Samsung SOC git,
it is not simple to apply patch for both.

Thanks for pointing out it and I'll send a patch to remove those files
immediately.

On the other hands, merging those files will be also considered.

Regards,
KyongHo Cho.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* RE: [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
  2011-07-04  1:41   ` KyongHo Cho
  (?)
@ 2011-08-31  1:18     ` Kukjin Kim
  -1 siblings, 0 replies; 27+ messages in thread
From: Kukjin Kim @ 2011-08-31  1:18 UTC (permalink / raw)
  To: 'KyongHo Cho',
	linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: 'Joerg Roedel', 'Sanghyun Lee',
	'Kwanghyun La', 'Ilho Lee'

KyongHo Cho wrote:
> 
> Implemented IOMMU API for Exynos4 platform that has IOMMU(System MMU).
> The previous System MMU driver exposed its own functions and was lack
> of page table management.
> 
> This patch includes complete implementation of IOMMU API
> and it is capable of mapping and unmapping any number of orders.
> 
> iommu_map() for Exynos4 does not map 16MiB page because it is not
> practical now. 1MiB page is sufficient for larger physically contiguous
> memory mapping. Performance degradation due to TLB miss is not such a
> big problem with 1MiB page.
> 
> Since archdata field of structure 'device' contains nothing in ARM,
> I've used linked list to manage the relation between domain and device.
> 
> Marek is trying to add domain and dma_map_ops fields into dev_archdata.
> This way of relation management will be changed after the Marek's work
> is merged into the mainline.
> 
> Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
> ---
>  arch/arm/mach-exynos4/include/mach/sysmmu.h |    9 +-
>  drivers/iommu/Kconfig                       |   11 +
>  drivers/iommu/Makefile                      |    1 +
>  drivers/iommu/exynos4_sysmmu.c              |  343
> ++++++++++++++++++
>  drivers/iommu/exynos4_sysmmu.h              |   18 +
>  drivers/iommu/exynos_iommu.c                |  496
> +++++++++++++++++++++++++++
>  6 files changed, 872 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/iommu/exynos4_sysmmu.c
>  create mode 100644 drivers/iommu/exynos4_sysmmu.h
>  create mode 100644 drivers/iommu/exynos_iommu.c
> 

Hi KyongHo,

Basically, we need this for EXYNOS System MMU but I think, this is required
to re-work based on latest kernel and if possible, please make it look as a
single patch set about regarding patches to prevent missing something
important.

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.


> diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-
> exynos4/include/mach/sysmmu.h
> index 6a5fbb5..2be20c5 100644
> --- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
> +++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
> @@ -34,13 +34,10 @@ enum exynos4_sysmmu_ips {
>  };
> 
>  #define S5P_SYSMMU_TOTAL_IPNUM
> 	EXYNOS4_SYSMMU_TOTAL_IPNUM
> -
> -extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
> +#define SYSMMU_NONE
> 	S5P_SYSMMU_TOTAL_IPNUM
> 
>  typedef enum exynos4_sysmmu_ips sysmmu_ips;
> 
> -void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
> -void sysmmu_clk_enable(sysmmu_ips ips);
> -void sysmmu_clk_disable(sysmmu_ips ips);
> -
> +#else /* __ASM_ARM_ARCH_SYSMMU_H */
> +#error mach/sysmmu.h must not be included by device drivers
>  #endif /* __ASM_ARM_ARCH_SYSMMU_H */
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index b57b3fa..a9fcc79 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -107,4 +107,15 @@ config INTR_REMAP
>  	  To use x2apic mode in the CPU's which support x2APIC enhancements
> or
>  	  to support platforms with CPU's having > 8 bit APIC ID, say Y.
> 
> +# EXYNOS IOMMU support
> +config EXYNOS_IOMMU
> +	bool "Exynos4 IOMMU(System MMU) Support"
> +	depends on ARCH_EXYNOS4
> +	select IOMMU_API
> +	help
> +	  Support for the System MMU embedded in Samsung Exynos4 SOCs.
> +	  These IOMMUs allow virtualization of the address space used by
most
> +	  cores within the multimedia subsystem.
> +
> +	  If unsure, say N here.
>  endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 4d4d77d..446bbdd 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
> msm_iommu_dev.o
>  obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
>  obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
>  obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
> +obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o exynos4_sysmmu.o
> diff --git a/drivers/iommu/exynos4_sysmmu.c
b/drivers/iommu/exynos4_sysmmu.c
> new file mode 100644
> index 0000000..01e0966
> --- /dev/null
> +++ b/drivers/iommu/exynos4_sysmmu.c
> @@ -0,0 +1,343 @@
> +/* linux/drivers/iommu/exynos4_sysmmu.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +
> +#include <asm/pgtable.h>
> +
> +#include <mach/map.h>
> +#include <mach/regs-sysmmu.h>
> +
> +#include "exynos4_sysmmu.h"
> +
> +#define CTRL_ENABLE	0x5
> +#define CTRL_BLOCK	0x7
> +#define CTRL_DISABLE	0x0
> +
> +static struct device *dev_sysmmu[S5P_SYSMMU_TOTAL_IPNUM];
> +
> +static void sysmmu_clk_enable(sysmmu_ips ips)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_get(dev_sysmmu[ips], NULL);
> +	if (clk)
> +		clk_enable(clk);
> +}
> +
> +static void sysmmu_clk_disable(sysmmu_ips ips)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_get(dev_sysmmu[ips], NULL);
> +	if (clk)
> +		clk_disable(clk);
> +}
> +
> +enum S5P_SYSMMU_INTERRUPT_TYPE {
> +	SYSMMU_PAGEFAULT,
> +	SYSMMU_AR_MULTIHIT,
> +	SYSMMU_AW_MULTIHIT,
> +	SYSMMU_BUSERROR,
> +	SYSMMU_AR_SECURITY,
> +	SYSMMU_AR_ACCESS,
> +	SYSMMU_AW_SECURITY,
> +	SYSMMU_AW_PROTECTION, /* 7 */
> +	SYSMMU_FAULTS_NUM
> +};
> +
> +static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
> +	S5P_PAGE_FAULT_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR,
> +	S5P_DEFAULT_SLAVE_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR
> +};
> +
> +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
> +	"PAGE FAULT",
> +	"AR MULTI-HIT FAULT",
> +	"AW MULTI-HIT FAULT",
> +	"BUS ERROR",
> +	"AR SECURITY PROTECTION FAULT",
> +	"AR ACCESS PROTECTION FAULT",
> +	"AW SECURITY PROTECTION FAULT",
> +	"AW ACCESS PROTECTION FAULT"
> +};
> +
> +static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])(
> +		enum S5P_SYSMMU_INTERRUPT_TYPE itype,
> +		unsigned long pgtable_base,
> +		unsigned long fault_addr);
> +
> +/*
> + * If adjacent 2 bits are true, the system MMU is enabled.
> + * The system MMU is disabled, otherwise.
> + */
> +static unsigned long sysmmu_states;
> +
> +static inline int set_sysmmu_active(sysmmu_ips ips)
> +{
> +	/* return true if it is not set */
> +	return !test_and_set_bit(ips, &sysmmu_states);
> +}
> +
> +static inline int set_sysmmu_inactive(sysmmu_ips ips)
> +{
> +	/* return true if it is set */
> +	return test_and_clear_bit(ips, &sysmmu_states);
> +}
> +
> +static inline int is_sysmmu_active(sysmmu_ips ips)
> +{
> +	/* BUG_ON(ips >= S5P_SYSMMU_TOTAL_IPNUM); */
> +	return sysmmu_states & (1 << ips);
> +}
> +
> +static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM];
> +
> +static inline void sysmmu_block(sysmmu_ips ips)
> +{
> +	__raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
> +	dev_dbg(dev_sysmmu[ips], "blocked.\n");
> +}
> +
> +static inline void sysmmu_unblock(sysmmu_ips ips)
> +{
> +	__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
> +	dev_dbg(dev_sysmmu[ips], "unblocked.\n");
> +}
> +
> +static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
> +{
> +	__raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
> +	dev_dbg(dev_sysmmu[ips], "TLB is invalidated.\n");
> +}
> +
> +static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (unlikely(pgd == 0)) {
> +		pgd = (unsigned long)ZERO_PAGE(0);
> +		__raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB
> LV1 */
> +	} else {
> +		__raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB
> LV1 */
> +	}
> +
> +	__raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
> +
> +	dev_dbg(dev_sysmmu[ips], "Page table base initialized with
0x%08lX.\n",
> +
> 	pgd);
> +	__sysmmu_tlb_invalidate(ips);
> +}
> +
> +static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
> +{
> +	/* SYSMMU is in blocked when interrupt occurred. */
> +	unsigned long base = 0;
> +	unsigned long addr;
> +	sysmmu_ips ips = (sysmmu_ips)dev_id;
> +	enum S5P_SYSMMU_INTERRUPT_TYPE itype;
> +
> +	BUG_ON(!is_sysmmu_active(ips));
> +
> +	itype = (enum S5P_SYSMMU_INTERRUPT_TYPE)
> +		__ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
> +
> +	BUG_ON(!((itype >= 0) && (itype < 8)));
> +
> +	base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
> +	addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
> +
> +	dev_alert(dev_sysmmu[ips], "%s occurred at %08lx
> (PT_BASE_ADDR: %08lx)."
> +		"\n", sysmmu_fault_name[itype], addr, base);
> +
> +	if (fault_handlers[ips]) {
> +
> +
> +		if (fault_handlers[ips](itype, base, addr)) {
> +			__raw_writel(1 << itype,
> +					sysmmusfrs[ips] + S5P_INT_CLEAR);
> +			dev_notice(dev_sysmmu[ips], "%s is resolved.
> Retrying"
> +				" translation.\n",
sysmmu_fault_name[itype]);
> +		} else {
> +			base = 0;
> +		}
> +	}
> +
> +	sysmmu_unblock(ips);
> +
> +	if (!base)
> +		dev_notice(dev_sysmmu[ips], "%s is not handled.\n",
> +						sysmmu_fault_name[itype]);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (is_sysmmu_active(ips)) {
> +		sysmmu_block(ips);
> +		__sysmmu_set_ptbase(ips, pgd);
> +		sysmmu_unblock(ips);
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "disabled. Skipping initializing
page"
> +				" table base...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (set_sysmmu_active(ips)) {
> +		sysmmu_clk_enable(ips);
> +
> +		__sysmmu_set_ptbase(ips, pgd);
> +
> +		__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] +
> S5P_MMU_CTRL);
> +
> +		set_sysmmu_active(ips);
> +		dev_dbg(dev_sysmmu[ips], "enabled.\n");
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "already enabled."
> +					" Skipping initialization...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_disable(sysmmu_ips ips)
> +{
> +	if (set_sysmmu_inactive(ips)) {
> +		__raw_writel(CTRL_DISABLE, sysmmusfrs[ips] +
> S5P_MMU_CTRL);
> +		sysmmu_clk_disable(ips);
> +		dev_dbg(dev_sysmmu[ips], "disabled.\n");
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "already disabled."
> +					" Skipping deinitialization...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
> +{
> +	if (is_sysmmu_active(ips)) {
> +		sysmmu_block(ips);
> +		__sysmmu_tlb_invalidate(ips);
> +		sysmmu_unblock(ips);
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "is disabled. "
> +			"Skipping invalidating TLB...\n");
> +	}
> +}
> +
> +static int s5p_sysmmu_probe(struct platform_device *pdev)
> +{
> +	sysmmu_ips id;
> +	struct resource *res, *ioarea;
> +	int ret;
> +	int irq;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to get resource.");
> +		return -ENOENT;
> +	}
> +
> +	id = (sysmmu_ips)pdev->id;
> +
> +	if (id >= S5P_SYSMMU_TOTAL_IPNUM) {
> +		dev_err(&pdev->dev, "Unknown System MMU ID %d.", id);
> +		return -ENOENT;
> +	}
> +
> +	ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
> +	if (ioarea == NULL) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +					"failed to request memory region.");
> +		return -ENOMEM;
> +	}
> +
> +	sysmmusfrs[id] = ioremap(res->start, resource_size(res));
> +	if (!sysmmusfrs[id]) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to call
ioremap().");
> +		ret = -ENOENT;
> +		goto err_ioremap;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to get irq
resource.");
> +		ret = irq;
> +		goto err_irq;
> +	}
> +
> +	if (request_irq(irq, s5p_sysmmu_irq, 0, dev_name(&pdev->dev),
> +								(void *)id))
> {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to request irq.");
> +		ret = -ENOENT;
> +		goto err_irq;
> +	}
> +
> +	dev_sysmmu[id] = &pdev->dev;
> +
> +	dev_dbg(&pdev->dev, "Probing system MMU succeeded.");
> +	return 0;
> +
> +err_irq:
> +	iounmap(sysmmusfrs[id]);
> +err_ioremap:
> +	release_resource(ioarea);
> +	kfree(ioarea);
> +	dev_err(&pdev->dev, "Probing system MMU failed.");
> +	return ret;
> +}
> +
> +static int s5p_sysmmu_remove(struct platform_device *pdev)
> +{
> +	return 0;
> +}
> +static int s5p_sysmmu_runtime_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_sysmmu_runtime_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops s5p_sysmmu_pm_ops = {
> +	.runtime_suspend	= s5p_sysmmu_runtime_suspend,
> +	.runtime_resume		= s5p_sysmmu_runtime_resume,
> +};
> +
> +static struct platform_driver s5p_sysmmu_driver = {
> +	.probe		= s5p_sysmmu_probe,
> +	.remove		= s5p_sysmmu_remove,
> +	.driver		= {
> +		.owner		= THIS_MODULE,
> +		.name		= "s5p-sysmmu",
> +		.pm		= &s5p_sysmmu_pm_ops,
> +	}
> +};
> +
> +static int __init s5p_sysmmu_init(void)
> +{
> +	return platform_driver_register(&s5p_sysmmu_driver);
> +}
> +arch_initcall(s5p_sysmmu_init);
> diff --git a/drivers/iommu/exynos4_sysmmu.h
b/drivers/iommu/exynos4_sysmmu.h
> new file mode 100644
> index 0000000..f739240
> --- /dev/null
> +++ b/drivers/iommu/exynos4_sysmmu.h
> @@ -0,0 +1,18 @@
> +/* linux/drivers/iommu/exynos4_sysmmu.h
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung System MMU driver for Exynos4 platforms
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <mach/sysmmu.h>
> +
> +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd);
> +void s5p_sysmmu_disable(sysmmu_ips ips);
> +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd);
> +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips);
> diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c
> new file mode 100644
> index 0000000..cbb94df
> --- /dev/null
> +++ b/drivers/iommu/exynos_iommu.c
> @@ -0,0 +1,496 @@
> +/* linux/drivers/iommu/exynos_iommu.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/mm.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/iommu.h>
> +#include <linux/errno.h>
> +#include <linux/list.h>
> +
> +#include <asm/cacheflush.h>
> +
> +#include "exynos4_sysmmu.h"
> +
> +#ifdef CONFIG_S5P_SYSTEM_MMU_DEBUG
> +#define DEBUG /* for dev_dbg() */
> +#endif
> +
> +/* We does not consider super section mapping (16MB) */
> +#define S5P_SPAGE_SHIFT		12
> +#define S5P_LPAGE_SHIFT		16
> +#define S5P_SECTION_SHIFT	20
> +
> +#define S5P_SPAGE_SIZE		(1 << S5P_SPAGE_SHIFT)
> +#define S5P_LPAGE_SIZE		(1 << S5P_LPAGE_SHIFT)
> +#define S5P_SECTION_SIZE	(1 << S5P_SECTION_SHIFT)
> +
> +#define S5P_SPAGE_MASK		(~(S5P_SPAGE_SIZE - 1))
> +#define S5P_LPAGE_MASK		(~(S5P_LPAGE_SIZE - 1))
> +#define S5P_SECTION_MASK	(~(S5P_SECTION_SIZE - 1))
> +
> +#define S5P_SPAGE_ORDER		(S5P_SPAGE_SHIFT - PAGE_SHIFT)
> +#define S5P_LPAGE_ORDER		(S5P_LPAGE_SHIFT -
> S5P_SPAGE_SHIFT)
> +#define S5P_SECTION_ORDER	(S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT)
> +
> +#define S5P_LV1TABLE_ENTRIES	(1 << (BITS_PER_LONG -
> S5P_SECTION_SHIFT))
> +
> +#define S5P_LV2TABLE_ENTRIES	(1 << S5P_SECTION_ORDER)
> +#define S5P_LV2TABLE_SIZE	(S5P_LV2TABLE_ENTRIES * sizeof(long))
> +#define S5P_LV2TABLE_MASK	(~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */
> +
> +#define S5P_SECTION_LV1_ENTRY(entry)	((entry & 0x40003) == 2)
> +#define S5P_SUPSECT_LV1_ENTRY(entry)	((entry & 0x40003) ==
> 0x40002)
> +#define S5P_PAGE_LV1_ENTRY(entry)	((entry & 3) == 1)
> +#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) ==
3)
> +
> +#define S5P_LPAGE_LV2_ENTRY(entry)	((entry & 3) == 1)
> +#define S5P_SPAGE_LV2_ENTRY(entry)	((entry & 2) == 2)
> +#define S5P_FAULT_LV2_ENTRY(entry)	((entry & 3) == 0)
> +
> +#define MAKE_FAULT_ENTRY(entry)		do { entry = 0; } while (0)
> +#define MAKE_SECTION_ENTRY(entry, pa)	do { entry = pa | 2; } while
(0)
> +#define MAKE_SUPSECT_ENTRY(entry, pa)	do { entry = pa | 0x40002; }
> while (0)
> +#define MAKE_LV2TABLE_ENTRY(entry, pa)	do { entry = pa | 1; } while
(0)
> +
> +#define MAKE_LPAGE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
> +#define MAKE_SPAGE_ENTRY(entry, pa)	do { entry = pa | 3; } while (0)
> +
> +#define GET_LV2ENTRY(entry, iova) (\
> +	(unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\
> +	((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT))
> +
> +struct s5p_iommu_domain {
> +	struct device *dev;
> +	sysmmu_ips ips;
> +	unsigned long *pgtable;
> +};
> +
> +struct s5p_finddev_struct {
> +	char *name;
> +	int id;
> +};
> +/* Shared page table implementation */
> +static unsigned long *pgtable;
> +
> +static struct s5p_finddev_struct pdev_names[S5P_SYSMMU_TOTAL_IPNUM] = {
> +	{"s3c-pl330", 0},
> +	{"s5p-sss", -1},
> +	{"s3c-fimc", 0},
> +	{"s3c-fimc", 1},
> +	{"s3c-fimc", 2},
> +	{"s3c-fimc", 3},
> +	{"s5p-jpeg", -1},
> +	{"s5p-fb", 0},
> +	{"s5p-fb", 1},
> +	{"s5p-pcie", -1},
> +	{"s5p-fimg2d", -1},
> +	{"s5p-rotator", -1},
> +	{"s5p-mdma2", -1},
> +	{"s5p-mixer", -1},
> +	{"mfc", -1}, /* SYSMMU_MFC_L */
> +};
> +/* slab cache for level 2 page tables */
> +static struct kmem_cache *l2table_cachep;
> +
> +LIST_HEAD(dev_lookup_list);
> +
> +struct dev_dom {
> +	struct list_head node;
> +	struct s5p_iommu_domain *dom;
> +	struct device *dev;
> +};
> +
> +static inline struct dev_dom *lookup_dev(struct device *dev)
> +{
> +	struct list_head *pos;
> +	struct dev_dom *rel = NULL;
> +
> +	list_for_each(pos, &dev_lookup_list) {
> +		rel = list_entry(pos, struct dev_dom, node);
> +		if (rel->dev == dev)
> +			return rel;
> +	}
> +
> +	return NULL;
> +}
> +
> +static inline int bind_dev(struct s5p_iommu_domain *dom, struct device
*dev)
> +{
> +	struct dev_dom *rel;
> +
> +	rel = kmalloc(sizeof(*rel), GFP_KERNEL);
> +	if (!rel)
> +		return -ENOMEM;
> +
> +	if (!lookup_dev(dev)) {
> +		rel->dom = dom;
> +		rel->dev = dev;
> +		list_add(&rel->node, &dev_lookup_list);
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void unbind_dev(struct device *dev)
> +{
> +	struct dev_dom *rel;
> +
> +	rel = lookup_dev(dev);
> +	if (rel)
> +		list_del(&rel->node);
> +}
> +
> +static inline void pgtable_flush(void *vastart, void *vaend)
> +{
> +	dmac_flush_range(vastart, vaend);
> +	outer_flush_range(virt_to_phys(vastart),
> +				virt_to_phys(vaend));
> +}
> +
> +static int s5p_iommu_domain_init(struct iommu_domain *domain)
> +{
> +	struct s5p_iommu_domain *priv;
> +
> +	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
> +		(S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >>
> PAGE_SHIFT);
> +	if (!priv->pgtable) {
> +		kfree(priv);
> +		return -ENOMEM;
> +	}
> +
> +	memset(pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long));
> +	pgtable_flush(pgtable, pgtable + S5P_LV1TABLE_ENTRIES);
> +
> +	domain->priv = priv;
> +	return 0;
> +}
> +
> +static void s5p_iommu_domain_destroy(struct iommu_domain *domain)
> +{
> +	struct s5p_iommu_domain *priv = domain->priv;
> +
> +	free_pages((unsigned long)priv->pgtable, 2);
> +	kfree(domain->priv);
> +	domain->priv = NULL;
> +}
> +
> +static sysmmu_ips get_sysmmu_id(struct platform_device *pdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++)
> +		if ((strcmp(pdev->name, pdev_names[i].name) == 0) &&
> +				(pdev->id == pdev_names[i].id))
> +			break;
> +
> +	return (sysmmu_ips)i;
> +}
> +
> +static int s5p_iommu_attach_device(struct iommu_domain *domain,
> +				   struct device *dev)
> +{
> +	sysmmu_ips ips;
> +	int ret;
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	struct platform_device *pdev =
> +				container_of(dev, struct platform_device,
dev);
> +
> +	ips = get_sysmmu_id(pdev);
> +	if (ips == SYSMMU_NONE)
> +		return -ENODEV;
> +
> +	s5p_domain->ips = ips;
> +
> +	s5p_sysmmu_enable(ips, (unsigned long)s5p_domain->pgtable);
> +	if (ips == SYSMMU_MFC_L)
> +		s5p_sysmmu_enable(ips + 1, (unsigned long)s5p_domain-
> >pgtable);
> +
> +	s5p_domain->dev = dev;
> +
> +	ret = bind_dev(s5p_domain, dev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void s5p_iommu_detach_device(struct iommu_domain *domain,
> +				    struct device *dev)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +
> +	if (s5p_domain->dev == dev) {
> +		s5p_sysmmu_disable(s5p_domain->ips);
> +		if (s5p_domain->ips == SYSMMU_MFC_L)
> +			s5p_sysmmu_disable(s5p_domain->ips + 1);
> +		unbind_dev(dev);
> +	}
> +}
> +
> +static bool section_available(struct iommu_domain *domain,
> +			      unsigned long *lv1entry)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +
> +	if (S5P_SECTION_LV1_ENTRY(*lv1entry)) {
> +		dev_err(s5p_domain->dev,
> +				"1MB entry alread exists at 0x%08x\n",
> +				(lv1entry - s5p_domain->pgtable) * SZ_1M);
> +		return false;
> +	}
> +
> +	if (S5P_PAGE_LV1_ENTRY(*lv1entry)) {
> +		unsigned long *lv2end, *lv2base;
> +
> +		lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK);
> +		lv2end = lv2base + S5P_LV2TABLE_ENTRIES;
> +		while (lv2base != lv2end) {
> +			if (S5P_FAULT_LV2_ENTRY(*lv2base)) {
> +				dev_err(s5p_domain->dev,
> +					"Failed to free L2 page table for"
> +					"section mapping.\n");
> +				return false;
> +			}
> +			lv2base++;
> +		}
> +
> +		kmem_cache_free(l2table_cachep,
> +				phys_to_virt(*lv1entry &
> S5P_LV2TABLE_MASK));
> +
> +		MAKE_FAULT_ENTRY(*lv1entry);
> +	}
> +
> +	return true;
> +}
> +
> +static bool write_lpage(unsigned long *head_entry, unsigned long
phys_addr)
> +{
> +	unsigned long *entry, *end;
> +
> +	entry = head_entry;
> +	end = entry + (1 << S5P_LPAGE_ORDER);
> +
> +	while (entry != end) {
> +		if (!S5P_FAULT_LV2_ENTRY(*entry))
> +			break;
> +
> +		MAKE_LPAGE_ENTRY(*entry, phys_addr);
> +
> +		entry++;
> +	}
> +
> +	if (entry != end) {
> +		end = entry;
> +		while (entry != head_entry)
> +			MAKE_FAULT_ENTRY(*(--entry));
> +
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +int s5p_iommu_map(struct iommu_domain *domain, unsigned long iova,
> +			 phys_addr_t paddr, int gfp_order, int prot)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *start_entry, *entry, *end_entry;
> +	int num_entry;
> +
> +	BUG_ON(s5p_domain->dev == NULL);
> +
> +	start_entry = entry = s5p_domain->pgtable + (iova >>
> S5P_SECTION_SHIFT);
> +
> +	if (gfp_order >= S5P_SECTION_ORDER) {
> +		BUG_ON((paddr | iova) & ~S5P_SECTION_MASK);
> +		/* 1MiB mapping */
> +
> +		num_entry = 1 << (gfp_order - S5P_SECTION_ORDER);
> +		end_entry = entry + num_entry;
> +
> +		while (entry != end_entry) {
> +			if (!section_available(domain, entry))
> +				break;
> +
> +			MAKE_SECTION_ENTRY(*entry, paddr);
> +
> +			paddr += S5P_SECTION_SIZE;
> +			entry++;
> +		}
> +
> +		if (entry != end_entry)
> +			goto mapping_error;
> +
> +		return 0;
> +	}
> +
> +	if (S5P_FAULT_LV1_ENTRY(*entry)) {
> +		unsigned long *l2table;
> +
> +		l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL);
> +		if (!l2table)
> +			return -ENOMEM;
> +
> +		pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES);
> +
> +		MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table));
> +		pgtable_flush(entry, entry + 1);
> +	}
> +
> +	/* 'entry' points level 2 entries, hereafter */
> +	entry = GET_LV2ENTRY(*entry, iova);
> +
> +	start_entry = entry;
> +	num_entry = 1 << gfp_order;
> +	end_entry = entry + num_entry;
> +
> +	if (gfp_order >= S5P_LPAGE_ORDER) {
> +		/* large page(64KiB) mapping */
> +		BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK);
> +
> +		while (entry != end_entry) {
> +			if (!write_lpage(entry, paddr)) {
> +				dev_err(s5p_domain->dev,
> +					"Failed to allocate large page
entry.\n"
> +					);
> +				break;
> +			}
> +
> +			paddr += S5P_LPAGE_SIZE;
> +			entry += (1 << S5P_LPAGE_ORDER);
> +		}
> +
> +		if (entry != end_entry) {
> +			entry -= 1 << S5P_LPAGE_ORDER;
> +			goto mapping_error;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* page (4KiB) mapping */
> +	while (entry != end_entry && !S5P_FAULT_LV2_ENTRY(*entry)) {
> +
> +		MAKE_SPAGE_ENTRY(*entry, paddr);
> +
> +		entry++;
> +		paddr += S5P_SPAGE_SIZE;
> +	}
> +
> +	if (entry != end_entry) {
> +		dev_err(s5p_domain->dev,
> +			"Failed to allocate small page entry.\n");
> +		goto mapping_error;
> +	}
> +
> +	pgtable_flush(start_entry, entry);
> +mapping_error:
> +	if (entry != end_entry) {
> +		while (entry != start_entry)
> +			MAKE_FAULT_ENTRY(*(--entry));
> +		return -EADDRINUSE;
> +	}
> +
> +	return 0;
> +}
> +
> +int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
> +			   int gfp_order)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *entry;
> +	int num_entry;
> +
> +	BUG_ON(s5p_domain->dev == NULL);
> +
> +	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
> +
> +	if (gfp_order >= S5P_SECTION_ORDER)
> +		gfp_order -= S5P_SECTION_ORDER;
> +	else
> +		entry = GET_LV2ENTRY(*entry, iova);
> +
> +	BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && (gfp_order <
> S5P_LPAGE_ORDER));
> +
> +	num_entry = 1 << gfp_order;
> +
> +	while (num_entry-- > 0) {
> +		MAKE_FAULT_ENTRY(*entry);
> +		entry++;
> +	}
> +
> +	s5p_sysmmu_tlb_invalidate(s5p_domain->ips);
> +	if (s5p_domain->ips == SYSMMU_MFC_L)
> +		s5p_sysmmu_tlb_invalidate(s5p_domain->ips + 1);
> +	return 0;
> +}
> +
> +phys_addr_t s5p_iommu_iova_to_phys(struct iommu_domain *domain,
> +					  unsigned long iova)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *entry;
> +	unsigned long offset;
> +
> +	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
> +
> +	if (S5P_FAULT_LV1_ENTRY(*entry))
> +		return 0;
> +
> +	offset = iova & ~S5P_SECTION_MASK;
> +
> +	if (S5P_SECTION_LV1_ENTRY(*entry))
> +		return (*entry & S5P_SECTION_MASK) + offset;
> +
> +	entry = GET_LV2ENTRY(*entry, iova);
> +
> +	if (S5P_SPAGE_LV2_ENTRY(*entry))
> +		return (*entry & S5P_SPAGE_MASK) + (iova &
> ~S5P_SPAGE_MASK);
> +
> +	if (S5P_LPAGE_LV2_ENTRY(*entry))
> +		return (*entry & S5P_LPAGE_MASK) + (iova &
> ~S5P_LPAGE_MASK);
> +
> +	return 0;
> +}
> +
> +static int s5p_iommu_domain_has_cap(struct iommu_domain *domain,
> +				    unsigned long cap)
> +{
> +	return 0;
> +}
> +
> +static struct iommu_ops s5p_iommu_ops = {
> +	.domain_init = &s5p_iommu_domain_init,
> +	.domain_destroy = &s5p_iommu_domain_destroy,
> +	.attach_dev = &s5p_iommu_attach_device,
> +	.detach_dev = &s5p_iommu_detach_device,
> +	.map = &s5p_iommu_map,
> +	.unmap = &s5p_iommu_unmap,
> +	.iova_to_phys = &s5p_iommu_iova_to_phys,
> +	.domain_has_cap = &s5p_iommu_domain_has_cap
> +};
> +
> +static int __init s5p_iommu_init(void)
> +{
> +	l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables",
> +				S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE,
> 0, NULL);
> +	if (!l2table_cachep)
> +		return -ENOMEM;
> +
> +	register_iommu(&s5p_iommu_ops);
> +	return 0;
> +}
> +arch_initcall(s5p_iommu_init);
> --
> 1.7.1


^ permalink raw reply	[flat|nested] 27+ messages in thread

* RE: [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
@ 2011-08-31  1:18     ` Kukjin Kim
  0 siblings, 0 replies; 27+ messages in thread
From: Kukjin Kim @ 2011-08-31  1:18 UTC (permalink / raw)
  To: 'KyongHo Cho',
	linux-arm-kernel, linux-samsung-soc, iommu, linux-kernel
  Cc: 'Joerg Roedel', 'Sanghyun Lee',
	'Ilho Lee', 'Kwanghyun La'

KyongHo Cho wrote:
> 
> Implemented IOMMU API for Exynos4 platform that has IOMMU(System MMU).
> The previous System MMU driver exposed its own functions and was lack
> of page table management.
> 
> This patch includes complete implementation of IOMMU API
> and it is capable of mapping and unmapping any number of orders.
> 
> iommu_map() for Exynos4 does not map 16MiB page because it is not
> practical now. 1MiB page is sufficient for larger physically contiguous
> memory mapping. Performance degradation due to TLB miss is not such a
> big problem with 1MiB page.
> 
> Since archdata field of structure 'device' contains nothing in ARM,
> I've used linked list to manage the relation between domain and device.
> 
> Marek is trying to add domain and dma_map_ops fields into dev_archdata.
> This way of relation management will be changed after the Marek's work
> is merged into the mainline.
> 
> Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
> ---
>  arch/arm/mach-exynos4/include/mach/sysmmu.h |    9 +-
>  drivers/iommu/Kconfig                       |   11 +
>  drivers/iommu/Makefile                      |    1 +
>  drivers/iommu/exynos4_sysmmu.c              |  343
> ++++++++++++++++++
>  drivers/iommu/exynos4_sysmmu.h              |   18 +
>  drivers/iommu/exynos_iommu.c                |  496
> +++++++++++++++++++++++++++
>  6 files changed, 872 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/iommu/exynos4_sysmmu.c
>  create mode 100644 drivers/iommu/exynos4_sysmmu.h
>  create mode 100644 drivers/iommu/exynos_iommu.c
> 

Hi KyongHo,

Basically, we need this for EXYNOS System MMU but I think, this is required
to re-work based on latest kernel and if possible, please make it look as a
single patch set about regarding patches to prevent missing something
important.

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.


> diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-
> exynos4/include/mach/sysmmu.h
> index 6a5fbb5..2be20c5 100644
> --- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
> +++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
> @@ -34,13 +34,10 @@ enum exynos4_sysmmu_ips {
>  };
> 
>  #define S5P_SYSMMU_TOTAL_IPNUM
> 	EXYNOS4_SYSMMU_TOTAL_IPNUM
> -
> -extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
> +#define SYSMMU_NONE
> 	S5P_SYSMMU_TOTAL_IPNUM
> 
>  typedef enum exynos4_sysmmu_ips sysmmu_ips;
> 
> -void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
> -void sysmmu_clk_enable(sysmmu_ips ips);
> -void sysmmu_clk_disable(sysmmu_ips ips);
> -
> +#else /* __ASM_ARM_ARCH_SYSMMU_H */
> +#error mach/sysmmu.h must not be included by device drivers
>  #endif /* __ASM_ARM_ARCH_SYSMMU_H */
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index b57b3fa..a9fcc79 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -107,4 +107,15 @@ config INTR_REMAP
>  	  To use x2apic mode in the CPU's which support x2APIC enhancements
> or
>  	  to support platforms with CPU's having > 8 bit APIC ID, say Y.
> 
> +# EXYNOS IOMMU support
> +config EXYNOS_IOMMU
> +	bool "Exynos4 IOMMU(System MMU) Support"
> +	depends on ARCH_EXYNOS4
> +	select IOMMU_API
> +	help
> +	  Support for the System MMU embedded in Samsung Exynos4 SOCs.
> +	  These IOMMUs allow virtualization of the address space used by
most
> +	  cores within the multimedia subsystem.
> +
> +	  If unsure, say N here.
>  endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 4d4d77d..446bbdd 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
> msm_iommu_dev.o
>  obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
>  obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
>  obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
> +obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o exynos4_sysmmu.o
> diff --git a/drivers/iommu/exynos4_sysmmu.c
b/drivers/iommu/exynos4_sysmmu.c
> new file mode 100644
> index 0000000..01e0966
> --- /dev/null
> +++ b/drivers/iommu/exynos4_sysmmu.c
> @@ -0,0 +1,343 @@
> +/* linux/drivers/iommu/exynos4_sysmmu.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +
> +#include <asm/pgtable.h>
> +
> +#include <mach/map.h>
> +#include <mach/regs-sysmmu.h>
> +
> +#include "exynos4_sysmmu.h"
> +
> +#define CTRL_ENABLE	0x5
> +#define CTRL_BLOCK	0x7
> +#define CTRL_DISABLE	0x0
> +
> +static struct device *dev_sysmmu[S5P_SYSMMU_TOTAL_IPNUM];
> +
> +static void sysmmu_clk_enable(sysmmu_ips ips)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_get(dev_sysmmu[ips], NULL);
> +	if (clk)
> +		clk_enable(clk);
> +}
> +
> +static void sysmmu_clk_disable(sysmmu_ips ips)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_get(dev_sysmmu[ips], NULL);
> +	if (clk)
> +		clk_disable(clk);
> +}
> +
> +enum S5P_SYSMMU_INTERRUPT_TYPE {
> +	SYSMMU_PAGEFAULT,
> +	SYSMMU_AR_MULTIHIT,
> +	SYSMMU_AW_MULTIHIT,
> +	SYSMMU_BUSERROR,
> +	SYSMMU_AR_SECURITY,
> +	SYSMMU_AR_ACCESS,
> +	SYSMMU_AW_SECURITY,
> +	SYSMMU_AW_PROTECTION, /* 7 */
> +	SYSMMU_FAULTS_NUM
> +};
> +
> +static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
> +	S5P_PAGE_FAULT_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR,
> +	S5P_DEFAULT_SLAVE_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR
> +};
> +
> +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
> +	"PAGE FAULT",
> +	"AR MULTI-HIT FAULT",
> +	"AW MULTI-HIT FAULT",
> +	"BUS ERROR",
> +	"AR SECURITY PROTECTION FAULT",
> +	"AR ACCESS PROTECTION FAULT",
> +	"AW SECURITY PROTECTION FAULT",
> +	"AW ACCESS PROTECTION FAULT"
> +};
> +
> +static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])(
> +		enum S5P_SYSMMU_INTERRUPT_TYPE itype,
> +		unsigned long pgtable_base,
> +		unsigned long fault_addr);
> +
> +/*
> + * If adjacent 2 bits are true, the system MMU is enabled.
> + * The system MMU is disabled, otherwise.
> + */
> +static unsigned long sysmmu_states;
> +
> +static inline int set_sysmmu_active(sysmmu_ips ips)
> +{
> +	/* return true if it is not set */
> +	return !test_and_set_bit(ips, &sysmmu_states);
> +}
> +
> +static inline int set_sysmmu_inactive(sysmmu_ips ips)
> +{
> +	/* return true if it is set */
> +	return test_and_clear_bit(ips, &sysmmu_states);
> +}
> +
> +static inline int is_sysmmu_active(sysmmu_ips ips)
> +{
> +	/* BUG_ON(ips >= S5P_SYSMMU_TOTAL_IPNUM); */
> +	return sysmmu_states & (1 << ips);
> +}
> +
> +static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM];
> +
> +static inline void sysmmu_block(sysmmu_ips ips)
> +{
> +	__raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
> +	dev_dbg(dev_sysmmu[ips], "blocked.\n");
> +}
> +
> +static inline void sysmmu_unblock(sysmmu_ips ips)
> +{
> +	__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
> +	dev_dbg(dev_sysmmu[ips], "unblocked.\n");
> +}
> +
> +static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
> +{
> +	__raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
> +	dev_dbg(dev_sysmmu[ips], "TLB is invalidated.\n");
> +}
> +
> +static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (unlikely(pgd == 0)) {
> +		pgd = (unsigned long)ZERO_PAGE(0);
> +		__raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB
> LV1 */
> +	} else {
> +		__raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB
> LV1 */
> +	}
> +
> +	__raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
> +
> +	dev_dbg(dev_sysmmu[ips], "Page table base initialized with
0x%08lX.\n",
> +
> 	pgd);
> +	__sysmmu_tlb_invalidate(ips);
> +}
> +
> +static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
> +{
> +	/* SYSMMU is in blocked when interrupt occurred. */
> +	unsigned long base = 0;
> +	unsigned long addr;
> +	sysmmu_ips ips = (sysmmu_ips)dev_id;
> +	enum S5P_SYSMMU_INTERRUPT_TYPE itype;
> +
> +	BUG_ON(!is_sysmmu_active(ips));
> +
> +	itype = (enum S5P_SYSMMU_INTERRUPT_TYPE)
> +		__ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
> +
> +	BUG_ON(!((itype >= 0) && (itype < 8)));
> +
> +	base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
> +	addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
> +
> +	dev_alert(dev_sysmmu[ips], "%s occurred at %08lx
> (PT_BASE_ADDR: %08lx)."
> +		"\n", sysmmu_fault_name[itype], addr, base);
> +
> +	if (fault_handlers[ips]) {
> +
> +
> +		if (fault_handlers[ips](itype, base, addr)) {
> +			__raw_writel(1 << itype,
> +					sysmmusfrs[ips] + S5P_INT_CLEAR);
> +			dev_notice(dev_sysmmu[ips], "%s is resolved.
> Retrying"
> +				" translation.\n",
sysmmu_fault_name[itype]);
> +		} else {
> +			base = 0;
> +		}
> +	}
> +
> +	sysmmu_unblock(ips);
> +
> +	if (!base)
> +		dev_notice(dev_sysmmu[ips], "%s is not handled.\n",
> +						sysmmu_fault_name[itype]);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (is_sysmmu_active(ips)) {
> +		sysmmu_block(ips);
> +		__sysmmu_set_ptbase(ips, pgd);
> +		sysmmu_unblock(ips);
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "disabled. Skipping initializing
page"
> +				" table base...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (set_sysmmu_active(ips)) {
> +		sysmmu_clk_enable(ips);
> +
> +		__sysmmu_set_ptbase(ips, pgd);
> +
> +		__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] +
> S5P_MMU_CTRL);
> +
> +		set_sysmmu_active(ips);
> +		dev_dbg(dev_sysmmu[ips], "enabled.\n");
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "already enabled."
> +					" Skipping initialization...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_disable(sysmmu_ips ips)
> +{
> +	if (set_sysmmu_inactive(ips)) {
> +		__raw_writel(CTRL_DISABLE, sysmmusfrs[ips] +
> S5P_MMU_CTRL);
> +		sysmmu_clk_disable(ips);
> +		dev_dbg(dev_sysmmu[ips], "disabled.\n");
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "already disabled."
> +					" Skipping deinitialization...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
> +{
> +	if (is_sysmmu_active(ips)) {
> +		sysmmu_block(ips);
> +		__sysmmu_tlb_invalidate(ips);
> +		sysmmu_unblock(ips);
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "is disabled. "
> +			"Skipping invalidating TLB...\n");
> +	}
> +}
> +
> +static int s5p_sysmmu_probe(struct platform_device *pdev)
> +{
> +	sysmmu_ips id;
> +	struct resource *res, *ioarea;
> +	int ret;
> +	int irq;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to get resource.");
> +		return -ENOENT;
> +	}
> +
> +	id = (sysmmu_ips)pdev->id;
> +
> +	if (id >= S5P_SYSMMU_TOTAL_IPNUM) {
> +		dev_err(&pdev->dev, "Unknown System MMU ID %d.", id);
> +		return -ENOENT;
> +	}
> +
> +	ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
> +	if (ioarea == NULL) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +					"failed to request memory region.");
> +		return -ENOMEM;
> +	}
> +
> +	sysmmusfrs[id] = ioremap(res->start, resource_size(res));
> +	if (!sysmmusfrs[id]) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to call
ioremap().");
> +		ret = -ENOENT;
> +		goto err_ioremap;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to get irq
resource.");
> +		ret = irq;
> +		goto err_irq;
> +	}
> +
> +	if (request_irq(irq, s5p_sysmmu_irq, 0, dev_name(&pdev->dev),
> +								(void *)id))
> {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to request irq.");
> +		ret = -ENOENT;
> +		goto err_irq;
> +	}
> +
> +	dev_sysmmu[id] = &pdev->dev;
> +
> +	dev_dbg(&pdev->dev, "Probing system MMU succeeded.");
> +	return 0;
> +
> +err_irq:
> +	iounmap(sysmmusfrs[id]);
> +err_ioremap:
> +	release_resource(ioarea);
> +	kfree(ioarea);
> +	dev_err(&pdev->dev, "Probing system MMU failed.");
> +	return ret;
> +}
> +
> +static int s5p_sysmmu_remove(struct platform_device *pdev)
> +{
> +	return 0;
> +}
> +static int s5p_sysmmu_runtime_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_sysmmu_runtime_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops s5p_sysmmu_pm_ops = {
> +	.runtime_suspend	= s5p_sysmmu_runtime_suspend,
> +	.runtime_resume		= s5p_sysmmu_runtime_resume,
> +};
> +
> +static struct platform_driver s5p_sysmmu_driver = {
> +	.probe		= s5p_sysmmu_probe,
> +	.remove		= s5p_sysmmu_remove,
> +	.driver		= {
> +		.owner		= THIS_MODULE,
> +		.name		= "s5p-sysmmu",
> +		.pm		= &s5p_sysmmu_pm_ops,
> +	}
> +};
> +
> +static int __init s5p_sysmmu_init(void)
> +{
> +	return platform_driver_register(&s5p_sysmmu_driver);
> +}
> +arch_initcall(s5p_sysmmu_init);
> diff --git a/drivers/iommu/exynos4_sysmmu.h
b/drivers/iommu/exynos4_sysmmu.h
> new file mode 100644
> index 0000000..f739240
> --- /dev/null
> +++ b/drivers/iommu/exynos4_sysmmu.h
> @@ -0,0 +1,18 @@
> +/* linux/drivers/iommu/exynos4_sysmmu.h
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung System MMU driver for Exynos4 platforms
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <mach/sysmmu.h>
> +
> +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd);
> +void s5p_sysmmu_disable(sysmmu_ips ips);
> +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd);
> +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips);
> diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c
> new file mode 100644
> index 0000000..cbb94df
> --- /dev/null
> +++ b/drivers/iommu/exynos_iommu.c
> @@ -0,0 +1,496 @@
> +/* linux/drivers/iommu/exynos_iommu.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/mm.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/iommu.h>
> +#include <linux/errno.h>
> +#include <linux/list.h>
> +
> +#include <asm/cacheflush.h>
> +
> +#include "exynos4_sysmmu.h"
> +
> +#ifdef CONFIG_S5P_SYSTEM_MMU_DEBUG
> +#define DEBUG /* for dev_dbg() */
> +#endif
> +
> +/* We does not consider super section mapping (16MB) */
> +#define S5P_SPAGE_SHIFT		12
> +#define S5P_LPAGE_SHIFT		16
> +#define S5P_SECTION_SHIFT	20
> +
> +#define S5P_SPAGE_SIZE		(1 << S5P_SPAGE_SHIFT)
> +#define S5P_LPAGE_SIZE		(1 << S5P_LPAGE_SHIFT)
> +#define S5P_SECTION_SIZE	(1 << S5P_SECTION_SHIFT)
> +
> +#define S5P_SPAGE_MASK		(~(S5P_SPAGE_SIZE - 1))
> +#define S5P_LPAGE_MASK		(~(S5P_LPAGE_SIZE - 1))
> +#define S5P_SECTION_MASK	(~(S5P_SECTION_SIZE - 1))
> +
> +#define S5P_SPAGE_ORDER		(S5P_SPAGE_SHIFT - PAGE_SHIFT)
> +#define S5P_LPAGE_ORDER		(S5P_LPAGE_SHIFT -
> S5P_SPAGE_SHIFT)
> +#define S5P_SECTION_ORDER	(S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT)
> +
> +#define S5P_LV1TABLE_ENTRIES	(1 << (BITS_PER_LONG -
> S5P_SECTION_SHIFT))
> +
> +#define S5P_LV2TABLE_ENTRIES	(1 << S5P_SECTION_ORDER)
> +#define S5P_LV2TABLE_SIZE	(S5P_LV2TABLE_ENTRIES * sizeof(long))
> +#define S5P_LV2TABLE_MASK	(~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */
> +
> +#define S5P_SECTION_LV1_ENTRY(entry)	((entry & 0x40003) == 2)
> +#define S5P_SUPSECT_LV1_ENTRY(entry)	((entry & 0x40003) ==
> 0x40002)
> +#define S5P_PAGE_LV1_ENTRY(entry)	((entry & 3) == 1)
> +#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) ==
3)
> +
> +#define S5P_LPAGE_LV2_ENTRY(entry)	((entry & 3) == 1)
> +#define S5P_SPAGE_LV2_ENTRY(entry)	((entry & 2) == 2)
> +#define S5P_FAULT_LV2_ENTRY(entry)	((entry & 3) == 0)
> +
> +#define MAKE_FAULT_ENTRY(entry)		do { entry = 0; } while (0)
> +#define MAKE_SECTION_ENTRY(entry, pa)	do { entry = pa | 2; } while
(0)
> +#define MAKE_SUPSECT_ENTRY(entry, pa)	do { entry = pa | 0x40002; }
> while (0)
> +#define MAKE_LV2TABLE_ENTRY(entry, pa)	do { entry = pa | 1; } while
(0)
> +
> +#define MAKE_LPAGE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
> +#define MAKE_SPAGE_ENTRY(entry, pa)	do { entry = pa | 3; } while (0)
> +
> +#define GET_LV2ENTRY(entry, iova) (\
> +	(unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\
> +	((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT))
> +
> +struct s5p_iommu_domain {
> +	struct device *dev;
> +	sysmmu_ips ips;
> +	unsigned long *pgtable;
> +};
> +
> +struct s5p_finddev_struct {
> +	char *name;
> +	int id;
> +};
> +/* Shared page table implementation */
> +static unsigned long *pgtable;
> +
> +static struct s5p_finddev_struct pdev_names[S5P_SYSMMU_TOTAL_IPNUM] = {
> +	{"s3c-pl330", 0},
> +	{"s5p-sss", -1},
> +	{"s3c-fimc", 0},
> +	{"s3c-fimc", 1},
> +	{"s3c-fimc", 2},
> +	{"s3c-fimc", 3},
> +	{"s5p-jpeg", -1},
> +	{"s5p-fb", 0},
> +	{"s5p-fb", 1},
> +	{"s5p-pcie", -1},
> +	{"s5p-fimg2d", -1},
> +	{"s5p-rotator", -1},
> +	{"s5p-mdma2", -1},
> +	{"s5p-mixer", -1},
> +	{"mfc", -1}, /* SYSMMU_MFC_L */
> +};
> +/* slab cache for level 2 page tables */
> +static struct kmem_cache *l2table_cachep;
> +
> +LIST_HEAD(dev_lookup_list);
> +
> +struct dev_dom {
> +	struct list_head node;
> +	struct s5p_iommu_domain *dom;
> +	struct device *dev;
> +};
> +
> +static inline struct dev_dom *lookup_dev(struct device *dev)
> +{
> +	struct list_head *pos;
> +	struct dev_dom *rel = NULL;
> +
> +	list_for_each(pos, &dev_lookup_list) {
> +		rel = list_entry(pos, struct dev_dom, node);
> +		if (rel->dev == dev)
> +			return rel;
> +	}
> +
> +	return NULL;
> +}
> +
> +static inline int bind_dev(struct s5p_iommu_domain *dom, struct device
*dev)
> +{
> +	struct dev_dom *rel;
> +
> +	rel = kmalloc(sizeof(*rel), GFP_KERNEL);
> +	if (!rel)
> +		return -ENOMEM;
> +
> +	if (!lookup_dev(dev)) {
> +		rel->dom = dom;
> +		rel->dev = dev;
> +		list_add(&rel->node, &dev_lookup_list);
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void unbind_dev(struct device *dev)
> +{
> +	struct dev_dom *rel;
> +
> +	rel = lookup_dev(dev);
> +	if (rel)
> +		list_del(&rel->node);
> +}
> +
> +static inline void pgtable_flush(void *vastart, void *vaend)
> +{
> +	dmac_flush_range(vastart, vaend);
> +	outer_flush_range(virt_to_phys(vastart),
> +				virt_to_phys(vaend));
> +}
> +
> +static int s5p_iommu_domain_init(struct iommu_domain *domain)
> +{
> +	struct s5p_iommu_domain *priv;
> +
> +	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
> +		(S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >>
> PAGE_SHIFT);
> +	if (!priv->pgtable) {
> +		kfree(priv);
> +		return -ENOMEM;
> +	}
> +
> +	memset(pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long));
> +	pgtable_flush(pgtable, pgtable + S5P_LV1TABLE_ENTRIES);
> +
> +	domain->priv = priv;
> +	return 0;
> +}
> +
> +static void s5p_iommu_domain_destroy(struct iommu_domain *domain)
> +{
> +	struct s5p_iommu_domain *priv = domain->priv;
> +
> +	free_pages((unsigned long)priv->pgtable, 2);
> +	kfree(domain->priv);
> +	domain->priv = NULL;
> +}
> +
> +static sysmmu_ips get_sysmmu_id(struct platform_device *pdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++)
> +		if ((strcmp(pdev->name, pdev_names[i].name) == 0) &&
> +				(pdev->id == pdev_names[i].id))
> +			break;
> +
> +	return (sysmmu_ips)i;
> +}
> +
> +static int s5p_iommu_attach_device(struct iommu_domain *domain,
> +				   struct device *dev)
> +{
> +	sysmmu_ips ips;
> +	int ret;
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	struct platform_device *pdev =
> +				container_of(dev, struct platform_device,
dev);
> +
> +	ips = get_sysmmu_id(pdev);
> +	if (ips == SYSMMU_NONE)
> +		return -ENODEV;
> +
> +	s5p_domain->ips = ips;
> +
> +	s5p_sysmmu_enable(ips, (unsigned long)s5p_domain->pgtable);
> +	if (ips == SYSMMU_MFC_L)
> +		s5p_sysmmu_enable(ips + 1, (unsigned long)s5p_domain-
> >pgtable);
> +
> +	s5p_domain->dev = dev;
> +
> +	ret = bind_dev(s5p_domain, dev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void s5p_iommu_detach_device(struct iommu_domain *domain,
> +				    struct device *dev)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +
> +	if (s5p_domain->dev == dev) {
> +		s5p_sysmmu_disable(s5p_domain->ips);
> +		if (s5p_domain->ips == SYSMMU_MFC_L)
> +			s5p_sysmmu_disable(s5p_domain->ips + 1);
> +		unbind_dev(dev);
> +	}
> +}
> +
> +static bool section_available(struct iommu_domain *domain,
> +			      unsigned long *lv1entry)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +
> +	if (S5P_SECTION_LV1_ENTRY(*lv1entry)) {
> +		dev_err(s5p_domain->dev,
> +				"1MB entry alread exists at 0x%08x\n",
> +				(lv1entry - s5p_domain->pgtable) * SZ_1M);
> +		return false;
> +	}
> +
> +	if (S5P_PAGE_LV1_ENTRY(*lv1entry)) {
> +		unsigned long *lv2end, *lv2base;
> +
> +		lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK);
> +		lv2end = lv2base + S5P_LV2TABLE_ENTRIES;
> +		while (lv2base != lv2end) {
> +			if (S5P_FAULT_LV2_ENTRY(*lv2base)) {
> +				dev_err(s5p_domain->dev,
> +					"Failed to free L2 page table for"
> +					"section mapping.\n");
> +				return false;
> +			}
> +			lv2base++;
> +		}
> +
> +		kmem_cache_free(l2table_cachep,
> +				phys_to_virt(*lv1entry &
> S5P_LV2TABLE_MASK));
> +
> +		MAKE_FAULT_ENTRY(*lv1entry);
> +	}
> +
> +	return true;
> +}
> +
> +static bool write_lpage(unsigned long *head_entry, unsigned long
phys_addr)
> +{
> +	unsigned long *entry, *end;
> +
> +	entry = head_entry;
> +	end = entry + (1 << S5P_LPAGE_ORDER);
> +
> +	while (entry != end) {
> +		if (!S5P_FAULT_LV2_ENTRY(*entry))
> +			break;
> +
> +		MAKE_LPAGE_ENTRY(*entry, phys_addr);
> +
> +		entry++;
> +	}
> +
> +	if (entry != end) {
> +		end = entry;
> +		while (entry != head_entry)
> +			MAKE_FAULT_ENTRY(*(--entry));
> +
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +int s5p_iommu_map(struct iommu_domain *domain, unsigned long iova,
> +			 phys_addr_t paddr, int gfp_order, int prot)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *start_entry, *entry, *end_entry;
> +	int num_entry;
> +
> +	BUG_ON(s5p_domain->dev == NULL);
> +
> +	start_entry = entry = s5p_domain->pgtable + (iova >>
> S5P_SECTION_SHIFT);
> +
> +	if (gfp_order >= S5P_SECTION_ORDER) {
> +		BUG_ON((paddr | iova) & ~S5P_SECTION_MASK);
> +		/* 1MiB mapping */
> +
> +		num_entry = 1 << (gfp_order - S5P_SECTION_ORDER);
> +		end_entry = entry + num_entry;
> +
> +		while (entry != end_entry) {
> +			if (!section_available(domain, entry))
> +				break;
> +
> +			MAKE_SECTION_ENTRY(*entry, paddr);
> +
> +			paddr += S5P_SECTION_SIZE;
> +			entry++;
> +		}
> +
> +		if (entry != end_entry)
> +			goto mapping_error;
> +
> +		return 0;
> +	}
> +
> +	if (S5P_FAULT_LV1_ENTRY(*entry)) {
> +		unsigned long *l2table;
> +
> +		l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL);
> +		if (!l2table)
> +			return -ENOMEM;
> +
> +		pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES);
> +
> +		MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table));
> +		pgtable_flush(entry, entry + 1);
> +	}
> +
> +	/* 'entry' points level 2 entries, hereafter */
> +	entry = GET_LV2ENTRY(*entry, iova);
> +
> +	start_entry = entry;
> +	num_entry = 1 << gfp_order;
> +	end_entry = entry + num_entry;
> +
> +	if (gfp_order >= S5P_LPAGE_ORDER) {
> +		/* large page(64KiB) mapping */
> +		BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK);
> +
> +		while (entry != end_entry) {
> +			if (!write_lpage(entry, paddr)) {
> +				dev_err(s5p_domain->dev,
> +					"Failed to allocate large page
entry.\n"
> +					);
> +				break;
> +			}
> +
> +			paddr += S5P_LPAGE_SIZE;
> +			entry += (1 << S5P_LPAGE_ORDER);
> +		}
> +
> +		if (entry != end_entry) {
> +			entry -= 1 << S5P_LPAGE_ORDER;
> +			goto mapping_error;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* page (4KiB) mapping */
> +	while (entry != end_entry && !S5P_FAULT_LV2_ENTRY(*entry)) {
> +
> +		MAKE_SPAGE_ENTRY(*entry, paddr);
> +
> +		entry++;
> +		paddr += S5P_SPAGE_SIZE;
> +	}
> +
> +	if (entry != end_entry) {
> +		dev_err(s5p_domain->dev,
> +			"Failed to allocate small page entry.\n");
> +		goto mapping_error;
> +	}
> +
> +	pgtable_flush(start_entry, entry);
> +mapping_error:
> +	if (entry != end_entry) {
> +		while (entry != start_entry)
> +			MAKE_FAULT_ENTRY(*(--entry));
> +		return -EADDRINUSE;
> +	}
> +
> +	return 0;
> +}
> +
> +int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
> +			   int gfp_order)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *entry;
> +	int num_entry;
> +
> +	BUG_ON(s5p_domain->dev == NULL);
> +
> +	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
> +
> +	if (gfp_order >= S5P_SECTION_ORDER)
> +		gfp_order -= S5P_SECTION_ORDER;
> +	else
> +		entry = GET_LV2ENTRY(*entry, iova);
> +
> +	BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && (gfp_order <
> S5P_LPAGE_ORDER));
> +
> +	num_entry = 1 << gfp_order;
> +
> +	while (num_entry-- > 0) {
> +		MAKE_FAULT_ENTRY(*entry);
> +		entry++;
> +	}
> +
> +	s5p_sysmmu_tlb_invalidate(s5p_domain->ips);
> +	if (s5p_domain->ips == SYSMMU_MFC_L)
> +		s5p_sysmmu_tlb_invalidate(s5p_domain->ips + 1);
> +	return 0;
> +}
> +
> +phys_addr_t s5p_iommu_iova_to_phys(struct iommu_domain *domain,
> +					  unsigned long iova)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *entry;
> +	unsigned long offset;
> +
> +	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
> +
> +	if (S5P_FAULT_LV1_ENTRY(*entry))
> +		return 0;
> +
> +	offset = iova & ~S5P_SECTION_MASK;
> +
> +	if (S5P_SECTION_LV1_ENTRY(*entry))
> +		return (*entry & S5P_SECTION_MASK) + offset;
> +
> +	entry = GET_LV2ENTRY(*entry, iova);
> +
> +	if (S5P_SPAGE_LV2_ENTRY(*entry))
> +		return (*entry & S5P_SPAGE_MASK) + (iova &
> ~S5P_SPAGE_MASK);
> +
> +	if (S5P_LPAGE_LV2_ENTRY(*entry))
> +		return (*entry & S5P_LPAGE_MASK) + (iova &
> ~S5P_LPAGE_MASK);
> +
> +	return 0;
> +}
> +
> +static int s5p_iommu_domain_has_cap(struct iommu_domain *domain,
> +				    unsigned long cap)
> +{
> +	return 0;
> +}
> +
> +static struct iommu_ops s5p_iommu_ops = {
> +	.domain_init = &s5p_iommu_domain_init,
> +	.domain_destroy = &s5p_iommu_domain_destroy,
> +	.attach_dev = &s5p_iommu_attach_device,
> +	.detach_dev = &s5p_iommu_detach_device,
> +	.map = &s5p_iommu_map,
> +	.unmap = &s5p_iommu_unmap,
> +	.iova_to_phys = &s5p_iommu_iova_to_phys,
> +	.domain_has_cap = &s5p_iommu_domain_has_cap
> +};
> +
> +static int __init s5p_iommu_init(void)
> +{
> +	l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables",
> +				S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE,
> 0, NULL);
> +	if (!l2table_cachep)
> +		return -ENOMEM;
> +
> +	register_iommu(&s5p_iommu_ops);
> +	return 0;
> +}
> +arch_initcall(s5p_iommu_init);
> --
> 1.7.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu
@ 2011-08-31  1:18     ` Kukjin Kim
  0 siblings, 0 replies; 27+ messages in thread
From: Kukjin Kim @ 2011-08-31  1:18 UTC (permalink / raw)
  To: linux-arm-kernel

KyongHo Cho wrote:
> 
> Implemented IOMMU API for Exynos4 platform that has IOMMU(System MMU).
> The previous System MMU driver exposed its own functions and was lack
> of page table management.
> 
> This patch includes complete implementation of IOMMU API
> and it is capable of mapping and unmapping any number of orders.
> 
> iommu_map() for Exynos4 does not map 16MiB page because it is not
> practical now. 1MiB page is sufficient for larger physically contiguous
> memory mapping. Performance degradation due to TLB miss is not such a
> big problem with 1MiB page.
> 
> Since archdata field of structure 'device' contains nothing in ARM,
> I've used linked list to manage the relation between domain and device.
> 
> Marek is trying to add domain and dma_map_ops fields into dev_archdata.
> This way of relation management will be changed after the Marek's work
> is merged into the mainline.
> 
> Signed-off-by: KyongHo Cho <pullip.cho@samsung.com>
> ---
>  arch/arm/mach-exynos4/include/mach/sysmmu.h |    9 +-
>  drivers/iommu/Kconfig                       |   11 +
>  drivers/iommu/Makefile                      |    1 +
>  drivers/iommu/exynos4_sysmmu.c              |  343
> ++++++++++++++++++
>  drivers/iommu/exynos4_sysmmu.h              |   18 +
>  drivers/iommu/exynos_iommu.c                |  496
> +++++++++++++++++++++++++++
>  6 files changed, 872 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/iommu/exynos4_sysmmu.c
>  create mode 100644 drivers/iommu/exynos4_sysmmu.h
>  create mode 100644 drivers/iommu/exynos_iommu.c
> 

Hi KyongHo,

Basically, we need this for EXYNOS System MMU but I think, this is required
to re-work based on latest kernel and if possible, please make it look as a
single patch set about regarding patches to prevent missing something
important.

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.


> diff --git a/arch/arm/mach-exynos4/include/mach/sysmmu.h b/arch/arm/mach-
> exynos4/include/mach/sysmmu.h
> index 6a5fbb5..2be20c5 100644
> --- a/arch/arm/mach-exynos4/include/mach/sysmmu.h
> +++ b/arch/arm/mach-exynos4/include/mach/sysmmu.h
> @@ -34,13 +34,10 @@ enum exynos4_sysmmu_ips {
>  };
> 
>  #define S5P_SYSMMU_TOTAL_IPNUM
> 	EXYNOS4_SYSMMU_TOTAL_IPNUM
> -
> -extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM];
> +#define SYSMMU_NONE
> 	S5P_SYSMMU_TOTAL_IPNUM
> 
>  typedef enum exynos4_sysmmu_ips sysmmu_ips;
> 
> -void sysmmu_clk_init(struct device *dev, sysmmu_ips ips);
> -void sysmmu_clk_enable(sysmmu_ips ips);
> -void sysmmu_clk_disable(sysmmu_ips ips);
> -
> +#else /* __ASM_ARM_ARCH_SYSMMU_H */
> +#error mach/sysmmu.h must not be included by device drivers
>  #endif /* __ASM_ARM_ARCH_SYSMMU_H */
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index b57b3fa..a9fcc79 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -107,4 +107,15 @@ config INTR_REMAP
>  	  To use x2apic mode in the CPU's which support x2APIC enhancements
> or
>  	  to support platforms with CPU's having > 8 bit APIC ID, say Y.
> 
> +# EXYNOS IOMMU support
> +config EXYNOS_IOMMU
> +	bool "Exynos4 IOMMU(System MMU) Support"
> +	depends on ARCH_EXYNOS4
> +	select IOMMU_API
> +	help
> +	  Support for the System MMU embedded in Samsung Exynos4 SOCs.
> +	  These IOMMUs allow virtualization of the address space used by
most
> +	  cores within the multimedia subsystem.
> +
> +	  If unsure, say N here.
>  endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 4d4d77d..446bbdd 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -3,3 +3,4 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
> msm_iommu_dev.o
>  obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
>  obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
>  obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
> +obj-$(CONFIG_EXYNOS_IOMMU) += exynos_iommu.o exynos4_sysmmu.o
> diff --git a/drivers/iommu/exynos4_sysmmu.c
b/drivers/iommu/exynos4_sysmmu.c
> new file mode 100644
> index 0000000..01e0966
> --- /dev/null
> +++ b/drivers/iommu/exynos4_sysmmu.c
> @@ -0,0 +1,343 @@
> +/* linux/drivers/iommu/exynos4_sysmmu.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +
> +#include <asm/pgtable.h>
> +
> +#include <mach/map.h>
> +#include <mach/regs-sysmmu.h>
> +
> +#include "exynos4_sysmmu.h"
> +
> +#define CTRL_ENABLE	0x5
> +#define CTRL_BLOCK	0x7
> +#define CTRL_DISABLE	0x0
> +
> +static struct device *dev_sysmmu[S5P_SYSMMU_TOTAL_IPNUM];
> +
> +static void sysmmu_clk_enable(sysmmu_ips ips)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_get(dev_sysmmu[ips], NULL);
> +	if (clk)
> +		clk_enable(clk);
> +}
> +
> +static void sysmmu_clk_disable(sysmmu_ips ips)
> +{
> +	struct clk *clk;
> +
> +	clk = clk_get(dev_sysmmu[ips], NULL);
> +	if (clk)
> +		clk_disable(clk);
> +}
> +
> +enum S5P_SYSMMU_INTERRUPT_TYPE {
> +	SYSMMU_PAGEFAULT,
> +	SYSMMU_AR_MULTIHIT,
> +	SYSMMU_AW_MULTIHIT,
> +	SYSMMU_BUSERROR,
> +	SYSMMU_AR_SECURITY,
> +	SYSMMU_AR_ACCESS,
> +	SYSMMU_AW_SECURITY,
> +	SYSMMU_AW_PROTECTION, /* 7 */
> +	SYSMMU_FAULTS_NUM
> +};
> +
> +static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
> +	S5P_PAGE_FAULT_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR,
> +	S5P_DEFAULT_SLAVE_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AR_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR,
> +	S5P_AW_FAULT_ADDR
> +};
> +
> +static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
> +	"PAGE FAULT",
> +	"AR MULTI-HIT FAULT",
> +	"AW MULTI-HIT FAULT",
> +	"BUS ERROR",
> +	"AR SECURITY PROTECTION FAULT",
> +	"AR ACCESS PROTECTION FAULT",
> +	"AW SECURITY PROTECTION FAULT",
> +	"AW ACCESS PROTECTION FAULT"
> +};
> +
> +static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])(
> +		enum S5P_SYSMMU_INTERRUPT_TYPE itype,
> +		unsigned long pgtable_base,
> +		unsigned long fault_addr);
> +
> +/*
> + * If adjacent 2 bits are true, the system MMU is enabled.
> + * The system MMU is disabled, otherwise.
> + */
> +static unsigned long sysmmu_states;
> +
> +static inline int set_sysmmu_active(sysmmu_ips ips)
> +{
> +	/* return true if it is not set */
> +	return !test_and_set_bit(ips, &sysmmu_states);
> +}
> +
> +static inline int set_sysmmu_inactive(sysmmu_ips ips)
> +{
> +	/* return true if it is set */
> +	return test_and_clear_bit(ips, &sysmmu_states);
> +}
> +
> +static inline int is_sysmmu_active(sysmmu_ips ips)
> +{
> +	/* BUG_ON(ips >= S5P_SYSMMU_TOTAL_IPNUM); */
> +	return sysmmu_states & (1 << ips);
> +}
> +
> +static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM];
> +
> +static inline void sysmmu_block(sysmmu_ips ips)
> +{
> +	__raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL);
> +	dev_dbg(dev_sysmmu[ips], "blocked.\n");
> +}
> +
> +static inline void sysmmu_unblock(sysmmu_ips ips)
> +{
> +	__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL);
> +	dev_dbg(dev_sysmmu[ips], "unblocked.\n");
> +}
> +
> +static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips)
> +{
> +	__raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH);
> +	dev_dbg(dev_sysmmu[ips], "TLB is invalidated.\n");
> +}
> +
> +static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (unlikely(pgd == 0)) {
> +		pgd = (unsigned long)ZERO_PAGE(0);
> +		__raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB
> LV1 */
> +	} else {
> +		__raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB
> LV1 */
> +	}
> +
> +	__raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
> +
> +	dev_dbg(dev_sysmmu[ips], "Page table base initialized with
0x%08lX.\n",
> +
> 	pgd);
> +	__sysmmu_tlb_invalidate(ips);
> +}
> +
> +static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id)
> +{
> +	/* SYSMMU is in blocked when interrupt occurred. */
> +	unsigned long base = 0;
> +	unsigned long addr;
> +	sysmmu_ips ips = (sysmmu_ips)dev_id;
> +	enum S5P_SYSMMU_INTERRUPT_TYPE itype;
> +
> +	BUG_ON(!is_sysmmu_active(ips));
> +
> +	itype = (enum S5P_SYSMMU_INTERRUPT_TYPE)
> +		__ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS));
> +
> +	BUG_ON(!((itype >= 0) && (itype < 8)));
> +
> +	base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR);
> +	addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]);
> +
> +	dev_alert(dev_sysmmu[ips], "%s occurred at %08lx
> (PT_BASE_ADDR: %08lx)."
> +		"\n", sysmmu_fault_name[itype], addr, base);
> +
> +	if (fault_handlers[ips]) {
> +
> +
> +		if (fault_handlers[ips](itype, base, addr)) {
> +			__raw_writel(1 << itype,
> +					sysmmusfrs[ips] + S5P_INT_CLEAR);
> +			dev_notice(dev_sysmmu[ips], "%s is resolved.
> Retrying"
> +				" translation.\n",
sysmmu_fault_name[itype]);
> +		} else {
> +			base = 0;
> +		}
> +	}
> +
> +	sysmmu_unblock(ips);
> +
> +	if (!base)
> +		dev_notice(dev_sysmmu[ips], "%s is not handled.\n",
> +						sysmmu_fault_name[itype]);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (is_sysmmu_active(ips)) {
> +		sysmmu_block(ips);
> +		__sysmmu_set_ptbase(ips, pgd);
> +		sysmmu_unblock(ips);
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "disabled. Skipping initializing
page"
> +				" table base...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd)
> +{
> +	if (set_sysmmu_active(ips)) {
> +		sysmmu_clk_enable(ips);
> +
> +		__sysmmu_set_ptbase(ips, pgd);
> +
> +		__raw_writel(CTRL_ENABLE, sysmmusfrs[ips] +
> S5P_MMU_CTRL);
> +
> +		set_sysmmu_active(ips);
> +		dev_dbg(dev_sysmmu[ips], "enabled.\n");
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "already enabled."
> +					" Skipping initialization...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_disable(sysmmu_ips ips)
> +{
> +	if (set_sysmmu_inactive(ips)) {
> +		__raw_writel(CTRL_DISABLE, sysmmusfrs[ips] +
> S5P_MMU_CTRL);
> +		sysmmu_clk_disable(ips);
> +		dev_dbg(dev_sysmmu[ips], "disabled.\n");
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "already disabled."
> +					" Skipping deinitialization...\n");
> +	}
> +}
> +
> +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips)
> +{
> +	if (is_sysmmu_active(ips)) {
> +		sysmmu_block(ips);
> +		__sysmmu_tlb_invalidate(ips);
> +		sysmmu_unblock(ips);
> +	} else {
> +		dev_dbg(dev_sysmmu[ips], "is disabled. "
> +			"Skipping invalidating TLB...\n");
> +	}
> +}
> +
> +static int s5p_sysmmu_probe(struct platform_device *pdev)
> +{
> +	sysmmu_ips id;
> +	struct resource *res, *ioarea;
> +	int ret;
> +	int irq;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to get resource.");
> +		return -ENOENT;
> +	}
> +
> +	id = (sysmmu_ips)pdev->id;
> +
> +	if (id >= S5P_SYSMMU_TOTAL_IPNUM) {
> +		dev_err(&pdev->dev, "Unknown System MMU ID %d.", id);
> +		return -ENOENT;
> +	}
> +
> +	ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
> +	if (ioarea == NULL) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +					"failed to request memory region.");
> +		return -ENOMEM;
> +	}
> +
> +	sysmmusfrs[id] = ioremap(res->start, resource_size(res));
> +	if (!sysmmusfrs[id]) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to call
ioremap().");
> +		ret = -ENOENT;
> +		goto err_ioremap;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq <= 0) {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to get irq
resource.");
> +		ret = irq;
> +		goto err_irq;
> +	}
> +
> +	if (request_irq(irq, s5p_sysmmu_irq, 0, dev_name(&pdev->dev),
> +								(void *)id))
> {
> +		dev_err(&pdev->dev, "Failed probing system MMU: "
> +						"failed to request irq.");
> +		ret = -ENOENT;
> +		goto err_irq;
> +	}
> +
> +	dev_sysmmu[id] = &pdev->dev;
> +
> +	dev_dbg(&pdev->dev, "Probing system MMU succeeded.");
> +	return 0;
> +
> +err_irq:
> +	iounmap(sysmmusfrs[id]);
> +err_ioremap:
> +	release_resource(ioarea);
> +	kfree(ioarea);
> +	dev_err(&pdev->dev, "Probing system MMU failed.");
> +	return ret;
> +}
> +
> +static int s5p_sysmmu_remove(struct platform_device *pdev)
> +{
> +	return 0;
> +}
> +static int s5p_sysmmu_runtime_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int s5p_sysmmu_runtime_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops s5p_sysmmu_pm_ops = {
> +	.runtime_suspend	= s5p_sysmmu_runtime_suspend,
> +	.runtime_resume		= s5p_sysmmu_runtime_resume,
> +};
> +
> +static struct platform_driver s5p_sysmmu_driver = {
> +	.probe		= s5p_sysmmu_probe,
> +	.remove		= s5p_sysmmu_remove,
> +	.driver		= {
> +		.owner		= THIS_MODULE,
> +		.name		= "s5p-sysmmu",
> +		.pm		= &s5p_sysmmu_pm_ops,
> +	}
> +};
> +
> +static int __init s5p_sysmmu_init(void)
> +{
> +	return platform_driver_register(&s5p_sysmmu_driver);
> +}
> +arch_initcall(s5p_sysmmu_init);
> diff --git a/drivers/iommu/exynos4_sysmmu.h
b/drivers/iommu/exynos4_sysmmu.h
> new file mode 100644
> index 0000000..f739240
> --- /dev/null
> +++ b/drivers/iommu/exynos4_sysmmu.h
> @@ -0,0 +1,18 @@
> +/* linux/drivers/iommu/exynos4_sysmmu.h
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * Samsung System MMU driver for Exynos4 platforms
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <mach/sysmmu.h>
> +
> +void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd);
> +void s5p_sysmmu_disable(sysmmu_ips ips);
> +void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd);
> +void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips);
> diff --git a/drivers/iommu/exynos_iommu.c b/drivers/iommu/exynos_iommu.c
> new file mode 100644
> index 0000000..cbb94df
> --- /dev/null
> +++ b/drivers/iommu/exynos_iommu.c
> @@ -0,0 +1,496 @@
> +/* linux/drivers/iommu/exynos_iommu.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/mm.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/iommu.h>
> +#include <linux/errno.h>
> +#include <linux/list.h>
> +
> +#include <asm/cacheflush.h>
> +
> +#include "exynos4_sysmmu.h"
> +
> +#ifdef CONFIG_S5P_SYSTEM_MMU_DEBUG
> +#define DEBUG /* for dev_dbg() */
> +#endif
> +
> +/* We does not consider super section mapping (16MB) */
> +#define S5P_SPAGE_SHIFT		12
> +#define S5P_LPAGE_SHIFT		16
> +#define S5P_SECTION_SHIFT	20
> +
> +#define S5P_SPAGE_SIZE		(1 << S5P_SPAGE_SHIFT)
> +#define S5P_LPAGE_SIZE		(1 << S5P_LPAGE_SHIFT)
> +#define S5P_SECTION_SIZE	(1 << S5P_SECTION_SHIFT)
> +
> +#define S5P_SPAGE_MASK		(~(S5P_SPAGE_SIZE - 1))
> +#define S5P_LPAGE_MASK		(~(S5P_LPAGE_SIZE - 1))
> +#define S5P_SECTION_MASK	(~(S5P_SECTION_SIZE - 1))
> +
> +#define S5P_SPAGE_ORDER		(S5P_SPAGE_SHIFT - PAGE_SHIFT)
> +#define S5P_LPAGE_ORDER		(S5P_LPAGE_SHIFT -
> S5P_SPAGE_SHIFT)
> +#define S5P_SECTION_ORDER	(S5P_SECTION_SHIFT - S5P_SPAGE_SHIFT)
> +
> +#define S5P_LV1TABLE_ENTRIES	(1 << (BITS_PER_LONG -
> S5P_SECTION_SHIFT))
> +
> +#define S5P_LV2TABLE_ENTRIES	(1 << S5P_SECTION_ORDER)
> +#define S5P_LV2TABLE_SIZE	(S5P_LV2TABLE_ENTRIES * sizeof(long))
> +#define S5P_LV2TABLE_MASK	(~(S5P_LV2TABLE_SIZE - 1)) /* 0xFFFFFC00 */
> +
> +#define S5P_SECTION_LV1_ENTRY(entry)	((entry & 0x40003) == 2)
> +#define S5P_SUPSECT_LV1_ENTRY(entry)	((entry & 0x40003) ==
> 0x40002)
> +#define S5P_PAGE_LV1_ENTRY(entry)	((entry & 3) == 1)
> +#define S5P_FAULT_LV1_ENTRY(entry) (((entry & 3) == 0) || (entry & 3) ==
3)
> +
> +#define S5P_LPAGE_LV2_ENTRY(entry)	((entry & 3) == 1)
> +#define S5P_SPAGE_LV2_ENTRY(entry)	((entry & 2) == 2)
> +#define S5P_FAULT_LV2_ENTRY(entry)	((entry & 3) == 0)
> +
> +#define MAKE_FAULT_ENTRY(entry)		do { entry = 0; } while (0)
> +#define MAKE_SECTION_ENTRY(entry, pa)	do { entry = pa | 2; } while
(0)
> +#define MAKE_SUPSECT_ENTRY(entry, pa)	do { entry = pa | 0x40002; }
> while (0)
> +#define MAKE_LV2TABLE_ENTRY(entry, pa)	do { entry = pa | 1; } while
(0)
> +
> +#define MAKE_LPAGE_ENTRY(entry, pa)	do { entry = pa | 1; } while (0)
> +#define MAKE_SPAGE_ENTRY(entry, pa)	do { entry = pa | 3; } while (0)
> +
> +#define GET_LV2ENTRY(entry, iova) (\
> +	(unsigned long *)phys_to_virt(entry & S5P_LV2TABLE_MASK) +\
> +	((iova & (~S5P_SECTION_MASK)) >> S5P_SPAGE_SHIFT))
> +
> +struct s5p_iommu_domain {
> +	struct device *dev;
> +	sysmmu_ips ips;
> +	unsigned long *pgtable;
> +};
> +
> +struct s5p_finddev_struct {
> +	char *name;
> +	int id;
> +};
> +/* Shared page table implementation */
> +static unsigned long *pgtable;
> +
> +static struct s5p_finddev_struct pdev_names[S5P_SYSMMU_TOTAL_IPNUM] = {
> +	{"s3c-pl330", 0},
> +	{"s5p-sss", -1},
> +	{"s3c-fimc", 0},
> +	{"s3c-fimc", 1},
> +	{"s3c-fimc", 2},
> +	{"s3c-fimc", 3},
> +	{"s5p-jpeg", -1},
> +	{"s5p-fb", 0},
> +	{"s5p-fb", 1},
> +	{"s5p-pcie", -1},
> +	{"s5p-fimg2d", -1},
> +	{"s5p-rotator", -1},
> +	{"s5p-mdma2", -1},
> +	{"s5p-mixer", -1},
> +	{"mfc", -1}, /* SYSMMU_MFC_L */
> +};
> +/* slab cache for level 2 page tables */
> +static struct kmem_cache *l2table_cachep;
> +
> +LIST_HEAD(dev_lookup_list);
> +
> +struct dev_dom {
> +	struct list_head node;
> +	struct s5p_iommu_domain *dom;
> +	struct device *dev;
> +};
> +
> +static inline struct dev_dom *lookup_dev(struct device *dev)
> +{
> +	struct list_head *pos;
> +	struct dev_dom *rel = NULL;
> +
> +	list_for_each(pos, &dev_lookup_list) {
> +		rel = list_entry(pos, struct dev_dom, node);
> +		if (rel->dev == dev)
> +			return rel;
> +	}
> +
> +	return NULL;
> +}
> +
> +static inline int bind_dev(struct s5p_iommu_domain *dom, struct device
*dev)
> +{
> +	struct dev_dom *rel;
> +
> +	rel = kmalloc(sizeof(*rel), GFP_KERNEL);
> +	if (!rel)
> +		return -ENOMEM;
> +
> +	if (!lookup_dev(dev)) {
> +		rel->dom = dom;
> +		rel->dev = dev;
> +		list_add(&rel->node, &dev_lookup_list);
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void unbind_dev(struct device *dev)
> +{
> +	struct dev_dom *rel;
> +
> +	rel = lookup_dev(dev);
> +	if (rel)
> +		list_del(&rel->node);
> +}
> +
> +static inline void pgtable_flush(void *vastart, void *vaend)
> +{
> +	dmac_flush_range(vastart, vaend);
> +	outer_flush_range(virt_to_phys(vastart),
> +				virt_to_phys(vaend));
> +}
> +
> +static int s5p_iommu_domain_init(struct iommu_domain *domain)
> +{
> +	struct s5p_iommu_domain *priv;
> +
> +	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
> +		(S5P_LV1TABLE_ENTRIES * sizeof(unsigned long)) >>
> PAGE_SHIFT);
> +	if (!priv->pgtable) {
> +		kfree(priv);
> +		return -ENOMEM;
> +	}
> +
> +	memset(pgtable, 0, S5P_LV1TABLE_ENTRIES * sizeof(unsigned long));
> +	pgtable_flush(pgtable, pgtable + S5P_LV1TABLE_ENTRIES);
> +
> +	domain->priv = priv;
> +	return 0;
> +}
> +
> +static void s5p_iommu_domain_destroy(struct iommu_domain *domain)
> +{
> +	struct s5p_iommu_domain *priv = domain->priv;
> +
> +	free_pages((unsigned long)priv->pgtable, 2);
> +	kfree(domain->priv);
> +	domain->priv = NULL;
> +}
> +
> +static sysmmu_ips get_sysmmu_id(struct platform_device *pdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++)
> +		if ((strcmp(pdev->name, pdev_names[i].name) == 0) &&
> +				(pdev->id == pdev_names[i].id))
> +			break;
> +
> +	return (sysmmu_ips)i;
> +}
> +
> +static int s5p_iommu_attach_device(struct iommu_domain *domain,
> +				   struct device *dev)
> +{
> +	sysmmu_ips ips;
> +	int ret;
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	struct platform_device *pdev =
> +				container_of(dev, struct platform_device,
dev);
> +
> +	ips = get_sysmmu_id(pdev);
> +	if (ips == SYSMMU_NONE)
> +		return -ENODEV;
> +
> +	s5p_domain->ips = ips;
> +
> +	s5p_sysmmu_enable(ips, (unsigned long)s5p_domain->pgtable);
> +	if (ips == SYSMMU_MFC_L)
> +		s5p_sysmmu_enable(ips + 1, (unsigned long)s5p_domain-
> >pgtable);
> +
> +	s5p_domain->dev = dev;
> +
> +	ret = bind_dev(s5p_domain, dev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static void s5p_iommu_detach_device(struct iommu_domain *domain,
> +				    struct device *dev)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +
> +	if (s5p_domain->dev == dev) {
> +		s5p_sysmmu_disable(s5p_domain->ips);
> +		if (s5p_domain->ips == SYSMMU_MFC_L)
> +			s5p_sysmmu_disable(s5p_domain->ips + 1);
> +		unbind_dev(dev);
> +	}
> +}
> +
> +static bool section_available(struct iommu_domain *domain,
> +			      unsigned long *lv1entry)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +
> +	if (S5P_SECTION_LV1_ENTRY(*lv1entry)) {
> +		dev_err(s5p_domain->dev,
> +				"1MB entry alread exists at 0x%08x\n",
> +				(lv1entry - s5p_domain->pgtable) * SZ_1M);
> +		return false;
> +	}
> +
> +	if (S5P_PAGE_LV1_ENTRY(*lv1entry)) {
> +		unsigned long *lv2end, *lv2base;
> +
> +		lv2base = phys_to_virt(*lv1entry & S5P_LV2TABLE_MASK);
> +		lv2end = lv2base + S5P_LV2TABLE_ENTRIES;
> +		while (lv2base != lv2end) {
> +			if (S5P_FAULT_LV2_ENTRY(*lv2base)) {
> +				dev_err(s5p_domain->dev,
> +					"Failed to free L2 page table for"
> +					"section mapping.\n");
> +				return false;
> +			}
> +			lv2base++;
> +		}
> +
> +		kmem_cache_free(l2table_cachep,
> +				phys_to_virt(*lv1entry &
> S5P_LV2TABLE_MASK));
> +
> +		MAKE_FAULT_ENTRY(*lv1entry);
> +	}
> +
> +	return true;
> +}
> +
> +static bool write_lpage(unsigned long *head_entry, unsigned long
phys_addr)
> +{
> +	unsigned long *entry, *end;
> +
> +	entry = head_entry;
> +	end = entry + (1 << S5P_LPAGE_ORDER);
> +
> +	while (entry != end) {
> +		if (!S5P_FAULT_LV2_ENTRY(*entry))
> +			break;
> +
> +		MAKE_LPAGE_ENTRY(*entry, phys_addr);
> +
> +		entry++;
> +	}
> +
> +	if (entry != end) {
> +		end = entry;
> +		while (entry != head_entry)
> +			MAKE_FAULT_ENTRY(*(--entry));
> +
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +int s5p_iommu_map(struct iommu_domain *domain, unsigned long iova,
> +			 phys_addr_t paddr, int gfp_order, int prot)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *start_entry, *entry, *end_entry;
> +	int num_entry;
> +
> +	BUG_ON(s5p_domain->dev == NULL);
> +
> +	start_entry = entry = s5p_domain->pgtable + (iova >>
> S5P_SECTION_SHIFT);
> +
> +	if (gfp_order >= S5P_SECTION_ORDER) {
> +		BUG_ON((paddr | iova) & ~S5P_SECTION_MASK);
> +		/* 1MiB mapping */
> +
> +		num_entry = 1 << (gfp_order - S5P_SECTION_ORDER);
> +		end_entry = entry + num_entry;
> +
> +		while (entry != end_entry) {
> +			if (!section_available(domain, entry))
> +				break;
> +
> +			MAKE_SECTION_ENTRY(*entry, paddr);
> +
> +			paddr += S5P_SECTION_SIZE;
> +			entry++;
> +		}
> +
> +		if (entry != end_entry)
> +			goto mapping_error;
> +
> +		return 0;
> +	}
> +
> +	if (S5P_FAULT_LV1_ENTRY(*entry)) {
> +		unsigned long *l2table;
> +
> +		l2table = kmem_cache_zalloc(l2table_cachep, GFP_KERNEL);
> +		if (!l2table)
> +			return -ENOMEM;
> +
> +		pgtable_flush(entry, entry + S5P_LV2TABLE_ENTRIES);
> +
> +		MAKE_LV2TABLE_ENTRY(*entry, virt_to_phys(l2table));
> +		pgtable_flush(entry, entry + 1);
> +	}
> +
> +	/* 'entry' points level 2 entries, hereafter */
> +	entry = GET_LV2ENTRY(*entry, iova);
> +
> +	start_entry = entry;
> +	num_entry = 1 << gfp_order;
> +	end_entry = entry + num_entry;
> +
> +	if (gfp_order >= S5P_LPAGE_ORDER) {
> +		/* large page(64KiB) mapping */
> +		BUG_ON((paddr | iova) & ~S5P_LPAGE_MASK);
> +
> +		while (entry != end_entry) {
> +			if (!write_lpage(entry, paddr)) {
> +				dev_err(s5p_domain->dev,
> +					"Failed to allocate large page
entry.\n"
> +					);
> +				break;
> +			}
> +
> +			paddr += S5P_LPAGE_SIZE;
> +			entry += (1 << S5P_LPAGE_ORDER);
> +		}
> +
> +		if (entry != end_entry) {
> +			entry -= 1 << S5P_LPAGE_ORDER;
> +			goto mapping_error;
> +		}
> +
> +		return 0;
> +	}
> +
> +	/* page (4KiB) mapping */
> +	while (entry != end_entry && !S5P_FAULT_LV2_ENTRY(*entry)) {
> +
> +		MAKE_SPAGE_ENTRY(*entry, paddr);
> +
> +		entry++;
> +		paddr += S5P_SPAGE_SIZE;
> +	}
> +
> +	if (entry != end_entry) {
> +		dev_err(s5p_domain->dev,
> +			"Failed to allocate small page entry.\n");
> +		goto mapping_error;
> +	}
> +
> +	pgtable_flush(start_entry, entry);
> +mapping_error:
> +	if (entry != end_entry) {
> +		while (entry != start_entry)
> +			MAKE_FAULT_ENTRY(*(--entry));
> +		return -EADDRINUSE;
> +	}
> +
> +	return 0;
> +}
> +
> +int s5p_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
> +			   int gfp_order)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *entry;
> +	int num_entry;
> +
> +	BUG_ON(s5p_domain->dev == NULL);
> +
> +	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
> +
> +	if (gfp_order >= S5P_SECTION_ORDER)
> +		gfp_order -= S5P_SECTION_ORDER;
> +	else
> +		entry = GET_LV2ENTRY(*entry, iova);
> +
> +	BUG_ON(S5P_LPAGE_LV2_ENTRY(*entry) && (gfp_order <
> S5P_LPAGE_ORDER));
> +
> +	num_entry = 1 << gfp_order;
> +
> +	while (num_entry-- > 0) {
> +		MAKE_FAULT_ENTRY(*entry);
> +		entry++;
> +	}
> +
> +	s5p_sysmmu_tlb_invalidate(s5p_domain->ips);
> +	if (s5p_domain->ips == SYSMMU_MFC_L)
> +		s5p_sysmmu_tlb_invalidate(s5p_domain->ips + 1);
> +	return 0;
> +}
> +
> +phys_addr_t s5p_iommu_iova_to_phys(struct iommu_domain *domain,
> +					  unsigned long iova)
> +{
> +	struct s5p_iommu_domain *s5p_domain = domain->priv;
> +	unsigned long *entry;
> +	unsigned long offset;
> +
> +	entry = s5p_domain->pgtable + (iova >> S5P_SECTION_SHIFT);
> +
> +	if (S5P_FAULT_LV1_ENTRY(*entry))
> +		return 0;
> +
> +	offset = iova & ~S5P_SECTION_MASK;
> +
> +	if (S5P_SECTION_LV1_ENTRY(*entry))
> +		return (*entry & S5P_SECTION_MASK) + offset;
> +
> +	entry = GET_LV2ENTRY(*entry, iova);
> +
> +	if (S5P_SPAGE_LV2_ENTRY(*entry))
> +		return (*entry & S5P_SPAGE_MASK) + (iova &
> ~S5P_SPAGE_MASK);
> +
> +	if (S5P_LPAGE_LV2_ENTRY(*entry))
> +		return (*entry & S5P_LPAGE_MASK) + (iova &
> ~S5P_LPAGE_MASK);
> +
> +	return 0;
> +}
> +
> +static int s5p_iommu_domain_has_cap(struct iommu_domain *domain,
> +				    unsigned long cap)
> +{
> +	return 0;
> +}
> +
> +static struct iommu_ops s5p_iommu_ops = {
> +	.domain_init = &s5p_iommu_domain_init,
> +	.domain_destroy = &s5p_iommu_domain_destroy,
> +	.attach_dev = &s5p_iommu_attach_device,
> +	.detach_dev = &s5p_iommu_detach_device,
> +	.map = &s5p_iommu_map,
> +	.unmap = &s5p_iommu_unmap,
> +	.iova_to_phys = &s5p_iommu_iova_to_phys,
> +	.domain_has_cap = &s5p_iommu_domain_has_cap
> +};
> +
> +static int __init s5p_iommu_init(void)
> +{
> +	l2table_cachep = kmem_cache_create("SysMMU Lv2 Tables",
> +				S5P_LV2TABLE_SIZE, S5P_LV2TABLE_SIZE,
> 0, NULL);
> +	if (!l2table_cachep)
> +		return -ENOMEM;
> +
> +	register_iommu(&s5p_iommu_ops);
> +	return 0;
> +}
> +arch_initcall(s5p_iommu_init);
> --
> 1.7.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2011-08-31  1:18 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-04  1:41 [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver KyongHo Cho
2011-07-04  1:41 ` KyongHo Cho
2011-07-04  1:41 ` [PATCH 1/6] ARM: EXYNOS4: SYSMMU: Remove SYSMMU_MDMA2 KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-07-04  1:41 ` [PATCH 2/6] ARM: EXYNOS4: SYSMMU: Enable clock gating for System MMU of SSS KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-07-04  1:41 ` [PATCH 3/6] ARM: EXYNOS4: SYSMMU: Enhancement on device definition KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-07-04  1:41 ` [PATCH 4/6] ARM: EXYNOS4: SYSMMU: add devname in SYSMMU clock to support clkdev KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-07-04  1:41 ` [PATCH 5/6] ARM: EXYNOS4: SYSMMU: Add SYSMMU_NONE KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-07-04  1:41 ` [PATCH 6/6] ARM: EXYNOS4: SYSMMU: Move clock gating functions to SYSMMU device driver KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-07-04  1:41 ` [PATCH] ARM: EXYNOS4: iommu: Add IOMMU API and moved to drivers/iommu KyongHo Cho
2011-07-04  1:41   ` KyongHo Cho
2011-08-31  1:18   ` Kukjin Kim
2011-08-31  1:18     ` Kukjin Kim
2011-08-31  1:18     ` Kukjin Kim
2011-07-04  6:47 ` [PATCH 0/6+1] ARM: EXYNOS4: SYSMMU: Improvements on SYSMMU driver Marek Szyprowski
2011-07-04  6:47   ` Marek Szyprowski
2011-07-04 23:38   ` KyongHo Cho
2011-07-04 23:38     ` KyongHo Cho
2011-07-05 11:14     ` Marek Szyprowski
2011-07-05 11:14       ` Marek Szyprowski
2011-07-05 23:51       ` KyongHo Cho
2011-07-05 23:51         ` KyongHo Cho

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.