Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem
Subsystems (10)
Marek Szyprowski
maintainerv2 cover Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem
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(-)
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 1/18 arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size
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.
--- arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
arch/arm/mm/dma-mapping.c +16 -0
@@ -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;
Signature · 1 line
1.9.2
PATCH v2 1/18 arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size
Hi Marek,
Quoted 19 lines · Marek Szyprowski
If device has no
max_seg_sizeset, we assume that there is no limit and force it toDMA_BIT_MASK(32)to always use contiguous mappings in DMA address space.Signed-off-by Marek Szyprowski <[email protected]>--- arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)arch/arm/mm/dma-mapping.c +2
-0@@ -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
arch/arm/mm/dma-mapping.c int arm_iommu_attach_device(struct device *dev, · line 2064
Quoted 10 lines
arch/arm/mm/dma-mapping.c +10
-0+* 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
Marek Szyprowski
maintainerPATCH v2 1/18 arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size
Hello,
Quoted 29 lines · Will Deacon
Hi Marek,
Quoted 26 lines · Marek Szyprowski
If device has no
max_seg_sizeset, we assume that there is no limit and force it toDMA_BIT_MASK(32)to always use contiguous mappings in DMA address space.Signed-off-by Marek Szyprowski <[email protected]>--- arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)arch/arm/mm/dma-mapping.c +9
-0@@ -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 ;
Quoted 8 lines
arch/arm/mm/dma-mapping.c int arm_iommu_attach_device(struct device *dev, · line 2064
Quoted 3 lines
arch/arm/mm/dma-mapping.c +3
-0+ } +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_configurepasses back a size parameter toarch_setup_dma_opswhich 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 hardcodedDMA_BIT_MASK(32). Do you want to include an updated patch in next version of yourarch_setup_dma_ops patchset?
Best regards
Signature · 2 lines
Marek Szyprowski, PhD Samsung R&D Institute Poland
PATCH v2 1/18 arm: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size
Quoted 37 lines · Marek Szyprowski
Quoted 27 lines · Will Deacon
Quoted 26 lines · Marek Szyprowski
If device has no
max_seg_sizeset, we assume that there is no limit and force it toDMA_BIT_MASK(32)to always use contiguous mappings in DMA address space.Signed-off-by Marek Szyprowski <[email protected]>--- arch/arm/mm/dma-mapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)arch/arm/mm/dma-mapping.c +9
-0@@ -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 ;Quoted 8 lines
arch/arm/mm/dma-mapping.c int arm_iommu_attach_device(struct device *dev, · line 2064
Quoted 3 lines
arch/arm/mm/dma-mapping.c +3
-0+ } +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_configurepasses back a size parameter toarch_setup_dma_opswhich 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?
Quoted 6 lines
Right, good idea. This patch predates your
arch_setup_dma_opschanges, so I had to use something. The value taken fromdma_maskis much better than hardcodedDMA_BIT_MASK(32). Do you want to include an updated patch in next version of yourarch_setup_dma_opspatchset?
Sure, I'll have a go at rolling that in.
Cheers,
Will
Marek Szyprowski
maintainerPATCH v2 2/18 arm: exynos: bind power domains earlier, on device creation
In reply to Marek SzyprowskiThis 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.
--- arch/arm/mach-exynos/pm_domains.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
arch/arm/mach-exynos/pm_domains.c +10 -2
@@ -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 ; }
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 3/18 drm: exynos: detach from default dma-mapping domain on init
In reply to Marek SzyprowskiThis patch adds code, which detach sub-device nodes from default iommu domain if such has been configured.
--- drivers/gpu/drm/exynos/exynos_drm_iommu.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/exynos/exynos_drm_iommu.c +3 -0
@@ -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" );
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 4/18 clk: exynos: add missing smmu_g2d clock and update comments
In reply to Marek SzyprowskiThis 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.
--- drivers/clk/samsung/clk-exynos4.c | 1 + include/dt-bindings/clock/exynos4.h | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-)
drivers/clk/samsung/clk-exynos4.c +1 -0
@@ -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), };
include/dt-bindings/clock/exynos4.h +5 -5
@@ -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 */
Signature · 1 line
1.9.2
PATCH v2 4/18 clk: exynos: add missing smmu_g2d clock and update comments
Quoted 5 lines · Marek Szyprowski
This patch adds missing
smmu_g2dclock 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.
Quoted 7 lines
Signed-off-by Marek Szyprowski <[email protected]>Acked-by Tomasz Figa <[email protected]>--- 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
Marek Szyprowski
maintainerPATCH v2 5/18 ARM: DTS: Exynos4: add System MMU nodes
In reply to Marek SzyprowskiThis patch adds System MMU nodes that are specific to Exynos4210/4x12 series.
--- 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(+)
arch/arm/boot/dts/exynos4.dtsi +117 -0
@@ -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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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>; + }; };
arch/arm/boot/dts/exynos4210.dtsi +23 -0
@@ -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 at 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 at 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>; + }; };
arch/arm/boot/dts/exynos4x12.dtsi +82 -0
110 lines not on this page · raw message
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 6/18 iommu: exynos: don't read version register on every tlb operation
In reply to Marek SzyprowskiThis patch removes reading of REG_MMU_VERSION register on every tlb operation and caches SYSMMU version in driver's internal data.
--- drivers/iommu/exynos-iommu.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
drivers/iommu/exynos-iommu.c +5 -8
@@ -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)) {
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 7/18 iommu: exynos: remove unused functions
In reply to Marek SzyprowskiThis patch removes two unneeded functions, which are not a part of generic IOMMU API and were never used by any other driver.
--- drivers/iommu/exynos-iommu.c | 31 ------------------------------- 1 file changed, 31 deletions(-)
drivers/iommu/exynos-iommu.c +0 -31
@@ -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;
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 8/18 iommu: exynos: remove useless spinlock
In reply to Marek SzyprowskiThis 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.
--- drivers/iommu/exynos-iommu.c | 11 ----------- 1 file changed, 11 deletions(-)
drivers/iommu/exynos-iommu.c +0 -11
@@ -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; }
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 9/18 iommu: exynos: refactor function parameters to simplify code
In reply to Marek SzyprowskiThis 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
--- drivers/iommu/exynos-iommu.c | 93 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 47 deletions(-)
drivers/iommu/exynos-iommu.c +46 -47
@@ -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); }
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 10/18 iommu: exynos: remove unused functions, part 2
In reply to Marek SzyprowskiAfter refactoring functions to use pointer to struct sysmmu_drvdata directly, some functions became useless and thus never used, so remove them completely.
--- drivers/iommu/exynos-iommu.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-)
drivers/iommu/exynos-iommu.c +0 -43
@@ -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) {
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 11/18 iommu: exynos: remove useless device_add/remove callbacks
In reply to Marek SzyprowskiThe 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.
--- drivers/iommu/exynos-iommu.c | 28 ---------------------------- 1 file changed, 28 deletions(-)
drivers/iommu/exynos-iommu.c +0 -28
@@ -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, };
Signature · 1 line
1.9.2
PATCH v2 11/18 iommu: exynos: remove useless device_add/remove callbacks
Le mardi 16 septembre 2014 ? 13:54 +0200, Marek Szyprowski a ?crit :
Quoted 6 lines
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 <[email protected]>
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
Quoted 50 lines
--- drivers/iommu/exynos-iommu.c | 28 ---------------------------- 1 file changed, 28 deletions(-)drivers/iommu/exynos-iommu.c +0
-28@@ -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, };
PATCH v2 11/18 iommu: exynos: remove useless device_add/remove callbacks
In reply to Marek SzyprowskiLe mardi 16 septembre 2014 ? 13:54 +0200, Marek Szyprowski a ?crit :
Quoted 6 lines
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 <[email protected]>
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
Quoted 50 lines
--- drivers/iommu/exynos-iommu.c | 28 ---------------------------- 1 file changed, 28 deletions(-)drivers/iommu/exynos-iommu.c +0
-28@@ -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, };
PATCH v2 11/18 iommu: exynos: remove useless device_add/remove callbacks
Quoted 16 lines · On Wednesday 22 October 2014 11:15:00 Alban Browaeys wrote:
Le mardi 16 septembre 2014 ? 13:54 +0200, Marek Szyprowski a ?crit :
Quoted 6 lines
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 <[email protected]>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
Quoted 3 lines
that is (4)
add_iommu_groupreturns ENODEV tobus_set_iommu, the latter doing so toexynos_iommu_init. Which thus error out before theinit_doneis set to true.
On a related note, I found that the exynos iommu driver does not check for the presence of an exynos iommu before calling bus_set_iommu, and this breaks multiplatform kernels.
Please fix.
Arnd
Marek Szyprowski
maintainerPATCH v2 11/18 iommu: exynos: remove useless device_add/remove callbacks
Hello,
Quoted 16 lines · Arnd Bergmann
Quoted 15 lines · On Wednesday 22 October 2014 11:15:00 Alban Browaeys wrote:
Le mardi 16 septembre 2014 ? 13:54 +0200, Marek Szyprowski a ?crit :
Quoted 6 lines
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 <[email protected]>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
Quoted 8 lines
Quoted 3 lines
that is (4)
add_iommu_groupreturns ENODEV tobus_set_iommu, the latter doing so toexynos_iommu_init. Which thus error out before theinit_doneis set to true.On a related note, I found that the exynos iommu driver does not check for the presence of an exynos iommu before calling
bus_set_iommu, and this breaks multiplatform kernels.Please fix.
For the time being please apply this patch:
https://lkml.org/lkml/2014/8/5/163
The proper solution will be applied when driver will be adapted to common of iommu support, which is still under discussion.
Best regards
Signature · 2 lines
Marek Szyprowski, PhD Samsung R&D Institute Poland
PATCH v2 11/18 iommu: exynos: remove useless device_add/remove callbacks
Quoted 24 lines · On Wednesday 22 October 2014 11:54:34 Marek Szyprowski wrote:
Quoted 23 lines · Arnd Bergmann
Quoted 15 lines · On Wednesday 22 October 2014 11:15:00 Alban Browaeys wrote:
Le mardi 16 septembre 2014 ? 13:54 +0200, Marek Szyprowski a ?crit :
Quoted 6 lines
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 <[email protected]>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_groupQuoted 3 lines
that is (4)
add_iommu_groupreturns ENODEV tobus_set_iommu, the latter doing so toexynos_iommu_init. Which thus error out before theinit_doneis set to true.On a related note, I found that the exynos iommu driver does not check for the presence of an exynos iommu before calling
bus_set_iommu, and this breaks multiplatform kernels.
Quoted 8 lines
Quoted 1 line
Please fix.
For the time being please apply this patch:
https://lkml.org/lkml/2014/8/5/163The proper solution will be applied when driver will be adapted to common of iommu support, which is still under discussion.
The patch looks good.
Can you send it to the iommu maintainer please?
Arnd
Marek Szyprowski
maintainerPATCH 1/1 iommu: exynos: make driver multiarch friendly
Initialize all structures and register to iommu subsystem only on Exynos compatible platforms.
--- drivers/iommu/exynos-iommu.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/iommu/exynos-iommu.c +6 -0
@@ -20,6 +20,7 @@#include <linux/clk.h> #include <linux/err.h> #include <linux/mm.h> +#include <linux/of.h> #include <linux/iommu.h> #include <linux/errno.h> #include <linux/list.h>
@@ -1188,6 +1189,11 @@ static int __init exynos_iommu_init(void) {int ret; +if (!of_machine_is_compatible ("samsung,exynos3" ) && + !of_machine_is_compatible ("samsung,exynos4" ) && + !of_machine_is_compatible ("samsung,exynos5" )) +return -ENODEV ; + lv2table_kmem_cache =kmem_cache_create ("exynos-iommu-lv2table" ,LV2TABLE_SIZE ,LV2TABLE_SIZE ,0 ,NULL );if (!lv2table_kmem_cache) {
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 12/18 iommu: exynos: add support for binding more than one sysmmu to master device
In reply to Marek SzyprowskiThis 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.
--- drivers/iommu/exynos-iommu.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
drivers/iommu/exynos-iommu.c +5 -6
@@ -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);
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 13/18 iommu: exynos: add support for runtime_pm
In reply to Marek SzyprowskiThis patch fixes support for runtime power management for SYSMMU controllers, so they are enabled when master device is attached.
--- drivers/iommu/exynos-iommu.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/iommu/exynos-iommu.c +2 -0
@@ -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; } }
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 14/18 iommu: exynos: rename variables to reflect their purpose
In reply to Marek SzyprowskiThis 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.
--- drivers/iommu/exynos-iommu.c | 191 ++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 94 deletions(-)
drivers/iommu/exynos-iommu.c +97 -94
@@ -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; }
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 15/18 iommu: exynos: document internal structures
In reply to Marek SzyprowskiAdd a few words of comment to all internal structures used by the driver.
--- drivers/iommu/exynos-iommu.c | 49 +++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-)
drivers/iommu/exynos-iommu.c +33 -16
@@ -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)
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 16/18 iommu: exynos: remove excessive includes and sort others alphabetically
In reply to Marek SzyprowskiRemoved following unused includes: <linux/mm.h>, <linux/errno.h>, <linux/memblock.h> and <linux/export.h>.
--- drivers/iommu/exynos-iommu.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-)
drivers/iommu/exynos-iommu.c +5 -9
@@ -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>
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 17/18 iommu: exynos: init from dt-specific callback instead of initcall
In reply to Marek SzyprowskiThis 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.
--- drivers/iommu/exynos-iommu.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
drivers/iommu/exynos-iommu.c +31 -1
@@ -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);
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH v2 18/18 iommu: exynos: add callback for initializing devices from device tree
In reply to Marek SzyprowskiThis 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.
--- drivers/iommu/exynos-iommu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
drivers/iommu/exynos-iommu.c +28 -0
@@ -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;
Signature · 1 line
1.9.2