|
@@ -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);
|
|
|
|