summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2015-05-19 15:20:44 +0200
committerSeung-Woo Kim <sw0312.kim@samsung.com>2016-12-14 13:45:13 +0900
commit82e325004dd397ff781c12266d2212f72c47089c (patch)
treed9331484e58c8b5029f42c6e7e09ef6b33192299
parent2b56dc6190e196b2e065e8e4a1044955d78bda03 (diff)
downloadlinux-exynos-82e325004dd397ff781c12266d2212f72c47089c.tar.gz
linux-exynos-82e325004dd397ff781c12266d2212f72c47089c.tar.bz2
linux-exynos-82e325004dd397ff781c12266d2212f72c47089c.zip
ARM: DMA-mapping: Update to v7: add support for creating reserved mappings in iova space
Update to v7 of Marek Szyprowski's Exynos SYSMMU (IOMMU) integration with DT and DMA-mapping subsystem. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
-rw-r--r--Documentation/devicetree/bindings/iommu/iommu.txt44
-rw-r--r--arch/arm/mm/dma-mapping.c29
2 files changed, 67 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/iommu/iommu.txt b/Documentation/devicetree/bindings/iommu/iommu.txt
index 5a8b4624defc..da620d1ff976 100644
--- a/Documentation/devicetree/bindings/iommu/iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/iommu.txt
@@ -86,6 +86,35 @@ have a means to turn off translation. But it is invalid in such cases to
disable the IOMMU's device tree node in the first place because it would
prevent any driver from properly setting up the translations.
+Optional properties:
+--------------------
+- iommu-reserved-mapping: A list of entries describing additional
+ reserved mapping, that will be inserted to the default IO address space
+ created for given master device. Each entry consist of IO address,
+ physical memory address and size of the region.
+
+Some devices (like frame buffers) are enabled by bootloader and configured
+to perform DMA operations automatically (like displaying boot logo or splash
+screen). Such devices operate and perform DMA operation usually until the
+proper driver for them is loaded and probed. However before that happens,
+system usually loads IOMMU drivers and configures DMA parameters for each
+device. When such initial configuration is created and enabled, it usually
+contains empty translation rules between IO address space and physical
+memory, because no buffers nor memory regions have been requested by the
+respective driver.
+
+To avoid IOMMU page fault, one can provide "iommu-reserved-mapping"
+property, which defines all memory regions which must be mapped to IO
+address space to boot properly when device has been enabled by the
+bootloader. More than one region can be defined for given master device.
+Each region is defined by the following triplet: first entry is IO
+address (encoded in "address" cells), second is base physical memory
+address for this regions (also encoded in "address" cells) and the last
+is size of the region (encoded in "size" cells). To ensure that that
+given master device will not trigger page fault after enabling IOMMU,
+one should define identity mapping between physical memory and IO
+address space for the range of addresses accessed by the device.
+
Notes:
======
@@ -113,6 +142,21 @@ Single-master IOMMU:
iommus = <&{/iommu}>;
};
+
+Single-master IOMMU, which has been left enabled by bootloader:
+---------------------------------------------------------------
+
+ iommu {
+ #iommu-cells = <0>;
+ };
+
+ master {
+ iommus = <&{/iommu}>;
+ /* bootloader configures framebuffer at 0x40000000 (32MiB)
+ iommu-reserved-mapping = <0x40000000 0x40000000 0x2000000>;
+ };
+
+
Multiple-master IOMMU with fixed associations:
----------------------------------------------
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 55add24c3b4c..c36abc603712 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -2128,7 +2128,7 @@ static int arm_iommu_init_reserved(struct device *dev,
{
const char *name = "iommu-reserved-mapping";
const __be32 *prop = NULL;
- int len, naddr, nsize;
+ int ret = 0, len, naddr, nsize, regions, cells;
struct device_node *node = dev->of_node;
phys_addr_t phys;
dma_addr_t dma;
@@ -2145,18 +2145,27 @@ static int arm_iommu_init_reserved(struct device *dev,
return 0;
len /= sizeof(u32);
+ cells = 2 * naddr + nsize;
+ regions = len / cells;
- if (len < 2 * naddr + nsize) {
+ if (len % cells) {
dev_err(dev, "invalid length (%d cells) of %s property\n",
len, name);
return -EINVAL;
}
- phys = of_read_number(prop, naddr);
- dma = of_read_number(prop + naddr, naddr);
- size = of_read_number(prop + 2*naddr, nsize);
+ while (regions--) {
+ phys = of_read_number(prop, naddr);
+ dma = of_read_number(prop + naddr, naddr);
+ size = of_read_number(prop + 2*naddr, nsize);
+ prop += cells;
- return arm_iommu_add_reserved(dev, domain, phys, dma, size);
+ ret = arm_iommu_add_reserved(dev, domain, phys, dma, size);
+ if (ret)
+ break;
+ }
+
+ return ret;
}
static struct dma_map_ops *arm_get_iommu_dma_map_ops(bool coherent)
@@ -2187,6 +2196,14 @@ static bool arm_setup_iommu_dma_ops(struct device *dev, u64 dma_base, u64 size,
return false;
}
+ if (arm_iommu_init_reserved(dev, mapping) != 0) {
+ pr_warn("Failed to initialize reserved mapping for device %s\n",
+ dev_name(dev));
+ __arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(mapping);
+ return false;
+ }
+
if (__arm_iommu_attach_device(dev, mapping)) {
pr_warn("Failed to attached device %s to IOMMU_mapping\n",
dev_name(dev));