diff options
Diffstat (limited to 'arch/arm/mm')
-rw-r--r-- | arch/arm/mm/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/mm/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mm/blockops.c | 185 | ||||
-rw-r--r-- | arch/arm/mm/consistent.c | 67 | ||||
-rw-r--r-- | arch/arm/mm/discontig.c | 4 | ||||
-rw-r--r-- | arch/arm/mm/flush.c | 7 | ||||
-rw-r--r-- | arch/arm/mm/init.c | 24 | ||||
-rw-r--r-- | arch/arm/mm/ioremap.c | 52 | ||||
-rw-r--r-- | arch/arm/mm/mm-armv.c | 1 |
9 files changed, 106 insertions, 244 deletions
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index e84fdde6edf..3b79d0e2345 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -62,8 +62,8 @@ config CPU_ARM720T # ARM920T config CPU_ARM920T bool "Support ARM920T processor" if !ARCH_S3C2410 - depends on ARCH_INTEGRATOR || ARCH_S3C2410 || ARCH_IMX || ARCH_AAEC2000 - default y if ARCH_S3C2410 + depends on ARCH_INTEGRATOR || ARCH_S3C2410 || ARCH_IMX || ARCH_AAEC2000 || ARCH_AT91RM9200 + default y if ARCH_S3C2410 || ARCH_AT91RM9200 select CPU_32v4 select CPU_ABRT_EV4T select CPU_CACHE_V4WT @@ -83,8 +83,8 @@ config CPU_ARM920T # ARM922T config CPU_ARM922T bool "Support ARM922T processor" if ARCH_INTEGRATOR - depends on ARCH_CAMELOT || ARCH_LH7A40X || ARCH_INTEGRATOR - default y if ARCH_CAMELOT || ARCH_LH7A40X + depends on ARCH_LH7A40X || ARCH_INTEGRATOR + default y if ARCH_LH7A40X select CPU_32v4 select CPU_ABRT_EV4T select CPU_CACHE_V4WT diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 59f47d4c2df..ffe73ba2bf1 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -51,4 +51,4 @@ obj-$(CONFIG_CPU_ARM1026) += proc-arm1026.o obj-$(CONFIG_CPU_SA110) += proc-sa110.o obj-$(CONFIG_CPU_SA1100) += proc-sa1100.o obj-$(CONFIG_CPU_XSCALE) += proc-xscale.o -obj-$(CONFIG_CPU_V6) += proc-v6.o blockops.o +obj-$(CONFIG_CPU_V6) += proc-v6.o diff --git a/arch/arm/mm/blockops.c b/arch/arm/mm/blockops.c deleted file mode 100644 index 4f5ee2d0899..00000000000 --- a/arch/arm/mm/blockops.c +++ /dev/null @@ -1,185 +0,0 @@ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/mm.h> - -#include <asm/memory.h> -#include <asm/ptrace.h> -#include <asm/cacheflush.h> -#include <asm/traps.h> - -extern struct cpu_cache_fns blk_cache_fns; - -#define HARVARD_CACHE - -/* - * blk_flush_kern_dcache_page(kaddr) - * - * Ensure that the data held in the page kaddr is written back - * to the page in question. - * - * - kaddr - kernel address (guaranteed to be page aligned) - */ -static void __attribute__((naked)) -blk_flush_kern_dcache_page(void *kaddr) -{ - asm( - "add r1, r0, %0 \n\ - sub r1, r1, %1 \n\ -1: .word 0xec401f0e @ mcrr p15, 0, r0, r1, c14, 0 @ blocking \n\ - mov r0, #0 \n\ - mcr p15, 0, r0, c7, c5, 0 \n\ - mcr p15, 0, r0, c7, c10, 4 \n\ - mov pc, lr" - : - : "I" (PAGE_SIZE), "I" (L1_CACHE_BYTES)); -} - -/* - * blk_dma_inv_range(start,end) - * - * Invalidate the data cache within the specified region; we will - * be performing a DMA operation in this region and we want to - * purge old data in the cache. - * - * - start - virtual start address of region - * - end - virtual end address of region - */ -static void __attribute__((naked)) -blk_dma_inv_range_unified(unsigned long start, unsigned long end) -{ - asm( - "tst r0, %0 \n\ - mcrne p15, 0, r0, c7, c11, 1 @ clean unified line \n\ - tst r1, %0 \n\ - mcrne p15, 0, r1, c7, c15, 1 @ clean & invalidate unified line\n\ - .word 0xec401f06 @ mcrr p15, 0, r1, r0, c6, 0 @ blocking \n\ - mov r0, #0 \n\ - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer \n\ - mov pc, lr" - : - : "I" (L1_CACHE_BYTES - 1)); -} - -static void __attribute__((naked)) -blk_dma_inv_range_harvard(unsigned long start, unsigned long end) -{ - asm( - "tst r0, %0 \n\ - mcrne p15, 0, r0, c7, c10, 1 @ clean D line \n\ - tst r1, %0 \n\ - mcrne p15, 0, r1, c7, c14, 1 @ clean & invalidate D line \n\ - .word 0xec401f06 @ mcrr p15, 0, r1, r0, c6, 0 @ blocking \n\ - mov r0, #0 \n\ - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer \n\ - mov pc, lr" - : - : "I" (L1_CACHE_BYTES - 1)); -} - -/* - * blk_dma_clean_range(start,end) - * - start - virtual start address of region - * - end - virtual end address of region - */ -static void __attribute__((naked)) -blk_dma_clean_range(unsigned long start, unsigned long end) -{ - asm( - ".word 0xec401f0c @ mcrr p15, 0, r1, r0, c12, 0 @ blocking \n\ - mov r0, #0 \n\ - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer \n\ - mov pc, lr"); -} - -/* - * blk_dma_flush_range(start,end) - * - start - virtual start address of region - * - end - virtual end address of region - */ -static void __attribute__((naked)) -blk_dma_flush_range(unsigned long start, unsigned long end) -{ - asm( - ".word 0xec401f0e @ mcrr p15, 0, r1, r0, c14, 0 @ blocking \n\ - mov pc, lr"); -} - -static int blockops_trap(struct pt_regs *regs, unsigned int instr) -{ - regs->ARM_r4 |= regs->ARM_r2; - regs->ARM_pc += 4; - return 0; -} - -static char *func[] = { - "Prefetch data range", - "Clean+Invalidate data range", - "Clean data range", - "Invalidate data range", - "Invalidate instr range" -}; - -static struct undef_hook blockops_hook __initdata = { - .instr_mask = 0x0fffffd0, - .instr_val = 0x0c401f00, - .cpsr_mask = PSR_T_BIT, - .cpsr_val = 0, - .fn = blockops_trap, -}; - -static int __init blockops_check(void) -{ - register unsigned int err asm("r4") = 0; - unsigned int err_pos = 1; - unsigned int cache_type; - int i; - - asm("mrc p15, 0, %0, c0, c0, 1" : "=r" (cache_type)); - - printk("Checking V6 block cache operations:\n"); - register_undef_hook(&blockops_hook); - - __asm__ ("mov r0, %0\n\t" - "mov r1, %1\n\t" - "mov r2, #1\n\t" - ".word 0xec401f2c @ mcrr p15, 0, r1, r0, c12, 2\n\t" - "mov r2, #2\n\t" - ".word 0xec401f0e @ mcrr p15, 0, r1, r0, c14, 0\n\t" - "mov r2, #4\n\t" - ".word 0xec401f0c @ mcrr p15, 0, r1, r0, c12, 0\n\t" - "mov r2, #8\n\t" - ".word 0xec401f06 @ mcrr p15, 0, r1, r0, c6, 0\n\t" - "mov r2, #16\n\t" - ".word 0xec401f05 @ mcrr p15, 0, r1, r0, c5, 0\n\t" - : - : "r" (PAGE_OFFSET), "r" (PAGE_OFFSET + 128) - : "r0", "r1", "r2"); - - unregister_undef_hook(&blockops_hook); - - for (i = 0; i < ARRAY_SIZE(func); i++, err_pos <<= 1) - printk("%30s: %ssupported\n", func[i], err & err_pos ? "not " : ""); - - if ((err & 8) == 0) { - printk(" --> Using %s block cache invalidate\n", - cache_type & (1 << 24) ? "harvard" : "unified"); - if (cache_type & (1 << 24)) - cpu_cache.dma_inv_range = blk_dma_inv_range_harvard; - else - cpu_cache.dma_inv_range = blk_dma_inv_range_unified; - } - if ((err & 4) == 0) { - printk(" --> Using block cache clean\n"); - cpu_cache.dma_clean_range = blk_dma_clean_range; - } - if ((err & 2) == 0) { - printk(" --> Using block cache clean+invalidate\n"); - cpu_cache.dma_flush_range = blk_dma_flush_range; - cpu_cache.flush_kern_dcache_page = blk_flush_kern_dcache_page; - } - - return 0; -} - -__initcall(blockops_check); diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c index 47b0b767f08..c2ee18d2075 100644 --- a/arch/arm/mm/consistent.c +++ b/arch/arm/mm/consistent.c @@ -19,17 +19,26 @@ #include <linux/dma-mapping.h> #include <asm/cacheflush.h> -#include <asm/io.h> #include <asm/tlbflush.h> +#include <asm/sizes.h> + +/* Sanity check size */ +#if (CONSISTENT_DMA_SIZE % SZ_2M) +#error "CONSISTENT_DMA_SIZE must be multiple of 2MiB" +#endif -#define CONSISTENT_BASE (0xffc00000) #define CONSISTENT_END (0xffe00000) +#define CONSISTENT_BASE (CONSISTENT_END - CONSISTENT_DMA_SIZE) + #define CONSISTENT_OFFSET(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PAGE_SHIFT) +#define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PGDIR_SHIFT) +#define NUM_CONSISTENT_PTES (CONSISTENT_DMA_SIZE >> PGDIR_SHIFT) + /* - * This is the page table (2MB) covering uncached, DMA consistent allocations + * These are the page tables (2MB each) covering uncached, DMA consistent allocations */ -static pte_t *consistent_pte; +static pte_t *consistent_pte[NUM_CONSISTENT_PTES]; static DEFINE_SPINLOCK(consistent_lock); /* @@ -66,6 +75,7 @@ struct vm_region { unsigned long vm_start; unsigned long vm_end; struct page *vm_pages; + int vm_active; }; static struct vm_region consistent_head = { @@ -104,6 +114,7 @@ vm_region_alloc(struct vm_region *head, size_t size, gfp_t gfp) list_add_tail(&new->vm_list, &c->vm_list); new->vm_start = addr; new->vm_end = addr + size; + new->vm_active = 1; spin_unlock_irqrestore(&consistent_lock, flags); return new; @@ -120,7 +131,7 @@ static struct vm_region *vm_region_find(struct vm_region *head, unsigned long ad struct vm_region *c; list_for_each_entry(c, &head->vm_list, vm_list) { - if (c->vm_start == addr) + if (c->vm_active && c->vm_start == addr) goto out; } c = NULL; @@ -141,7 +152,7 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, unsigned long order; u64 mask = ISA_DMA_THRESHOLD, limit; - if (!consistent_pte) { + if (!consistent_pte[0]) { printk(KERN_ERR "%s: not initialised\n", __func__); dump_stack(); return NULL; @@ -204,9 +215,12 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, c = vm_region_alloc(&consistent_head, size, gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); if (c) { - pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + pte_t *pte; struct page *end = page + (1 << order); + int idx = CONSISTENT_PTE_INDEX(c->vm_start); + u32 off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1); + pte = consistent_pte[idx] + off; c->vm_pages = page; /* @@ -225,6 +239,11 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, set_pte(pte, mk_pte(page, prot)); page++; pte++; + off++; + if (off >= PTRS_PER_PTE) { + off = 0; + pte = consistent_pte[++idx]; + } } while (size -= PAGE_SIZE); /* @@ -319,21 +338,28 @@ EXPORT_SYMBOL(dma_mmap_writecombine); /* * free a page as defined by the above mapping. + * Must not be called with IRQs disabled. */ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle) { struct vm_region *c; unsigned long flags, addr; pte_t *ptep; + int idx; + u32 off; + + WARN_ON(irqs_disabled()); size = PAGE_ALIGN(size); spin_lock_irqsave(&consistent_lock, flags); - c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); if (!c) goto no_area; + c->vm_active = 0; + spin_unlock_irqrestore(&consistent_lock, flags); + if ((c->vm_end - c->vm_start) != size) { printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", __func__, c->vm_end - c->vm_start, size); @@ -341,7 +367,9 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr size = c->vm_end - c->vm_start; } - ptep = consistent_pte + CONSISTENT_OFFSET(c->vm_start); + idx = CONSISTENT_PTE_INDEX(c->vm_start); + off = CONSISTENT_OFFSET(c->vm_start) & (PTRS_PER_PTE-1); + ptep = consistent_pte[idx] + off; addr = c->vm_start; do { pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep); @@ -349,6 +377,11 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr ptep++; addr += PAGE_SIZE; + off++; + if (off >= PTRS_PER_PTE) { + off = 0; + ptep = consistent_pte[++idx]; + } if (!pte_none(pte) && pte_present(pte)) { pfn = pte_pfn(pte); @@ -372,8 +405,8 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr flush_tlb_kernel_range(c->vm_start, c->vm_end); + spin_lock_irqsave(&consistent_lock, flags); list_del(&c->vm_list); - spin_unlock_irqrestore(&consistent_lock, flags); kfree(c); @@ -395,11 +428,12 @@ static int __init consistent_init(void) pgd_t *pgd; pmd_t *pmd; pte_t *pte; - int ret = 0; + int ret = 0, i = 0; + u32 base = CONSISTENT_BASE; do { - pgd = pgd_offset(&init_mm, CONSISTENT_BASE); - pmd = pmd_alloc(&init_mm, pgd, CONSISTENT_BASE); + pgd = pgd_offset(&init_mm, base); + pmd = pmd_alloc(&init_mm, pgd, base); if (!pmd) { printk(KERN_ERR "%s: no pmd tables\n", __func__); ret = -ENOMEM; @@ -407,15 +441,16 @@ static int __init consistent_init(void) } WARN_ON(!pmd_none(*pmd)); - pte = pte_alloc_kernel(pmd, CONSISTENT_BASE); + pte = pte_alloc_kernel(pmd, base); if (!pte) { printk(KERN_ERR "%s: no pte tables\n", __func__); ret = -ENOMEM; break; } - consistent_pte = pte; - } while (0); + consistent_pte[i++] = pte; + base += (1 << PGDIR_SHIFT); + } while (base < CONSISTENT_END); return ret; } diff --git a/arch/arm/mm/discontig.c b/arch/arm/mm/discontig.c index 0d097bb1bc4..1e560218950 100644 --- a/arch/arm/mm/discontig.c +++ b/arch/arm/mm/discontig.c @@ -9,10 +9,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - #include <linux/module.h> -#include <linux/mm.h> -#include <linux/init.h> +#include <linux/mmzone.h> #include <linux/bootmem.h> #if MAX_NUMNODES != 4 && MAX_NUMNODES != 16 diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index c9a03981b78..330695b6b19 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -155,14 +155,19 @@ static void __flush_dcache_aliases(struct address_space *mapping, struct page *p * space mappings, we can be lazy and remember that we may have dirty * kernel cache lines for later. Otherwise, we assume we have * aliasing mappings. + * + * Note that we disable the lazy flush for SMP. */ void flush_dcache_page(struct page *page) { struct address_space *mapping = page_mapping(page); +#ifndef CONFIG_SMP if (mapping && !mapping_mapped(mapping)) set_bit(PG_dcache_dirty, &page->flags); - else { + else +#endif + { __flush_dcache_page(mapping, page); if (mapping && cache_is_vivt()) __flush_dcache_aliases(mapping, page); diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index c168f322ef8..8b276ee38ac 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -420,7 +420,8 @@ static void __init bootmem_init(struct meminfo *mi) * Set up device the mappings. Since we clear out the page tables for all * mappings above VMALLOC_END, we will remove any debug device mappings. * This means you have to be careful how you debug this function, or any - * called function. (Do it by code inspection!) + * called function. This means you can't use any function or debugging + * method which may touch any device, otherwise the kernel _will_ crash. */ static void __init devicemaps_init(struct machine_desc *mdesc) { @@ -428,6 +429,12 @@ static void __init devicemaps_init(struct machine_desc *mdesc) unsigned long addr; void *vectors; + /* + * Allocate the vector page early. + */ + vectors = alloc_bootmem_low_pages(PAGE_SIZE); + BUG_ON(!vectors); + for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE) pmd_clear(pmd_off_k(addr)); @@ -461,12 +468,6 @@ static void __init devicemaps_init(struct machine_desc *mdesc) create_mapping(&map); #endif - flush_cache_all(); - local_flush_tlb_all(); - - vectors = alloc_bootmem_low_pages(PAGE_SIZE); - BUG_ON(!vectors); - /* * Create a mapping for the machine vectors at the high-vectors * location (0xffff0000). If we aren't using high-vectors, also @@ -491,12 +492,13 @@ static void __init devicemaps_init(struct machine_desc *mdesc) mdesc->map_io(); /* - * Finally flush the tlb again - this ensures that we're in a - * consistent state wrt the writebuffer if the writebuffer needs - * draining. After this point, we can start to touch devices - * again. + * Finally flush the caches and tlb to ensure that we're in a + * consistent state wrt the writebuffer. This also ensures that + * any write-allocated cache lines in the vector page are written + * back. After this point, we can start to touch devices again. */ local_flush_tlb_all(); + flush_cache_all(); } /* diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 0f128c28fee..de3ce1eec2e 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -86,11 +86,12 @@ remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, } static int -remap_area_pages(unsigned long start, unsigned long phys_addr, +remap_area_pages(unsigned long start, unsigned long pfn, unsigned long size, unsigned long flags) { unsigned long address = start; unsigned long end = start + size; + unsigned long phys_addr = __pfn_to_phys(pfn); int err = 0; pgd_t * dir; @@ -130,37 +131,44 @@ remap_area_pages(unsigned long start, unsigned long phys_addr, * mapping. See include/asm-arm/proc-armv/pgtable.h for more information. */ void __iomem * -__ioremap(unsigned long phys_addr, size_t size, unsigned long flags, - unsigned long align) +__ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, + unsigned long flags) { - void * addr; - struct vm_struct * area; - unsigned long offset, last_addr; + unsigned long addr; + struct vm_struct * area; + + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + addr = (unsigned long)area->addr; + if (remap_area_pages(addr, pfn, size, flags)) { + vfree(addr); + return NULL; + } + return (void __iomem *) (offset + (char *)addr); +} +EXPORT_SYMBOL(__ioremap_pfn); - /* Don't allow wraparound or zero size */ +void __iomem * +__ioremap(unsigned long phys_addr, size_t size, unsigned long flags) +{ + unsigned long last_addr; + unsigned long offset = phys_addr & ~PAGE_MASK; + unsigned long pfn = __phys_to_pfn(phys_addr); + + /* + * Don't allow wraparound or zero size + */ last_addr = phys_addr + size - 1; if (!size || last_addr < phys_addr) return NULL; /* - * Mappings have to be page-aligned + * Page align the mapping size */ - offset = phys_addr & ~PAGE_MASK; - phys_addr &= PAGE_MASK; size = PAGE_ALIGN(last_addr + 1) - phys_addr; - /* - * Ok, go for it.. - */ - area = get_vm_area(size, VM_IOREMAP); - if (!area) - return NULL; - addr = area->addr; - if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { - vfree(addr); - return NULL; - } - return (void __iomem *) (offset + (char *)addr); + return __ioremap_pfn(pfn, offset, size, flags); } EXPORT_SYMBOL(__ioremap); diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 9e50127be63..d0245a31d4d 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -19,7 +19,6 @@ #include <asm/pgalloc.h> #include <asm/page.h> -#include <asm/io.h> #include <asm/setup.h> #include <asm/tlbflush.h> |