Hello Everyone,
This is yet another attempt to finally make Exynos SYSMMU driver fully integrated with DMA-mapping subsystem.
Previous approach is available here: https://lkml.org/lkml/2014/8/5/183
I meantime, there have been a discussion about the way the iommu driver should be integrated with dma-mapping subsystem, which resulted in "[RFC PATCH v3 0/7] Introduce automatic DMA configuration for IOMMU masters" patches prepared by Will Deacon: http://www.spinics.net/lists/arm-kernel/msg362076.html Those patches removed the need to use bus-specific notifiers for initialization.
Main changes since previous version of my patches:
1. rebased onto "[RFC PATCH v3 0/7] Introduce automatic DMA configuration for IOMMU masters" patches, changed initialization from bus notifiers to DT related callbacks
2. removed support for separate IO address spaces - this will be discussed separately after the basic support gets merged
3. removed support for power domain notifier-based runtime power management - this also will be discussed separately later
I hope that the driver with above changes will be easier to be merged to v3.18.
Best regards Marek Szyprowski Samsung R&D Institute Poland
Patch summary:
Marek Szyprowski (18): arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size arm: exynos: bind power domains earlier, on device creation drm: exynos: detach from default dma-mapping domain on init clk: exynos: add missing smmu_g2d clock and update comments ARM: DTS: Exynos4: add System MMU nodes iommu: exynos: don't read version register on every tlb operation iommu: exynos: remove unused functions iommu: exynos: remove useless spinlock iommu: exynos: refactor function parameters to simplify code iommu: exynos: remove unused functions, part 2 iommu: exynos: remove useless device_add/remove callbacks iommu: exynos: add support for binding more than one sysmmu to master device iommu: exynos: add support for runtime_pm iommu: exynos: rename variables to reflect their purpose iommu: exynos: document internal structures iommu: exynos: remove excessive includes and sort others alphabetically iommu: exynos: init from dt-specific callback instead of initcall iommu: exynos: add callback for initializing devices from device tree
arch/arm/boot/dts/exynos4.dtsi | 117 +++++++ arch/arm/boot/dts/exynos4210.dtsi | 23 ++ arch/arm/boot/dts/exynos4x12.dtsi | 82 +++++ arch/arm/mach-exynos/pm_domains.c | 12 +- arch/arm/mm/dma-mapping.c | 16 + drivers/clk/samsung/clk-exynos4.c | 1 + drivers/gpu/drm/exynos/exynos_drm_iommu.c | 3 + drivers/iommu/exynos-iommu.c | 494 ++++++++++++++---------------- include/dt-bindings/clock/exynos4.h | 10 +- 9 files changed, 483 insertions(+), 275 deletions(-)
If device has no max_seg_size set, we assume that there is no limit and force it to DMA_BIT_MASK(32) to always use contiguous mappings in DMA address space.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bcd5f836f27e..84705e24571b 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2050,6 +2050,22 @@ int arm_iommu_attach_device(struct device *dev, { int err;
+ /* + * if device has no max_seg_size set, we assume that there is no limit + * and force it to DMA_BIT_MASK(32) to always use contiguous mappings + * in DMA address space + */ + if (!dev->dma_parms) { + dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL); + if (!dev->dma_parms) + return -ENOMEM; + } + if (!dev->dma_parms->max_segment_size) { + err = dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + if (err) + return err; + } + err = iommu_attach_device(mapping->domain, dev); if (err) return err;
Hi Marek,
On Tue, Sep 16, 2014 at 12:54:28PM +0100, Marek Szyprowski wrote:
If device has no max_seg_size set, we assume that there is no limit and force it to DMA_BIT_MASK(32) to always use contiguous mappings in DMA address space.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bcd5f836f27e..84705e24571b 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2050,6 +2050,22 @@ int arm_iommu_attach_device(struct device *dev, { int err;
- /*
* if device has no max_seg_size set, we assume that there is no limit
* and force it to DMA_BIT_MASK(32) to always use contiguous mappings
* in DMA address space
*/
- if (!dev->dma_parms) {
dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
if (!dev->dma_parms)
return -ENOMEM;
- }
- if (!dev->dma_parms->max_segment_size) {
err = dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
Would it make more sense to base this default value off the dma_mask? In my IOMMU series, of_dma_configure passes back a size parameter to arch_setup_dma_ops which is calculated from the dma-ranges property or the coherent dma mask if the ranges property is absent, so maybe we should set this there too?
Will
Hello,
On 2014-09-24 19:06, Will Deacon wrote:
Hi Marek,
On Tue, Sep 16, 2014 at 12:54:28PM +0100, Marek Szyprowski wrote:
If device has no max_seg_size set, we assume that there is no limit and force it to DMA_BIT_MASK(32) to always use contiguous mappings in DMA address space.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bcd5f836f27e..84705e24571b 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2050,6 +2050,22 @@ int arm_iommu_attach_device(struct device *dev, { int err;
- /*
* if device has no max_seg_size set, we assume that there is no limit
* and force it to DMA_BIT_MASK(32) to always use contiguous mappings
* in DMA address space
*/
- if (!dev->dma_parms) {
dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
if (!dev->dma_parms)
return -ENOMEM;
- }
- if (!dev->dma_parms->max_segment_size) {
err = dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
Would it make more sense to base this default value off the dma_mask? In my IOMMU series, of_dma_configure passes back a size parameter to arch_setup_dma_ops which is calculated from the dma-ranges property or the coherent dma mask if the ranges property is absent, so maybe we should set this there too?
Right, good idea. This patch predates your arch_setup_dma_ops changes, so I had to use something. The value taken from dma_mask is much better than hardcoded DMA_BIT_MASK(32). Do you want to include an updated patch in next version of your arch_setup_dma_ops patchset?
Best regards
On Thu, Sep 25, 2014 at 11:43:35AM +0100, Marek Szyprowski wrote:
On 2014-09-24 19:06, Will Deacon wrote:
On Tue, Sep 16, 2014 at 12:54:28PM +0100, Marek Szyprowski wrote:
If device has no max_seg_size set, we assume that there is no limit and force it to DMA_BIT_MASK(32) to always use contiguous mappings in DMA address space.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index bcd5f836f27e..84705e24571b 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -2050,6 +2050,22 @@ int arm_iommu_attach_device(struct device *dev, { int err;
- /*
* if device has no max_seg_size set, we assume that there is no limit
* and force it to DMA_BIT_MASK(32) to always use contiguous mappings
* in DMA address space
*/
- if (!dev->dma_parms) {
dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
if (!dev->dma_parms)
return -ENOMEM;
- }
- if (!dev->dma_parms->max_segment_size) {
err = dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
Would it make more sense to base this default value off the dma_mask? In my IOMMU series, of_dma_configure passes back a size parameter to arch_setup_dma_ops which is calculated from the dma-ranges property or the coherent dma mask if the ranges property is absent, so maybe we should set this there too?
Right, good idea. This patch predates your arch_setup_dma_ops changes, so I had to use something. The value taken from dma_mask is much better than hardcoded DMA_BIT_MASK(32). Do you want to include an updated patch in next version of your arch_setup_dma_ops patchset?
Sure, I'll have a go at rolling that in.
Cheers,
Will
This patches change initialization time of power domain driver from client device driver bind to device creation. This lets other core drivers to register power domain notifiers before client driver is bound.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- arch/arm/mach-exynos/pm_domains.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index fd76e1b5a471..1d368a26528c 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -159,13 +159,13 @@ static int exynos_pm_notifier_call(struct notifier_block *nb, struct device *dev = data;
switch (event) { - case BUS_NOTIFY_BIND_DRIVER: + case BUS_NOTIFY_ADD_DEVICE: if (dev->of_node) exynos_read_domain_from_dt(dev);
break;
- case BUS_NOTIFY_UNBOUND_DRIVER: + case BUS_NOTIFY_DEL_DEVICE: exynos_remove_device_from_domain(dev);
break; @@ -177,6 +177,13 @@ static struct notifier_block platform_nb = { .notifier_call = exynos_pm_notifier_call, };
+static int exynos_pm_domain_add(struct device *dev, void *priv) +{ + if (dev->of_node) + exynos_read_domain_from_dt(dev); + return 0; +} + static __init int exynos4_pm_init_power_domain(void) { struct platform_device *pdev; @@ -236,6 +243,7 @@ no_clk: }
bus_register_notifier(&platform_bus_type, &platform_nb); + bus_for_each_dev(&platform_bus_type, NULL, NULL, exynos_pm_domain_add);
return 0; }
This patch adds code, which detach sub-device nodes from default iommu domain if such has been configured.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_iommu.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index b32b291f88ff..323601a52a25 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c @@ -100,6 +100,9 @@ int drm_iommu_attach_device(struct drm_device *drm_dev,
dma_set_max_seg_size(subdrv_dev, 0xffffffffu);
+ if (subdrv_dev->archdata.mapping) + arm_iommu_detach_device(subdrv_dev); + ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping); if (ret < 0) { DRM_DEBUG_KMS("failed iommu attach.\n");
This patch adds missing smmu_g2d clock implementation and updates comment about Exynos4 clocks from 278-282 range. Those clocks are available on all Exynos4 SoC series, so the misleading comment has been removed.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Tomasz Figa t.figa@samsung.com --- drivers/clk/samsung/clk-exynos4.c | 1 + include/dt-bindings/clock/exynos4.h | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index ac163d7f5bc3..12a7cc3b5953 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -1183,6 +1183,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { GATE(CLK_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13, CLK_IGNORE_UNUSED | CLK_GET_RATE_NOCACHE, 0), GATE(CLK_G2D, "g2d", "aclk200", GATE_IP_DMC, 23, 0, 0), + GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk200", GATE_IP_DMC, 24, 0, 0), GATE(CLK_TMU_APBIF, "tmu_apbif", "aclk100", E4X12_GATE_IP_PERIR, 17, 0, 0), }; diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index 459bd2bd411f..fb9816354079 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -115,11 +115,11 @@ #define CLK_SMMU_MFCR 275 #define CLK_G3D 276 #define CLK_G2D 277 -#define CLK_ROTATOR 278 /* Exynos4210 only */ -#define CLK_MDMA 279 /* Exynos4210 only */ -#define CLK_SMMU_G2D 280 /* Exynos4210 only */ -#define CLK_SMMU_ROTATOR 281 /* Exynos4210 only */ -#define CLK_SMMU_MDMA 282 /* Exynos4210 only */ +#define CLK_ROTATOR 278 +#define CLK_MDMA 279 +#define CLK_SMMU_G2D 280 +#define CLK_SMMU_ROTATOR 281 +#define CLK_SMMU_MDMA 282 #define CLK_FIMD0 283 #define CLK_MIE0 284 #define CLK_MDNIE0 285 /* Exynos4412 only */
On 16.09.2014 13:54, Marek Szyprowski wrote:
This patch adds missing smmu_g2d clock implementation and updates comment about Exynos4 clocks from 278-282 range. Those clocks are available on all Exynos4 SoC series, so the misleading comment has been removed.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Acked-by: Tomasz Figa t.figa@samsung.com
drivers/clk/samsung/clk-exynos4.c | 1 + include/dt-bindings/clock/exynos4.h | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-)
Applied for next.
Best regards, Tomasz
This patch adds System MMU nodes that are specific to Exynos4210/4x12 series.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- arch/arm/boot/dts/exynos4.dtsi | 117 ++++++++++++++++++++++++++++++++++++++ arch/arm/boot/dts/exynos4210.dtsi | 23 ++++++++ arch/arm/boot/dts/exynos4x12.dtsi | 82 ++++++++++++++++++++++++++ 3 files changed, 222 insertions(+)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index e0278ecbc816..bfc1bbbb9ec5 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -174,6 +174,7 @@ clock-names = "fimc", "sclk_fimc"; samsung,power-domain = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc0>; status = "disabled"; };
@@ -185,6 +186,7 @@ clock-names = "fimc", "sclk_fimc"; samsung,power-domain = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc1>; status = "disabled"; };
@@ -196,6 +198,7 @@ clock-names = "fimc", "sclk_fimc"; samsung,power-domain = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc2>; status = "disabled"; };
@@ -207,6 +210,7 @@ clock-names = "fimc", "sclk_fimc"; samsung,power-domain = <&pd_cam>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimc3>; status = "disabled"; };
@@ -395,6 +399,8 @@ clocks = <&clock CLK_MFC>; clock-names = "mfc"; status = "disabled"; + iommus = <&sysmmu_mfc_l>, <&sysmmu_mfc_r>; + iommu-names = "left", "right"; };
serial_0: serial@13800000 { @@ -643,6 +649,117 @@ clock-names = "sclk_fimd", "fimd"; samsung,power-domain = <&pd_lcd0>; samsung,sysreg = <&sys_reg>; + iommus = <&sysmmu_fimd0>; status = "disabled"; }; + + sysmmu_mfc_l: sysmmu@13620000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13620000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCL>, <&clock CLK_MFC>; + samsung,power-domain = <&pd_mfc>; + #iommu-cells = <0>; + }; + + sysmmu_mfc_r: sysmmu@13630000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x13630000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_MFCR>, <&clock CLK_MFC>; + samsung,power-domain = <&pd_mfc>; + #iommu-cells = <0>; + }; + + sysmmu_tv: sysmmu@12E20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12E20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_TV>, <&clock CLK_MIXER>; + samsung,power-domain = <&pd_tv>; + #iommu-cells = <0>; + }; + + sysmmu_fimc0: sysmmu@11A20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC0>, <&clock CLK_FIMC0>; + samsung,power-domain = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_fimc1: sysmmu@11A30000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A30000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 3>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC1>, <&clock CLK_FIMC1>; + samsung,power-domain = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_fimc2: sysmmu@11A40000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 4>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC2>, <&clock CLK_FIMC2>; + samsung,power-domain = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_fimc3: sysmmu@11A50000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A50000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 5>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMC3>, <&clock CLK_FIMC3>; + samsung,power-domain = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_jpeg: sysmmu@11A60000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11A60000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 6>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_JPEG>, <&clock CLK_JPEG>; + samsung,power-domain = <&pd_cam>; + #iommu-cells = <0>; + }; + + sysmmu_rotator: sysmmu@12A30000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12A30000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 0>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_ROTATOR>, <&clock CLK_ROTATOR>; + samsung,power-domain = <&pd_lcd0>; + #iommu-cells = <0>; + }; + + sysmmu_fimd0: sysmmu@11E20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x11E20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <5 2>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD0>, <&clock CLK_FIMD0>; + samsung,power-domain = <&pd_lcd0>; + #iommu-cells = <0>; + }; }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 807bb5bf91fc..9ae48c236a6b 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -142,6 +142,7 @@ interrupts = <0 89 0>; clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; clock-names = "sclk_fimg2d", "fimg2d"; + iommus = <&sysmmu_g2d>; status = "disabled"; };
@@ -175,4 +176,26 @@ samsung,lcd-wb; }; }; + + sysmmu_g2d: sysmmu@12A20000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12A20000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 7>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + samsung,power-domain = <&pd_lcd0>; + #iommu-cells = <0>; + }; + + sysmmu_fimd1: sysmmu@12220000 { + compatible = "samsung,exynos-sysmmu"; + interrupt-parent = <&combiner>; + reg = <0x12220000 0x1000>; + interrupts = <5 3>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_FIMD1>, <&clock CLK_FIMD1>; + samsung,power-domain = <&pd_lcd1>; + #iommu-cells = <0>; + }; }; diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi index 861bb919f6d3..cc97e18cebb8 100644 --- a/arch/arm/boot/dts/exynos4x12.dtsi +++ b/arch/arm/boot/dts/exynos4x12.dtsi @@ -148,6 +148,7 @@ interrupts = <0 89 0>; clocks = <&clock CLK_SCLK_FIMG2D>, <&clock CLK_G2D>; clock-names = "sclk_fimg2d", "fimg2d"; + iommus = <&sysmmu_g2d>; status = "disabled"; };
@@ -197,6 +198,7 @@ samsung,power-domain = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE0>; clock-names = "flite"; + iommus = <&sysmmu_fimc_lite0>; status = "disabled"; };
@@ -207,6 +209,7 @@ samsung,power-domain = <&pd_isp>; clocks = <&clock CLK_FIMC_LITE1>; clock-names = "flite"; + iommus = <&sysmmu_fimc_lite1>; status = "disabled"; };
@@ -235,6 +238,9 @@ "mcuispdiv1", "uart", "aclk200", "div_aclk200", "aclk400mcuisp", "div_aclk400mcuisp"; + iommus = <&sysmmu_fimc_isp>, <&sysmmu_fimc_drc>, + <&sysmmu_fimc_fd>, <&sysmmu_fimc_mcuctl>; + iommu-names = "isp", "drc", "fd", "mcuctl"; #address-cells = <1>; #size-cells = <1>; ranges; @@ -271,4 +277,80 @@ compatible = "samsung,exynos4x12-usb2-phy"; samsung,sysreg-phandle = <&sys_reg>; }; + + sysmmu_g2d: sysmmu@10A40000{ + compatible = "samsung,exynos-sysmmu"; + reg = <0x10A40000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <4 7>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_G2D>, <&clock CLK_G2D>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_isp: sysmmu@12260000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12260000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 2>; + samsung,power-domain = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_ISP>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_drc: sysmmu@12270000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x12270000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 3>; + samsung,power-domain = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_DRC>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_fd: sysmmu@122A0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x122A0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 4>; + samsung,power-domain = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_FD>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_mcuctl: sysmmu@122B0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x122B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 5>; + samsung,power-domain = <&pd_isp>; + clock-names = "sysmmu"; + clocks = <&clock CLK_SMMU_ISPCX>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite0: sysmmu@123B0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x123B0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 0>; + samsung,power-domain = <&pd_isp>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_LITE0>, <&clock CLK_FIMC_LITE0>; + #iommu-cells = <0>; + }; + + sysmmu_fimc_lite1: sysmmu@123C0000 { + compatible = "samsung,exynos-sysmmu"; + reg = <0x123C0000 0x1000>; + interrupt-parent = <&combiner>; + interrupts = <16 1>; + samsung,power-domain = <&pd_isp>; + clock-names = "sysmmu", "master"; + clocks = <&clock CLK_SMMU_LITE1>, <&clock CLK_FIMC_LITE1>; + #iommu-cells = <0>; + }; };
This patch removes reading of REG_MMU_VERSION register on every tlb operation and caches SYSMMU version in driver's internal data.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index d037e87a1fe5..73499b05e62e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -212,6 +212,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; phys_addr_t pgtable; + int version; };
static bool set_sysmmu_active(struct sysmmu_drvdata *data) @@ -238,11 +239,6 @@ static void sysmmu_unblock(void __iomem *sfrbase) __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL); }
-static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data) -{ - return MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); -} - static bool sysmmu_block(void __iomem *sfrbase) { int i = 120; @@ -402,7 +398,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) unsigned int cfg = CFG_LRU | CFG_QOS(15); unsigned int ver;
- ver = __raw_sysmmu_version(data); + ver = MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION)); if (MMU_MAJ_VER(ver) == 3) { if (MMU_MIN_VER(ver) >= 2) { cfg |= CFG_FLPDCACHE; @@ -416,6 +412,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) }
__raw_writel(cfg, data->sfrbase + REG_MMU_CFG); + data->version = ver; }
static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) @@ -525,7 +522,7 @@ static bool exynos_sysmmu_disable(struct device *dev) static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { - if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3)) + if (data->version == MAKE_MMU_VER(3, 3)) __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY); }
@@ -574,7 +571,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, * 1MB page can be cached in one of all sets. * 64KB page can be one of 16 consecutive sets. */ - if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2) + if (MMU_MAJ_VER(data->version) == 2) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64);
if (sysmmu_block(data->sfrbase)) {
This patch removes two unneeded functions, which are not a part of generic IOMMU API and were never used by any other driver.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 31 ------------------------------- 1 file changed, 31 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 73499b05e62e..91feeca56abc 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -490,13 +490,6 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, return ret; }
-int exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable) -{ - BUG_ON(!memblock_is_memory(pgtable)); - - return __exynos_sysmmu_enable(dev, pgtable, NULL); -} - static bool exynos_sysmmu_disable(struct device *dev) { unsigned long flags; @@ -588,30 +581,6 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, spin_unlock_irqrestore(&data->lock, flags); }
-void exynos_sysmmu_tlb_invalidate(struct device *dev) -{ - struct exynos_iommu_owner *owner = dev->archdata.iommu; - unsigned long flags; - struct sysmmu_drvdata *data; - - data = dev_get_drvdata(owner->sysmmu); - - spin_lock_irqsave(&data->lock, flags); - if (is_sysmmu_active(data)) { - if (!IS_ERR(data->clk_master)) - clk_enable(data->clk_master); - if (sysmmu_block(data->sfrbase)) { - __sysmmu_tlb_invalidate(data->sfrbase); - sysmmu_unblock(data->sfrbase); - } - if (!IS_ERR(data->clk_master)) - clk_disable(data->clk_master); - } else { - dev_dbg(dev, "disabled. Skipping TLB invalidation\n"); - } - spin_unlock_irqrestore(&data->lock, flags); -} - static int __init exynos_sysmmu_probe(struct platform_device *pdev) { int irq, ret;
This patch removes useless spinlocks and other unused members from struct exynos_iommu_owner. There is no point is protecting this structure by spinlock because content of this structure doesn't change and other structures have their own spinlocks.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 11 ----------- 1 file changed, 11 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 91feeca56abc..ae2703ed91d8 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -189,9 +189,6 @@ struct exynos_iommu_owner { struct list_head client; /* entry of exynos_iommu_domain.clients */ struct device *dev; struct device *sysmmu; - struct iommu_domain *domain; - void *vmm_data; /* IO virtual memory manager's data */ - spinlock_t lock; /* Lock to preserve consistency of System MMU */ };
struct exynos_iommu_domain { @@ -477,16 +474,12 @@ static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable,
BUG_ON(!has_sysmmu(dev));
- spin_lock_irqsave(&owner->lock, flags); - data = dev_get_drvdata(owner->sysmmu);
ret = __sysmmu_enable(data, pgtable, domain); if (ret >= 0) data->master = dev;
- spin_unlock_irqrestore(&owner->lock, flags); - return ret; }
@@ -499,16 +492,12 @@ static bool exynos_sysmmu_disable(struct device *dev)
BUG_ON(!has_sysmmu(dev));
- spin_lock_irqsave(&owner->lock, flags); - data = dev_get_drvdata(owner->sysmmu);
disabled = __sysmmu_disable(data); if (disabled) data->master = NULL;
- spin_unlock_irqrestore(&owner->lock, flags); - return disabled; }
This patch simplifies the code by: - refactoring function parameters from struct device pointer to direct pointer to struct sysmmu drvdata - moving list_head enteries from struct exynos_iommu_owner directly to struct sysmmu_drvdata
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 93 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 47 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ae2703ed91d8..ef30890f4069 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -186,8 +186,6 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
/* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct list_head client; /* entry of exynos_iommu_domain.clients */ - struct device *dev; struct device *sysmmu; };
@@ -208,6 +206,7 @@ struct sysmmu_drvdata { int activations; spinlock_t lock; struct iommu_domain *domain; + struct list_head domain_node; phys_addr_t pgtable; int version; }; @@ -508,12 +507,10 @@ static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY); }
-static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, +static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) { unsigned long flags; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data = dev_get_drvdata(owner->sysmmu);
if (!IS_ERR(data->clk_master)) clk_enable(data->clk_master); @@ -527,14 +524,10 @@ static void sysmmu_tlb_invalidate_flpdcache(struct device *dev, clk_disable(data->clk_master); }
-static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, - size_t size) +static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, + sysmmu_iova_t iova, size_t size) { - struct exynos_iommu_owner *owner = dev->archdata.iommu; unsigned long flags; - struct sysmmu_drvdata *data; - - data = dev_get_drvdata(owner->sysmmu);
spin_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { @@ -564,8 +557,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova, if (!IS_ERR(data->clk_master)) clk_disable(data->clk_master); } else { - dev_dbg(dev, "disabled. Skipping TLB invalidation @ %#x\n", - iova); + dev_dbg(data->master, + "disabled. Skipping TLB invalidation @ %#x\n", iova); } spin_unlock_irqrestore(&data->lock, flags); } @@ -704,7 +697,7 @@ err_pgtable: static void exynos_iommu_domain_destroy(struct iommu_domain *domain) { struct exynos_iommu_domain *priv = domain->priv; - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; unsigned long flags; int i;
@@ -712,14 +705,12 @@ static void exynos_iommu_domain_destroy(struct iommu_domain *domain)
spin_lock_irqsave(&priv->lock, flags);
- list_for_each_entry(owner, &priv->clients, client) { - while (!exynos_sysmmu_disable(owner->dev)) - ; /* until System MMU is actually disabled */ + list_for_each_entry(data, &priv->clients, domain_node) { + if (__sysmmu_disable(data)) + data->master = NULL; + list_del_init(&data->domain_node); }
- while (!list_empty(&priv->clients)) - list_del_init(priv->clients.next); - spin_unlock_irqrestore(&priv->lock, flags);
for (i = 0; i < NUM_LV1ENTRIES; i++) @@ -738,20 +729,26 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, { struct exynos_iommu_owner *owner = dev->archdata.iommu; struct exynos_iommu_domain *priv = domain->priv; + struct sysmmu_drvdata *data; phys_addr_t pagetable = virt_to_phys(priv->pgtable); unsigned long flags; - int ret; + int ret = -ENODEV;
- spin_lock_irqsave(&priv->lock, flags); + if (!has_sysmmu(dev)) + return -ENODEV;
- ret = __exynos_sysmmu_enable(dev, pagetable, domain); - if (ret == 0) { - list_add_tail(&owner->client, &priv->clients); - owner->domain = domain; + data = dev_get_drvdata(owner->sysmmu); + if (data) { + ret = __sysmmu_enable(data, pagetable, domain); + if (ret >= 0) { + data->master = dev; + + spin_lock_irqsave(&priv->lock, flags); + list_add_tail(&data->domain_node, &priv->clients); + spin_unlock_irqrestore(&priv->lock, flags); + } }
- spin_unlock_irqrestore(&priv->lock, flags); - if (ret < 0) { dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n", __func__, &pagetable); @@ -767,26 +764,29 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, static void exynos_iommu_detach_device(struct iommu_domain *domain, struct device *dev) { - struct exynos_iommu_owner *owner; struct exynos_iommu_domain *priv = domain->priv; phys_addr_t pagetable = virt_to_phys(priv->pgtable); + struct sysmmu_drvdata *data; unsigned long flags; + int found = 0;
- spin_lock_irqsave(&priv->lock, flags); + if (!has_sysmmu(dev)) + return;
- list_for_each_entry(owner, &priv->clients, client) { - if (owner == dev->archdata.iommu) { - if (exynos_sysmmu_disable(dev)) { - list_del_init(&owner->client); - owner->domain = NULL; + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(data, &priv->clients, domain_node) { + if (data->master == dev) { + if (__sysmmu_disable(data)) { + data->master = NULL; + list_del_init(&data->domain_node); } + found = true; break; } } - spin_unlock_irqrestore(&priv->lock, flags);
- if (owner == dev->archdata.iommu) + if (found) dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", __func__, &pagetable); else @@ -833,12 +833,11 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, * not currently mapped. */ if (need_flush_flpd_cache) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data;
spin_lock(&priv->lock); - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_flpdcache( - owner->dev, iova); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_flpdcache(data, iova); spin_unlock(&priv->lock); } } @@ -873,13 +872,13 @@ static int lv1set_section(struct exynos_iommu_domain *priv,
spin_lock(&priv->lock); if (lv1ent_page_zero(sent)) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; /* * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD * entry by speculative prefetch of SLPD which has no mapping. */ - list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_flpdcache(owner->dev, iova); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_flpdcache(data, iova); } spin_unlock(&priv->lock);
@@ -984,13 +983,13 @@ static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, sysmmu_iova_t iova, size_t size) { - struct exynos_iommu_owner *owner; + struct sysmmu_drvdata *data; unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
- list_for_each_entry(owner, &priv->clients, client) - sysmmu_tlb_invalidate_entry(owner->dev, iova, size); + list_for_each_entry(data, &priv->clients, domain_node) + sysmmu_tlb_invalidate_entry(data, iova, size);
spin_unlock_irqrestore(&priv->lock, flags); }
After refactoring functions to use pointer to struct sysmmu_drvdata directly, some functions became useless and thus never used, so remove them completely.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ef30890f4069..b271348a4ec1 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -457,49 +457,6 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, return ret; }
-/* __exynos_sysmmu_enable: Enables System MMU - * - * returns -error if an error occurred and System MMU is not enabled, - * 0 if the System MMU has been just enabled and 1 if System MMU was already - * enabled before. - */ -static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable, - struct iommu_domain *domain) -{ - int ret = 0; - unsigned long flags; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner->sysmmu); - - ret = __sysmmu_enable(data, pgtable, domain); - if (ret >= 0) - data->master = dev; - - return ret; -} - -static bool exynos_sysmmu_disable(struct device *dev) -{ - unsigned long flags; - bool disabled = true; - struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct sysmmu_drvdata *data; - - BUG_ON(!has_sysmmu(dev)); - - data = dev_get_drvdata(owner->sysmmu); - - disabled = __sysmmu_disable(data); - if (disabled) - data->master = NULL; - - return disabled; -} - static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, sysmmu_iova_t iova) {
The driver doesn't need to do anything important in device add/remove callbacks, because initialization will be done from device-tree specific callbacks added later. IOMMU groups created by current code were never used.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 28 ---------------------------- 1 file changed, 28 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b271348a4ec1..1b3f00726cd4 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1055,32 +1055,6 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; }
-static int exynos_iommu_add_device(struct device *dev) -{ - struct iommu_group *group; - int ret; - - group = iommu_group_get(dev); - - if (!group) { - group = iommu_group_alloc(); - if (IS_ERR(group)) { - dev_err(dev, "Failed to allocate IOMMU group\n"); - return PTR_ERR(group); - } - } - - ret = iommu_group_add_device(group, dev); - iommu_group_put(group); - - return ret; -} - -static void exynos_iommu_remove_device(struct device *dev) -{ - iommu_group_remove_device(dev); -} - static const struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1089,8 +1063,6 @@ static const struct iommu_ops exynos_iommu_ops = { .map = exynos_iommu_map, .unmap = exynos_iommu_unmap, .iova_to_phys = exynos_iommu_iova_to_phys, - .add_device = exynos_iommu_add_device, - .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, };
Le mardi 16 septembre 2014 à 13:54 +0200, Marek Szyprowski a écrit :
The driver doesn't need to do anything important in device add/remove callbacks, because initialization will be done from device-tree specific callbacks added later. IOMMU groups created by current code were never used.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com
The exyons iommu init fails if those are removed, that is it never reach init_done: 1. exynos_iommu_setup 2. - exynos_iommu_init 3. ---bus_set_iommu 4. ------ add_iommu_group
that is (4) add_iommu_group returns ENODEV to bus_set_iommu, the latter doing so to exynos_iommu_init. Which thus error out before the init_done is set to true.
Mind restoring device_add/remove is not that easy. Either I did not and modified add_iommu_group to return 0 if add_device was not defined. This works. Otherwise I did revert this patch but had to also move the iommu_init from iommu.c before of_iommu_init in arch/arm/kernel/setup.c (switching iommu_init to global namespace in the process). This to avoid use of not yet initialized mutex iommu_group_mutex and crash that follow suit. This mutex is used in iommu_group_add_device called by exynos_iommu_add_device.
The logs of the first issue, ie exynos_iommo_init and bus_set_iommu returns ENODEV
[ 0.602816] exynos_iommu_init: Failed to register exynos-iommu driver. [ 0.603358] platform 13620000.sysmmu: device is not dma coherent [ 0.603384] platform 13620000.sysmmu: device is not behind an iommu [ 0.605243] exynos-sysmmu 13620000.sysmmu: genpd_dev_pm_attach() failed to find PM domain: -2 [ 0.606399] exynos_iommu_init: Failed to register exynos-iommu driver. [ 0.607263] platform 13630000.sysmmu: device is not dma coherent [ 0.607290] platform 13630000.sysmmu: device is not behind an iommu [ 0.609111] exynos-sysmmu 13620000.sysmmu: genpd_dev_pm_attach() failed to find PM domain: -2 [ 0.609464] exynos-sysmmu 13620000.sysmmu: Unbalanced pm_runtime_enable! [ 0.609868] exynos-sysmmu 13630000.sysmmu: genpd_dev_pm_attach() failed to find PM domain: -2 (...) [ 0.946506] platform 10800000.g2d: device is not dma coherent [ 0.946553] platform 10800000.g2d: device is not behind an iommu (...)
Mind the power domain failure comes from me not finding a way to apply the power domain registration this early yet .
Best regards, Alban Broweys
drivers/iommu/exynos-iommu.c | 28 ---------------------------- 1 file changed, 28 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index b271348a4ec1..1b3f00726cd4 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1055,32 +1055,6 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, return phys; } -static int exynos_iommu_add_device(struct device *dev) -{
- struct iommu_group *group;
- int ret;
- group = iommu_group_get(dev);
- if (!group) {
group = iommu_group_alloc();
if (IS_ERR(group)) {
dev_err(dev, "Failed to allocate IOMMU group\n");
return PTR_ERR(group);
}
- }
- ret = iommu_group_add_device(group, dev);
- iommu_group_put(group);
- return ret;
-}
-static void exynos_iommu_remove_device(struct device *dev) -{
- iommu_group_remove_device(dev);
-}
static const struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1089,8 +1063,6 @@ static const struct iommu_ops exynos_iommu_ops = { .map = exynos_iommu_map, .unmap = exynos_iommu_unmap, .iova_to_phys = exynos_iommu_iova_to_phys,
- .add_device = exynos_iommu_add_device,
- .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
};
This patch adds support for assigning more than one SYSMMU controller to the master device. This has been achieved simply by chaning the struct device pointer in struct exynos_iommu_owner into the list of struct sysmmu_drvdata of all controllers assigned to the given master device.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 1b3f00726cd4..cf36cdecf335 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -186,7 +186,7 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
/* attached to dev.archdata.iommu of the master device */ struct exynos_iommu_owner { - struct device *sysmmu; + struct list_head clients; };
struct exynos_iommu_domain { @@ -207,6 +207,7 @@ struct sysmmu_drvdata { spinlock_t lock; struct iommu_domain *domain; struct list_head domain_node; + struct list_head owner_node; phys_addr_t pgtable; int version; }; @@ -694,8 +695,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return -ENODEV;
- data = dev_get_drvdata(owner->sysmmu); - if (data) { + list_for_each_entry(data, &owner->clients, owner_node) { ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; @@ -723,7 +723,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, { struct exynos_iommu_domain *priv = domain->priv; phys_addr_t pagetable = virt_to_phys(priv->pgtable); - struct sysmmu_drvdata *data; + struct sysmmu_drvdata *data, *next; unsigned long flags; int found = 0;
@@ -731,14 +731,13 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, return;
spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(data, &priv->clients, domain_node) { + list_for_each_entry_safe(data, next, &priv->clients, domain_node) { if (data->master == dev) { if (__sysmmu_disable(data)) { data->master = NULL; list_del_init(&data->domain_node); } found = true; - break; } } spin_unlock_irqrestore(&priv->lock, flags);
This patch fixes support for runtime power management for SYSMMU controllers, so they are enabled when master device is attached.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index cf36cdecf335..5a6d3d0af61e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -696,6 +696,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return -ENODEV;
list_for_each_entry(data, &owner->clients, owner_node) { + pm_runtime_get_sync(data->sysmmu); ret = __sysmmu_enable(data, pagetable, domain); if (ret >= 0) { data->master = dev; @@ -737,6 +738,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, data->master = NULL; list_del_init(&data->domain_node); } + pm_runtime_put(data->sysmmu); found = true; } }
This patch renames some variables to make the code easier to understand. 'domain' is replaced by 'iommu_domain' (more generic entity) and really meaning less 'priv' by 'domain' to reflect its purpose.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 191 ++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 94 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5a6d3d0af61e..ae61d0680b9a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -430,8 +430,8 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) clk_disable(data->clk_master); }
-static int __sysmmu_enable(struct sysmmu_drvdata *data, - phys_addr_t pgtable, struct iommu_domain *domain) +static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable, + struct iommu_domain *iommu_domain) { int ret = 0; unsigned long flags; @@ -439,7 +439,7 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, spin_lock_irqsave(&data->lock, flags); if (set_sysmmu_active(data)) { data->pgtable = pgtable; - data->domain = domain; + data->domain = iommu_domain;
__sysmmu_enable_nocount(data);
@@ -603,92 +603,93 @@ static inline void pgtable_flush(void *vastart, void *vaend) virt_to_phys(vaend)); }
-static int exynos_iommu_domain_init(struct iommu_domain *domain) +static int exynos_iommu_domain_init(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv; + struct exynos_iommu_domain *domain; int i;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) return -ENOMEM;
- priv->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); - if (!priv->pgtable) + domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2); + if (!domain->pgtable) goto err_pgtable;
- priv->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); - if (!priv->lv2entcnt) + domain->lv2entcnt = (short *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (!domain->lv2entcnt) goto err_counter;
/* w/a of System MMU v3.3 to prevent caching 1MiB mapping */ for (i = 0; i < NUM_LV1ENTRIES; i += 8) { - priv->pgtable[i + 0] = ZERO_LV2LINK; - priv->pgtable[i + 1] = ZERO_LV2LINK; - priv->pgtable[i + 2] = ZERO_LV2LINK; - priv->pgtable[i + 3] = ZERO_LV2LINK; - priv->pgtable[i + 4] = ZERO_LV2LINK; - priv->pgtable[i + 5] = ZERO_LV2LINK; - priv->pgtable[i + 6] = ZERO_LV2LINK; - priv->pgtable[i + 7] = ZERO_LV2LINK; + domain->pgtable[i + 0] = ZERO_LV2LINK; + domain->pgtable[i + 1] = ZERO_LV2LINK; + domain->pgtable[i + 2] = ZERO_LV2LINK; + domain->pgtable[i + 3] = ZERO_LV2LINK; + domain->pgtable[i + 4] = ZERO_LV2LINK; + domain->pgtable[i + 5] = ZERO_LV2LINK; + domain->pgtable[i + 6] = ZERO_LV2LINK; + domain->pgtable[i + 7] = ZERO_LV2LINK; }
- pgtable_flush(priv->pgtable, priv->pgtable + NUM_LV1ENTRIES); + pgtable_flush(domain->pgtable, domain->pgtable + NUM_LV1ENTRIES);
- spin_lock_init(&priv->lock); - spin_lock_init(&priv->pgtablelock); - INIT_LIST_HEAD(&priv->clients); + spin_lock_init(&domain->lock); + spin_lock_init(&domain->pgtablelock); + INIT_LIST_HEAD(&domain->clients);
- domain->geometry.aperture_start = 0; - domain->geometry.aperture_end = ~0UL; - domain->geometry.force_aperture = true; + iommu_domain->geometry.aperture_start = 0; + iommu_domain->geometry.aperture_end = ~0UL; + iommu_domain->geometry.force_aperture = true;
- domain->priv = priv; + iommu_domain->priv = domain; return 0;
err_counter: - free_pages((unsigned long)priv->pgtable, 2); + free_pages((unsigned long)domain->pgtable, 2); err_pgtable: - kfree(priv); + kfree(domain); return -ENOMEM; }
-static void exynos_iommu_domain_destroy(struct iommu_domain *domain) +static void exynos_iommu_domain_destroy(struct iommu_domain *iommu_domain) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; struct sysmmu_drvdata *data; unsigned long flags; int i;
- WARN_ON(!list_empty(&priv->clients)); + WARN_ON(!list_empty(&domain->clients));
- spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags);
- list_for_each_entry(data, &priv->clients, domain_node) { + list_for_each_entry(data, &domain->clients, domain_node) { if (__sysmmu_disable(data)) data->master = NULL; list_del_init(&data->domain_node); }
- spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags);
for (i = 0; i < NUM_LV1ENTRIES; i++) - if (lv1ent_page(priv->pgtable + i)) + if (lv1ent_page(domain->pgtable + i)) kmem_cache_free(lv2table_kmem_cache, - phys_to_virt(lv2table_base(priv->pgtable + i))); + phys_to_virt(lv2table_base(domain->pgtable + i)));
- free_pages((unsigned long)priv->pgtable, 2); - free_pages((unsigned long)priv->lv2entcnt, 1); - kfree(domain->priv); - domain->priv = NULL; + free_pages((unsigned long)domain->pgtable, 2); + free_pages((unsigned long)domain->lv2entcnt, 1); + kfree(iommu_domain->priv); + iommu_domain->priv = NULL; }
-static int exynos_iommu_attach_device(struct iommu_domain *domain, +static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain, struct device *dev) { struct exynos_iommu_owner *owner = dev->archdata.iommu; - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; struct sysmmu_drvdata *data; - phys_addr_t pagetable = virt_to_phys(priv->pgtable); + phys_addr_t pagetable = virt_to_phys(domain->pgtable); unsigned long flags; int ret = -ENODEV;
@@ -697,13 +698,13 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain,
list_for_each_entry(data, &owner->clients, owner_node) { pm_runtime_get_sync(data->sysmmu); - ret = __sysmmu_enable(data, pagetable, domain); + ret = __sysmmu_enable(data, pagetable, iommu_domain); if (ret >= 0) { data->master = dev;
- spin_lock_irqsave(&priv->lock, flags); - list_add_tail(&data->domain_node, &priv->clients); - spin_unlock_irqrestore(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags); + list_add_tail(&data->domain_node, &domain->clients); + spin_unlock_irqrestore(&domain->lock, flags); } }
@@ -719,11 +720,11 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain, return ret; }
-static void exynos_iommu_detach_device(struct iommu_domain *domain, +static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain, struct device *dev) { - struct exynos_iommu_domain *priv = domain->priv; - phys_addr_t pagetable = virt_to_phys(priv->pgtable); + struct exynos_iommu_domain *domain = iommu_domain->priv; + phys_addr_t pagetable = virt_to_phys(domain->pgtable); struct sysmmu_drvdata *data, *next; unsigned long flags; int found = 0; @@ -731,8 +732,8 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, if (!has_sysmmu(dev)) return;
- spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry_safe(data, next, &priv->clients, domain_node) { + spin_lock_irqsave(&domain->lock, flags); + list_for_each_entry_safe(data, next, &domain->clients, domain_node) { if (data->master == dev) { if (__sysmmu_disable(data)) { data->master = NULL; @@ -742,7 +743,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, found = true; } } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags);
if (found) dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n", @@ -751,7 +752,7 @@ static void exynos_iommu_detach_device(struct iommu_domain *domain, dev_err(dev, "%s: No IOMMU is attached\n", __func__); }
-static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, +static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter) { if (lv1ent_section(sent)) { @@ -793,17 +794,17 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv, if (need_flush_flpd_cache) { struct sysmmu_drvdata *data;
- spin_lock(&priv->lock); - list_for_each_entry(data, &priv->clients, domain_node) + spin_lock(&domain->lock); + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_flpdcache(data, iova); - spin_unlock(&priv->lock); + spin_unlock(&domain->lock); } }
return page_entry(sent, iova); }
-static int lv1set_section(struct exynos_iommu_domain *priv, +static int lv1set_section(struct exynos_iommu_domain *domain, sysmmu_pte_t *sent, sysmmu_iova_t iova, phys_addr_t paddr, short *pgcnt) { @@ -828,17 +829,17 @@ static int lv1set_section(struct exynos_iommu_domain *priv,
pgtable_flush(sent, sent + 1);
- spin_lock(&priv->lock); + spin_lock(&domain->lock); if (lv1ent_page_zero(sent)) { struct sysmmu_drvdata *data; /* * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD * entry by speculative prefetch of SLPD which has no mapping. */ - list_for_each_entry(data, &priv->clients, domain_node) + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_flpdcache(data, iova); } - spin_unlock(&priv->lock); + spin_unlock(&domain->lock);
return 0; } @@ -898,74 +899,76 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, * than or equal size to 128KiB. * - Start address of an I/O virtual region must be aligned by 128KiB. */ -static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova, - phys_addr_t paddr, size_t size, int prot) +static int exynos_iommu_map(struct iommu_domain *iommu_domain, + unsigned long l_iova, phys_addr_t paddr, size_t size, + int prot) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; sysmmu_pte_t *entry; sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; unsigned long flags; int ret = -ENOMEM;
- BUG_ON(priv->pgtable == NULL); + BUG_ON(domain->pgtable == NULL);
- spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags);
- entry = section_entry(priv->pgtable, iova); + entry = section_entry(domain->pgtable, iova);
if (size == SECT_SIZE) { - ret = lv1set_section(priv, entry, iova, paddr, - &priv->lv2entcnt[lv1ent_offset(iova)]); + ret = lv1set_section(domain, entry, iova, paddr, + &domain->lv2entcnt[lv1ent_offset(iova)]); } else { sysmmu_pte_t *pent;
- pent = alloc_lv2entry(priv, entry, iova, - &priv->lv2entcnt[lv1ent_offset(iova)]); + pent = alloc_lv2entry(domain, entry, iova, + &domain->lv2entcnt[lv1ent_offset(iova)]);
if (IS_ERR(pent)) ret = PTR_ERR(pent); else ret = lv2set_page(pent, paddr, size, - &priv->lv2entcnt[lv1ent_offset(iova)]); + &domain->lv2entcnt[lv1ent_offset(iova)]); }
if (ret) pr_err("%s: Failed(%d) to map %#zx bytes @ %#x\n", __func__, ret, size, iova);
- spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags);
return ret; }
-static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv, - sysmmu_iova_t iova, size_t size) +static +void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain, + sysmmu_iova_t iova, size_t size) { struct sysmmu_drvdata *data; unsigned long flags;
- spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&domain->lock, flags);
- list_for_each_entry(data, &priv->clients, domain_node) + list_for_each_entry(data, &domain->clients, domain_node) sysmmu_tlb_invalidate_entry(data, iova, size);
- spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&domain->lock, flags); }
-static size_t exynos_iommu_unmap(struct iommu_domain *domain, +static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, unsigned long l_iova, size_t size) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; sysmmu_iova_t iova = (sysmmu_iova_t)l_iova; sysmmu_pte_t *ent; size_t err_pgsize; unsigned long flags;
- BUG_ON(priv->pgtable == NULL); + BUG_ON(domain->pgtable == NULL);
- spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags);
- ent = section_entry(priv->pgtable, iova); + ent = section_entry(domain->pgtable, iova);
if (lv1ent_section(ent)) { if (WARN_ON(size < SECT_SIZE)) { @@ -998,7 +1001,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, *ent = 0; size = SPAGE_SIZE; pgtable_flush(ent, ent + 1); - priv->lv2entcnt[lv1ent_offset(iova)] += 1; + domain->lv2entcnt[lv1ent_offset(iova)] += 1; goto done; }
@@ -1012,15 +1015,15 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain, pgtable_flush(ent, ent + SPAGES_PER_LPAGE);
size = LPAGE_SIZE; - priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; + domain->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE; done: - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags);
- exynos_iommu_tlb_invalidate_entry(priv, iova, size); + exynos_iommu_tlb_invalidate_entry(domain, iova, size);
return size; err: - spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags);
pr_err("%s: Failed: size(%#zx) @ %#x is smaller than page size %#zx\n", __func__, size, iova, err_pgsize); @@ -1028,17 +1031,17 @@ err: return 0; }
-static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, +static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, dma_addr_t iova) { - struct exynos_iommu_domain *priv = domain->priv; + struct exynos_iommu_domain *domain = iommu_domain->priv; sysmmu_pte_t *entry; unsigned long flags; phys_addr_t phys = 0;
- spin_lock_irqsave(&priv->pgtablelock, flags); + spin_lock_irqsave(&domain->pgtablelock, flags);
- entry = section_entry(priv->pgtable, iova); + entry = section_entry(domain->pgtable, iova);
if (lv1ent_section(entry)) { phys = section_phys(entry) + section_offs(iova); @@ -1051,7 +1054,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain, phys = spage_phys(entry) + spage_offs(iova); }
- spin_unlock_irqrestore(&priv->pgtablelock, flags); + spin_unlock_irqrestore(&domain->pgtablelock, flags);
return phys; }
Add a few words of comment to all internal structures used by the driver.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 49 +++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index ae61d0680b9a..a75b06365f97 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -184,32 +184,49 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { "UNKNOWN FAULT" };
-/* attached to dev.archdata.iommu of the master device */ +/* + * This structure is attached to dev.archdata.iommu of the master device + * on device add, contains a list of SYSMMU controllers defined by device tree, + * which are bound to given master device. It is usually referenced by 'owner' + * pointer. + */ struct exynos_iommu_owner { - struct list_head clients; + struct list_head clients; /* list of sysmmu_drvdata.owner_node */ };
+/* + * This structure is stored in ->priv field of generic struct iommu_domain, + * contains list of SYSMMU controllers from all master devices, which has been + * attached to this domain and page tables of IO address space defined by this + * domain. It is usually referenced by 'domain' pointer. + */ struct exynos_iommu_domain { - struct list_head clients; /* list of sysmmu_drvdata.node */ + struct list_head clients; /* list of sysmmu_drvdata.domain_node */ sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */ short *lv2entcnt; /* free lv2 entry counter for each section */ - spinlock_t lock; /* lock for this structure */ + spinlock_t lock; /* lock for modyfying list of clients */ spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */ };
+/* + * This structure hold all data of a single SYSMMU controller, this includes + * hw resources like registers and clocks, pointers and list nodes to connect + * it to all other structures, internal state and parameters read from device + * tree. It is usually referenced by 'data' pointer. + */ struct sysmmu_drvdata { - struct device *sysmmu; /* System MMU's device descriptor */ - struct device *master; /* Owner of system MMU */ - void __iomem *sfrbase; - struct clk *clk; - struct clk *clk_master; - int activations; - spinlock_t lock; - struct iommu_domain *domain; - struct list_head domain_node; - struct list_head owner_node; - phys_addr_t pgtable; - int version; + struct device *sysmmu; /* SYSMMU controller device */ + struct device *master; /* master device (owner of given SYSMMU) */ + void __iomem *sfrbase; /* our registers */ + struct clk *clk; /* SYSMMU's clock */ + struct clk *clk_master; /* master's device clock */ + int activations; /* number of calls to sysmmu_enable */ + spinlock_t lock; /* lock for modyfying enable/disable state */ + struct iommu_domain *domain; /* domain we belong to */ + struct list_head domain_node; /* node for domain clients list */ + struct list_head owner_node; /* node for owner clients list */ + phys_addr_t pgtable; /* assigned page table structure */ + int version; /* our version */ };
static bool set_sysmmu_active(struct sysmmu_drvdata *data)
Removed following unused includes: <linux/mm.h>, <linux/errno.h>, <linux/memblock.h> and <linux/export.h>.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index a75b06365f97..5eb999d7653a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -12,19 +12,15 @@ #define DEBUG #endif
-#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/err.h> -#include <linux/mm.h> +#include <linux/io.h> #include <linux/iommu.h> -#include <linux/errno.h> +#include <linux/interrupt.h> #include <linux/list.h> -#include <linux/memblock.h> -#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h>
#include <asm/cacheflush.h> #include <asm/pgtable.h>
This patch introduces IOMMU_OF_DECLARE-based initialization to the driver, which replaces subsys_initcall-based procedure. exynos_iommu_of_setup ensures that each sysmmu controller is probed before its master device.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 5eb999d7653a..0d304c62956e 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -13,16 +13,21 @@ #endif
#include <linux/clk.h> +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/io.h> #include <linux/iommu.h> #include <linux/interrupt.h> #include <linux/list.h> +#include <linux/of.h> +#include <linux/of_iommu.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h>
#include <asm/cacheflush.h> +#include <asm/dma-iommu.h> #include <asm/pgtable.h>
typedef u32 sysmmu_iova_t; @@ -1083,6 +1088,8 @@ static const struct iommu_ops exynos_iommu_ops = { .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, };
+static int init_done; + static int __init exynos_iommu_init(void) { int ret; @@ -1115,6 +1122,8 @@ static int __init exynos_iommu_init(void) goto err_set_iommu; }
+ init_done = true; + return 0; err_set_iommu: kmem_cache_free(lv2table_kmem_cache, zero_lv2_table); @@ -1124,4 +1133,25 @@ err_reg_driver: kmem_cache_destroy(lv2table_kmem_cache); return ret; } -subsys_initcall(exynos_iommu_init); + +static struct iommu_data exynos_of_data = { + .ops = &exynos_iommu_ops, +}; + +static int __init exynos_iommu_of_setup(struct device_node *np) +{ + struct platform_device *pdev; + + if (!init_done) + exynos_iommu_init(); + + pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + of_iommu_set_data(np, &exynos_of_data); + return 0; +} + +IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu", + exynos_iommu_of_setup);
This patch adds implementation of of_xlate callback, which prepares masters device for attaching to IOMMU. This callback is called during creating devices from device tree.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- drivers/iommu/exynos-iommu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 0d304c62956e..6b8fe57d17f2 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -1077,6 +1077,33 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain, return phys; }
+static int exynos_iommu_of_xlate(struct device *dev, + struct of_phandle_args *spec) +{ + struct exynos_iommu_owner *owner = dev->archdata.iommu; + struct platform_device *sysmmu = of_find_device_by_node(spec->np); + struct sysmmu_drvdata *data; + + if (!sysmmu) + return 0; + + data = platform_get_drvdata(sysmmu); + if (!data) + return 0; + + if (!owner) { + owner = kzalloc(sizeof(*owner), GFP_KERNEL); + if (!owner) + return 0; + + INIT_LIST_HEAD(&owner->clients); + dev->archdata.iommu = owner; + } + + list_add_tail(&data->owner_node, &owner->clients); + return 1; +} + static const struct iommu_ops exynos_iommu_ops = { .domain_init = exynos_iommu_domain_init, .domain_destroy = exynos_iommu_domain_destroy, @@ -1086,6 +1113,7 @@ static const struct iommu_ops exynos_iommu_ops = { .unmap = exynos_iommu_unmap, .iova_to_phys = exynos_iommu_iova_to_phys, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE, + .of_xlate = exynos_iommu_of_xlate, };
static int init_done;
linaro-mm-sig@lists.linaro.org