clocks: tegra12: Use static CPU-EMC co-relation
[linux-3.10.git] / drivers / iommu / omap-iommu.c
index bd5f606..e02e5d7 100644 (file)
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
-#include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/iommu.h>
+#include <linux/omap-iommu.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
 
 #include <asm/cacheflush.h>
 
-#include <plat/iommu.h>
+#include <linux/platform_data/iommu-omap.h>
 
-#include <plat/iopgtable.h>
+#include "omap-iopgtable.h"
+#include "omap-iommu.h"
 
 #define for_each_iotlb_cr(obj, n, __i, cr)                             \
        for (__i = 0;                                                   \
             (__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true);   \
             __i++)
 
+/* bitmap of the page sizes currently supported */
+#define OMAP_IOMMU_PGSIZES     (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+
 /**
  * struct omap_iommu_domain - omap iommu domain
  * @pgtable:   the page table
  * @iommu_dev: an omap iommu device attached to this domain. only a single
  *             iommu device can be attached for now.
+ * @dev:       Device using this domain.
  * @lock:      domain lock, should be taken when attaching/detaching
  */
 struct omap_iommu_domain {
        u32 *pgtable;
        struct omap_iommu *iommu_dev;
+       struct device *dev;
        spinlock_t lock;
 };
 
+#define MMU_LOCK_BASE_SHIFT    10
+#define MMU_LOCK_BASE_MASK     (0x1f << MMU_LOCK_BASE_SHIFT)
+#define MMU_LOCK_BASE(x)       \
+       ((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT)
+
+#define MMU_LOCK_VICT_SHIFT    4
+#define MMU_LOCK_VICT_MASK     (0x1f << MMU_LOCK_VICT_SHIFT)
+#define MMU_LOCK_VICT(x)       \
+       ((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT)
+
+struct iotlb_lock {
+       short base;
+       short vict;
+};
+
 /* accommodate the difference between omap1 and omap2/3 */
 static const struct iommu_functions *arch_iommu;
 
@@ -86,20 +109,24 @@ EXPORT_SYMBOL_GPL(omap_uninstall_iommu_arch);
 
 /**
  * omap_iommu_save_ctx - Save registers for pm off-mode support
- * @obj:       target iommu
+ * @dev:       client device
  **/
-void omap_iommu_save_ctx(struct omap_iommu *obj)
+void omap_iommu_save_ctx(struct device *dev)
 {
+       struct omap_iommu *obj = dev_to_omap_iommu(dev);
+
        arch_iommu->save_ctx(obj);
 }
 EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
 
 /**
  * omap_iommu_restore_ctx - Restore registers for pm off-mode support
- * @obj:       target iommu
+ * @dev:       client device
  **/
-void omap_iommu_restore_ctx(struct omap_iommu *obj)
+void omap_iommu_restore_ctx(struct device *dev)
 {
+       struct omap_iommu *obj = dev_to_omap_iommu(dev);
+
        arch_iommu->restore_ctx(obj);
 }
 EXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
@@ -116,31 +143,44 @@ EXPORT_SYMBOL_GPL(omap_iommu_arch_version);
 static int iommu_enable(struct omap_iommu *obj)
 {
        int err;
+       struct platform_device *pdev = to_platform_device(obj->dev);
+       struct iommu_platform_data *pdata = pdev->dev.platform_data;
 
-       if (!obj)
+       if (!pdata)
                return -EINVAL;
 
        if (!arch_iommu)
                return -ENODEV;
 
-       clk_enable(obj->clk);
+       if (pdata->deassert_reset) {
+               err = pdata->deassert_reset(pdev, pdata->reset_name);
+               if (err) {
+                       dev_err(obj->dev, "deassert_reset failed: %d\n", err);
+                       return err;
+               }
+       }
+
+       pm_runtime_get_sync(obj->dev);
 
        err = arch_iommu->enable(obj);
 
-       clk_disable(obj->clk);
        return err;
 }
 
 static void iommu_disable(struct omap_iommu *obj)
 {
-       if (!obj)
-               return;
+       struct platform_device *pdev = to_platform_device(obj->dev);
+       struct iommu_platform_data *pdata = pdev->dev.platform_data;
 
-       clk_enable(obj->clk);
+       if (!pdata)
+               return;
 
        arch_iommu->disable(obj);
 
-       clk_disable(obj->clk);
+       pm_runtime_put_sync(obj->dev);
+
+       if (pdata->assert_reset)
+               pdata->assert_reset(pdev, pdata->reset_name);
 }
 
 /*
@@ -263,7 +303,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
        if (!obj || !obj->nr_tlb_entries || !e)
                return -EINVAL;
 
-       clk_enable(obj->clk);
+       pm_runtime_get_sync(obj->dev);
 
        iotlb_lock_get(obj, &l);
        if (l.base == obj->nr_tlb_entries) {
@@ -293,7 +333,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
 
        cr = iotlb_alloc_cr(obj, e);
        if (IS_ERR(cr)) {
-               clk_disable(obj->clk);
+               pm_runtime_put_sync(obj->dev);
                return PTR_ERR(cr);
        }
 
@@ -307,7 +347,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
                l.vict = l.base;
        iotlb_lock_set(obj, &l);
 out:
-       clk_disable(obj->clk);
+       pm_runtime_put_sync(obj->dev);
        return err;
 }
 
@@ -337,7 +377,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
        int i;
        struct cr_regs cr;
 
-       clk_enable(obj->clk);
+       pm_runtime_get_sync(obj->dev);
 
        for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
                u32 start;
@@ -356,7 +396,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
                        iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
                }
        }
-       clk_disable(obj->clk);
+       pm_runtime_put_sync(obj->dev);
 
        if (i == obj->nr_tlb_entries)
                dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
@@ -370,7 +410,7 @@ static void flush_iotlb_all(struct omap_iommu *obj)
 {
        struct iotlb_lock l;
 
-       clk_enable(obj->clk);
+       pm_runtime_get_sync(obj->dev);
 
        l.base = 0;
        l.vict = 0;
@@ -378,21 +418,21 @@ static void flush_iotlb_all(struct omap_iommu *obj)
 
        iommu_write_reg(obj, 1, MMU_GFLUSH);
 
-       clk_disable(obj->clk);
+       pm_runtime_put_sync(obj->dev);
 }
 
-#if defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
+#if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
 
 ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes)
 {
        if (!obj || !buf)
                return -EINVAL;
 
-       clk_enable(obj->clk);
+       pm_runtime_get_sync(obj->dev);
 
        bytes = arch_iommu->dump_ctx(obj, buf, bytes);
 
-       clk_disable(obj->clk);
+       pm_runtime_put_sync(obj->dev);
 
        return bytes;
 }
@@ -406,7 +446,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
        struct cr_regs tmp;
        struct cr_regs *p = crs;
 
-       clk_enable(obj->clk);
+       pm_runtime_get_sync(obj->dev);
        iotlb_lock_get(obj, &saved);
 
        for_each_iotlb_cr(obj, num, i, tmp) {
@@ -416,7 +456,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
        }
 
        iotlb_lock_set(obj, &saved);
-       clk_disable(obj->clk);
+       pm_runtime_put_sync(obj->dev);
 
        return  p - crs;
 }
@@ -775,18 +815,17 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
        u32 da, errs;
        u32 *iopgd, *iopte;
        struct omap_iommu *obj = data;
+       struct iommu_domain *domain = obj->domain;
 
        if (!obj->refcount)
                return IRQ_NONE;
 
-       clk_enable(obj->clk);
        errs = iommu_report_fault(obj, &da);
-       clk_disable(obj->clk);
        if (errs == 0)
                return IRQ_HANDLED;
 
        /* Fault callback or TLB/PTE Dynamic loading */
-       if (obj->isr && !obj->isr(obj, da, errs, obj->isr_priv))
+       if (!report_iommu_fault(domain, obj->dev, da, 0))
                return IRQ_HANDLED;
 
        iommu_disable(obj);
@@ -819,35 +858,23 @@ static int device_match_by_alias(struct device *dev, void *data)
 }
 
 /**
- * omap_find_iommu_device() - find an omap iommu device by name
- * @name:      name of the iommu device
- *
- * The generic iommu API requires the caller to provide the device
- * he wishes to attach to a certain iommu domain.
- *
- * Drivers generally should not bother with this as it should just
- * be taken care of by the DMA-API using dev_archdata.
- *
- * This function is provided as an interim solution until the latter
- * materializes, and omap3isp is fully migrated to the DMA-API.
- */
-struct device *omap_find_iommu_device(const char *name)
-{
-       return driver_find_device(&omap_iommu_driver.driver, NULL,
-                               (void *)name,
-                               device_match_by_alias);
-}
-EXPORT_SYMBOL_GPL(omap_find_iommu_device);
-
-/**
  * omap_iommu_attach() - attach iommu device to an iommu domain
- * @dev:       target omap iommu device
+ * @name:      name of target omap iommu device
  * @iopgd:     page table
  **/
-static struct omap_iommu *omap_iommu_attach(struct device *dev, u32 *iopgd)
+static struct omap_iommu *omap_iommu_attach(const char *name, u32 *iopgd)
 {
        int err = -ENOMEM;
-       struct omap_iommu *obj = to_iommu(dev);
+       struct device *dev;
+       struct omap_iommu *obj;
+
+       dev = driver_find_device(&omap_iommu_driver.driver, NULL,
+                               (void *)name,
+                               device_match_by_alias);
+       if (!dev)
+               return NULL;
+
+       obj = to_iommu(dev);
 
        spin_lock(&obj->iommu_lock);
 
@@ -904,37 +931,10 @@ static void omap_iommu_detach(struct omap_iommu *obj)
        dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
 }
 
-int omap_iommu_set_isr(const char *name,
-                 int (*isr)(struct omap_iommu *obj, u32 da, u32 iommu_errs,
-                            void *priv),
-                 void *isr_priv)
-{
-       struct device *dev;
-       struct omap_iommu *obj;
-
-       dev = driver_find_device(&omap_iommu_driver.driver, NULL, (void *)name,
-                                device_match_by_alias);
-       if (!dev)
-               return -ENODEV;
-
-       obj = to_iommu(dev);
-       spin_lock(&obj->iommu_lock);
-       if (obj->refcount != 0) {
-               spin_unlock(&obj->iommu_lock);
-               return -EBUSY;
-       }
-       obj->isr = isr;
-       obj->isr_priv = isr_priv;
-       spin_unlock(&obj->iommu_lock);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(omap_iommu_set_isr);
-
 /*
  *     OMAP Device MMU(IOMMU) detection
  */
-static int __devinit omap_iommu_probe(struct platform_device *pdev)
+static int omap_iommu_probe(struct platform_device *pdev)
 {
        int err = -ENODEV;
        int irq;
@@ -942,17 +942,10 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
        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);
        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;
@@ -995,6 +988,9 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
                goto err_irq;
        platform_set_drvdata(pdev, obj);
 
+       pm_runtime_irq_safe(obj->dev);
+       pm_runtime_enable(obj->dev);
+
        dev_info(&pdev->dev, "%s registered\n", obj->name);
        return 0;
 
@@ -1003,13 +999,11 @@ err_irq:
 err_ioremap:
        release_mem_region(res->start, resource_size(res));
 err_mem:
-       clk_put(obj->clk);
-err_clk:
        kfree(obj);
        return err;
 }
 
-static int __devexit omap_iommu_remove(struct platform_device *pdev)
+static int omap_iommu_remove(struct platform_device *pdev)
 {
        int irq;
        struct resource *res;
@@ -1025,7 +1019,8 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev)
        release_mem_region(res->start, resource_size(res));
        iounmap(obj->regbase);
 
-       clk_put(obj->clk);
+       pm_runtime_disable(obj->dev);
+
        dev_info(&pdev->dev, "%s removed\n", obj->name);
        kfree(obj);
        return 0;
@@ -1033,7 +1028,7 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev)
 
 static struct platform_driver omap_iommu_driver = {
        .probe  = omap_iommu_probe,
-       .remove = __devexit_p(omap_iommu_remove),
+       .remove = omap_iommu_remove,
        .driver = {
                .name   = "omap-iommu",
        },
@@ -1044,13 +1039,29 @@ static void iopte_cachep_ctor(void *iopte)
        clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
 }
 
+static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa,
+                                  u32 flags)
+{
+       memset(e, 0, sizeof(*e));
+
+       e->da           = da;
+       e->pa           = pa;
+       e->valid        = 1;
+       /* FIXME: add OMAP1 support */
+       e->pgsz         = flags & MMU_CAM_PGSZ_MASK;
+       e->endian       = flags & MMU_RAM_ENDIAN_MASK;
+       e->elsz         = flags & MMU_RAM_ELSZ_MASK;
+       e->mixed        = flags & MMU_RAM_MIXED_MASK;
+
+       return iopgsz_to_bytes(e->pgsz);
+}
+
 static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
-                        phys_addr_t pa, int order, int prot)
+                        phys_addr_t pa, size_t bytes, int prot)
 {
        struct omap_iommu_domain *omap_domain = domain->priv;
        struct omap_iommu *oiommu = omap_domain->iommu_dev;
        struct device *dev = oiommu->dev;
-       size_t bytes = PAGE_SIZE << order;
        struct iotlb_entry e;
        int omap_pgsz;
        u32 ret, flags;
@@ -1075,19 +1086,16 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
        return ret;
 }
 
-static int omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
-                           int order)
+static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
+                           size_t size)
 {
        struct omap_iommu_domain *omap_domain = domain->priv;
        struct omap_iommu *oiommu = omap_domain->iommu_dev;
        struct device *dev = oiommu->dev;
-       size_t unmap_size;
-
-       dev_dbg(dev, "unmapping da 0x%lx order %d\n", da, order);
 
-       unmap_size = iopgtable_clear_entry(oiommu, da);
+       dev_dbg(dev, "unmapping da 0x%lx size %u\n", da, size);
 
-       return unmap_size ? get_order(unmap_size) : -EINVAL;
+       return iopgtable_clear_entry(oiommu, da);
 }
 
 static int
@@ -1095,6 +1103,7 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
 {
        struct omap_iommu_domain *omap_domain = domain->priv;
        struct omap_iommu *oiommu;
+       struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
        int ret = 0;
 
        spin_lock(&omap_domain->lock);
@@ -1107,41 +1116,49 @@ omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
        }
 
        /* get a handle to and enable the omap iommu */
-       oiommu = omap_iommu_attach(dev, omap_domain->pgtable);
+       oiommu = omap_iommu_attach(arch_data->name, omap_domain->pgtable);
        if (IS_ERR(oiommu)) {
                ret = PTR_ERR(oiommu);
                dev_err(dev, "can't get omap iommu: %d\n", ret);
                goto out;
        }
 
-       omap_domain->iommu_dev = oiommu;
+       omap_domain->iommu_dev = arch_data->iommu_dev = oiommu;
+       omap_domain->dev = dev;
+       oiommu->domain = domain;
 
 out:
        spin_unlock(&omap_domain->lock);
        return ret;
 }
 
-static void omap_iommu_detach_dev(struct iommu_domain *domain,
-                                struct device *dev)
+static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
+                       struct device *dev)
 {
-       struct omap_iommu_domain *omap_domain = domain->priv;
-       struct omap_iommu *oiommu = to_iommu(dev);
-
-       spin_lock(&omap_domain->lock);
+       struct omap_iommu *oiommu = dev_to_omap_iommu(dev);
+       struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
 
        /* only a single device is supported per domain for now */
        if (omap_domain->iommu_dev != oiommu) {
                dev_err(dev, "invalid iommu device\n");
-               goto out;
+               return;
        }
 
        iopgtable_clear_entry_all(oiommu);
 
        omap_iommu_detach(oiommu);
 
-       omap_domain->iommu_dev = NULL;
+       omap_domain->iommu_dev = arch_data->iommu_dev = NULL;
+       omap_domain->dev = NULL;
+}
 
-out:
+static void omap_iommu_detach_dev(struct iommu_domain *domain,
+                                struct device *dev)
+{
+       struct omap_iommu_domain *omap_domain = domain->priv;
+
+       spin_lock(&omap_domain->lock);
+       _omap_iommu_detach_dev(omap_domain, dev);
        spin_unlock(&omap_domain->lock);
 }
 
@@ -1172,6 +1189,10 @@ static int omap_iommu_domain_init(struct iommu_domain *domain)
 
        domain->priv = omap_domain;
 
+       domain->geometry.aperture_start = 0;
+       domain->geometry.aperture_end   = (1ULL << 32) - 1;
+       domain->geometry.force_aperture = true;
+
        return 0;
 
 fail_nomem:
@@ -1180,19 +1201,25 @@ out:
        return -ENOMEM;
 }
 
-/* assume device was already detached */
 static void omap_iommu_domain_destroy(struct iommu_domain *domain)
 {
        struct omap_iommu_domain *omap_domain = domain->priv;
 
        domain->priv = NULL;
 
+       /*
+        * An iommu device is still attached
+        * (currently, only one device can be attached) ?
+        */
+       if (omap_domain->iommu_dev)
+               _omap_iommu_detach_dev(omap_domain, omap_domain->dev);
+
        kfree(omap_domain->pgtable);
        kfree(omap_domain);
 }
 
 static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
-                                         unsigned long da)
+                                         dma_addr_t da)
 {
        struct omap_iommu_domain *omap_domain = domain->priv;
        struct omap_iommu *oiommu = omap_domain->iommu_dev;
@@ -1208,14 +1235,14 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
                else if (iopte_is_large(*pte))
                        ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
                else
-                       dev_err(dev, "bogus pte 0x%x", *pte);
+                       dev_err(dev, "bogus pte 0x%x, da 0x%lx", *pte, da);
        } else {
                if (iopgd_is_section(*pgd))
                        ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
                else if (iopgd_is_super(*pgd))
                        ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
                else
-                       dev_err(dev, "bogus pgd 0x%x", *pgd);
+                       dev_err(dev, "bogus pgd 0x%x, da 0x%lx", *pgd, da);
        }
 
        return ret;
@@ -1236,6 +1263,7 @@ static struct iommu_ops omap_iommu_ops = {
        .unmap          = omap_iommu_unmap,
        .iova_to_phys   = omap_iommu_iova_to_phys,
        .domain_has_cap = omap_iommu_domain_has_cap,
+       .pgsize_bitmap  = OMAP_IOMMU_PGSIZES,
 };
 
 static int __init omap_iommu_init(void)
@@ -1250,11 +1278,12 @@ static int __init omap_iommu_init(void)
                return -ENOMEM;
        iopte_cachep = p;
 
-       register_iommu(&omap_iommu_ops);
+       bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
 
        return platform_driver_register(&omap_iommu_driver);
 }
-module_init(omap_iommu_init);
+/* must be ready before omap3isp is probed */
+subsys_initcall(omap_iommu_init);
 
 static void __exit omap_iommu_exit(void)
 {