summaryrefslogtreecommitdiff
path: root/arch/arm/plat-omap/iommu.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap/iommu.c')
-rw-r--r--arch/arm/plat-omap/iommu.c297
1 files changed, 169 insertions, 128 deletions
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c
index 8a51fd58f65..90818cbd745 100644
--- a/arch/arm/plat-omap/iommu.c
+++ b/arch/arm/plat-omap/iommu.c
@@ -16,8 +16,8 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
-#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/eventfd.h>
#include <asm/cacheflush.h>
@@ -72,9 +72,19 @@ EXPORT_SYMBOL_GPL(uninstall_iommu_arch);
* iommu_save_ctx - Save registers for pm off-mode support
* @obj: target iommu
**/
-void iommu_save_ctx(struct iommu *obj)
+u32 iommu_save_ctx(struct iommu *obj)
{
- arch_iommu->save_ctx(obj);
+ u32 err;
+
+ err = iommu_save_tlb_entries(obj);
+ if (err)
+ goto error;
+
+ arch_iommu->disable(obj);
+
+ return 0;
+error:
+ return err;
}
EXPORT_SYMBOL_GPL(iommu_save_ctx);
@@ -82,18 +92,30 @@ EXPORT_SYMBOL_GPL(iommu_save_ctx);
* iommu_restore_ctx - Restore registers for pm off-mode support
* @obj: target iommu
**/
-void iommu_restore_ctx(struct iommu *obj)
+u32 iommu_restore_ctx(struct iommu *obj)
{
- arch_iommu->restore_ctx(obj);
+ u32 err;
+
+ err = arch_iommu->enable(obj);
+ if (err)
+ goto error;
+
+ err = iommu_restore_tlb_entries(obj);
+ if (err)
+ goto error;
+
+ return 0;
+error:
+ return err;
}
EXPORT_SYMBOL_GPL(iommu_restore_ctx);
/**
* iommu_arch_version - Return running iommu arch version
**/
-u32 iommu_arch_version(void)
+u32 iommu_arch_version(struct iommu *obj)
{
- return arch_iommu->version;
+ return arch_iommu->get_version(obj);
}
EXPORT_SYMBOL_GPL(iommu_arch_version);
@@ -103,15 +125,7 @@ static int iommu_enable(struct iommu *obj)
if (!obj)
return -EINVAL;
-
- if (!arch_iommu)
- return -ENODEV;
-
- clk_enable(obj->clk);
-
err = arch_iommu->enable(obj);
-
- clk_disable(obj->clk);
return err;
}
@@ -119,12 +133,7 @@ static void iommu_disable(struct iommu *obj)
{
if (!obj)
return;
-
- clk_enable(obj->clk);
-
arch_iommu->disable(obj);
-
- clk_disable(obj->clk);
}
/*
@@ -247,8 +256,6 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
if (!obj || !obj->nr_tlb_entries || !e)
return -EINVAL;
- clk_enable(obj->clk);
-
iotlb_lock_get(obj, &l);
if (l.base == obj->nr_tlb_entries) {
dev_warn(obj->dev, "%s: preserve entries full\n", __func__);
@@ -276,10 +283,8 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
}
cr = iotlb_alloc_cr(obj, e);
- if (IS_ERR(cr)) {
- clk_disable(obj->clk);
+ if (IS_ERR(cr))
return PTR_ERR(cr);
- }
iotlb_load_cr(obj, cr);
kfree(cr);
@@ -291,7 +296,6 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
l.vict = l.base;
iotlb_lock_set(obj, &l);
out:
- clk_disable(obj->clk);
return err;
}
EXPORT_SYMBOL_GPL(load_iotlb_entry);
@@ -308,8 +312,6 @@ void flush_iotlb_page(struct iommu *obj, u32 da)
int i;
struct cr_regs cr;
- clk_enable(obj->clk);
-
for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
u32 start;
size_t bytes;
@@ -327,8 +329,6 @@ void flush_iotlb_page(struct iommu *obj, u32 da)
iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
}
}
- clk_disable(obj->clk);
-
if (i == obj->nr_tlb_entries)
dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
}
@@ -362,15 +362,10 @@ void flush_iotlb_all(struct iommu *obj)
{
struct iotlb_lock l;
- clk_enable(obj->clk);
-
l.base = 0;
l.vict = 0;
iotlb_lock_set(obj, &l);
-
iommu_write_reg(obj, 1, MMU_GFLUSH);
-
- clk_disable(obj->clk);
}
EXPORT_SYMBOL_GPL(flush_iotlb_all);
@@ -385,12 +380,75 @@ EXPORT_SYMBOL_GPL(flush_iotlb_all);
*/
void iommu_set_twl(struct iommu *obj, bool on)
{
- clk_enable(obj->clk);
arch_iommu->set_twl(obj, on);
- clk_disable(obj->clk);
}
EXPORT_SYMBOL_GPL(iommu_set_twl);
+/**
+ * save_tlb_entries - save a num of locked tlb entries
+ * @obj: target iommu
+ *
+ */
+u32 iommu_save_tlb_entries(struct iommu *obj)
+{
+ int i;
+ struct cr_regs cr_tmp;
+ struct iotlb_entry *e;
+
+ if (!obj || !obj->tlbs_e)
+ goto error;
+
+ e = obj->tlbs_e;
+ for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr_tmp) {
+ iotlb_cr_to_e(&cr_tmp, e);
+ dev_dbg(obj->dev, "%s: %08x %08x %d %d %d", __func__, e->da,
+ e->pa, e->pgsz, e->prsvd,
+ e->valid);
+ e++;
+ }
+
+ return 0;
+error:
+ return -EINVAL;
+}
+
+/**
+ * restore_tlb_entries - restor a num of locked tlb entries
+ * @obj: target iommu
+ *
+ * Function used to restore exclusively the valid TLB entries
+ * based on the e->valid value
+ *
+ */
+u32 iommu_restore_tlb_entries(struct iommu *obj)
+{
+ int i;
+ int status;
+ struct iotlb_entry *e;
+
+ if (!obj || !obj->tlbs_e)
+ goto error;
+
+ e = obj->tlbs_e;
+ for (i = 0; i < obj->nr_tlb_entries; i++) {
+ if (!e->prsvd) {
+ e++;
+ continue;
+ }
+ dev_dbg(obj->dev, "%s: %08x %08x %d %d %d", __func__, e->da,
+ e->pa, e->pgsz, e->prsvd,
+ e->valid);
+ status = load_iotlb_entry(obj, e);
+ if (status)
+ goto error;
+ e++;
+ }
+
+ return 0;
+error:
+ return -EINVAL;
+}
+
#if defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
ssize_t iommu_dump_ctx(struct iommu *obj, char *buf, ssize_t bytes)
@@ -398,12 +456,7 @@ ssize_t iommu_dump_ctx(struct iommu *obj, char *buf, ssize_t bytes)
if (!obj || !buf)
return -EINVAL;
- clk_enable(obj->clk);
-
bytes = arch_iommu->dump_ctx(obj, buf, bytes);
-
- clk_disable(obj->clk);
-
return bytes;
}
EXPORT_SYMBOL_GPL(iommu_dump_ctx);
@@ -415,9 +468,7 @@ static int __dump_tlb_entries(struct iommu *obj, struct cr_regs *crs, int num)
struct cr_regs tmp;
struct cr_regs *p = crs;
- clk_enable(obj->clk);
iotlb_lock_get(obj, &saved);
-
for_each_iotlb_cr(obj, num, i, tmp) {
if (!iotlb_cr_valid(&tmp))
continue;
@@ -425,8 +476,6 @@ static int __dump_tlb_entries(struct iommu *obj, struct cr_regs *crs, int num)
}
iotlb_lock_set(obj, &saved);
- clk_disable(obj->clk);
-
return p - crs;
}
@@ -471,22 +520,15 @@ EXPORT_SYMBOL_GPL(foreach_iommu_device);
*/
static void flush_iopgd_range(u32 *first, u32 *last)
{
- /* FIXME: L2 cache should be taken care of if it exists */
- do {
- asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd"
- : : "r" (first));
- first += L1_CACHE_BYTES / sizeof(*first);
- } while (first <= last);
+ dmac_flush_range(first, last);
+ outer_flush_range(virt_to_phys(first), virt_to_phys(last));
}
+
static void flush_iopte_range(u32 *first, u32 *last)
{
- /* FIXME: L2 cache should be taken care of if it exists */
- do {
- asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte"
- : : "r" (first));
- first += L1_CACHE_BYTES / sizeof(*first);
- } while (first <= last);
+ dmac_flush_range(first, last);
+ outer_flush_range(virt_to_phys(first), virt_to_phys(last));
}
static void iopte_free(u32 *iopte)
@@ -515,7 +557,7 @@ static u32 *iopte_alloc(struct iommu *obj, u32 *iopgd, u32 da)
return ERR_PTR(-ENOMEM);
*iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
- flush_iopgd_range(iopgd, iopgd);
+ flush_iopgd_range(iopgd, iopgd + 1);
dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
} else {
@@ -544,7 +586,7 @@ static int iopgd_alloc_section(struct iommu *obj, u32 da, u32 pa, u32 prot)
}
*iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
- flush_iopgd_range(iopgd, iopgd);
+ flush_iopgd_range(iopgd, iopgd + 1);
return 0;
}
@@ -561,7 +603,7 @@ static int iopgd_alloc_super(struct iommu *obj, u32 da, u32 pa, u32 prot)
for (i = 0; i < 16; i++)
*(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
- flush_iopgd_range(iopgd, iopgd + 15);
+ flush_iopgd_range(iopgd, iopgd + 16);
return 0;
}
@@ -574,7 +616,7 @@ static int iopte_alloc_page(struct iommu *obj, u32 da, u32 pa, u32 prot)
return PTR_ERR(iopte);
*iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
- flush_iopte_range(iopte, iopte);
+ flush_iopte_range(iopte, iopte + 1);
dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
__func__, da, pa, iopte, *iopte);
@@ -599,7 +641,7 @@ static int iopte_alloc_large(struct iommu *obj, u32 da, u32 pa, u32 prot)
for (i = 0; i < 16; i++)
*(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
- flush_iopte_range(iopte, iopte + 15);
+ flush_iopte_range(iopte, iopte + 16);
return 0;
}
@@ -750,7 +792,7 @@ size_t iopgtable_clear_entry(struct iommu *obj, u32 da)
}
EXPORT_SYMBOL_GPL(iopgtable_clear_entry);
-static void iopgtable_clear_entry_all(struct iommu *obj)
+void iopgtable_clear_entry_all(struct iommu *obj)
{
int i;
@@ -770,13 +812,43 @@ static void iopgtable_clear_entry_all(struct iommu *obj)
iopte_free(iopte_offset(iopgd, 0));
*iopgd = 0;
- flush_iopgd_range(iopgd, iopgd);
+ flush_iopgd_range(iopgd, iopgd + 1);
}
flush_iotlb_all(obj);
spin_unlock(&obj->page_table_lock);
}
+EXPORT_SYMBOL_GPL(iopgtable_clear_entry_all);
+
+void eventfd_notification(struct iommu *obj)
+{
+ struct iommu_event_ntfy *fd_reg;
+
+ list_for_each_entry(fd_reg, &obj->event_list, list)
+ eventfd_signal(fd_reg->evt_ctx, 1);
+}
+
+int iommu_notify_event(struct iommu *obj, int event, void *data)
+{
+ return raw_notifier_call_chain(&obj->notifier, event, data);
+}
+
+int iommu_register_notifier(struct iommu *obj, struct notifier_block *nb)
+{
+ if (!nb)
+ return -EINVAL;
+ return raw_notifier_chain_register(&obj->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(iommu_register_notifier);
+
+int iommu_unregister_notifier(struct iommu *obj, struct notifier_block *nb)
+{
+ if (!nb)
+ return -EINVAL;
+ return raw_notifier_chain_unregister(&obj->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(iommu_unregister_notifier);
/*
* Device IOMMU generic operations
@@ -790,12 +862,14 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
if (!obj->refcount)
return IRQ_NONE;
- clk_enable(obj->clk);
- errs = iommu_report_fault(obj, &da);
- clk_disable(obj->clk);
+ eventfd_notification(obj);
+ /* Dynamic loading TLB or PTE */
+ errs = iommu_notify_event(obj, IOMMU_FAULT, data);
- /* Fault callback or TLB/PTE Dynamic loading */
- if (obj->isr && !obj->isr(obj, da, errs, obj->isr_priv))
+ if (errs == NOTIFY_OK)
+ return IRQ_HANDLED;
+ errs = iommu_report_fault(obj, &da);
+ if (!errs)
return IRQ_HANDLED;
iommu_disable(obj);
@@ -858,28 +932,29 @@ struct iommu *iommu_get(const char *name)
int err = -ENOMEM;
struct device *dev;
struct iommu *obj;
-
+ int rev;
dev = driver_find_device(&omap_iommu_driver.driver, NULL, (void *)name,
device_match_by_alias);
if (!dev)
return ERR_PTR(-ENODEV);
-
obj = to_iommu(dev);
mutex_lock(&obj->iommu_lock);
-
if (obj->refcount++ == 0) {
err = iommu_enable(obj);
if (err)
goto err_enable;
+ if (!strcmp(obj->name, "ducati")) {
+ rev = GET_OMAP_REVISION();
+ if (rev == 0x0)
+ iommu_set_twl(obj, false);
+ }
+
flush_iotlb_all(obj);
}
-
if (!try_module_get(obj->owner))
goto err_module;
-
mutex_unlock(&obj->iommu_lock);
-
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
return obj;
@@ -949,62 +1024,39 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
{
int err = -ENODEV;
void *p;
- int irq;
struct iommu *obj;
- struct resource *res;
struct iommu_platform_data *pdata = pdev->dev.platform_data;
- if (pdev->num_resources != 2)
- return -EINVAL;
-
- obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj)
return -ENOMEM;
- obj->clk = clk_get(&pdev->dev, pdata->clk_name);
- if (IS_ERR(obj->clk))
- goto err_clk;
-
obj->nr_tlb_entries = pdata->nr_tlb_entries;
obj->name = pdata->name;
obj->dev = &pdev->dev;
- obj->ctx = (void *)obj + sizeof(*obj);
+ obj->pdev = pdev;
obj->da_start = pdata->da_start;
obj->da_end = pdata->da_end;
+ obj->tlbs_e = kzalloc(sizeof(struct iotlb_entry) * obj->nr_tlb_entries,
+ GFP_KERNEL);
+ if (!obj->tlbs_e)
+ goto error;
mutex_init(&obj->iommu_lock);
mutex_init(&obj->mmap_lock);
spin_lock_init(&obj->page_table_lock);
INIT_LIST_HEAD(&obj->mmap);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto err_mem;
- }
-
- res = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!res) {
- err = -EIO;
- goto err_mem;
- }
+ spin_lock_init(&obj->event_lock);
+ INIT_LIST_HEAD(&obj->event_list);
- obj->regbase = ioremap(res->start, resource_size(res));
- if (!obj->regbase) {
- err = -ENOMEM;
- goto err_ioremap;
- }
+ obj->regbase = pdata->io_base;
+ RAW_INIT_NOTIFIER_HEAD(&obj->notifier);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- err = -ENODEV;
- goto err_irq;
- }
- err = request_irq(irq, iommu_fault_handler, IRQF_SHARED,
+ err = request_irq(pdata->irq, iommu_fault_handler, IRQF_SHARED,
dev_name(&pdev->dev), obj);
if (err < 0)
- goto err_irq;
+ goto error;
platform_set_drvdata(pdev, obj);
p = (void *)__get_free_pages(GFP_KERNEL, get_order(IOPGD_TABLE_SIZE));
@@ -1022,36 +1074,24 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
return 0;
err_pgd:
- free_irq(irq, obj);
-err_irq:
- iounmap(obj->regbase);
-err_ioremap:
- release_mem_region(res->start, resource_size(res));
-err_mem:
- clk_put(obj->clk);
-err_clk:
+ free_irq(pdata->irq, obj);
+error:
kfree(obj);
return err;
}
static int __devexit omap_iommu_remove(struct platform_device *pdev)
{
- int irq;
- struct resource *res;
struct iommu *obj = platform_get_drvdata(pdev);
+ struct iommu_platform_data *pdata = pdev->dev.platform_data;
+
+ free_irq(pdata->irq, obj);
platform_set_drvdata(pdev, NULL);
iopgtable_clear_entry_all(obj);
free_pages((unsigned long)obj->iopgd, get_order(IOPGD_TABLE_SIZE));
- irq = platform_get_irq(pdev, 0);
- free_irq(irq, obj);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- iounmap(obj->regbase);
-
- clk_put(obj->clk);
dev_info(&pdev->dev, "%s removed\n", obj->name);
kfree(obj);
return 0;
@@ -1070,6 +1110,7 @@ static void iopte_cachep_ctor(void *iopte)
clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
}
+
static int __init omap_iommu_init(void)
{
struct kmem_cache *p;