]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
ARM: OMAP: Add MMU framework
authorPaul Mundt <lethal@linux-sh.org>
Tue, 20 Feb 2007 11:55:50 +0000 (17:25 +0530)
committerTony Lindgren <tony@atomide.com>
Tue, 6 Mar 2007 10:51:49 +0000 (02:51 -0800)
- Taken from maemo.org N800 kernel package.
- Converted struct "class_device" to "struct device".
- Changed set_pte to set_pte_ext.

Signed-off-by: Trilok Soni <soni.trilok@gmail.com>
Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
arch/arm/plat-omap/Makefile
arch/arm/plat-omap/mmu.c [new file with mode: 0644]
include/asm-arm/arch-omap/mmu.h [new file with mode: 0644]

index 112708729ef4c5fc8cf0967c42ccf383b5c6be1c..05eb3cb1dc9017cdb1426e044bd7d40c832af2ca 100644 (file)
@@ -29,4 +29,4 @@ obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
 
 # DSP subsystem
 obj-y += dsp/
-obj-$(CONFIG_OMAP_DSP) += mailbox.o
+obj-$(CONFIG_OMAP_DSP) += mailbox.o mmu.o
diff --git a/arch/arm/plat-omap/mmu.c b/arch/arm/plat-omap/mmu.c
new file mode 100644 (file)
index 0000000..d5af168
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+ * linux/arch/arm/plat-omap/mmu.c
+ *
+ * OMAP MMU management framework
+ *
+ * Copyright (C) 2002-2005 Nokia Corporation
+ *
+ * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
+ *        and Paul Mundt <paul.mundt@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/mempool.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/arch/mmu.h>
+#include <asm/sizes.h>
+
+#if defined(CONFIG_ARCH_OMAP1)
+#include "../mach-omap1/mmu.h"
+#elif defined(CONFIG_ARCH_OMAP2)
+#include "../mach-omap2/mmu.h"
+#endif
+
+/*
+ * On OMAP2 MMU_LOCK_xxx_MASK only applies to the IVA and DSP, the camera
+ * MMU has base and victim implemented in different bits in the LOCK
+ * register (shifts are still the same), all of the other registers are
+ * the same on all of the MMUs..
+ */
+#define MMU_LOCK_BASE_SHIFT            10
+#define MMU_LOCK_VICTIM_SHIFT          4
+
+#define CAMERA_MMU_LOCK_BASE_MASK      (0x7 << MMU_LOCK_BASE_SHIFT)
+#define CAMERA_MMU_LOCK_VICTIM_MASK    (0x7 << MMU_LOCK_VICTIM_SHIFT)
+
+#define is_aligned(adr,align)  (!((adr)&((align)-1)))
+#define ORDER_1MB      (20 - PAGE_SHIFT)
+#define ORDER_64KB     (16 - PAGE_SHIFT)
+#define ORDER_4KB      (12 - PAGE_SHIFT)
+
+static mempool_t *mempool_1M;
+static mempool_t *mempool_64K;
+
+#define omap_mmu_for_each_tlb_entry(mmu, entry)                        \
+       for (entry = mmu->exmap_tbl; prefetch(entry + 1),       \
+            entry < (mmu->exmap_tbl + mmu->nr_tlb_entries);    \
+            entry++)
+
+#define to_dev(obj)    container_of(obj, struct device, kobj)
+
+static void *mempool_alloc_from_pool(mempool_t *pool,
+                                    unsigned int __nocast gfp_mask)
+{
+       spin_lock_irq(&pool->lock);
+       if (likely(pool->curr_nr)) {
+               void *element = pool->elements[--pool->curr_nr];
+               spin_unlock_irq(&pool->lock);
+               return element;
+       }
+
+       spin_unlock_irq(&pool->lock);
+       return mempool_alloc(pool, gfp_mask);
+}
+
+/*
+ * kmem_reserve(), kmem_release():
+ * reserve or release kernel memory for exmap().
+ *
+ * exmap() might request consecutive 1MB or 64kB,
+ * but it will be difficult after memory pages are fragmented.
+ * So, user can reserve such memory blocks in the early phase
+ * through kmem_reserve().
+ */
+static void *omap_mmu_pool_alloc(unsigned int __nocast gfp, void *order)
+{
+       return (void *)__get_dma_pages(gfp, (unsigned int)order);
+}
+
+static void omap_mmu_pool_free(void *buf, void *order)
+{
+       free_pages((unsigned long)buf, (unsigned int)order);
+}
+
+int omap_mmu_kmem_reserve(struct omap_mmu *mmu, unsigned long size)
+{
+       unsigned long len = size;
+
+       /* alignment check */
+       if (!is_aligned(size, SZ_64K)) {
+               printk(KERN_ERR
+                      "omapdsp: size(0x%lx) is not multiple of 64KB.\n", size);
+               return -EINVAL;
+       }
+
+       if (size > (1 << mmu->addrspace)) {
+               printk(KERN_ERR
+                      "omapdsp: size(0x%lx) is larger than DSP memory space "
+                      "size (0x%x.\n", size, (1 << mmu->addrspace));
+               return -EINVAL;
+       }
+
+       if (size >= SZ_1M) {
+               int nr = size >> 20;
+
+               if (likely(!mempool_1M))
+                       mempool_1M = mempool_create(nr, omap_mmu_pool_alloc,
+                                                   omap_mmu_pool_free,
+                                                   (void *)ORDER_1MB);
+               else
+                       mempool_resize(mempool_1M, mempool_1M->min_nr + nr,
+                                      GFP_KERNEL);
+
+               size &= ~(0xf << 20);
+       }
+
+       if (size >= SZ_64K) {
+               int nr = size >> 16;
+
+               if (likely(!mempool_64K))
+                       mempool_64K = mempool_create(nr, omap_mmu_pool_alloc,
+                                                    omap_mmu_pool_free,
+                                                    (void *)ORDER_64KB);
+               else
+                       mempool_resize(mempool_64K, mempool_64K->min_nr + nr,
+                                      GFP_KERNEL);
+
+               size &= ~(0xf << 16);
+       }
+
+       if (size)
+               len -= size;
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_kmem_reserve);
+
+void omap_mmu_kmem_release(void)
+{
+       if (mempool_64K) {
+               mempool_destroy(mempool_64K);
+               mempool_64K = NULL;
+       }
+
+       if (mempool_1M) {
+               mempool_destroy(mempool_1M);
+               mempool_1M = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(omap_mmu_kmem_release);
+
+static void omap_mmu_free_pages(unsigned long buf, unsigned int order)
+{
+       struct page *page, *ps, *pe;
+
+       ps = virt_to_page(buf);
+       pe = virt_to_page(buf + (1 << (PAGE_SHIFT + order)));
+
+       for (page = ps; page < pe; page++)
+               ClearPageReserved(page);
+
+       if ((order == ORDER_64KB) && likely(mempool_64K))
+               mempool_free((void *)buf, mempool_64K);
+       else if ((order == ORDER_1MB) && likely(mempool_1M))
+               mempool_free((void *)buf, mempool_1M);
+       else
+               free_pages(buf, order);
+}
+
+/*
+ * ARM MMU operations
+ */
+int exmap_set_armmmu(unsigned long virt, unsigned long phys, unsigned long size)
+{
+       long off;
+       unsigned long sz_left;
+       pmd_t *pmdp;
+       pte_t *ptep;
+       int prot_pmd, prot_pte;
+
+       printk(KERN_DEBUG
+              "MMU: mapping in ARM MMU, v=0x%08lx, p=0x%08lx, sz=0x%lx\n",
+              virt, phys, size);
+
+       prot_pmd = PMD_TYPE_TABLE | PMD_DOMAIN(DOMAIN_IO);
+       prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE;
+
+       pmdp = pmd_offset(pgd_offset_k(virt), virt);
+       if (pmd_none(*pmdp)) {
+               ptep = pte_alloc_one_kernel(&init_mm, 0);
+               if (ptep == NULL)
+                       return -ENOMEM;
+               /* note: two PMDs will be set  */
+               pmd_populate_kernel(&init_mm, pmdp, ptep);
+       }
+
+       off = phys - virt;
+       for (sz_left = size;
+            sz_left >= PAGE_SIZE;
+            sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
+               ptep = pte_offset_kernel(pmdp, virt);
+               set_pte_ext(ptep, __pte((virt + off) | prot_pte), 0);
+       }
+       if (sz_left)
+               BUG();
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(exmap_set_armmmu);
+
+void exmap_clear_armmmu(unsigned long virt, unsigned long size)
+{
+       unsigned long sz_left;
+       pmd_t *pmdp;
+       pte_t *ptep;
+
+       printk(KERN_DEBUG
+              "MMU: unmapping in ARM MMU, v=0x%08lx, sz=0x%lx\n",
+              virt, size);
+
+       for (sz_left = size;
+            sz_left >= PAGE_SIZE;
+            sz_left -= PAGE_SIZE, virt += PAGE_SIZE) {
+               pmdp = pmd_offset(pgd_offset_k(virt), virt);
+               ptep = pte_offset_kernel(pmdp, virt);
+               pte_clear(&init_mm, virt, ptep);
+       }
+       if (sz_left)
+               BUG();
+}
+EXPORT_SYMBOL_GPL(exmap_clear_armmmu);
+
+int exmap_valid(struct omap_mmu *mmu, void *vadr, size_t len)
+{
+       /* exmap_sem should be held before calling this function */
+       struct exmap_tbl *ent;
+
+start:
+       omap_mmu_for_each_tlb_entry(mmu, ent) {
+               void *mapadr;
+               unsigned long mapsize;
+
+               if (!ent->valid)
+                       continue;
+               mapadr = (void *)ent->vadr;
+               mapsize = 1 << (ent->order + PAGE_SHIFT);
+               if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) {
+                       if (vadr + len <= mapadr + mapsize) {
+                               /* this map covers whole address. */
+                               return 1;
+                       } else {
+                               /*
+                                * this map covers partially.
+                                * check rest portion.
+                                */
+                               len -= mapadr + mapsize - vadr;
+                               vadr = mapadr + mapsize;
+                               goto start;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * omap_mmu_exmap_use(), unuse():
+ * when the mapped area is exported to user space with mmap,
+ * the usecount is incremented.
+ * while the usecount > 0, that area can't be released.
+ */
+void omap_mmu_exmap_use(struct omap_mmu *mmu, void *vadr, size_t len)
+{
+       struct exmap_tbl *ent;
+
+       down_write(&mmu->exmap_sem);
+       omap_mmu_for_each_tlb_entry(mmu, ent) {
+               void *mapadr;
+               unsigned long mapsize;
+
+               if (!ent->valid)
+                       continue;
+               mapadr = (void *)ent->vadr;
+               mapsize = 1 << (ent->order + PAGE_SHIFT);
+               if ((vadr + len > mapadr) && (vadr < mapadr + mapsize))
+                       ent->usecount++;
+       }
+       up_write(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap_use);
+
+void omap_mmu_exmap_unuse(struct omap_mmu *mmu, void *vadr, size_t len)
+{
+       struct exmap_tbl *ent;
+
+       down_write(&mmu->exmap_sem);
+       omap_mmu_for_each_tlb_entry(mmu, ent) {
+               void *mapadr;
+               unsigned long mapsize;
+
+               if (!ent->valid)
+                       continue;
+               mapadr = (void *)ent->vadr;
+               mapsize = 1 << (ent->order + PAGE_SHIFT);
+               if ((vadr + len > mapadr) && (vadr < mapadr + mapsize))
+                       ent->usecount--;
+       }
+       up_write(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap_unuse);
+
+/*
+ * omap_mmu_virt_to_phys()
+ * returns physical address, and sets len to valid length
+ */
+unsigned long
+omap_mmu_virt_to_phys(struct omap_mmu *mmu, void *vadr, size_t *len)
+{
+       struct exmap_tbl *ent;
+
+       if (omap_mmu_internal_memory(mmu, vadr)) {
+               unsigned long addr = (unsigned long)vadr;
+               *len = mmu->membase + mmu->memsize - addr;
+               return addr;
+       }
+
+       /* EXRAM */
+       omap_mmu_for_each_tlb_entry(mmu, ent) {
+               void *mapadr;
+               unsigned long mapsize;
+
+               if (!ent->valid)
+                       continue;
+               mapadr = (void *)ent->vadr;
+               mapsize = 1 << (ent->order + PAGE_SHIFT);
+               if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) {
+                       *len = mapadr + mapsize - vadr;
+                       return __pa(ent->buf) + vadr - mapadr;
+               }
+       }
+
+       /* valid mapping not found */
+       return 0;
+}
+
+/*
+ * MMU operations
+ */
+static struct cam_ram_regset *
+omap_mmu_cam_ram_alloc(struct omap_mmu *mmu, struct omap_mmu_tlb_entry *entry)
+{
+       return mmu->ops->cam_ram_alloc(entry);
+}
+
+static int omap_mmu_cam_ram_valid(struct omap_mmu *mmu,
+                                 struct cam_ram_regset *cr)
+{
+       return mmu->ops->cam_ram_valid(cr);
+}
+
+static inline void
+omap_mmu_get_tlb_lock(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *tlb_lock)
+{
+       unsigned long lock = omap_mmu_read_reg(mmu, MMU_LOCK);
+       int mask;
+
+       mask = (mmu->type == OMAP_MMU_CAMERA) ?
+                       CAMERA_MMU_LOCK_BASE_MASK : MMU_LOCK_BASE_MASK;
+       tlb_lock->base = (lock & mask) >> MMU_LOCK_BASE_SHIFT;
+
+       mask = (mmu->type == OMAP_MMU_CAMERA) ?
+                       CAMERA_MMU_LOCK_VICTIM_MASK : MMU_LOCK_VICTIM_MASK;
+       tlb_lock->victim = (lock & mask) >> MMU_LOCK_VICTIM_SHIFT;
+}
+
+static inline void
+omap_mmu_set_tlb_lock(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *lock)
+{
+       omap_mmu_write_reg(mmu,
+                          (lock->base << MMU_LOCK_BASE_SHIFT) |
+                          (lock->victim << MMU_LOCK_VICTIM_SHIFT), MMU_LOCK);
+}
+
+static inline void omap_mmu_flush(struct omap_mmu *mmu)
+{
+       omap_mmu_write_reg(mmu, 0x1, MMU_FLUSH_ENTRY);
+}
+
+static inline void omap_mmu_ldtlb(struct omap_mmu *mmu)
+{
+       omap_mmu_write_reg(mmu, 0x1, MMU_LD_TLB);
+}
+
+void omap_mmu_read_tlb(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *lock,
+                      struct cam_ram_regset *cr)
+{
+       /* set victim */
+       omap_mmu_set_tlb_lock(mmu, lock);
+
+       if (likely(mmu->ops->read_tlb))
+               mmu->ops->read_tlb(mmu, cr);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_read_tlb);
+
+void omap_mmu_load_tlb(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+       if (likely(mmu->ops->load_tlb))
+               mmu->ops->load_tlb(mmu, cr);
+
+       /* flush the entry */
+       omap_mmu_flush(mmu);
+
+       /* load a TLB entry */
+       omap_mmu_ldtlb(mmu);
+}
+
+int omap_mmu_load_tlb_entry(struct omap_mmu *mmu,
+                           struct omap_mmu_tlb_entry *entry)
+{
+       struct omap_mmu_tlb_lock lock;
+       struct cam_ram_regset *cr;
+
+       clk_enable(mmu->clk);
+       omap_dsp_request_mem();
+
+       omap_mmu_get_tlb_lock(mmu, &lock);
+       for (lock.victim = 0; lock.victim < lock.base; lock.victim++) {
+               struct cam_ram_regset tmp;
+
+               /* read a TLB entry */
+               omap_mmu_read_tlb(mmu, &lock, &tmp);
+               if (!omap_mmu_cam_ram_valid(mmu, &tmp))
+                       goto found_victim;
+       }
+       omap_mmu_set_tlb_lock(mmu, &lock);
+
+found_victim:
+       /* The last entry cannot be locked? */
+       if (lock.victim == (mmu->nr_tlb_entries - 1)) {
+               printk(KERN_ERR "MMU: TLB is full.\n");
+               return -EBUSY;
+       }
+
+       cr = omap_mmu_cam_ram_alloc(mmu, entry);
+       if (IS_ERR(cr))
+               return PTR_ERR(cr);
+
+       omap_mmu_load_tlb(mmu, cr);
+       kfree(cr);
+
+       /* update lock base */
+       if (lock.victim == lock.base)
+               lock.base++;
+
+       omap_mmu_set_tlb_lock(mmu, &lock);
+
+       omap_dsp_release_mem();
+       clk_disable(mmu->clk);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_load_tlb_entry);
+
+static inline unsigned long
+omap_mmu_cam_va(struct omap_mmu *mmu, struct cam_ram_regset *cr)
+{
+       return mmu->ops->cam_va(cr);
+}
+
+int omap_mmu_clear_tlb_entry(struct omap_mmu *mmu, unsigned long vadr)
+{
+       struct omap_mmu_tlb_lock lock;
+       int i;
+       int max_valid = 0;
+
+       clk_enable(mmu->clk);
+       omap_dsp_request_mem();
+
+       omap_mmu_get_tlb_lock(mmu, &lock);
+       for (i = 0; i < lock.base; i++) {
+               struct cam_ram_regset cr;
+
+               /* read a TLB entry */
+               lock.victim = i;
+               omap_mmu_read_tlb(mmu, &lock, &cr);
+               if (!omap_mmu_cam_ram_valid(mmu, &cr))
+                       continue;
+
+               if (omap_mmu_cam_va(mmu, &cr) == vadr)
+                       /* flush the entry */
+                       omap_mmu_flush(mmu);
+               else
+                       max_valid = i;
+       }
+
+       /* set new lock base */
+       lock.base = lock.victim = max_valid + 1;
+       omap_mmu_set_tlb_lock(mmu, &lock);
+
+       omap_dsp_release_mem();
+       clk_disable(mmu->clk);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_clear_tlb_entry);
+
+static void omap_mmu_gflush(struct omap_mmu *mmu)
+{
+       struct omap_mmu_tlb_lock lock;
+
+       clk_enable(mmu->clk);
+       omap_dsp_request_mem();
+
+       omap_mmu_write_reg(mmu, 0x1, MMU_GFLUSH);
+       lock.base = lock.victim = mmu->nr_exmap_preserved;
+       omap_mmu_set_tlb_lock(mmu, &lock);
+
+       omap_dsp_release_mem();
+       clk_disable(mmu->clk);
+}
+
+/*
+ * omap_mmu_exmap()
+ *
+ * MEM_IOCTL_EXMAP ioctl calls this function with padr=0.
+ * In this case, the buffer for DSP is allocated in this routine,
+ * then it is mapped.
+ * On the other hand, for example - frame buffer sharing, calls
+ * this function with padr set. It means some known address space
+ * pointed with padr is going to be shared with DSP.
+ */
+int omap_mmu_exmap(struct omap_mmu *mmu, unsigned long dspadr,
+                  unsigned long padr, unsigned long size,
+                  enum exmap_type type)
+{
+       unsigned long pgsz;
+       void *buf;
+       unsigned int order = 0;
+       unsigned long unit;
+       int prev = -1;
+       unsigned long _dspadr = dspadr;
+       unsigned long _padr = padr;
+       void *_vadr = omap_mmu_to_virt(mmu, dspadr);
+       unsigned long _size = size;
+       struct omap_mmu_tlb_entry tlb_ent;
+       struct exmap_tbl *exmap_ent, *tmp_ent;
+       int status;
+       int idx;
+
+#define MINIMUM_PAGESZ SZ_4K
+       /*
+        * alignment check
+        */
+       if (!is_aligned(size, MINIMUM_PAGESZ)) {
+               printk(KERN_ERR
+                      "MMU: size(0x%lx) is not multiple of 4KB.\n", size);
+               return -EINVAL;
+       }
+       if (!is_aligned(dspadr, MINIMUM_PAGESZ)) {
+               printk(KERN_ERR
+                      "MMU: DSP address(0x%lx) is not aligned.\n", dspadr);
+               return -EINVAL;
+       }
+       if (!is_aligned(padr, MINIMUM_PAGESZ)) {
+               printk(KERN_ERR
+                      "MMU: physical address(0x%lx) is not aligned.\n",
+                      padr);
+               return -EINVAL;
+       }
+
+       /* address validity check */
+       if ((dspadr < mmu->memsize) ||
+           (dspadr >= (1 << mmu->addrspace))) {
+               printk(KERN_ERR
+                      "MMU: illegal address/size for %s().\n",
+                      __FUNCTION__);
+               return -EINVAL;
+       }
+
+       down_write(&mmu->exmap_sem);
+
+       /* overlap check */
+       omap_mmu_for_each_tlb_entry(mmu, tmp_ent) {
+               unsigned long mapsize;
+
+               if (!tmp_ent->valid)
+                       continue;
+               mapsize = 1 << (tmp_ent->order + PAGE_SHIFT);
+               if ((_vadr + size > tmp_ent->vadr) &&
+                   (_vadr < tmp_ent->vadr + mapsize)) {
+                       printk(KERN_ERR "MMU: exmap page overlap!\n");
+                       up_write(&mmu->exmap_sem);
+                       return -EINVAL;
+               }
+       }
+
+start:
+       buf = NULL;
+       /* Are there any free TLB lines?  */
+       for (idx = 0; idx < mmu->nr_tlb_entries; idx++)
+               if (!mmu->exmap_tbl[idx].valid)
+                       goto found_free;
+
+       printk(KERN_ERR "MMU: DSP TLB is full.\n");
+       status = -EBUSY;
+       goto fail;
+
+found_free:
+       exmap_ent = mmu->exmap_tbl + idx;
+
+       if ((_size >= SZ_1M) &&
+           (is_aligned(_padr, SZ_1M) || (padr == 0)) &&
+           is_aligned(_dspadr, SZ_1M)) {
+               unit = SZ_1M;
+               pgsz = OMAP_MMU_CAM_PAGESIZE_1MB;
+       } else if ((_size >= SZ_64K) &&
+                  (is_aligned(_padr, SZ_64K) || (padr == 0)) &&
+                  is_aligned(_dspadr, SZ_64K)) {
+               unit = SZ_64K;
+               pgsz = OMAP_MMU_CAM_PAGESIZE_64KB;
+       } else {
+               unit = SZ_4K;
+               pgsz = OMAP_MMU_CAM_PAGESIZE_4KB;
+       }
+
+       order = get_order(unit);
+
+       /* buffer allocation */
+       if (type == EXMAP_TYPE_MEM) {
+               struct page *page, *ps, *pe;
+
+               if ((order == ORDER_1MB) && likely(mempool_1M))
+                       buf = mempool_alloc_from_pool(mempool_1M, GFP_KERNEL);
+               else if ((order == ORDER_64KB) && likely(mempool_64K))
+                       buf = mempool_alloc_from_pool(mempool_64K, GFP_KERNEL);
+               else {
+                       buf = (void *)__get_dma_pages(GFP_KERNEL, order);
+                       if (buf == NULL) {
+                               status = -ENOMEM;
+                               goto fail;
+                       }
+               }
+
+               /* mark the pages as reserved; this is needed for mmap */
+               ps = virt_to_page(buf);
+               pe = virt_to_page(buf + unit);
+
+               for (page = ps; page < pe; page++)
+                       SetPageReserved(page);
+
+               _padr = __pa(buf);
+       }
+
+       /*
+        * mapping for ARM MMU:
+        * we should not access to the allocated memory through 'buf'
+        * since this area should not be cached.
+        */
+       status = exmap_set_armmmu((unsigned long)_vadr, _padr, unit);
+       if (status < 0)
+               goto fail;
+
+       /* loading DSP TLB entry */
+       INIT_TLB_ENTRY(&tlb_ent, _dspadr, _padr, pgsz);
+       status = omap_mmu_load_tlb_entry(mmu, &tlb_ent);
+       if (status < 0) {
+               exmap_clear_armmmu((unsigned long)_vadr, unit);
+               goto fail;
+       }
+
+       INIT_EXMAP_TBL_ENTRY(exmap_ent, buf, _vadr, type, order);
+       exmap_ent->link.prev = prev;
+       if (prev >= 0)
+               mmu->exmap_tbl[prev].link.next = idx;
+
+       if ((_size -= unit) == 0) {     /* normal completion */
+               up_write(&mmu->exmap_sem);
+               return size;
+       }
+
+       _dspadr += unit;
+       _vadr   += unit;
+       _padr = padr ? _padr + unit : 0;
+       prev = idx;
+       goto start;
+
+fail:
+       up_write(&mmu->exmap_sem);
+       if (buf)
+               omap_mmu_free_pages((unsigned long)buf, order);
+       omap_mmu_exunmap(mmu, dspadr);
+       return status;
+}
+
+static unsigned long unmap_free_arm(struct exmap_tbl *ent)
+{
+       unsigned long size;
+
+       /* clearing ARM MMU */
+       size = 1 << (ent->order + PAGE_SHIFT);
+       exmap_clear_armmmu((unsigned long)ent->vadr, size);
+
+       /* freeing allocated memory */
+       if (ent->type == EXMAP_TYPE_MEM) {
+               omap_mmu_free_pages((unsigned long)ent->buf, ent->order);
+               printk(KERN_DEBUG
+                      "MMU: freeing 0x%lx bytes @ adr 0x%8p\n",
+                      size, ent->buf);
+       }
+
+       ent->valid = 0;
+       return size;
+}
+
+int omap_mmu_exunmap(struct omap_mmu *mmu, unsigned long dspadr)
+{
+       void *vadr;
+       unsigned long size;
+       int total = 0;
+       struct exmap_tbl *ent;
+       int idx;
+
+       vadr = omap_mmu_to_virt(mmu, dspadr);
+       down_write(&mmu->exmap_sem);
+       for (idx = 0; idx < mmu->nr_tlb_entries; idx++) {
+               ent = mmu->exmap_tbl + idx;
+               if (!ent->valid || ent->prsvd)
+                       continue;
+               if (ent->vadr == vadr)
+                       goto found_map;
+       }
+       up_write(&mmu->exmap_sem);
+       printk(KERN_WARNING
+              "MMU: address %06lx not found in exmap_tbl.\n", dspadr);
+       return -EINVAL;
+
+found_map:
+       if (ent->usecount > 0) {
+               printk(KERN_ERR
+                      "MMU: exmap reference count is not 0.\n"
+                      "   idx=%d, vadr=%p, order=%d, usecount=%d\n",
+                      idx, ent->vadr, ent->order, ent->usecount);
+               up_write(&mmu->exmap_sem);
+               return -EINVAL;
+       }
+       /* clearing DSP TLB entry */
+       omap_mmu_clear_tlb_entry(mmu, dspadr);
+
+       /* clear ARM MMU and free buffer */
+       size = unmap_free_arm(ent);
+       total += size;
+
+       /* we don't free PTEs */
+
+       /* flush TLB */
+       flush_tlb_kernel_range((unsigned long)vadr, (unsigned long)vadr + size);
+
+       /* check if next mapping is in same group */
+       idx = ent->link.next;
+       if (idx < 0)
+               goto up_out;    /* normal completion */
+       ent = mmu->exmap_tbl + idx;
+       dspadr += size;
+       vadr   += size;
+       if (ent->vadr == vadr)
+               goto found_map; /* continue */
+
+       printk(KERN_ERR
+              "MMU: illegal exmap_tbl grouping!\n"
+              "expected vadr = %p, exmap_tbl[%d].vadr = %p\n",
+              vadr, idx, ent->vadr);
+       up_write(&mmu->exmap_sem);
+       return -EINVAL;
+
+up_out:
+       up_write(&mmu->exmap_sem);
+       return total;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exunmap);
+
+void omap_mmu_exmap_flush(struct omap_mmu *mmu)
+{
+       struct exmap_tbl *ent;
+
+       down_write(&mmu->exmap_sem);
+
+       /* clearing TLB entry */
+       omap_mmu_gflush(mmu);
+
+       omap_mmu_for_each_tlb_entry(mmu, ent)
+               if (ent->valid && !ent->prsvd)
+                       unmap_free_arm(ent);
+
+       /* flush TLB */
+       if (likely(mmu->membase))
+               flush_tlb_kernel_range(mmu->membase + mmu->memsize,
+                                      mmu->membase + (1 << mmu->addrspace));
+
+       up_write(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_exmap_flush);
+
+void exmap_setup_preserved_mem_page(struct omap_mmu *mmu, void *buf,
+                                   unsigned long dspadr, int index)
+{
+       unsigned long phys;
+       void *virt;
+       struct omap_mmu_tlb_entry tlb_ent;
+
+       phys = __pa(buf);
+       virt = omap_mmu_to_virt(mmu, dspadr);
+       exmap_set_armmmu((unsigned long)virt, phys, PAGE_SIZE);
+       INIT_EXMAP_TBL_ENTRY_4KB_PRESERVED(mmu->exmap_tbl + index, buf, virt);
+       INIT_TLB_ENTRY_4KB_PRESERVED(&tlb_ent, dspadr, phys);
+       omap_mmu_load_tlb_entry(mmu, &tlb_ent);
+}
+
+void exmap_clear_mem_page(struct omap_mmu *mmu, unsigned long dspadr)
+{
+       void *virt = omap_mmu_to_virt(mmu, dspadr);
+
+       exmap_clear_armmmu((unsigned long)virt, PAGE_SIZE);
+       /* DSP MMU is shutting down. not handled here. */
+}
+
+static void omap_mmu_reset(struct omap_mmu *mmu)
+{
+       int i;
+
+       omap_mmu_write_reg(mmu, 0x2, MMU_SYSCONFIG);
+
+       for (i = 0; i < 10000; i++)
+               if (likely(omap_mmu_read_reg(mmu, MMU_SYSSTATUS) & 0x1))
+                       break;
+}
+
+void omap_mmu_disable(struct omap_mmu *mmu)
+{
+       omap_mmu_write_reg(mmu, 0x00, MMU_CNTL);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_disable);
+
+void omap_mmu_enable(struct omap_mmu *mmu, int reset)
+{
+       if (likely(reset))
+               omap_mmu_reset(mmu);
+
+       omap_mmu_write_reg(mmu, 0x2, MMU_CNTL);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_enable);
+
+static int omap_mmu_init(struct omap_mmu *mmu)
+{
+       struct omap_mmu_tlb_lock tlb_lock;
+       int ret = 0;
+
+       clk_enable(mmu->clk);
+       omap_dsp_request_mem();
+       down_write(&mmu->exmap_sem);
+
+       omap_mmu_disable(mmu);  /* clear all */
+       udelay(100);
+       omap_mmu_enable(mmu, 1);
+
+       memset(&tlb_lock, 0, sizeof(struct omap_mmu_tlb_lock));
+       omap_mmu_set_tlb_lock(mmu, &tlb_lock);
+
+       if (unlikely(mmu->ops->startup))
+               ret = mmu->ops->startup(mmu);
+
+       up_write(&mmu->exmap_sem);
+       omap_dsp_release_mem();
+       clk_disable(mmu->clk);
+
+       return ret;
+}
+
+static void omap_mmu_shutdown(struct omap_mmu *mmu)
+{
+       if (unlikely(mmu->ops->shutdown))
+               mmu->ops->shutdown(mmu);
+
+       omap_mmu_exmap_flush(mmu);
+       omap_mmu_disable(mmu); /* clear all */
+}
+
+/*
+ * omap_mmu_mem_enable() / disable()
+ */
+int omap_mmu_mem_enable(struct omap_mmu *mmu, void *addr)
+{
+       if (unlikely(mmu->ops->mem_enable))
+               return mmu->ops->mem_enable(mmu, addr);
+
+       down_read(&mmu->exmap_sem);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_mmu_mem_enable);
+
+void omap_mmu_mem_disable(struct omap_mmu *mmu, void *addr)
+{
+       if (unlikely(mmu->ops->mem_disable)) {
+               mmu->ops->mem_disable(mmu, addr);
+               return;
+       }
+
+       up_read(&mmu->exmap_sem);
+}
+EXPORT_SYMBOL_GPL(omap_mmu_mem_disable);
+
+/*
+ * dsp_mem file operations
+ */
+static ssize_t intmem_read(struct omap_mmu *mmu, char *buf, size_t count,
+                          loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = omap_mmu_to_virt(mmu, p);
+       ssize_t size = mmu->memsize;
+       ssize_t read;
+
+       if (p >= size)
+               return 0;
+       clk_enable(mmu->memclk);
+       read = count;
+       if (count > size - p)
+               read = size - p;
+       if (copy_to_user(buf, vadr, read)) {
+               read = -EFAULT;
+               goto out;
+       }
+       *ppos += read;
+out:
+       clk_disable(mmu->memclk);
+       return read;
+}
+
+static ssize_t exmem_read(struct omap_mmu *mmu, char *buf, size_t count,
+                         loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = omap_mmu_to_virt(mmu, p);
+
+       if (!exmap_valid(mmu, vadr, count)) {
+               printk(KERN_ERR
+                      "MMU: DSP address %08lx / size %08x "
+                      "is not valid!\n", p, count);
+               return -EFAULT;
+       }
+       if (count > (1 << mmu->addrspace) - p)
+               count = (1 << mmu->addrspace) - p;
+       if (copy_to_user(buf, vadr, count))
+               return -EFAULT;
+       *ppos += count;
+
+       return count;
+}
+
+static ssize_t omap_mmu_mem_read(struct kobject *kobj, char *buf,
+                                loff_t offset, size_t count)
+{
+       struct device *dev = to_dev(kobj);
+       struct omap_mmu *mmu = dev_get_drvdata(dev);
+       unsigned long p = (unsigned long)offset;
+       void *vadr = omap_mmu_to_virt(mmu, p);
+       int ret;
+
+       if (omap_mmu_mem_enable(mmu, vadr) < 0)
+               return -EBUSY;
+
+       if (p < mmu->memsize)
+               ret = intmem_read(mmu, buf, count, &offset);
+       else
+               ret = exmem_read(mmu, buf, count, &offset);
+
+       omap_mmu_mem_disable(mmu, vadr);
+
+       return ret;
+}
+
+static ssize_t intmem_write(struct omap_mmu *mmu, const char *buf, size_t count,
+                           loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = omap_mmu_to_virt(mmu, p);
+       ssize_t size = mmu->memsize;
+       ssize_t written;
+
+       if (p >= size)
+               return 0;
+       clk_enable(mmu->memclk);
+       written = count;
+       if (count > size - p)
+               written = size - p;
+       if (copy_from_user(vadr, buf, written)) {
+               written = -EFAULT;
+               goto out;
+       }
+       *ppos += written;
+out:
+       clk_disable(mmu->memclk);
+       return written;
+}
+
+static ssize_t exmem_write(struct omap_mmu *mmu, char *buf, size_t count,
+                          loff_t *ppos)
+{
+       unsigned long p = *ppos;
+       void *vadr = omap_mmu_to_virt(mmu, p);
+
+       if (!exmap_valid(mmu, vadr, count)) {
+               printk(KERN_ERR
+                      "MMU: DSP address %08lx / size %08x "
+                      "is not valid!\n", p, count);
+               return -EFAULT;
+       }
+       if (count > (1 << mmu->addrspace) - p)
+               count = (1 << mmu->addrspace) - p;
+       if (copy_from_user(vadr, buf, count))
+               return -EFAULT;
+       *ppos += count;
+
+       return count;
+}
+
+static ssize_t omap_mmu_mem_write(struct kobject *kobj, char *buf,
+                                 loff_t offset, size_t count)
+{
+       struct device *dev = to_dev(kobj);
+       struct omap_mmu *mmu = dev_get_drvdata(dev);
+       unsigned long p = (unsigned long)offset;
+       void *vadr = omap_mmu_to_virt(mmu, p);
+       int ret;
+
+       if (omap_mmu_mem_enable(mmu, vadr) < 0)
+               return -EBUSY;
+
+       if (p < mmu->memsize)
+               ret = intmem_write(mmu, buf, count, &offset);
+       else
+               ret = exmem_write(mmu, buf, count, &offset);
+
+       omap_mmu_mem_disable(mmu, vadr);
+
+       return ret;
+}
+
+static struct bin_attribute dev_attr_mem = {
+       .attr   = {
+               .name   = "mem",
+               .owner  = THIS_MODULE,
+               .mode   = S_IRUSR | S_IWUSR | S_IRGRP,
+       },
+
+       .read   = omap_mmu_mem_read,
+       .write  = omap_mmu_mem_write,
+};
+
+/* To be obsolete for backward compatibility */
+ssize_t __omap_mmu_mem_read(struct omap_mmu *mmu, char *buf,
+                           loff_t offset, size_t count)
+{
+       return omap_mmu_mem_read(&mmu->dev.kobj, buf, offset, count);
+}
+EXPORT_SYMBOL_GPL(__omap_mmu_mem_read);
+
+ssize_t __omap_mmu_mem_write(struct omap_mmu *mmu, char *buf,
+                            loff_t offset, size_t count)
+{
+       return omap_mmu_mem_write(&mmu->dev.kobj, buf, offset, count);
+}
+EXPORT_SYMBOL_GPL(__omap_mmu_mem_write);
+
+/*
+ * sysfs files
+ */
+static ssize_t omap_mmu_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct omap_mmu *mmu = dev_get_drvdata(dev);
+       struct omap_mmu_tlb_lock tlb_lock;
+       int ret = -EIO;
+
+       clk_enable(mmu->clk);
+       omap_dsp_request_mem();
+
+       down_read(&mmu->exmap_sem);
+
+       omap_mmu_get_tlb_lock(mmu, &tlb_lock);
+
+       if (likely(mmu->ops->show))
+               ret = mmu->ops->show(mmu, buf, &tlb_lock);
+
+       /* restore victim entry */
+       omap_mmu_set_tlb_lock(mmu, &tlb_lock);
+
+       up_read(&mmu->exmap_sem);
+       omap_dsp_release_mem();
+       clk_disable(mmu->clk);
+
+       return ret;
+}
+
+static DEVICE_ATTR(mmu, S_IRUGO, omap_mmu_show, NULL);
+
+static ssize_t exmap_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct omap_mmu *mmu = dev_get_drvdata(dev);
+       struct exmap_tbl *ent;
+       int len;
+       int i = 0;
+
+       down_read(&mmu->exmap_sem);
+       len = sprintf(buf, "  dspadr     size         buf     size uc\n");
+                        /* 0x300000 0x123000  0xc0171000 0x100000  0*/
+
+       omap_mmu_for_each_tlb_entry(mmu, ent) {
+               void *vadr;
+               unsigned long size;
+               enum exmap_type type;
+               int idx;
+
+               /* find a top of link */
+               if (!ent->valid || (ent->link.prev >= 0))
+                       continue;
+
+               vadr = ent->vadr;
+               type = ent->type;
+               size = 0;
+               idx = i;
+               do {
+                       ent = mmu->exmap_tbl + idx;
+                       size += PAGE_SIZE << ent->order;
+               } while ((idx = ent->link.next) >= 0);
+
+               len += sprintf(buf + len, "0x%06lx %#8lx",
+                              virt_to_omap_mmu(mmu, vadr), size);
+
+               if (type == EXMAP_TYPE_FB) {
+                       len += sprintf(buf + len, "    framebuf\n");
+               } else {
+                       len += sprintf(buf + len, "\n");
+                       idx = i;
+                       do {
+                               ent = mmu->exmap_tbl + idx;
+                               len += sprintf(buf + len,
+                                              /* 0xc0171000 0x100000  0*/
+                                              "%19s0x%8p %#8lx %2d\n",
+                                              "", ent->buf,
+                                              PAGE_SIZE << ent->order,
+                                              ent->usecount);
+                       } while ((idx = ent->link.next) >= 0);
+               }
+
+               i++;
+       }
+
+       up_read(&mmu->exmap_sem);
+       return len;
+}
+
+static ssize_t exmap_store(struct device *dev, struct device_attribute *attr,
+                          const char *buf,
+                          size_t count)
+{
+       struct omap_mmu *mmu = dev_get_drvdata(dev);
+       unsigned long base = 0, len = 0;
+       int ret;
+
+       sscanf(buf, "%lx %lx", &base, &len);
+
+       if (!base)
+               return -EINVAL;
+
+       if (len) {
+               /* Add the mapping */
+               ret = omap_mmu_exmap(mmu, base, 0, len, EXMAP_TYPE_MEM);
+               if (ret < 0)
+                       return ret;
+       } else {
+               /* Remove the mapping */
+               ret = omap_mmu_exunmap(mmu, base);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(exmap, S_IRUGO | S_IWUSR, exmap_show, exmap_store);
+
+static ssize_t mempool_show(struct class *class, char *buf)
+{
+       int min_nr_1M = 0, curr_nr_1M = 0;
+       int min_nr_64K = 0, curr_nr_64K = 0;
+       int total = 0;
+
+       if (likely(mempool_1M)) {
+               min_nr_1M  = mempool_1M->min_nr;
+               curr_nr_1M = mempool_1M->curr_nr;
+               total += min_nr_1M * SZ_1M;
+       }
+       if (likely(mempool_64K)) {
+               min_nr_64K  = mempool_64K->min_nr;
+               curr_nr_64K = mempool_64K->curr_nr;
+               total += min_nr_64K * SZ_64K;
+       }
+
+       return sprintf(buf,
+                      "0x%x\n"
+                      "1M  buffer: %d (%d free)\n"
+                      "64K buffer: %d (%d free)\n",
+                      total, min_nr_1M, curr_nr_1M, min_nr_64K, curr_nr_64K);
+}
+
+
+static CLASS_ATTR(mempool, S_IRUGO, mempool_show, NULL);
+
+static void omap_mmu_class_dev_release(struct class_device *cd)
+{
+}
+
+static struct class omap_mmu_class = {
+       .name           = "mmu",
+       .release        = omap_mmu_class_dev_release,
+};
+
+int omap_mmu_register(struct omap_mmu *mmu)
+{
+       int ret;
+
+       mmu->dev.class = &omap_mmu_class;
+       strlcpy(mmu->dev.bus_id, mmu->name, KOBJ_NAME_LEN);
+       dev_set_drvdata(&mmu->dev, mmu);
+
+       mmu->exmap_tbl = kzalloc(sizeof(struct exmap_tbl) * mmu->nr_tlb_entries,
+                                GFP_KERNEL);
+       if (!mmu->exmap_tbl)
+               return -ENOMEM;
+
+       ret = device_register(&mmu->dev);
+       if (unlikely(ret))
+               goto err_dev_register;
+
+       init_rwsem(&mmu->exmap_sem);
+
+       ret = omap_mmu_read_reg(mmu, MMU_REVISION);
+       printk(KERN_NOTICE "MMU: OMAP %s MMU initialized (HW v%d.%d)\n",
+              mmu->name, (ret >> 4) & 0xf, ret & 0xf);
+
+       ret = omap_mmu_init(mmu);
+       if (unlikely(ret))
+               goto err_mmu_init;
+
+       ret = device_create_file(&mmu->dev, &dev_attr_mmu);
+       if (unlikely(ret))
+               goto err_dev_create_mmu;
+       ret = device_create_file(&mmu->dev, &dev_attr_exmap);
+       if (unlikely(ret))
+               goto err_dev_create_exmap;
+
+       if (likely(mmu->membase)) {
+               dev_attr_mem.size = mmu->memsize;
+               ret = device_create_bin_file(&mmu->dev,
+                                            &dev_attr_mem);
+               if (unlikely(ret))
+                       goto err_bin_create_mem;
+       }
+
+       return 0;
+
+err_bin_create_mem:
+       device_remove_file(&mmu->dev, &dev_attr_exmap);
+err_dev_create_exmap:
+       device_remove_file(&mmu->dev, &dev_attr_mmu);
+err_dev_create_mmu:
+       omap_mmu_shutdown(mmu);
+err_mmu_init:
+       device_unregister(&mmu->dev);
+err_dev_register:
+       kfree(mmu->exmap_tbl);
+       mmu->exmap_tbl = NULL;
+       return ret;
+}
+
+void omap_mmu_unregister(struct omap_mmu *mmu)
+{
+       omap_mmu_shutdown(mmu);
+       omap_mmu_kmem_release();
+
+       device_remove_file(&mmu->dev, &dev_attr_mmu);
+       device_remove_file(&mmu->dev, &dev_attr_exmap);
+
+       if (likely(mmu->membase))
+               device_remove_bin_file(&mmu->dev,
+                                            &dev_attr_mem);
+
+       kfree(mmu->exmap_tbl);
+       mmu->exmap_tbl = NULL;
+
+       device_unregister(&mmu->dev);
+}
+
+static int __init omap_mmu_class_init(void)
+{
+       int ret = class_register(&omap_mmu_class);
+       if (!ret)
+               ret = class_create_file(&omap_mmu_class, &class_attr_mempool);
+
+       return ret;
+}
+
+static void __exit omap_mmu_class_exit(void)
+{
+       class_remove_file(&omap_mmu_class, &class_attr_mempool);
+       class_unregister(&omap_mmu_class);
+}
+
+subsys_initcall(omap_mmu_class_init);
+module_exit(omap_mmu_class_exit);
diff --git a/include/asm-arm/arch-omap/mmu.h b/include/asm-arm/arch-omap/mmu.h
new file mode 100644 (file)
index 0000000..dd2a09a
--- /dev/null
@@ -0,0 +1,198 @@
+#ifndef __ARCH_OMAP_MMU_H
+#define __ARCH_OMAP_MMU_H
+
+#include <linux/device.h>
+
+#define MMU_REVISION           0x00
+#define MMU_SYSCONFIG          0x10
+#define MMU_SYSSTATUS          0x14
+#define MMU_IRQSTATUS          0x18
+#define MMU_IRQENABLE          0x1c
+#define MMU_WALKING_ST         0x40
+#define MMU_CNTL               0x44
+#define MMU_FAULT_AD           0x48
+#define MMU_TTB                        0x4c
+#define MMU_LOCK               0x50
+#define MMU_LD_TLB             0x54
+#define MMU_CAM                        0x58
+#define MMU_RAM                        0x5c
+#define MMU_GFLUSH             0x60
+#define MMU_FLUSH_ENTRY                0x64
+#define MMU_READ_CAM           0x68
+#define MMU_READ_RAM           0x6c
+#define MMU_EMU_FAULT_AD       0x70
+
+enum exmap_type {
+       EXMAP_TYPE_MEM,
+       EXMAP_TYPE_FB
+};
+
+enum omap_mmu_type {
+       OMAP_MMU_DSP,
+       OMAP_MMU_IVA1,
+       OMAP_MMU_CAMERA,
+};
+
+struct exmap_tbl {
+       unsigned int valid:1;
+       unsigned int prsvd:1;
+       int usecount;           /* reference count by mmap */
+       enum exmap_type type;
+       void *buf;              /* virtual address of the buffer,
+                                * i.e. 0xc0000000 - */
+       void *vadr;             /* DSP shadow space,
+                                * i.e. 0xe0000000 - 0xe0ffffff */
+       unsigned int order;
+       struct {
+               int prev;
+               int next;
+       } link;                 /* grouping */
+};
+
+struct cam_ram_regset {
+       union {
+               struct {
+                       u16 cam_l;
+                       u16 cam_h;
+               };
+
+               u32 cam;
+       };
+
+       union {
+               struct {
+                       u16 ram_l;
+                       u16 ram_h;
+               };
+
+               u32 ram;
+       };
+};
+
+struct omap_mmu_tlb_lock {
+       int base;
+       int victim;
+};
+
+struct omap_mmu;
+struct omap_mmu_tlb_entry;
+
+struct omap_mmu_ops {
+       int (*startup)(struct omap_mmu *mmu);
+       void (*shutdown)(struct omap_mmu *mmu);
+
+       /* TLB operations */
+       void (*read_tlb)(struct omap_mmu *, struct cam_ram_regset *);
+       void (*load_tlb)(struct omap_mmu *, struct cam_ram_regset *);
+       ssize_t (*show)(struct omap_mmu *, char *, struct omap_mmu_tlb_lock *);
+
+       /* CAM / RAM operations */
+       struct cam_ram_regset *(*cam_ram_alloc)(struct omap_mmu_tlb_entry *);
+       int (*cam_ram_valid)(struct cam_ram_regset *);
+       unsigned long (*cam_va)(struct cam_ram_regset *);
+
+       /* Memory operations */
+       int (*mem_enable)(struct omap_mmu *, void *);
+       int (*mem_disable)(struct omap_mmu *, void *);
+};
+
+struct omap_mmu {
+       const char *name;
+       unsigned long base;
+       struct clk *clk;
+
+       unsigned long membase, memsize;
+       struct clk *memclk;
+
+       enum omap_mmu_type type;
+
+       struct device dev;
+
+       struct rw_semaphore exmap_sem;
+       struct exmap_tbl *exmap_tbl;
+
+       unsigned int nr_tlb_entries;
+       unsigned int nr_exmap_preserved;
+
+       /* Size of virtual address space, in bits */
+       unsigned int addrspace;
+
+       struct omap_mmu_ops *ops;
+};
+
+#define omap_mmu_internal_memory(mmu, addr)                                    \
+       (likely(mmu->membase) && (((unsigned long)(addr) >= mmu->membase) &&    \
+                ((unsigned long)(addr) < mmu->membase + mmu->memsize)))
+
+#define INIT_EXMAP_TBL_ENTRY(ent,b,v,typ,od)   \
+do {                                           \
+       (ent)->buf              = (b);          \
+       (ent)->vadr             = (v);          \
+       (ent)->valid            = 1;            \
+       (ent)->prsvd            = 0;            \
+       (ent)->usecount         = 0;            \
+       (ent)->type             = (typ);        \
+       (ent)->order            = (od);         \
+       (ent)->link.next        = -1;           \
+       (ent)->link.prev        = -1;           \
+} while (0)
+
+#define INIT_EXMAP_TBL_ENTRY_4KB_PRESERVED(ent,b,v)    \
+do {                                                   \
+       (ent)->buf              = (b);                  \
+       (ent)->vadr             = (v);                  \
+       (ent)->valid            = 1;                    \
+       (ent)->prsvd            = 1;                    \
+       (ent)->usecount         = 0;                    \
+       (ent)->type             = EXMAP_TYPE_MEM;       \
+       (ent)->order            = 0;                    \
+       (ent)->link.next        = -1;                   \
+       (ent)->link.prev        = -1;                   \
+} while (0)
+
+#define omap_mmu_to_virt(mmu, db)      ((void *)((mmu)->membase + (db)))
+#define virt_to_omap_mmu(mmu, va) \
+       (((unsigned long)(va) - (mmu)->membase))
+
+/* arch/arm/plat-omap/mmu.c */
+int omap_mmu_register(struct omap_mmu *mmu);
+void omap_mmu_unregister(struct omap_mmu *mmu);
+
+void omap_mmu_enable(struct omap_mmu *mmu, int reset);
+void omap_mmu_disable(struct omap_mmu *mmu);
+
+int omap_mmu_mem_enable(struct omap_mmu *mmu, void *addr);
+void omap_mmu_mem_disable(struct omap_mmu *mmu, void *addr);
+
+void omap_mmu_read_tlb(struct omap_mmu *mmu, struct omap_mmu_tlb_lock *lock,
+                      struct cam_ram_regset *cr);
+
+int omap_mmu_load_tlb_entry(struct omap_mmu *, struct omap_mmu_tlb_entry *);
+int omap_mmu_clear_tlb_entry(struct omap_mmu *, unsigned long vadr);
+
+int omap_mmu_kmem_reserve(struct omap_mmu *mmu, unsigned long size);
+void omap_mmu_kmem_release(void);
+
+unsigned long omap_mmu_virt_to_phys(struct omap_mmu *mmu, void *vadr,
+                                   size_t *len);
+
+int omap_mmu_exmap(struct omap_mmu *mmu, unsigned long dspadr,
+                  unsigned long padr, unsigned long size,
+                  enum exmap_type type);
+int omap_mmu_exunmap(struct omap_mmu *mmu, unsigned long dspadr);
+void omap_mmu_exmap_flush(struct omap_mmu *mmu);
+void omap_mmu_exmap_use(struct omap_mmu *mmu, void *vadr, size_t len);
+void omap_mmu_exmap_unuse(struct omap_mmu *mmu, void *vadr, size_t len);
+
+int exmap_set_armmmu(unsigned long virt, unsigned long phys, unsigned long size);
+void exmap_clear_armmmu(unsigned long virt, unsigned long size);
+void exmap_setup_preserved_mem_page(struct omap_mmu *mmu, void *buf,
+                                   unsigned long dspadr, int index);
+void exmap_clear_mem_page(struct omap_mmu *mmu, unsigned long dspadr);
+int exmap_valid(struct omap_mmu *mmu, void *vadr, size_t len);
+
+/* To be obsolete for backward compatibility */
+ssize_t __omap_mmu_mem_read(struct omap_mmu *mmu, char *buf, loff_t offset, size_t count);
+ssize_t __omap_mmu_mem_write(struct omap_mmu *mmu, char *buf, loff_t offset, size_t count);
+
+#endif /* __ARCH_OMAP_MMU_H */