[PATCH 07/29] ARM: dma-mapping: arm_iommu_attach_device: automatically set max_seg_size

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 7a996aa..8161102 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2051,6 +2051,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;
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 00/29] Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem

2014-08-05 Thread Marek Szyprowski
Hello Everyone,

A lot of things has happened in the area of improving Exynos IOMMU
driver and discussion about generic IOMMU bindings, which finally
motivated me to get back to IOMMU related tasks. Just to remind, here
are those 2 important threads:

1. [PATCH v13 00/19] iommu/exynos: Fixes and Enhancements of System MMU
driver with DT: https://lkml.org/lkml/2014/5/12/34

2. [PATCH v4] devicetree: Add generic IOMMU device tree bindings:
https://lkml.org/lkml/2014/7/4/349

As a follow up of those discussions I've decided to finish our internal
code, which adapts Exynos SYSMMU driver to meet generic IOMMU bindings
requirements and implement all needed glue code to finally demonstare
seemless integration IOMMU controller with DMA-mapping subsystem for
the drivers available on Exynos SoCs.


1. Introduction - a few words for those who are not fully aware of the
Exynos SoC hardware

Exynos SoC consists of various devices integrated directly into SoCs.
Most of them are multimedia devices, which usually process large
buffers. Some of them (like i.e. MFC - a multimedia codec or FIMD - a
multi-window framebuffer device  lcd panel controller) are equipped
with more than one memory interface for higher processing performance.
There are also really complex subsystems (like ISP, the camera sensor
interface  processor), which consist of many sub-blocks, each having
its own memory interface/channel/bus (different names are used for the
same thing).

Each such memory controller might be equipped with SYSMMU device, which
acts as IOMMU controller for the parent device (called master device, a
device which that memory interface belongs to). Each SYSMMU controller
has its own register set and clock, belongs to the same power domain as
master device. There is also some non-direct relation from master's
device gate clock - SYSMMU registers can be accessed only when master's
gate clock is enabled.

Basically we have following dependencies between hardware and drivers:
- each multimedia device might have 1 or more SYSMMU controller
- each SYSMMU controller belongs only to 1 master device
- all SYSMMU controllers are independent of each other, there is no
  global hardware ID that must be assigned to enable given SYSMMU
  controller
- multimedia devices are modeled usually by a separate node in device
  tree with it's own compatible string and separate driver for them
- sub-blocks of complex devices right now are not modeled by a separate
  device tree nodes, but this might be changed in the future
- some multimedia devices have limited address space per each memory
  controller/channel (i.e. codec might access buffers only in a 256MiB
  window for each of it's memory channels)
- some drivers for independent device are used together to provide a
  more complex subsystem, i.e. FIMD, HDMI-mixer and others form together
  Exynos DRM subsystem; it is highly welcome to let them to operate in
  the same, shared DMA address space to simplify buffer sharing


2. Introduction part 2 - a few word of summary of the discussions about
generic IOMMU DT bindings

There have been a lot of discussions on the method of modeling IOMMU
controllers in device tree. The approach which has been selected as the
generic IOMMU binding candidate has been described in the '[PATCH v4]
devicetree: Add generic IOMMU device tree bindings' thread.

Those bindings describe how to link an IOMMU controller with its master
device. Basically an 'iommus' property placed in the master's device
node has been introduced. This property contains phandle to IOMMU
controller node. Optional properties of the particular binding can also
be specified after the phandle, assuming that IOMMU controller node
contains '#iommu-cells' property, which defines number of cells used for
those parameters. Those parameters are then interpreted by particular
IOMMU controller driver. Those parameters might be some hw channel id
required for correct hardware setup, base address and size pair for
limited IO address space window or others hardware dependant properties.


3. IOMMU integration to DMA-mapping subsystem

By default we assume that each master device, which has been equipped
with IOMMU controller gets its own DMA (IO) address space. This is
created automatically and transparently without any changes in the
device driver code. All DMA-mapping functions are replaced with the
IOMMU aware versions. This has to be done somewhere by the architecture
or SoC startup code, so when master's driver probe() function is called,
everything is in place.

However some device drivers might need (for various reasons) to manually
manage DMA (IO) address space. For this case a driver need to notify
kernel about that and do the management of DMA address space on its own.
This has been achieved by introducing DRIVER_HAS_OWN_IOMMU_MANAGER flag,
which can be set in struct device_driver. This way the startup code can
easily determine if creating the default per-device separate DMA
address space is required 

[PATCH 08/29] ARM: dma-mapping: add helpers for managing default per-device dma mappings

2014-08-05 Thread Marek Szyprowski
This patch adds 2 helpers: arm_iommu_create_default_mapping and
arm_iommu_release_default_mapping for managing default iommu-based
dma-mapping address space, created for exlusive use only by the given
device. Those helpers are convenient for setting up iommu-based
dma-mapping for most typical devices in the system.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 arch/arm/include/asm/dma-iommu.h |  5 +
 arch/arm/mm/dma-mapping.c| 31 +++
 2 files changed, 36 insertions(+)

diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h
index 8e3fcb9..ae3dac0 100644
--- a/arch/arm/include/asm/dma-iommu.h
+++ b/arch/arm/include/asm/dma-iommu.h
@@ -33,5 +33,10 @@ int arm_iommu_attach_device(struct device *dev,
struct dma_iommu_mapping *mapping);
 void arm_iommu_detach_device(struct device *dev);
 
+int arm_iommu_create_default_mapping(struct device *dev, dma_addr_t base,
+size_t size);
+
+void arm_iommu_release_default_mapping(struct device *dev);
+
 #endif /* __KERNEL__ */
 #endif
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 8161102..233a8cf 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2106,4 +2106,35 @@ void arm_iommu_detach_device(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(arm_iommu_detach_device);
 
+int arm_iommu_create_default_mapping(struct device *dev, dma_addr_t base,
+size_t size)
+{
+   struct dma_iommu_mapping *mapping;
+   int ret;
+
+   mapping = arm_iommu_create_mapping(dev-bus, base, size);
+   if (IS_ERR(mapping))
+   return PTR_ERR(mapping);
+
+   ret = arm_iommu_attach_device(dev, mapping);
+   if (ret) {
+   arm_iommu_release_mapping(mapping);
+   return ret;
+   }
+
+   return 0;
+}
+EXPORT_SYMBOL_GPL(arm_iommu_create_default_mapping);
+
+void arm_iommu_release_default_mapping(struct device *dev)
+{
+   struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
+   if (!mapping)
+   return;
+
+   arm_iommu_detach_device(dev);
+   arm_iommu_release_mapping(mapping);
+}
+EXPORT_SYMBOL_GPL(arm_iommu_release_default_mapping);
+
 #endif
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 02/29] ARM: Exynos: bind power domains earlier, on device creation

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 fd76e1b..1d368a2 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;
 }
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 11/29] DRM: exynos: add DRIVER_HAS_OWN_IOMMU_MANAGER flag to all sub-drivers

2014-08-05 Thread Marek Szyprowski
Exynos DRM drivers do their own management of IO address space of all
controlled devices, so set DRIVER_HAS_OWN_IOMMU_MANAGER flag to instruct
IOMMU subsystem not to create any defaults for them.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/gpu/drm/exynos/exynos_drm_fimc.c| 1 +
 drivers/gpu/drm/exynos/exynos_drm_fimd.c| 1 +
 drivers/gpu/drm/exynos/exynos_drm_g2d.c | 1 +
 drivers/gpu/drm/exynos/exynos_drm_gsc.c | 1 +
 drivers/gpu/drm/exynos/exynos_drm_rotator.c | 1 +
 drivers/gpu/drm/exynos/exynos_mixer.c   | 1 +
 6 files changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c 
b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
index 831dde9..fe84215 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c
@@ -1896,6 +1896,7 @@ struct platform_driver fimc_driver = {
.name   = exynos-drm-fimc,
.owner  = THIS_MODULE,
.pm = fimc_pm_ops,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
 };
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c 
b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 33161ad..41904df 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -1031,5 +1031,6 @@ struct platform_driver fimd_driver = {
.name   = exynos4-fb,
.owner  = THIS_MODULE,
.of_match_table = fimd_driver_dt_match,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
 };
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c 
b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 8001587..dddeae3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -1555,5 +1555,6 @@ struct platform_driver g2d_driver = {
.owner  = THIS_MODULE,
.pm = g2d_pm_ops,
.of_match_table = exynos_g2d_match,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
 };
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c 
b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
index 9e3ff16..76e8b1e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c
@@ -1797,6 +1797,7 @@ struct platform_driver gsc_driver = {
.name   = exynos-drm-gsc,
.owner  = THIS_MODULE,
.pm = gsc_pm_ops,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
 };
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c 
b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
index f01fbb6..2da91c4 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -852,5 +852,6 @@ struct platform_driver rotator_driver = {
.owner  = THIS_MODULE,
.pm = rotator_pm_ops,
.of_match_table = exynos_rotator_match,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
 };
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c 
b/drivers/gpu/drm/exynos/exynos_mixer.c
index 7529946..79b1309a 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -1320,6 +1320,7 @@ struct platform_driver mixer_driver = {
.name = exynos-mixer,
.owner = THIS_MODULE,
.of_match_table = mixer_match_types,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
.probe = mixer_probe,
.remove = mixer_remove,
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 09/29] ARM: dma-mapping: provide stubs if no ARM_DMA_USE_IOMMU has been selected

2014-08-05 Thread Marek Szyprowski
This patch provides stubs returing errors for all iommu related arm
dma-mapping functions, which are used when CONFIG_ARM_DMA_USE_IOMMU is
not set. This let drivers to use common code for iommu and non-iommu
cases without additional ifdefs.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 arch/arm/include/asm/dma-iommu.h | 31 +++
 1 file changed, 31 insertions(+)

diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h
index ae3dac0..1e57569 100644
--- a/arch/arm/include/asm/dma-iommu.h
+++ b/arch/arm/include/asm/dma-iommu.h
@@ -9,6 +9,8 @@
 #include linux/kmemcheck.h
 #include linux/kref.h
 
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
+
 struct dma_iommu_mapping {
/* iommu specific data */
struct iommu_domain *domain;
@@ -38,5 +40,34 @@ int arm_iommu_create_default_mapping(struct device *dev, 
dma_addr_t base,
 
 void arm_iommu_release_default_mapping(struct device *dev);
 
+#else
+
+static inline struct dma_iommu_mapping *
+arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size)
+{
+   return ERR_PTR(-ENOSYS);
+}
+
+static inline void
+arm_iommu_release_mapping(struct dma_iommu_mapping *mapping) { }
+
+static inline int arm_iommu_attach_device(struct device *dev,
+   struct dma_iommu_mapping *mapping)
+{
+   return -ENOSYS;
+}
+
+static inline void arm_iommu_detach_device(struct device *dev) { }
+
+static inline int arm_iommu_create_default_mapping(struct device *dev,
+   dma_addr_t base, size_t size)
+{
+   return -ENOSYS;
+}
+
+static inline void arm_iommu_release_default_mapping(struct device *dev) { }
+
+#endif
+
 #endif /* __KERNEL__ */
 #endif
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 18/29] iommu: exynos: remove unused functions

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 8927923..ec3c882 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -491,13 +491,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;
@@ -589,30 +582,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;
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 14/29] devicetree: Update Exynos SYSMMU device tree bindings

2014-08-05 Thread Marek Szyprowski
This patch describes how generic iommu bindings are implemented by
Exynos SYSMMU driver.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 .../devicetree/bindings/iommu/samsung,sysmmu.txt   | 93 +++---
 1 file changed, 84 insertions(+), 9 deletions(-)

diff --git a/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt 
b/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt
index 6fa4c73..999ba6d 100644
--- a/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt
+++ b/Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt
@@ -23,16 +23,33 @@ MMUs.
   for window 1, 2 and 3.
 * M2M Scalers and G2D in Exynos5420 has one System MMU on the read channel and
   the other System MMU on the write channel.
-The drivers must consider how to handle those System MMUs. One of the idea is
-to implement child devices or sub-devices which are the client devices of the
-System MMU.
 
-Note:
-The current DT binding for the Exynos System MMU is incomplete.
-The following properties can be removed or changed, if found incompatible with
-the Generic IOMMU Binding support for attaching devices to the IOMMU.
+The drivers must consider how to handle those System MMUs. When device
+have more than one SYSMMU controller it is neccessary to add
+iommu-names property, which specifies which SYSMMU controller operates
+on which bus or memory channel.
 
-Required properties:
+It is up to the master device driver to decide how such case will be
+handled. It is possible to create separate IO address spaces for each
+SYSMMU or to bind them together to one common IO address space. It is
+also possible to bind more than one device to one IO address space. All
+this has to be handled by master device driver in its initialization
+procedure or flags and no changes to device tree nodes are needed.
+
+In Linux kernel, the general idea is that presence of the SYSMMU
+controllers is transparent to master drivers if they use standard DMA
+API. When driver wants to use IO separate address spaces for each bus or
+memory channel (each SYSMMU) or to bind more than one device to one IO
+address space, it has to specify this to SYSMMU driver by
+DRIVER_HAS_OWN_IOMMU_MANAGER flag. To get access to each SYSMMU bound to
+the given device, additional child devices with special names (matching
+parent:bus scheme) have to be registered. Once then, all standard
+IOMMU operations can be performed on such child devices, what will
+result in respective operations done on IO address space managed by
+SYSMMU of the given name. Other operating systems might implement those
+features differently.
+
+Required properties for SYSMMU controller node:
 - compatible: Should be samsung,exynos-sysmmu
 - reg: A tuple of base address and size of System MMU registers.
 - interrupt-parent: The phandle of the interrupt controller of System MMU
@@ -45,11 +62,27 @@ Required properties:
   Exynos4 SoCs, there needs no master clock.
   Exynos5 SoCs, some System MMUs must have master clocks.
 - clocks: Required if the System MMU is needed to gate its clock.
+- #iommu-cells: Specify number of cells describing IO address space parameters,
+   can be: 0 (zero), meaning all 32bit address space is available,
+   or 2, if address space is limited, first cell then stores
+   base IO address, second cell contains IO window size in bytes.
 - samsung,power-domain: Required if the System MMU is needed to gate its power.
  Please refer to the following document:
  Documentation/devicetree/bindings/arm/exynos/power_domain.txt
 
-Examples:
+Required properties for master device:
+- iommus: one or more phandles to the SYSMMU controller node, with optionally
+ specified IO address space (see #iommu-cells property above)
+- iommu-names: if more than one SYSMMU controller is specified, this property
+  must contain names for each of them. Those names are defined by
+  the bindings for a particular master device.
+
+For more information, please refer to generic iommu bindings defined in
+iommu.txt file.
+
+Example 1:
+GScaller device with one SYSMMU controller
+
gsc_0: gsc@13e0 {
compatible = samsung,exynos5-gsc;
reg = 0x13e0 0x1000;
@@ -57,6 +90,7 @@ Examples:
samsung,power-domain = pd_gsc;
clocks = clock CLK_GSCL0;
clock-names = gscl;
+   iommus = sysmmu_gsc0;
};
 
sysmmu_gsc0: sysmmu@13E8 {
@@ -67,4 +101,45 @@ Examples:
clock-names = sysmmu, master;
clocks = clock CLK_SMMU_GSCL0, clock CLK_GSCL0;
samsung,power-domain = pd_gsc;
+   #iommu-cells = 0;
+   };
+
+Example 2:
+MFC Codec with two SYSMMU controllers (on left and right bus), with address
+space limited to 256MiB each, left bus starts IO address space at 0x2000,
+while right bus at 0x3000
+
+  

[PATCH 05/29] drivers: convert suppress_bind_attrs parameter into flags

2014-08-05 Thread Marek Szyprowski
This patch extends struct device_driver with a flags member and converts
existing suppress_bind_attrs bool field to a flag. This way new flags
can be easily added in the future without changing the structure itself.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 arch/arm/mach-integrator/impd1.c | 2 +-
 drivers/base/bus.c   | 4 ++--
 drivers/base/platform.c  | 2 +-
 drivers/pci/host/pci-mvebu.c | 2 +-
 drivers/pci/host/pci-rcar-gen2.c | 2 +-
 drivers/pci/host/pci-tegra.c | 2 +-
 drivers/pci/host/pcie-rcar.c | 2 +-
 drivers/soc/tegra/pmc.c  | 2 +-
 include/linux/device.h   | 6 --
 9 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c
index 3ce8807..a7e7330 100644
--- a/arch/arm/mach-integrator/impd1.c
+++ b/arch/arm/mach-integrator/impd1.c
@@ -406,7 +406,7 @@ static struct lm_driver impd1_driver = {
 * As we're dropping the probe() function, suppress driver
 * binding from sysfs.
 */
-   .suppress_bind_attrs = true,
+   .flags = DRIVER_SUPPRESS_BIND_ATTRS,
},
.probe  = impd1_probe,
.remove = impd1_remove,
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 83e910a..f223f26 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -707,7 +707,7 @@ int bus_add_driver(struct device_driver *drv)
__func__, drv-name);
}
 
-   if (!drv-suppress_bind_attrs) {
+   if (!(drv-flags  DRIVER_SUPPRESS_BIND_ATTRS)) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
@@ -740,7 +740,7 @@ void bus_remove_driver(struct device_driver *drv)
if (!drv-bus)
return;
 
-   if (!drv-suppress_bind_attrs)
+   if (!(drv-flags  DRIVER_SUPPRESS_BIND_ATTRS))
remove_bind_files(drv);
driver_remove_groups(drv, drv-bus-drv_groups);
driver_remove_file(drv, driver_attr_uevent);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 68a8b77..c696058 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -608,7 +608,7 @@ int __init_or_module platform_driver_probe(struct 
platform_driver *drv,
drv-prevent_deferred_probe = true;
 
/* make sure driver won't have bind/unbind attributes */
-   drv-driver.suppress_bind_attrs = true;
+   drv-driver.flags = DRIVER_SUPPRESS_BIND_ATTRS;
 
/* temporary section violation during probe() */
drv-probe = probe;
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index a8c6f1a..6815c50 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -1086,7 +1086,7 @@ static struct platform_driver mvebu_pcie_driver = {
.name = mvebu-pcie,
.of_match_table = mvebu_pcie_of_match_table,
/* driver unloading/unbinding currently not supported */
-   .suppress_bind_attrs = true,
+   .flags = DRIVER_SUPPRESS_BIND_ATTRS,
},
.probe = mvebu_pcie_probe,
 };
diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c
index 3ef854f..6f1b890 100644
--- a/drivers/pci/host/pci-rcar-gen2.c
+++ b/drivers/pci/host/pci-rcar-gen2.c
@@ -413,7 +413,7 @@ static struct platform_driver rcar_pci_driver = {
.driver = {
.name = pci-rcar-gen2,
.owner = THIS_MODULE,
-   .suppress_bind_attrs = true,
+   .flags = DRIVER_SUPPRESS_BIND_ATTRS,
.of_match_table = rcar_pci_of_match,
},
.probe = rcar_pci_probe,
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 0fb0fdb..2e1698d 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -1927,7 +1927,7 @@ static struct platform_driver tegra_pcie_driver = {
.name = tegra-pcie,
.owner = THIS_MODULE,
.of_match_table = tegra_pcie_of_match,
-   .suppress_bind_attrs = true,
+   .flags = DRIVER_SUPPRESS_BIND_ATTRS,
},
.probe = tegra_pcie_probe,
 };
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
index 4884ee5..9a1936e 100644
--- a/drivers/pci/host/pcie-rcar.c
+++ b/drivers/pci/host/pcie-rcar.c
@@ -981,7 +981,7 @@ static struct platform_driver rcar_pcie_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = rcar_pcie_of_match,
-   .suppress_bind_attrs = true,
+   .flags = DRIVER_SUPPRESS_BIND_ATTRS,
},
.probe = rcar_pcie_probe,
 };
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index a2c0ceb..77c3eb3 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -892,7 +892,7 @@ static const struct 

[PATCH 20/29] iommu: exynos: refactor function parameters to simplify code

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 ed8c518..018a615 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -187,8 +187,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;
 };
 
@@ -209,6 +207,7 @@ struct sysmmu_drvdata {
int activations;
spinlock_t lock;
struct iommu_domain *domain;
+   struct list_head domain_node;
phys_addr_t pgtable;
int version;
 };
@@ -509,12 +508,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);
@@ -528,14 +525,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)) {
@@ -565,8 +558,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);
 }
@@ -705,7 +698,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;
 
@@ -713,14 +706,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++)
@@ -739,20 +730,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);
+   

[PATCH 13/29] temporary: media: s5p-mfc: remove DT hacks initialization custom memory init code

2014-08-05 Thread Marek Szyprowski
This patch removes custom initialization of reserved memory regions from
s5p-mfc driver. Driver will use main device pointer for all memory allocations.

This patch is temporary, do not merge it yet.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/media/platform/s5p-mfc/s5p_mfc.c | 73 +---
 1 file changed, 2 insertions(+), 71 deletions(-)

diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c 
b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index d35b041..77b99ae 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -996,55 +996,8 @@ static const struct v4l2_file_operations s5p_mfc_fops = {
.mmap = s5p_mfc_mmap,
 };
 
-static int match_child(struct device *dev, void *data)
-{
-   if (!dev_name(dev))
-   return 0;
-   return !strcmp(dev_name(dev), (char *)data);
-}
-
 static void *mfc_get_drv_data(struct platform_device *pdev);
 
-static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev)
-{
-   unsigned int mem_info[2] = { };
-
-   dev-mem_dev_l = devm_kzalloc(dev-plat_dev-dev,
-   sizeof(struct device), GFP_KERNEL);
-   if (!dev-mem_dev_l) {
-   mfc_err(Not enough memory\n);
-   return -ENOMEM;
-   }
-   device_initialize(dev-mem_dev_l);
-   of_property_read_u32_array(dev-plat_dev-dev.of_node,
-   samsung,mfc-l, mem_info, 2);
-   if (dma_declare_coherent_memory(dev-mem_dev_l, mem_info[0],
-   mem_info[0], mem_info[1],
-   DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
-   mfc_err(Failed to declare coherent memory for\n
-   MFC device\n);
-   return -ENOMEM;
-   }
-
-   dev-mem_dev_r = devm_kzalloc(dev-plat_dev-dev,
-   sizeof(struct device), GFP_KERNEL);
-   if (!dev-mem_dev_r) {
-   mfc_err(Not enough memory\n);
-   return -ENOMEM;
-   }
-   device_initialize(dev-mem_dev_r);
-   of_property_read_u32_array(dev-plat_dev-dev.of_node,
-   samsung,mfc-r, mem_info, 2);
-   if (dma_declare_coherent_memory(dev-mem_dev_r, mem_info[0],
-   mem_info[0], mem_info[1],
-   DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) {
-   pr_err(Failed to declare coherent memory for\n
-   MFC device\n);
-   return -ENOMEM;
-   }
-   return 0;
-}
-
 /* MFC probe function */
 static int s5p_mfc_probe(struct platform_device *pdev)
 {
@@ -1096,26 +1049,8 @@ static int s5p_mfc_probe(struct platform_device *pdev)
goto err_res;
}
 
-   if (pdev-dev.of_node) {
-   ret = s5p_mfc_alloc_memdevs(dev);
-   if (ret  0)
-   goto err_res;
-   } else {
-   dev-mem_dev_l = device_find_child(dev-plat_dev-dev,
-   s5p-mfc-l, match_child);
-   if (!dev-mem_dev_l) {
-   mfc_err(Mem child (L) device get failed\n);
-   ret = -ENODEV;
-   goto err_res;
-   }
-   dev-mem_dev_r = device_find_child(dev-plat_dev-dev,
-   s5p-mfc-r, match_child);
-   if (!dev-mem_dev_r) {
-   mfc_err(Mem child (R) device get failed\n);
-   ret = -ENODEV;
-   goto err_res;
-   }
-   }
+   dev-mem_dev_l = dev-plat_dev-dev;
+   dev-mem_dev_r = dev-plat_dev-dev;
 
dev-alloc_ctx[0] = vb2_dma_contig_init_ctx(dev-mem_dev_l);
if (IS_ERR(dev-alloc_ctx[0])) {
@@ -1246,10 +1181,6 @@ static int s5p_mfc_remove(struct platform_device *pdev)
s5p_mfc_release_firmware(dev);
vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[0]);
vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[1]);
-   if (pdev-dev.of_node) {
-   put_device(dev-mem_dev_l);
-   put_device(dev-mem_dev_r);
-   }
 
s5p_mfc_final_pm(dev);
return 0;
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 12/29] DRM: Exynos: fix window clear code

2014-08-05 Thread Marek Szyprowski
To correctly disable hardware window during driver init, both enable bits
(WINCONx_ENWIN in WINCON and SHADOWCON_CHx_ENABLE in SHADOWCON) must be
cleared, otherwise hardware fails to re-enable such window later.

While touching this function, also temporarily disable ctx-suspended flag
to let fimd_wait_for_vblank function really to do its job.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/gpu/drm/exynos/exynos_drm_fimd.c | 25 ++---
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c 
b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 41904df..7a363d2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -165,27 +165,38 @@ static void fimd_wait_for_vblank(struct 
exynos_drm_manager *mgr)
DRM_DEBUG_KMS(vblank wait timed out.\n);
 }
 
-
 static void fimd_clear_channel(struct exynos_drm_manager *mgr)
 {
struct fimd_context *ctx = mgr-ctx;
-   int win, ch_enabled = 0;
+   int state, win, ch_enabled = 0;
 
DRM_DEBUG_KMS(%s\n, __FILE__);
 
/* Check if any channel is enabled. */
for (win = 0; win  WINDOWS_NR; win++) {
-   u32 val = readl(ctx-regs + SHADOWCON);
-   if (val  SHADOWCON_CHx_ENABLE(win)) {
-   val = ~SHADOWCON_CHx_ENABLE(win);
-   writel(val, ctx-regs + SHADOWCON);
+   u32 val = readl(ctx-regs + WINCON(win));
+   if (val  WINCONx_ENWIN) {
+   /* wincon */
+   val = ~WINCONx_ENWIN;
+   writel(val, ctx-regs + WINCON(win));
+
+   /* unprotect windows */
+   if (ctx-driver_data-has_shadowcon) {
+   val = readl(ctx-regs + SHADOWCON);
+   val = ~SHADOWCON_CHx_ENABLE(win);
+   writel(val, ctx-regs + SHADOWCON);
+   }
ch_enabled = 1;
}
}
 
/* Wait for vsync, as disable channel takes effect at next vsync */
-   if (ch_enabled)
+   if (ch_enabled) {
+   state = ctx-suspended;
+   ctx-suspended = 0;
fimd_wait_for_vblank(mgr);
+   ctx-suspended = state;
+   }
 }
 
 static int fimd_mgr_initialize(struct exynos_drm_manager *mgr,
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 04/29] drivers: base: add notifier for failed driver bind

2014-08-05 Thread Marek Szyprowski
This patch adds support for getting a notify for failed device driver
bind, so all the items done in BUS_NOTIFY_BIND_DRIVER event can be
cleaned if the driver fails to bind.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/base/dd.c  | 10 +++---
 include/linux/device.h |  4 +++-
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index e4ffbcf..541a41f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -237,10 +237,14 @@ static int driver_sysfs_add(struct device *dev)
return ret;
 }
 
-static void driver_sysfs_remove(struct device *dev)
+static void driver_sysfs_remove(struct device *dev, int failed)
 {
struct device_driver *drv = dev-driver;
 
+   if (failed  dev-bus)
+   blocking_notifier_call_chain(dev-bus-p-bus_notifier,
+BUS_NOTIFY_DRVBIND_FAILED, dev);
+
if (drv) {
sysfs_remove_link(drv-p-kobj, kobject_name(dev-kobj));
sysfs_remove_link(dev-kobj, driver);
@@ -316,7 +320,7 @@ static int really_probe(struct device *dev, struct 
device_driver *drv)
 
 probe_failed:
devres_release_all(dev);
-   driver_sysfs_remove(dev);
+   driver_sysfs_remove(dev, true);
dev-driver = NULL;
dev_set_drvdata(dev, NULL);
 
@@ -509,7 +513,7 @@ static void __device_release_driver(struct device *dev)
if (drv) {
pm_runtime_get_sync(dev);
 
-   driver_sysfs_remove(dev);
+   driver_sysfs_remove(dev, false);
 
if (dev-bus)
blocking_notifier_call_chain(dev-bus-p-bus_notifier,
diff --git a/include/linux/device.h b/include/linux/device.h
index b387710..92daded 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -176,7 +176,7 @@ extern int bus_register_notifier(struct bus_type *bus,
 extern int bus_unregister_notifier(struct bus_type *bus,
   struct notifier_block *nb);
 
-/* All 4 notifers below get called with the target struct device *
+/* All 7 notifers below get called with the target struct device *
  * as an argument. Note that those functions are likely to be called
  * with the device lock held in the core, so be careful.
  */
@@ -189,6 +189,8 @@ extern int bus_unregister_notifier(struct bus_type *bus,
  unbound */
 #define BUS_NOTIFY_UNBOUND_DRIVER  0x0006 /* driver is unbound
  from the device */
+#define BUS_NOTIFY_DRVBIND_FAILED  0x0007 /* driver failed to bind
+ to device */
 
 extern struct kset *bus_get_kset(struct bus_type *bus);
 extern struct klist *bus_get_device_klist(struct bus_type *bus);
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 10/29] drivers: add DRIVER_HAS_OWN_IOMMU_MANAGER flag

2014-08-05 Thread Marek Szyprowski
This patch adds a new flags for device drivers. This flag instructs
kernel that the device driver does it own management of IOMMU assisted
IO address space translations, so no default dma-mapping structures
should be initialized.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 include/linux/device.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/device.h b/include/linux/device.h
index 5f4ff02..2e62371 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -253,6 +253,8 @@ struct device_driver {
 
 /* disables bind/unbind via sysfs */
 #define DRIVER_SUPPRESS_BIND_ATTRS (1  0)
+/* driver uses own methods to manage IO address space */
+#define DRIVER_HAS_OWN_IOMMU_MANAGER   (1  1)
 
 extern int __must_check driver_register(struct device_driver *drv);
 extern void driver_unregister(struct device_driver *drv);
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 03/29] clk: exynos: add missing smmu_g2d clock and update comments

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 ac163d7..12a7cc3 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 459bd2b..fb981635 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_G3D276
 #define CLK_G2D277
-#define CLK_ROTATOR278 /* 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_ROTATOR278
+#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 */
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 17/29] iommu: exynos: don't read version register on every tlb operation

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 64b3bc8..8927923 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -213,6 +213,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)
@@ -239,11 +240,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;
@@ -403,7 +399,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;
@@ -417,6 +413,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)
@@ -526,7 +523,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);
 }
 
@@ -575,7 +572,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)) {
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 25/29] iommu: exynos: add support for runtime_pm

2014-08-05 Thread Marek Szyprowski
This patch enables support for runtime pm for SYSMMU controllers. State
of each controller is saved before master's device power domain is
turned off and restored after it has been turned on.

exynos_iommu_attach_device() function might be called anytime, even when
power domain for master's device has been turned off, so to let SYSMMU
controllers to access its registers, a call to pm_runtime_get_sync() has
to be done, which turns on the power domain, which SYSMMU belongs to.
Later, once SYSMMU has been enabled, a call to pm_runtime_put() lets
runtime pm to turn off the power domain if there are no other devices
enabled. This way, the SYSMMU drivers get a genpd pm event and save its
state with sysmmu_save_state() function.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/iommu/exynos-iommu.c | 54 +++-
 1 file changed, 53 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 336b2f8..5cd91b11 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -28,6 +28,7 @@
 #include linux/list.h
 #include linux/memblock.h
 #include linux/export.h
+#include linux/pm_domain.h
 
 #include asm/cacheflush.h
 #include asm/dma-iommu.h
@@ -208,6 +209,7 @@ struct sysmmu_drvdata {
struct clk *clk;
struct clk *clk_master;
int activations;
+   int suspended;
spinlock_t lock;
struct iommu_domain *domain;
struct list_head domain_node;
@@ -217,6 +219,7 @@ struct sysmmu_drvdata {
const char *name;
dma_addr_t base;
size_t size;
+   struct notifier_block pm_notifier;
 };
 
 static bool set_sysmmu_active(struct sysmmu_drvdata *data)
@@ -235,7 +238,7 @@ static bool set_sysmmu_inactive(struct sysmmu_drvdata *data)
 
 static bool is_sysmmu_active(struct sysmmu_drvdata *data)
 {
-   return data-activations  0;
+   return (!data-suspended  data-activations  0);
 }
 
 static void sysmmu_unblock(void __iomem *sfrbase)
@@ -528,6 +531,51 @@ static void sysmmu_tlb_invalidate_entry(struct 
sysmmu_drvdata *data,
spin_unlock_irqrestore(data-lock, flags);
 }
 
+static void sysmmu_restore_state(struct sysmmu_drvdata *data)
+{
+   unsigned long flags;
+
+   spin_lock_irqsave(data-lock, flags);
+   if (data-activations  0) {
+   data-suspended = false;
+   __sysmmu_enable_nocount(data);
+   dev_dbg(data-sysmmu, restored state\n);
+   }
+   spin_unlock_irqrestore(data-lock, flags);
+}
+
+static void sysmmu_save_state(struct sysmmu_drvdata *data)
+{
+   unsigned long flags;
+
+   spin_lock_irqsave(data-lock, flags);
+   if (data-activations  0) {
+   __sysmmu_disable_nocount(data);
+   data-suspended = true;
+   dev_dbg(data-sysmmu, saved state\n);
+   }
+   spin_unlock_irqrestore(data-lock, flags);
+}
+
+static int sysmmu_runtime_genpd_event(struct notifier_block *this,
+   unsigned long event, void *ptr)
+{
+   struct sysmmu_drvdata *data;
+
+   data = container_of(this, struct sysmmu_drvdata, pm_notifier);
+
+   switch (event) {
+   case PM_GENPD_POST_POWER_ON:
+   sysmmu_restore_state(data);
+   break;
+   case PM_GENPD_POWER_OFF_PREPARE:
+   sysmmu_save_state(data);
+   break;
+   }
+
+   return NOTIFY_DONE;
+}
+
 static int __init exynos_sysmmu_probe(struct platform_device *pdev)
 {
int irq, ret;
@@ -580,6 +628,7 @@ static int __init exynos_sysmmu_probe(struct 
platform_device *pdev)
}
 
data-sysmmu = dev;
+   data-pm_notifier.notifier_call = sysmmu_runtime_genpd_event;
 
/* default io address space parameters */
data-base = SZ_1G;
@@ -708,6 +757,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;
@@ -716,6 +766,7 @@ static int exynos_iommu_attach_device(struct iommu_domain 
*domain,
list_add_tail(data-domain_node, priv-clients);
spin_unlock_irqrestore(priv-lock, flags);
}
+   pm_runtime_put(data-sysmmu);
}
 
if (ret  0) {
@@ -1156,6 +1207,7 @@ static int __init_master_sysmmu(struct device *dev)
}
 
list_add_tail(data-owner_node, owner-clients);
+   pm_genpd_register_notifier(dev, data-pm_notifier);
 
i++;
}
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 16/29] iommu: exynos: make driver multiarch friendly

2014-08-05 Thread Marek Szyprowski
Initialize all structures and register to iommu subsystem only on Exynos
compatible platforms.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/iommu/exynos-iommu.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index d037e87..64b3bc8 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -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
@@ -1187,6 +1188,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) {
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 19/29] iommu: exynos: remove useless spinlock

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 ec3c882..ed8c518 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -190,9 +190,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 {
@@ -478,16 +475,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;
 }
 
@@ -500,16 +493,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;
 }
 
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 15/29] ARM: DTS: Exynos4: add System MMU nodes

2014-08-05 Thread Marek Szyprowski
This patch adds System MMU nodes that are specific to Exynos4210/4x12 series.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 arch/arm/boot/dts/exynos4.dtsi| 118 ++
 arch/arm/boot/dts/exynos4210.dtsi |  23 
 arch/arm/boot/dts/exynos4x12.dtsi |  82 ++
 3 files changed, 223 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 3385b17..a76b4e5 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,9 @@
clocks = clock CLK_MFC;
clock-names = mfc;
status = disabled;
+   iommus = sysmmu_mfc_l 0x2000 0x1000,
+sysmmu_mfc_r 0x3000 0x1000;
+   iommu-names = left, right;
};
 
serial_0: serial@1380 {
@@ -642,6 +649,117 @@
clocks = clock CLK_SCLK_FIMD0, clock CLK_FIMD0;
clock-names = sclk_fimd, fimd;
samsung,power-domain = pd_lcd0;
+   iommus = sysmmu_fimd0;
status = disabled;
};
+
+   sysmmu_mfc_l: sysmmu@1362 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x1362 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 = 2;
+   };
+
+   sysmmu_mfc_r: sysmmu@1363 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x1363 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 = 2;
+   };
+
+   sysmmu_tv: sysmmu@12E2 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x12E2 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@11A2 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x11A2 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@11A3 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x11A3 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@11A4 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x11A4 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@11A5 {
+   compatible = samsung,exynos-sysmmu;
+   reg = 0x11A5 0x1000;
+   interrupt-parent = combiner;
+ 

[PATCH 23/29] iommu: exynos: init iommu controllers from device tree

2014-08-05 Thread Marek Szyprowski
This patch adds code to initialize and assign SYSMMU controllers to
their master devices defined in device tree.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/iommu/exynos-iommu.c | 152 +--
 1 file changed, 145 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 46e0edc..845f547 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -21,6 +21,7 @@
 #include linux/err.h
 #include linux/mm.h
 #include linux/of.h
+#include linux/of_platform.h
 #include linux/iommu.h
 #include linux/errno.h
 #include linux/list.h
@@ -211,6 +212,9 @@ struct sysmmu_drvdata {
struct list_head owner_node;
phys_addr_t pgtable;
int version;
+   const char *name;
+   dma_addr_t base;
+   size_t size;
 };
 
 static bool set_sysmmu_active(struct sysmmu_drvdata *data)
@@ -574,6 +578,11 @@ static int __init exynos_sysmmu_probe(struct 
platform_device *pdev)
}
 
data-sysmmu = dev;
+
+   /* default io address space parameters */
+   data-base = SZ_1G;
+   data-size = SZ_2G;
+
spin_lock_init(data-lock);
 
platform_set_drvdata(pdev, data);
@@ -1055,30 +1064,159 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct 
iommu_domain *domain,
return phys;
 }
 
+static void __free_owner_struct(struct exynos_iommu_owner *owner,
+   struct device *dev)
+{
+   while (!list_empty(owner-clients))
+   list_del_init(owner-clients.next);
+
+   kfree(owner);
+   dev-archdata.iommu = NULL;
+}
+
+static int __init_master_sysmmu(struct device *dev)
+{
+   struct of_phandle_args sysmmu_spec;
+   struct exynos_iommu_owner *owner;
+   int i = 0;
+   int ret;
+
+   owner = kzalloc(sizeof(*owner), GFP_KERNEL);
+   if (!owner)
+   return -ENOMEM;
+   INIT_LIST_HEAD(owner-clients);
+
+   while (!of_parse_phandle_with_args(dev-of_node, iommus,
+  #iommu-cells, i,
+  sysmmu_spec)) {
+   struct platform_device *sysmmu;
+   struct sysmmu_drvdata *data;
+
+   sysmmu = of_find_device_by_node(sysmmu_spec.np);
+   if (!sysmmu) {
+   dev_err(dev, sysmmu node not found\n);
+   ret = -ENODEV;
+   goto err;
+   }
+   data = platform_get_drvdata(sysmmu);
+   if (!data) {
+   ret = -ENODEV;
+   goto err;
+   }
+
+   of_property_read_string_index(dev-of_node, iommu-names, i,
+ data-name);
+
+   if (sysmmu_spec.args_count == 2) {
+   data-base = sysmmu_spec.args[0];
+   data-size = sysmmu_spec.args[1];
+   } else if (sysmmu_spec.args_count != 0) {
+   dev_err(dev, incorrect iommu property specified\n);
+   ret = -EINVAL;
+   goto err;
+   }
+
+   list_add_tail(data-owner_node, owner-clients);
+
+   i++;
+   }
+
+   if (i == 0) {
+   ret = -ENODEV;
+   goto err;
+   }
+
+   dev-archdata.iommu = owner;
+   dev_dbg(dev, registered %d sysmmu controllers\n, i);
+
+   return 0;
+err:
+   __free_owner_struct(owner, dev);
+   return ret;
+}
+
+static int __init_subdevice_sysmmu(struct device *dev)
+{
+   struct device *parent = dev-parent;
+   struct exynos_iommu_owner *owner;
+   struct sysmmu_drvdata *data;
+   char *name;
+
+   name = strrchr(dev_name(dev), ':');
+   if (!name)
+   return -ENODEV;
+   name++;
+
+   owner = parent-archdata.iommu;
+   if (!owner)
+   return -ENODEV;
+
+   list_for_each_entry(data, owner-clients, owner_node)
+   if (strcmp(name, data-name) == 0)
+   break;
+   if (!data)
+   return -ENODEV;
+
+   owner = kzalloc(sizeof(*owner), GFP_KERNEL);
+   if (!owner)
+   return -ENOMEM;
+   INIT_LIST_HEAD(owner-clients);
+
+   /* move sysmmu from parent to child device */
+   list_del(data-owner_node);
+   list_add_tail(data-owner_node, owner-clients);
+
+   dev-archdata.iommu = owner;
+   dev_dbg(dev-parent,
+   registered sysmmu controller for %s subdevice\n, data-name);
+
+   return 0;
+}
+
 static int exynos_iommu_add_device(struct device *dev)
 {
struct iommu_group *group;
int ret;
 
-   group = iommu_group_get(dev);
+   BUG_ON(dev-archdata.iommu != NULL);
 
-   if (!group) {
-   group = iommu_group_alloc();
-   if (IS_ERR(group)) {
-   dev_err(dev, Failed to 

[PATCH 27/29] iommu: exynos: document internal structures

2014-08-05 Thread Marek Szyprowski
Add a few words of comment to all internal structures used by the driver.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/iommu/exynos-iommu.c | 59 
 1 file changed, 38 insertions(+), 21 deletions(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 7600861..78ce618 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -189,37 +189,54 @@ 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;
-   int suspended;
-   spinlock_t lock;
-   struct iommu_domain *domain;
-   struct list_head domain_node;
-   struct list_head owner_node;
-   phys_addr_t pgtable;
-   int version;
-   const char *name;
-   dma_addr_t base;
-   size_t size;
-   struct notifier_block pm_notifier;
+   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 */
+   int suspended; /* status of the controller (managed by runtime pm) */
+   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 */
+   const char *name; /* our name from device tree */
+   dma_addr_t base; /* base addres of IO address space define in DT */
+   size_t size; /* size of IO address space define in DT */
+   struct notifier_block pm_notifier; /* notifier for pm domain on/off */
 };
 
 static bool set_sysmmu_active(struct sysmmu_drvdata *data)
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 21/29] iommu: exynos: remove unused functions, part 2

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 018a615..674d1fb 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -458,49 +458,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)
 {
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 26/29] iommu: exynos: rename variables to reflect their purpose

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 5cd91b11..7600861 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -440,8 +440,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;
@@ -449,7 +449,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);
 
@@ -664,92 +664,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) 

[PATCH 24/29] iommu: exynos: create default iommu-based dma-mapping for master devices

2014-08-05 Thread Marek Szyprowski
This patch adds code to create default per-device iommu-based
dma-mapping instance for all master devices, whose driver didn't set
DRIVER_HAS_OWN_IOMMU_MANAGER flag.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/iommu/exynos-iommu.c | 49 +++-
 1 file changed, 48 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 845f547..336b2f8 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -12,6 +12,7 @@
 #define DEBUG
 #endif
 
+#include linux/dma-mapping.h
 #include linux/io.h
 #include linux/interrupt.h
 #include linux/platform_device.h
@@ -29,6 +30,7 @@
 #include linux/export.h
 
 #include asm/cacheflush.h
+#include asm/dma-iommu.h
 #include asm/pgtable.h
 
 typedef u32 sysmmu_iova_t;
@@ -1064,6 +1066,43 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct 
iommu_domain *domain,
return phys;
 }
 
+static int sysmmu_master_device_event(struct notifier_block *nb,
+ unsigned long val, void *p)
+{
+   struct device *dev = p;
+   struct exynos_iommu_owner *owner = dev-archdata.iommu;
+   struct sysmmu_drvdata *data;
+
+   if (!owner)
+   return 0;
+
+   data = list_first_entry(owner-clients, struct sysmmu_drvdata,
+   owner_node);
+   if (!data)
+   return 0;
+
+   switch (val) {
+
+   case IOMMU_GROUP_NOTIFY_BIND_DRIVER:
+   if (!(dev-driver-flags  DRIVER_HAS_OWN_IOMMU_MANAGER))
+   arm_iommu_create_default_mapping(dev, data-base,
+data-size);
+   break;
+
+   case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER:
+   case IOMMU_GROUP_NOTIFY_DRVBIND_FAILED:
+   if (!(dev-driver-flags  DRIVER_HAS_OWN_IOMMU_MANAGER))
+   arm_iommu_release_default_mapping(dev);
+   break;
+   }
+
+   return 0;
+}
+
+static struct notifier_block sysmmu_master_device_notifier = {
+   .notifier_call = sysmmu_master_device_event,
+};
+
 static void __free_owner_struct(struct exynos_iommu_owner *owner,
struct device *dev)
 {
@@ -1202,6 +1241,7 @@ static int exynos_iommu_add_device(struct device *dev)
if (ret != 0)
goto err;
 
+   iommu_group_register_notifier(group, sysmmu_master_device_notifier);
iommu_group_put(group);
 
return 0;
@@ -1213,8 +1253,15 @@ err:
 static void exynos_iommu_remove_device(struct device *dev)
 {
struct exynos_iommu_owner *owner = dev-archdata.iommu;
+   struct iommu_group *group = iommu_group_get(dev);
+
+   if (group) {
+   iommu_group_unregister_notifier(group,
+   sysmmu_master_device_notifier);
+   iommu_group_remove_device(dev);
+   iommu_group_put(group);
+   }
 
-   iommu_group_remove_device(dev);
if (owner)
__free_owner_struct(owner, dev);
 }
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 28/29] iommu: exynos: remove excessive includes and sort others alphabetically

2014-08-05 Thread Marek Szyprowski
Removed following unused includes: linux/mm.h, linux/errno.h,
linux/memblock.h and linux/export.h.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/iommu/exynos-iommu.c | 18 +++---
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 78ce618..1bbb100 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -12,23 +12,19 @@
 #define DEBUG
 #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/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/list.h
 #include linux/of.h
 #include linux/of_platform.h
-#include linux/iommu.h
-#include linux/errno.h
-#include linux/list.h
-#include linux/memblock.h
-#include linux/export.h
+#include linux/platform_device.h
 #include linux/pm_domain.h
+#include linux/pm_runtime.h
+#include linux/slab.h
 
 #include asm/cacheflush.h
 #include asm/dma-iommu.h
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 22/29] iommu: exynos: add support for binding more than one sysmmu to master device

2014-08-05 Thread Marek Szyprowski
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.szyprow...@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 674d1fb..46e0edc 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -187,7 +187,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 {
@@ -208,6 +208,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;
 };
@@ -695,8 +696,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;
@@ -724,7 +724,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;
 
@@ -732,14 +732,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);
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 29/29] temporary: media: s5p-mfc: add support for IOMMU

2014-08-05 Thread Marek Szyprowski
This patch is an example how to use more than one IOMMU controller in a
device driver for hardware blocks, which have more then one dma master
(memory interface with iommu controller).

This patch is temporary, do not merge it yet.

Signed-off-by: Marek Szyprowski m.szyprow...@samsung.com
---
 drivers/media/platform/s5p-mfc/s5p_mfc.c | 46 ++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c 
b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 77b99ae..c2e95ab 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -22,7 +22,11 @@
 #include media/v4l2-event.h
 #include linux/workqueue.h
 #include linux/of.h
+#include linux/of_platform.h
 #include media/videobuf2-core.h
+
+#include asm/dma-iommu.h
+
 #include s5p_mfc_common.h
 #include s5p_mfc_ctrl.h
 #include s5p_mfc_debug.h
@@ -996,6 +1000,39 @@ static const struct v4l2_file_operations s5p_mfc_fops = {
.mmap = s5p_mfc_mmap,
 };
 
+static struct device *s5p_mfc_alloc_memdev(struct device *dev, const char 
*name)
+{
+   struct device *child;
+   int ret;
+
+   child = devm_kzalloc(dev, sizeof(struct device), GFP_KERNEL);
+   if (!child)
+   return NULL;
+
+   device_initialize(child);
+   dev_set_name(child, %s:%s, dev_name(dev), name);
+   child-parent = dev;
+   child-bus = dev-bus;
+   child-coherent_dma_mask = dev-coherent_dma_mask;
+   child-dma_mask = dev-dma_mask;
+
+   if (device_add(child) == 0) {
+   ret = arm_iommu_create_default_mapping(child, 0x2000,
+  SZ_256M);
+   if (ret == 0)
+   return child;
+   }
+
+   put_device(child);
+   return NULL;
+}
+
+void s5p_mfc_free_memdev(struct device *dev)
+{
+   arm_iommu_release_default_mapping(dev);
+   put_device(dev);
+}
+
 static void *mfc_get_drv_data(struct platform_device *pdev);
 
 /* MFC probe function */
@@ -1049,8 +1086,8 @@ static int s5p_mfc_probe(struct platform_device *pdev)
goto err_res;
}
 
-   dev-mem_dev_l = dev-plat_dev-dev;
-   dev-mem_dev_r = dev-plat_dev-dev;
+   dev-mem_dev_l = s5p_mfc_alloc_memdev(dev-plat_dev-dev, left);
+   dev-mem_dev_r = s5p_mfc_alloc_memdev(dev-plat_dev-dev, right);
 
dev-alloc_ctx[0] = vb2_dma_contig_init_ctx(dev-mem_dev_l);
if (IS_ERR(dev-alloc_ctx[0])) {
@@ -1181,6 +1218,10 @@ static int s5p_mfc_remove(struct platform_device *pdev)
s5p_mfc_release_firmware(dev);
vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[0]);
vb2_dma_contig_cleanup_ctx(dev-alloc_ctx[1]);
+   if (dev-mem_dev_l)
+   s5p_mfc_free_memdev(dev-mem_dev_l);
+   if (dev-mem_dev_r)
+   s5p_mfc_free_memdev(dev-mem_dev_r);
 
s5p_mfc_final_pm(dev);
return 0;
@@ -1436,6 +1477,7 @@ static struct platform_driver s5p_mfc_driver = {
.owner  = THIS_MODULE,
.pm = s5p_mfc_pm_ops,
.of_match_table = exynos_mfc_match,
+   .flags  = DRIVER_HAS_OWN_IOMMU_MANAGER,
},
 };
 
-- 
1.9.2

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] iommu/vt-d: Defer domain removal if device is assigned to a driver

2014-08-05 Thread Joerg Roedel
Hi,

On Mon, Aug 04, 2014 at 01:42:05PM +0200, Borislav Petkov wrote:
 It is always questionable when people remove BUG_ONs because relaxing
 assertions sound like a temporary fix more often than not. Sounds to me
 that the original commit which deals with BUS_NOTIFY_DEL_DEVICE needs to
 try again with the fix. :-)

Actually, as I thought about it again, there is a better fix for this
issue that does not require to remove the BUG_ON :) See attached patch:

From 57e2519d5b6e4d8ee840a921300d201ff742c826 Mon Sep 17 00:00:00 2001
From: Joerg Roedel jroe...@suse.de
Date: Tue, 5 Aug 2014 12:55:45 +0200
Subject: [PATCH] iommu/vt-d: Defer domain removal if device is assigned to a
 driver

When the BUS_NOTIFY_DEL_DEVICE event is received the device
might still be attached to a driver. In this case the domain
can't be released as the mappings might still be in use.

Defer the domain removal in this case until we receivce the
BUS_NOTIFY_UNBOUND_DRIVER event.

Cc: Jiang Liu jiang@linux.intel.com
Cc: David Woodhouse dw...@infradead.org
Cc: sta...@vger.kernel.org   # v3.15, v3.16
Signed-off-by: Joerg Roedel jroe...@suse.de
---
 drivers/iommu/intel-iommu.c | 8 
 1 file changed, 8 insertions(+)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index d1f5caa..5619f26 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3869,6 +3869,14 @@ static int device_notifier(struct notifier_block *nb,
action != BUS_NOTIFY_DEL_DEVICE)
return 0;
 
+   /*
+* If the device is still attached to a device driver we can't
+* tear down the domain yet as DMA mappings may still be in use.
+* Wait for the BUS_NOTIFY_UNBOUND_DRIVER event to do that.
+*/
+   if (action == BUS_NOTIFY_DEL_DEVICE  dev-driver != NULL)
+   return 0;
+
domain = find_domain(dev);
if (!domain)
return 0;
-- 
1.8.4.5

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 1/1] iommu-api: Add map_sg/unmap_sg functions

2014-08-05 Thread Konrad Rzeszutek Wilk
On Thu, Jul 31, 2014 at 05:54:44PM -0700, Olav Haugan wrote:
 Mapping and unmapping are more often than not in the critical path.
 map_sg and unmap_sg allows IOMMU driver implementations to optimize
 the process of mapping and unmapping buffers into the IOMMU page tables.
 
 Instead of mapping a buffer one page at a time and requiring potentially
 expensive TLB operations for each page, this function allows the driver
 to map all pages in one go and defer TLB maintenance until after all
 pages have been mapped.
 
 Additionally, the mapping operation would be faster in general since
 clients does not have to keep calling map API over and over again for
 each physically contiguous chunk of memory that needs to be mapped to a
 virtually contiguous region.

That is assuming that physical == bus topology.

 
 Signed-off-by: Olav Haugan ohau...@codeaurora.org
 ---
  drivers/iommu/iommu.c | 44 
  include/linux/iommu.h | 28 
  2 files changed, 72 insertions(+)
 
 diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
 index 1698360..1d5dc2e 100644
 --- a/drivers/iommu/iommu.c
 +++ b/drivers/iommu/iommu.c
 @@ -1088,6 +1088,50 @@ size_t iommu_unmap(struct iommu_domain *domain, 
 unsigned long iova, size_t size)
  }
  EXPORT_SYMBOL_GPL(iommu_unmap);
  
 +int iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
 + struct scatterlist *sg, unsigned int nents,
 + int prot, unsigned long flags)
 +{
 + int ret = 0;
 + unsigned long offset = 0;
 +
 + if (unlikely(domain-ops-map_sg == NULL)) {
 + unsigned int i;
 + struct scatterlist *s;
 +
 + for_each_sg(sg, s, nents, i) {
 + phys_addr_t phys = page_to_phys(sg_page(s));
 + size_t page_len = s-offset + s-length;
 +
 + ret = iommu_map(domain, iova + offset, phys, page_len,
 + prot);
 + if (ret)
 + goto fail;
 +
 + offset += page_len;
 + }

I think it would be better if you had an 'default_iommu_map_sg' with
the implementation above. And then the default ops-map_sg would point to
that and each IOMMU would over-write with its own version.

That way you don't need any of this 'if' and can have the 'iommu_map_sg'
be in the header file (either as static inline or an macro).


 + } else {
 + ret = domain-ops-map_sg(domain, iova, sg, nents, prot, flags);
 + }
 + goto out;
 +
 +fail:
 + /* undo mappings already done in case of error */
 + iommu_unmap(domain, iova, offset);
 +out:
 + return ret;
 +}
 +EXPORT_SYMBOL_GPL(iommu_map_sg);
 +
 +int iommu_unmap_sg(struct iommu_domain *domain, unsigned long iova,
 + size_t size, unsigned long flags)
 +{
 + if (unlikely(domain-ops-unmap_sg == NULL))
 + return iommu_unmap(domain, iova, size);
 + else
 + return domain-ops-unmap_sg(domain, iova, size, flags);
 +}
 +EXPORT_SYMBOL_GPL(iommu_unmap_sg);
  
  int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
  phys_addr_t paddr, u64 size, int prot)
 diff --git a/include/linux/iommu.h b/include/linux/iommu.h
 index 20f9a52..66ad543 100644
 --- a/include/linux/iommu.h
 +++ b/include/linux/iommu.h
 @@ -22,6 +22,7 @@
  #include linux/errno.h
  #include linux/err.h
  #include linux/types.h
 +#include linux/scatterlist.h
  #include trace/events/iommu.h
  
  #define IOMMU_READ   (1  0)
 @@ -93,6 +94,10 @@ enum iommu_attr {
   * @detach_dev: detach device from an iommu domain
   * @map: map a physically contiguous memory region to an iommu domain
   * @unmap: unmap a physically contiguous memory region from an iommu domain
 + * @map_sg: map a scatter-gather list of physically contiguous memory chunks
 + * to an iommu domain
 + * @unmap_sg: unmap a scatter-gather list of physically contiguous memory
 + * chunks from an iommu domain
   * @iova_to_phys: translate iova to physical address
   * @domain_has_cap: domain capabilities query
   * @add_device: add device to iommu grouping
 @@ -110,6 +115,11 @@ struct iommu_ops {
  phys_addr_t paddr, size_t size, int prot);
   size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
size_t size);
 + int (*map_sg)(struct iommu_domain *domain, unsigned long iova,
 + struct scatterlist *sg, unsigned int nents, int prot,
 + unsigned long flags);
 + int (*unmap_sg)(struct iommu_domain *domain, unsigned long iova,
 + size_t size, unsigned long flags);
   phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t 
 iova);
   int (*domain_has_cap)(struct iommu_domain *domain,
 unsigned long cap);
 @@ -153,6 +163,11 @@ extern int