1/* MN10300 Cache flushing routines 2 * 3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public Licence 8 * as published by the Free Software Foundation; either version 9 * 2 of the Licence, or (at your option) any later version. 10 */ 11#include <linux/module.h> 12#include <linux/mm.h> 13#include <linux/mman.h> 14#include <linux/threads.h> 15#include <asm/page.h> 16#include <asm/pgtable.h> 17#include <asm/processor.h> 18#include <asm/cacheflush.h> 19#include <asm/io.h> 20#include <asm/uaccess.h> 21 22EXPORT_SYMBOL(mn10300_icache_inv); 23EXPORT_SYMBOL(mn10300_dcache_inv); 24EXPORT_SYMBOL(mn10300_dcache_inv_range); 25EXPORT_SYMBOL(mn10300_dcache_inv_range2); 26EXPORT_SYMBOL(mn10300_dcache_inv_page); 27 28#ifdef CONFIG_MN10300_CACHE_WBACK 29EXPORT_SYMBOL(mn10300_dcache_flush); 30EXPORT_SYMBOL(mn10300_dcache_flush_inv); 31EXPORT_SYMBOL(mn10300_dcache_flush_inv_range); 32EXPORT_SYMBOL(mn10300_dcache_flush_inv_range2); 33EXPORT_SYMBOL(mn10300_dcache_flush_inv_page); 34EXPORT_SYMBOL(mn10300_dcache_flush_range); 35EXPORT_SYMBOL(mn10300_dcache_flush_range2); 36EXPORT_SYMBOL(mn10300_dcache_flush_page); 37#endif 38 39/* 40 * write a page back from the dcache and invalidate the icache so that we can 41 * run code from it that we've just written into it 42 */ 43void flush_icache_page(struct vm_area_struct *vma, struct page *page) 44{ 45 mn10300_dcache_flush_page(page_to_phys(page)); 46 mn10300_icache_inv(); 47} 48EXPORT_SYMBOL(flush_icache_page); 49 50/* 51 * write some code we've just written back from the dcache and invalidate the 52 * icache so that we can run that code 53 */ 54void flush_icache_range(unsigned long start, unsigned long end) 55{ 56#ifdef CONFIG_MN10300_CACHE_WBACK 57 unsigned long addr, size, base, off; 58 struct page *page; 59 pgd_t *pgd; 60 pud_t *pud; 61 pmd_t *pmd; 62 pte_t *ppte, pte; 63 64 if (end > 0x80000000UL) { 65 /* addresses above 0xa0000000 do not go through the cache */ 66 if (end > 0xa0000000UL) { 67 end = 0xa0000000UL; 68 if (start >= end) 69 return; 70 } 71 72 /* kernel addresses between 0x80000000 and 0x9fffffff do not 73 * require page tables, so we just map such addresses directly */ 74 base = (start >= 0x80000000UL) ? start : 0x80000000UL; 75 mn10300_dcache_flush_range(base, end); 76 if (base == start) 77 goto invalidate; 78 end = base; 79 } 80 81 for (; start < end; start += size) { 82 /* work out how much of the page to flush */ 83 off = start & (PAGE_SIZE - 1); 84 85 size = end - start; 86 if (size > PAGE_SIZE - off) 87 size = PAGE_SIZE - off; 88 89 /* get the physical address the page is mapped to from the page 90 * tables */ 91 pgd = pgd_offset(current->mm, start); 92 if (!pgd || !pgd_val(*pgd)) 93 continue; 94 95 pud = pud_offset(pgd, start); 96 if (!pud || !pud_val(*pud)) 97 continue; 98 99 pmd = pmd_offset(pud, start); 100 if (!pmd || !pmd_val(*pmd)) 101 continue; 102 103 ppte = pte_offset_map(pmd, start); 104 if (!ppte) 105 continue; 106 pte = *ppte; 107 pte_unmap(ppte); 108 109 if (pte_none(pte)) 110 continue; 111 112 page = pte_page(pte); 113 if (!page) 114 continue; 115 116 addr = page_to_phys(page); 117 118 /* flush the dcache and invalidate the icache coverage on that 119 * region */ 120 mn10300_dcache_flush_range2(addr + off, size); 121 } 122#endif 123 124invalidate: 125 mn10300_icache_inv(); 126} 127EXPORT_SYMBOL(flush_icache_range); 128 129/* 130 * allow userspace to flush the instruction cache 131 */ 132asmlinkage long sys_cacheflush(unsigned long start, unsigned long end) 133{ 134 if (end < start) 135 return -EINVAL; 136 137 flush_icache_range(start, end); 138 return 0; 139} 140