summaryrefslogtreecommitdiff
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-omap2/Makefile5
-rw-r--r--arch/arm/mach-omap2/iommu2.c6
-rw-r--r--arch/arm/mach-omap2/omap-iommu.c157
-rw-r--r--arch/arm/mach-omap2/omap3-iommu.c105
-rw-r--r--arch/arm/plat-omap/Kconfig9
-rw-r--r--arch/arm/plat-omap/include/plat/omap44xx.h3
-rw-r--r--arch/arm/plat-omap/iommu.c101
-rw-r--r--arch/arm/plat-omap/iovmm.c9
8 files changed, 244 insertions, 151 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 4b9fc57770db..7d2cf0f714c0 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -89,10 +89,7 @@ obj-$(CONFIG_OMAP3_EMU) += emu.o
obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o
mailbox_mach-objs := mailbox.o
-iommu-y += iommu2.o
-iommu-$(CONFIG_ARCH_OMAP3) += omap3-iommu.o
-
-obj-$(CONFIG_OMAP_IOMMU) += $(iommu-y)
+obj-$(CONFIG_OMAP_IOMMU) := iommu2.o omap-iommu.o
i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o
obj-y += $(i2c-omap-m) $(i2c-omap-y)
diff --git a/arch/arm/mach-omap2/iommu2.c b/arch/arm/mach-omap2/iommu2.c
index 4f63dc6859a4..e82da680d908 100644
--- a/arch/arm/mach-omap2/iommu2.c
+++ b/arch/arm/mach-omap2/iommu2.c
@@ -147,6 +147,7 @@ static u32 omap2_iommu_fault_isr(struct iommu *obj, u32 *ra)
printk("\n");
iommu_write_reg(obj, stat, MMU_IRQSTATUS);
+ omap2_iommu_disable(obj);
return stat;
}
@@ -184,7 +185,7 @@ static struct cr_regs *omap2_alloc_cr(struct iommu *obj, struct iotlb_entry *e)
if (!cr)
return ERR_PTR(-ENOMEM);
- cr->cam = (e->da & MMU_CAM_VATAG_MASK) | e->prsvd | e->pgsz;
+ cr->cam = (e->da & MMU_CAM_VATAG_MASK) | e->prsvd | e->pgsz | e->valid;
cr->ram = e->pa | e->endian | e->elsz | e->mixed;
return cr;
@@ -212,7 +213,8 @@ static ssize_t omap2_dump_cr(struct iommu *obj, struct cr_regs *cr, char *buf)
char *p = buf;
/* FIXME: Need more detail analysis of cam/ram */
- p += sprintf(p, "%08x %08x\n", cr->cam, cr->ram);
+ p += sprintf(p, "%08x %08x %01x\n", cr->cam, cr->ram,
+ (cr->cam & MMU_CAM_P) ? 1 : 0);
return p - buf;
}
diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c
new file mode 100644
index 000000000000..eb9bee73e0cb
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-iommu.c
@@ -0,0 +1,157 @@
+/*
+ * omap iommu: omap device registration
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+
+#include <plat/iommu.h>
+#include <plat/irqs.h>
+
+struct iommu_device {
+ resource_size_t base;
+ int irq;
+ struct iommu_platform_data pdata;
+ struct resource res[2];
+};
+static struct iommu_device *devices;
+static int num_iommu_devices;
+
+#ifdef CONFIG_ARCH_OMAP3
+static struct iommu_device omap3_devices[] = {
+ {
+ .base = 0x480bd400,
+ .irq = 24,
+ .pdata = {
+ .name = "isp",
+ .nr_tlb_entries = 8,
+ .clk_name = "cam_ick",
+ },
+ },
+#if defined(CONFIG_MPU_BRIDGE_IOMMU)
+ {
+ .base = 0x5d000000,
+ .irq = 28,
+ .pdata = {
+ .name = "iva2",
+ .nr_tlb_entries = 32,
+ .clk_name = "iva2_ck",
+ },
+ },
+#endif
+};
+#define NR_OMAP3_IOMMU_DEVICES ARRAY_SIZE(omap3_devices)
+static struct platform_device *omap3_iommu_pdev[NR_OMAP3_IOMMU_DEVICES];
+#else
+#define omap3_devices NULL
+#define NR_OMAP3_IOMMU_DEVICES 0
+#define omap3_iommu_pdev NULL
+#endif
+
+#ifdef CONFIG_ARCH_OMAP4
+static struct iommu_device omap4_devices[] = {
+ {
+ .base = OMAP4_MMU1_BASE,
+ .irq = INT_44XX_DUCATI_MMU_IRQ,
+ .pdata = {
+ .name = "ducati",
+ .nr_tlb_entries = 32,
+ .clk_name = "ducati_ick",
+ },
+ },
+#if defined(CONFIG_MPU_TESLA_IOMMU)
+ {
+ .base = OMAP4_MMU2_BASE,
+ .irq = INT_44XX_DSP_MMU,
+ .pdata = {
+ .name = "tesla",
+ .nr_tlb_entries = 32,
+ .clk_name = "tesla_ick",
+ },
+ },
+#endif
+};
+#define NR_OMAP4_IOMMU_DEVICES ARRAY_SIZE(omap4_devices)
+static struct platform_device *omap4_iommu_pdev[NR_OMAP4_IOMMU_DEVICES];
+#else
+#define omap4_devices NULL
+#define NR_OMAP4_IOMMU_DEVICES 0
+#define omap4_iommu_pdev NULL
+#endif
+
+static struct platform_device **omap_iommu_pdev;
+
+static int __init omap_iommu_init(void)
+{
+ int i, err;
+ struct resource res[] = {
+ { .flags = IORESOURCE_MEM },
+ { .flags = IORESOURCE_IRQ },
+ };
+
+ if (cpu_is_omap34xx()) {
+ devices = omap3_devices;
+ omap_iommu_pdev = omap3_iommu_pdev;
+ num_iommu_devices = NR_OMAP3_IOMMU_DEVICES;
+ } else if (cpu_is_omap44xx()) {
+ devices = omap4_devices;
+ omap_iommu_pdev = omap4_iommu_pdev;
+ num_iommu_devices = NR_OMAP4_IOMMU_DEVICES;
+ } else
+ return -ENODEV;
+
+ for (i = 0; i < num_iommu_devices; i++) {
+ struct platform_device *pdev;
+ const struct iommu_device *d = &devices[i];
+
+ pdev = platform_device_alloc("omap-iommu", i);
+ if (!pdev) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ res[0].start = d->base;
+ res[0].end = d->base + MMU_REG_SIZE - 1;
+ res[1].start = res[1].end = d->irq;
+
+ err = platform_device_add_resources(pdev, res,
+ ARRAY_SIZE(res));
+ if (err)
+ goto err_out;
+ err = platform_device_add_data(pdev, &d->pdata,
+ sizeof(d->pdata));
+ if (err)
+ goto err_out;
+ err = platform_device_add(pdev);
+ if (err)
+ goto err_out;
+ omap_iommu_pdev[i] = pdev;
+ }
+ return 0;
+
+err_out:
+ while (i--)
+ platform_device_put(omap_iommu_pdev[i]);
+ return err;
+}
+module_init(omap_iommu_init);
+
+static void __exit omap_iommu_exit(void)
+{
+ int i;
+
+ for (i = 0; i < num_iommu_devices; i++)
+ platform_device_unregister(omap_iommu_pdev[i]);
+}
+module_exit(omap_iommu_exit);
+
+MODULE_AUTHOR("Hiroshi DOYU");
+MODULE_DESCRIPTION("omap iommu: omap device registration");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-omap2/omap3-iommu.c b/arch/arm/mach-omap2/omap3-iommu.c
deleted file mode 100644
index fbbcb5c83367..000000000000
--- a/arch/arm/mach-omap2/omap3-iommu.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * omap iommu: omap3 device registration
- *
- * Copyright (C) 2008-2009 Nokia Corporation
- *
- * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/platform_device.h>
-
-#include <plat/iommu.h>
-
-struct iommu_device {
- resource_size_t base;
- int irq;
- struct iommu_platform_data pdata;
- struct resource res[2];
-};
-
-static struct iommu_device devices[] = {
- {
- .base = 0x480bd400,
- .irq = 24,
- .pdata = {
- .name = "isp",
- .nr_tlb_entries = 8,
- .clk_name = "cam_ick",
- },
- },
-#if defined(CONFIG_MPU_BRIDGE_IOMMU)
- {
- .base = 0x5d000000,
- .irq = 28,
- .pdata = {
- .name = "iva2",
- .nr_tlb_entries = 32,
- .clk_name = "iva2_ck",
- },
- },
-#endif
-};
-#define NR_IOMMU_DEVICES ARRAY_SIZE(devices)
-
-static struct platform_device *omap3_iommu_pdev[NR_IOMMU_DEVICES];
-
-static int __init omap3_iommu_init(void)
-{
- int i, err;
- struct resource res[] = {
- { .flags = IORESOURCE_MEM },
- { .flags = IORESOURCE_IRQ },
- };
-
- for (i = 0; i < NR_IOMMU_DEVICES; i++) {
- struct platform_device *pdev;
- const struct iommu_device *d = &devices[i];
-
- pdev = platform_device_alloc("omap-iommu", i);
- if (!pdev) {
- err = -ENOMEM;
- goto err_out;
- }
-
- res[0].start = d->base;
- res[0].end = d->base + MMU_REG_SIZE - 1;
- res[1].start = res[1].end = d->irq;
-
- err = platform_device_add_resources(pdev, res,
- ARRAY_SIZE(res));
- if (err)
- goto err_out;
- err = platform_device_add_data(pdev, &d->pdata,
- sizeof(d->pdata));
- if (err)
- goto err_out;
- err = platform_device_add(pdev);
- if (err)
- goto err_out;
- omap3_iommu_pdev[i] = pdev;
- }
- return 0;
-
-err_out:
- while (i--)
- platform_device_put(omap3_iommu_pdev[i]);
- return err;
-}
-module_init(omap3_iommu_init);
-
-static void __exit omap3_iommu_exit(void)
-{
- int i;
-
- for (i = 0; i < NR_IOMMU_DEVICES; i++)
- platform_device_unregister(omap3_iommu_pdev[i]);
-}
-module_exit(omap3_iommu_exit);
-
-MODULE_AUTHOR("Hiroshi DOYU");
-MODULE_DESCRIPTION("omap iommu: omap3 device registration");
-MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 6da796ef82bd..78b49a626d06 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -110,8 +110,13 @@ config OMAP_IOMMU
tristate
config OMAP_IOMMU_DEBUG
- depends on OMAP_IOMMU
- tristate
+ tristate "Export OMAP IOMMU internals in DebugFS"
+ depends on OMAP_IOMMU && DEBUG_FS
+ help
+ Select this to see extensive information about
+ the internal state of OMAP IOMMU in debugfs.
+
+ Say N unless you know you need this.
choice
prompt "System timer"
diff --git a/arch/arm/plat-omap/include/plat/omap44xx.h b/arch/arm/plat-omap/include/plat/omap44xx.h
index b3ef1a7f53cc..bb94a0baee8a 100644
--- a/arch/arm/plat-omap/include/plat/omap44xx.h
+++ b/arch/arm/plat-omap/include/plat/omap44xx.h
@@ -48,5 +48,8 @@
#define OMAP44XX_MAILBOX_BASE (L4_44XX_BASE + 0xF4000)
#define OMAP44XX_HSUSB_OTG_BASE (L4_44XX_BASE + 0xAB000)
+#define OMAP4_MMU1_BASE 0x55082000
+#define OMAP4_MMU2_BASE 0x4A066000
+
#endif /* __ASM_ARCH_OMAP44XX_H */
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c
index 0e137663349c..bc094dbacee6 100644
--- a/arch/arm/plat-omap/iommu.c
+++ b/arch/arm/plat-omap/iommu.c
@@ -25,6 +25,11 @@
#include "iopgtable.h"
+#define for_each_iotlb_cr(obj, n, __i, cr) \
+ for (__i = 0; \
+ (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
+ __i++)
+
/* accommodate the difference between omap1 and omap2/3 */
static const struct iommu_functions *arch_iommu;
@@ -172,15 +177,12 @@ static void iotlb_lock_get(struct iommu *obj, struct iotlb_lock *l)
l->base = MMU_LOCK_BASE(val);
l->vict = MMU_LOCK_VICT(val);
- BUG_ON(l->base != 0); /* Currently no preservation is used */
}
static void iotlb_lock_set(struct iommu *obj, struct iotlb_lock *l)
{
u32 val;
- BUG_ON(l->base != 0); /* Currently no preservation is used */
-
val = (l->base << MMU_LOCK_BASE_SHIFT);
val |= (l->vict << MMU_LOCK_VICT_SHIFT);
@@ -214,6 +216,20 @@ static inline ssize_t iotlb_dump_cr(struct iommu *obj, struct cr_regs *cr,
return arch_iommu->dump_cr(obj, cr, buf);
}
+/* only used in iotlb iteration for-loop */
+static struct cr_regs __iotlb_read_cr(struct iommu *obj, int n)
+{
+ struct cr_regs cr;
+ struct iotlb_lock l;
+
+ iotlb_lock_get(obj, &l);
+ l.vict = n;
+ iotlb_lock_set(obj, &l);
+ iotlb_read_cr(obj, &cr);
+
+ return cr;
+}
+
/**
* load_iotlb_entry - Set an iommu tlb entry
* @obj: target iommu
@@ -221,7 +237,6 @@ static inline ssize_t iotlb_dump_cr(struct iommu *obj, struct cr_regs *cr,
**/
int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
{
- int i;
int err = 0;
struct iotlb_lock l;
struct cr_regs *cr;
@@ -231,21 +246,30 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
clk_enable(obj->clk);
- for (i = 0; i < obj->nr_tlb_entries; i++) {
+ iotlb_lock_get(obj, &l);
+ if (l.base == obj->nr_tlb_entries) {
+ dev_warn(obj->dev, "%s: preserve entries full\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+ if (!e->prsvd) {
+ int i;
struct cr_regs tmp;
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, tmp)
+ if (!iotlb_cr_valid(&tmp))
+ break;
+
+ if (i == obj->nr_tlb_entries) {
+ dev_dbg(obj->dev, "%s: full: no entry\n", __func__);
+ err = -EBUSY;
+ goto out;
+ }
+
iotlb_lock_get(obj, &l);
- l.vict = i;
+ } else {
+ l.vict = l.base;
iotlb_lock_set(obj, &l);
- iotlb_read_cr(obj, &tmp);
- if (!iotlb_cr_valid(&tmp))
- break;
- }
-
- if (i == obj->nr_tlb_entries) {
- dev_dbg(obj->dev, "%s: full: no entry\n", __func__);
- err = -EBUSY;
- goto out;
}
cr = iotlb_alloc_cr(obj, e);
@@ -257,9 +281,11 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
iotlb_load_cr(obj, cr);
kfree(cr);
+ if (e->prsvd)
+ l.base++;
/* increment victim for next tlb load */
if (++l.vict == obj->nr_tlb_entries)
- l.vict = 0;
+ l.vict = l.base;
iotlb_lock_set(obj, &l);
out:
clk_disable(obj->clk);
@@ -276,20 +302,15 @@ EXPORT_SYMBOL_GPL(load_iotlb_entry);
**/
void flush_iotlb_page(struct iommu *obj, u32 da)
{
- struct iotlb_lock l;
int i;
+ struct cr_regs cr;
clk_enable(obj->clk);
- for (i = 0; i < obj->nr_tlb_entries; i++) {
- struct cr_regs cr;
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
u32 start;
size_t bytes;
- iotlb_lock_get(obj, &l);
- l.vict = i;
- iotlb_lock_set(obj, &l);
- iotlb_read_cr(obj, &cr);
if (!iotlb_cr_valid(&cr))
continue;
@@ -299,7 +320,6 @@ void flush_iotlb_page(struct iommu *obj, u32 da)
if ((start <= da) && (da < start + bytes)) {
dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n",
__func__, start, da, bytes);
- iotlb_load_cr(obj, &cr);
iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
}
}
@@ -370,26 +390,19 @@ EXPORT_SYMBOL_GPL(iommu_dump_ctx);
static int __dump_tlb_entries(struct iommu *obj, struct cr_regs *crs, int num)
{
int i;
- struct iotlb_lock saved, l;
+ struct iotlb_lock saved;
+ struct cr_regs tmp;
struct cr_regs *p = crs;
clk_enable(obj->clk);
-
iotlb_lock_get(obj, &saved);
- memcpy(&l, &saved, sizeof(saved));
- for (i = 0; i < num; i++) {
- struct cr_regs tmp;
-
- iotlb_lock_get(obj, &l);
- l.vict = i;
- iotlb_lock_set(obj, &l);
- iotlb_read_cr(obj, &tmp);
+ for_each_iotlb_cr(obj, num, i, tmp) {
if (!iotlb_cr_valid(&tmp))
continue;
-
*p++ = tmp;
}
+
iotlb_lock_set(obj, &saved);
clk_disable(obj->clk);
@@ -503,6 +516,12 @@ static int iopgd_alloc_section(struct iommu *obj, u32 da, u32 pa, u32 prot)
{
u32 *iopgd = iopgd_offset(obj, da);
+ if ((da | pa) & ~IOSECTION_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOSECTION_SIZE);
+ return -EINVAL;
+ }
+
*iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
flush_iopgd_range(iopgd, iopgd);
return 0;
@@ -513,6 +532,12 @@ static int iopgd_alloc_super(struct iommu *obj, u32 da, u32 pa, u32 prot)
u32 *iopgd = iopgd_offset(obj, da);
int i;
+ if ((da | pa) & ~IOSUPER_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOSUPER_SIZE);
+ return -EINVAL;
+ }
+
for (i = 0; i < 16; i++)
*(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
flush_iopgd_range(iopgd, iopgd + 15);
@@ -542,6 +567,12 @@ static int iopte_alloc_large(struct iommu *obj, u32 da, u32 pa, u32 prot)
u32 *iopte = iopte_alloc(obj, iopgd, da);
int i;
+ if ((da | pa) & ~IOLARGE_MASK) {
+ dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
+ __func__, da, pa, IOLARGE_SIZE);
+ return -EINVAL;
+ }
+
if (IS_ERR(iopte))
return PTR_ERR(iopte);
diff --git a/arch/arm/plat-omap/iovmm.c b/arch/arm/plat-omap/iovmm.c
index 65c6d1ff7237..e43983ba59c5 100644
--- a/arch/arm/plat-omap/iovmm.c
+++ b/arch/arm/plat-omap/iovmm.c
@@ -287,16 +287,19 @@ static struct iovm_struct *alloc_iovm_area(struct iommu *obj, u32 da,
prev_end = 0;
list_for_each_entry(tmp, &obj->mmap, list) {
- if ((prev_end <= start) && (start + bytes < tmp->da_start))
+ if (prev_end >= start)
+ break;
+
+ if (start + bytes < tmp->da_start)
goto found;
if (flags & IOVMF_DA_ANON)
- start = roundup(tmp->da_end, alignement);
+ start = roundup(tmp->da_end + 1, alignement);
prev_end = tmp->da_end;
}
- if ((start >= prev_end) && (ULONG_MAX - start >= bytes))
+ if ((start > prev_end) && (ULONG_MAX - start >= bytes))
goto found;
dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n",