public inbox for linux-omap@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4 take2] ARM: OMAP: Add MMU framework
@ 2007-03-01  9:47 Trilok Soni
  2007-03-06 10:45 ` Hiroshi DOYU
  0 siblings, 1 reply; 3+ messages in thread
From: Trilok Soni @ 2007-03-01  9:47 UTC (permalink / raw)
  To: linux-omap-open-source; +Cc: Hiroshi DOYU

[-- Attachment #1: Type: text/plain, Size: 18 bytes --]

-- 
--Trilok Soni

[-- Attachment #2: 0001-ARM-OMAP-Add-MMU-framework.txt --]
[-- Type: text/plain, Size: 39519 bytes --]

From nobody Mon Sep 17 00:00:00 2001
From: Trilok Soni <soni.trilok@gmail.com>
Date: Tue, 20 Feb 2007 17:25:50 +0530
Subject: [PATCH 1/4] ARM: OMAP: Add MMU framework

- 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>

---

 arch/arm/plat-omap/Makefile     |    2 
 arch/arm/plat-omap/mmu.c        | 1338 +++++++++++++++++++++++++++++++++++++++
 include/asm-arm/arch-omap/mmu.h |  198 ++++++
 3 files changed, 1537 insertions(+), 1 deletions(-)
 create mode 100644 arch/arm/plat-omap/mmu.c
 create mode 100644 include/asm-arm/arch-omap/mmu.h

c5965b1a9c0bec53f490d36caabd3b7425348a41
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index 1127087..05eb3cb 100644
--- a/arch/arm/plat-omap/Makefile
+++ b/arch/arm/plat-omap/Makefile
@@ -29,4 +29,4 @@ obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-l
 
 # 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
index 0000000..d5af168
--- /dev/null
+++ b/arch/arm/plat-omap/mmu.c
@@ -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
index 0000000..dd2a09a
--- /dev/null
+++ b/include/asm-arm/arch-omap/mmu.h
@@ -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 */
-- 
1.3.3


[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-03-06 10:56 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-01  9:47 [PATCH 1/4 take2] ARM: OMAP: Add MMU framework Trilok Soni
2007-03-06 10:45 ` Hiroshi DOYU
2007-03-06 10:56   ` Tony Lindgren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox