* [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* Re: [PATCH 1/4 take2] ARM: OMAP: Add MMU framework
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
0 siblings, 1 reply; 3+ messages in thread
From: Hiroshi DOYU @ 2007-03-06 10:45 UTC (permalink / raw)
To: soni.trilok; +Cc: lethal, linux-omap-open-source
From: "ext Trilok Soni" <soni.trilok@gmail.com>
Subject: [PATCH 1/4 take2] ARM: OMAP: Add MMU framework
Date: Thu, 1 Mar 2007 15:17:38 +0530
> 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.
The author of the above part should be "Paul Mundt <lethal@linux-sh.org>"
> - 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>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH 1/4 take2] ARM: OMAP: Add MMU framework
2007-03-06 10:45 ` Hiroshi DOYU
@ 2007-03-06 10:56 ` Tony Lindgren
0 siblings, 0 replies; 3+ messages in thread
From: Tony Lindgren @ 2007-03-06 10:56 UTC (permalink / raw)
To: Hiroshi DOYU; +Cc: lethal, linux-omap-open-source
* Hiroshi DOYU <Hiroshi.DOYU@nokia.com> [070306 02:44]:
> From: "ext Trilok Soni" <soni.trilok@gmail.com>
> Subject: [PATCH 1/4 take2] ARM: OMAP: Add MMU framework
> Date: Thu, 1 Mar 2007 15:17:38 +0530
>
> > 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.
>
> The author of the above part should be "Paul Mundt <lethal@linux-sh.org>"
>
> > - 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>
Thanks for the clarification, it is always nice to be able to credit the
original author. And in the maemo.org sources it may not show up.
BTW, you can just add a "From:" field to the comments, and then git-am
will use that field as the autor when applying.
Pushing these MMU patches today.
Tony
^ permalink raw reply [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