Merge branch 'v2.6.34-rc7.iommu' of git://gitorious.org/~doyu/lk/mainline into omap...
[pandora-kernel.git] / arch / arm / plat-omap / iommu.c
index 905ed83..bc094db 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/err.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/clk.h>
 
 #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;
 
@@ -171,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);
 
@@ -213,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
@@ -220,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;
@@ -230,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);
@@ -256,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);
@@ -275,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;
 
@@ -298,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);
                }
        }
@@ -369,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);
 
@@ -502,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;
@@ -512,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);
@@ -541,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);