diff options
author | James Hogan <james.hogan@imgtec.com> | 2012-10-09 10:54:17 +0100 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-03-02 20:09:19 +0000 |
commit | f5df8e268f749987c32c7eee001f7623fd7be69c (patch) | |
tree | eb727e5917b4f4e4e0de0ec085fd36a11aa8d898 /arch/metag/mm/mmu-meta1.c | |
parent | 99ef7c2ac1e3b01f532bfdebbe92e9960e95bebc (diff) | |
download | linux-f5df8e268f749987c32c7eee001f7623fd7be69c.tar.bz2 |
metag: Memory management
Add memory management files for metag.
Meta's 32bit virtual address space is split into two halves:
- local (0x08000000-0x7fffffff): traditionally local to a hardware
thread and incoherent between hardware threads. Each hardware thread
has it's own local MMU table. On Meta2 the local space can be
globally coherent (GCOn) if the cache partitions coincide.
- global (0x88000000-0xffff0000): coherent and traditionally global
between hardware threads. On Meta2, each hardware thread has it's own
global MMU table.
The low 128MiB of each half is non-MMUable and maps directly to the
physical address space:
- 0x00010000-0x07ffffff: contains Meta core registers and maps SoC bus
- 0x80000000-0x87ffffff: contains low latency global core memories
Linux usually further splits the local virtual address space like this:
- 0x08000000-0x3fffffff: user mappings
- 0x40000000-0x7fffffff: kernel mappings
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Diffstat (limited to 'arch/metag/mm/mmu-meta1.c')
-rw-r--r-- | arch/metag/mm/mmu-meta1.c | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/arch/metag/mm/mmu-meta1.c b/arch/metag/mm/mmu-meta1.c new file mode 100644 index 000000000000..91f4255bcb5c --- /dev/null +++ b/arch/metag/mm/mmu-meta1.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005,2006,2007,2008,2009 Imagination Technologies + * + * Meta 1 MMU handling code. + * + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/io.h> + +#include <asm/mmu.h> + +#define DM3_BASE (LINSYSDIRECT_BASE + (MMCU_DIRECTMAPn_ADDR_SCALE * 3)) + +/* + * This contains the physical address of the top level 2k pgd table. + */ +static unsigned long mmu_base_phys; + +/* + * Given a physical address, return a mapped virtual address that can be used + * to access that location. + * In practice, we use the DirectMap region to make this happen. + */ +static unsigned long map_addr(unsigned long phys) +{ + static unsigned long dm_base = 0xFFFFFFFF; + int offset; + + offset = phys - dm_base; + + /* Are we in the current map range ? */ + if ((offset < 0) || (offset >= MMCU_DIRECTMAPn_ADDR_SCALE)) { + /* Calculate new DM area */ + dm_base = phys & ~(MMCU_DIRECTMAPn_ADDR_SCALE - 1); + + /* Actually map it in! */ + metag_out32(dm_base, MMCU_DIRECTMAP3_ADDR); + + /* And calculate how far into that area our reference is */ + offset = phys - dm_base; + } + + return DM3_BASE + offset; +} + +/* + * Return the physical address of the base of our pgd table. + */ +static inline unsigned long __get_mmu_base(void) +{ + unsigned long base_phys; + unsigned int stride; + + if (is_global_space(PAGE_OFFSET)) + stride = 4; + else + stride = hard_processor_id(); /* [0..3] */ + + base_phys = metag_in32(MMCU_TABLE_PHYS_ADDR); + base_phys += (0x800 * stride); + + return base_phys; +} + +/* Given a virtual address, return the virtual address of the relevant pgd */ +static unsigned long pgd_entry_addr(unsigned long virt) +{ + unsigned long pgd_phys; + unsigned long pgd_virt; + + if (!mmu_base_phys) + mmu_base_phys = __get_mmu_base(); + + /* + * Are we trying to map a global address. If so, then index + * the global pgd table instead of our local one. + */ + if (is_global_space(virt)) { + /* Scale into 2gig map */ + virt &= ~0x80000000; + } + + /* Base of the pgd table plus our 4Meg entry, 4bytes each */ + pgd_phys = mmu_base_phys + ((virt >> PGDIR_SHIFT) * 4); + + pgd_virt = map_addr(pgd_phys); + + return pgd_virt; +} + +/* Given a virtual address, return the virtual address of the relevant pte */ +static unsigned long pgtable_entry_addr(unsigned long virt) +{ + unsigned long pgtable_phys; + unsigned long pgtable_virt, pte_virt; + + /* Find the physical address of the 4MB page table*/ + pgtable_phys = metag_in32(pgd_entry_addr(virt)) & MMCU_ENTRY_ADDR_BITS; + + /* Map it to a virtual address */ + pgtable_virt = map_addr(pgtable_phys); + + /* And index into it for our pte */ + pte_virt = pgtable_virt + ((virt >> PAGE_SHIFT) & 0x3FF) * 4; + + return pte_virt; +} + +unsigned long mmu_read_first_level_page(unsigned long vaddr) +{ + return metag_in32(pgd_entry_addr(vaddr)); +} + +unsigned long mmu_read_second_level_page(unsigned long vaddr) +{ + return metag_in32(pgtable_entry_addr(vaddr)); +} + +unsigned long mmu_get_base(void) +{ + static unsigned long __base; + + /* Find the base of our MMU pgd table */ + if (!__base) + __base = pgd_entry_addr(0); + + return __base; +} + +void __init mmu_init(unsigned long mem_end) +{ + unsigned long entry, addr; + pgd_t *p_swapper_pg_dir; + + /* + * Now copy over any MMU pgd entries already in the mmu page tables + * over to our root init process (swapper_pg_dir) map. This map is + * then inherited by all other processes, which means all processes + * inherit a map of the kernel space. + */ + addr = PAGE_OFFSET; + entry = pgd_index(PAGE_OFFSET); + p_swapper_pg_dir = pgd_offset_k(0) + entry; + + while (addr <= META_MEMORY_LIMIT) { + unsigned long pgd_entry; + /* copy over the current MMU value */ + pgd_entry = mmu_read_first_level_page(addr); + pgd_val(*p_swapper_pg_dir) = pgd_entry; + + p_swapper_pg_dir++; + addr += PGDIR_SIZE; + entry++; + } +} |