Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.33 338 lines 9.1 kB view raw
1/* 2 * arch/sh/mm/cache.c 3 * 4 * Copyright (C) 1999, 2000, 2002 Niibe Yutaka 5 * Copyright (C) 2002 - 2009 Paul Mundt 6 * 7 * Released under the terms of the GNU GPL v2.0. 8 */ 9#include <linux/mm.h> 10#include <linux/init.h> 11#include <linux/mutex.h> 12#include <linux/fs.h> 13#include <linux/smp.h> 14#include <linux/highmem.h> 15#include <linux/module.h> 16#include <asm/mmu_context.h> 17#include <asm/cacheflush.h> 18 19void (*local_flush_cache_all)(void *args) = cache_noop; 20void (*local_flush_cache_mm)(void *args) = cache_noop; 21void (*local_flush_cache_dup_mm)(void *args) = cache_noop; 22void (*local_flush_cache_page)(void *args) = cache_noop; 23void (*local_flush_cache_range)(void *args) = cache_noop; 24void (*local_flush_dcache_page)(void *args) = cache_noop; 25void (*local_flush_icache_range)(void *args) = cache_noop; 26void (*local_flush_icache_page)(void *args) = cache_noop; 27void (*local_flush_cache_sigtramp)(void *args) = cache_noop; 28 29void (*__flush_wback_region)(void *start, int size); 30EXPORT_SYMBOL(__flush_wback_region); 31void (*__flush_purge_region)(void *start, int size); 32EXPORT_SYMBOL(__flush_purge_region); 33void (*__flush_invalidate_region)(void *start, int size); 34EXPORT_SYMBOL(__flush_invalidate_region); 35 36static inline void noop__flush_region(void *start, int size) 37{ 38} 39 40static inline void cacheop_on_each_cpu(void (*func) (void *info), void *info, 41 int wait) 42{ 43 preempt_disable(); 44 smp_call_function(func, info, wait); 45 func(info); 46 preempt_enable(); 47} 48 49void copy_to_user_page(struct vm_area_struct *vma, struct page *page, 50 unsigned long vaddr, void *dst, const void *src, 51 unsigned long len) 52{ 53 if (boot_cpu_data.dcache.n_aliases && page_mapped(page) && 54 !test_bit(PG_dcache_dirty, &page->flags)) { 55 void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); 56 memcpy(vto, src, len); 57 kunmap_coherent(vto); 58 } else { 59 memcpy(dst, src, len); 60 if (boot_cpu_data.dcache.n_aliases) 61 set_bit(PG_dcache_dirty, &page->flags); 62 } 63 64 if (vma->vm_flags & VM_EXEC) 65 flush_cache_page(vma, vaddr, page_to_pfn(page)); 66} 67 68void copy_from_user_page(struct vm_area_struct *vma, struct page *page, 69 unsigned long vaddr, void *dst, const void *src, 70 unsigned long len) 71{ 72 if (boot_cpu_data.dcache.n_aliases && page_mapped(page) && 73 !test_bit(PG_dcache_dirty, &page->flags)) { 74 void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); 75 memcpy(dst, vfrom, len); 76 kunmap_coherent(vfrom); 77 } else { 78 memcpy(dst, src, len); 79 if (boot_cpu_data.dcache.n_aliases) 80 set_bit(PG_dcache_dirty, &page->flags); 81 } 82} 83 84void copy_user_highpage(struct page *to, struct page *from, 85 unsigned long vaddr, struct vm_area_struct *vma) 86{ 87 void *vfrom, *vto; 88 89 vto = kmap_atomic(to, KM_USER1); 90 91 if (boot_cpu_data.dcache.n_aliases && page_mapped(from) && 92 !test_bit(PG_dcache_dirty, &from->flags)) { 93 vfrom = kmap_coherent(from, vaddr); 94 copy_page(vto, vfrom); 95 kunmap_coherent(vfrom); 96 } else { 97 vfrom = kmap_atomic(from, KM_USER0); 98 copy_page(vto, vfrom); 99 kunmap_atomic(vfrom, KM_USER0); 100 } 101 102 if (pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) 103 __flush_purge_region(vto, PAGE_SIZE); 104 105 kunmap_atomic(vto, KM_USER1); 106 /* Make sure this page is cleared on other CPU's too before using it */ 107 smp_wmb(); 108} 109EXPORT_SYMBOL(copy_user_highpage); 110 111void clear_user_highpage(struct page *page, unsigned long vaddr) 112{ 113 void *kaddr = kmap_atomic(page, KM_USER0); 114 115 clear_page(kaddr); 116 117 if (pages_do_alias((unsigned long)kaddr, vaddr & PAGE_MASK)) 118 __flush_purge_region(kaddr, PAGE_SIZE); 119 120 kunmap_atomic(kaddr, KM_USER0); 121} 122EXPORT_SYMBOL(clear_user_highpage); 123 124void __update_cache(struct vm_area_struct *vma, 125 unsigned long address, pte_t pte) 126{ 127 struct page *page; 128 unsigned long pfn = pte_pfn(pte); 129 130 if (!boot_cpu_data.dcache.n_aliases) 131 return; 132 133 page = pfn_to_page(pfn); 134 if (pfn_valid(pfn)) { 135 int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); 136 if (dirty) 137 __flush_purge_region(page_address(page), PAGE_SIZE); 138 } 139} 140 141void __flush_anon_page(struct page *page, unsigned long vmaddr) 142{ 143 unsigned long addr = (unsigned long) page_address(page); 144 145 if (pages_do_alias(addr, vmaddr)) { 146 if (boot_cpu_data.dcache.n_aliases && page_mapped(page) && 147 !test_bit(PG_dcache_dirty, &page->flags)) { 148 void *kaddr; 149 150 kaddr = kmap_coherent(page, vmaddr); 151 /* XXX.. For now kunmap_coherent() does a purge */ 152 /* __flush_purge_region((void *)kaddr, PAGE_SIZE); */ 153 kunmap_coherent(kaddr); 154 } else 155 __flush_purge_region((void *)addr, PAGE_SIZE); 156 } 157} 158 159void flush_cache_all(void) 160{ 161 cacheop_on_each_cpu(local_flush_cache_all, NULL, 1); 162} 163EXPORT_SYMBOL(flush_cache_all); 164 165void flush_cache_mm(struct mm_struct *mm) 166{ 167 if (boot_cpu_data.dcache.n_aliases == 0) 168 return; 169 170 cacheop_on_each_cpu(local_flush_cache_mm, mm, 1); 171} 172 173void flush_cache_dup_mm(struct mm_struct *mm) 174{ 175 if (boot_cpu_data.dcache.n_aliases == 0) 176 return; 177 178 cacheop_on_each_cpu(local_flush_cache_dup_mm, mm, 1); 179} 180 181void flush_cache_page(struct vm_area_struct *vma, unsigned long addr, 182 unsigned long pfn) 183{ 184 struct flusher_data data; 185 186 data.vma = vma; 187 data.addr1 = addr; 188 data.addr2 = pfn; 189 190 cacheop_on_each_cpu(local_flush_cache_page, (void *)&data, 1); 191} 192 193void flush_cache_range(struct vm_area_struct *vma, unsigned long start, 194 unsigned long end) 195{ 196 struct flusher_data data; 197 198 data.vma = vma; 199 data.addr1 = start; 200 data.addr2 = end; 201 202 cacheop_on_each_cpu(local_flush_cache_range, (void *)&data, 1); 203} 204EXPORT_SYMBOL(flush_cache_range); 205 206void flush_dcache_page(struct page *page) 207{ 208 cacheop_on_each_cpu(local_flush_dcache_page, page, 1); 209} 210EXPORT_SYMBOL(flush_dcache_page); 211 212void flush_icache_range(unsigned long start, unsigned long end) 213{ 214 struct flusher_data data; 215 216 data.vma = NULL; 217 data.addr1 = start; 218 data.addr2 = end; 219 220 cacheop_on_each_cpu(local_flush_icache_range, (void *)&data, 1); 221} 222 223void flush_icache_page(struct vm_area_struct *vma, struct page *page) 224{ 225 /* Nothing uses the VMA, so just pass the struct page along */ 226 cacheop_on_each_cpu(local_flush_icache_page, page, 1); 227} 228 229void flush_cache_sigtramp(unsigned long address) 230{ 231 cacheop_on_each_cpu(local_flush_cache_sigtramp, (void *)address, 1); 232} 233 234static void compute_alias(struct cache_info *c) 235{ 236 c->alias_mask = ((c->sets - 1) << c->entry_shift) & ~(PAGE_SIZE - 1); 237 c->n_aliases = c->alias_mask ? (c->alias_mask >> PAGE_SHIFT) + 1 : 0; 238} 239 240static void __init emit_cache_params(void) 241{ 242 printk(KERN_NOTICE "I-cache : n_ways=%d n_sets=%d way_incr=%d\n", 243 boot_cpu_data.icache.ways, 244 boot_cpu_data.icache.sets, 245 boot_cpu_data.icache.way_incr); 246 printk(KERN_NOTICE "I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", 247 boot_cpu_data.icache.entry_mask, 248 boot_cpu_data.icache.alias_mask, 249 boot_cpu_data.icache.n_aliases); 250 printk(KERN_NOTICE "D-cache : n_ways=%d n_sets=%d way_incr=%d\n", 251 boot_cpu_data.dcache.ways, 252 boot_cpu_data.dcache.sets, 253 boot_cpu_data.dcache.way_incr); 254 printk(KERN_NOTICE "D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", 255 boot_cpu_data.dcache.entry_mask, 256 boot_cpu_data.dcache.alias_mask, 257 boot_cpu_data.dcache.n_aliases); 258 259 /* 260 * Emit Secondary Cache parameters if the CPU has a probed L2. 261 */ 262 if (boot_cpu_data.flags & CPU_HAS_L2_CACHE) { 263 printk(KERN_NOTICE "S-cache : n_ways=%d n_sets=%d way_incr=%d\n", 264 boot_cpu_data.scache.ways, 265 boot_cpu_data.scache.sets, 266 boot_cpu_data.scache.way_incr); 267 printk(KERN_NOTICE "S-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", 268 boot_cpu_data.scache.entry_mask, 269 boot_cpu_data.scache.alias_mask, 270 boot_cpu_data.scache.n_aliases); 271 } 272} 273 274void __init cpu_cache_init(void) 275{ 276 unsigned int cache_disabled = 0; 277 278#ifdef CCR 279 cache_disabled = !(__raw_readl(CCR) & CCR_CACHE_ENABLE); 280#endif 281 282 compute_alias(&boot_cpu_data.icache); 283 compute_alias(&boot_cpu_data.dcache); 284 compute_alias(&boot_cpu_data.scache); 285 286 __flush_wback_region = noop__flush_region; 287 __flush_purge_region = noop__flush_region; 288 __flush_invalidate_region = noop__flush_region; 289 290 /* 291 * No flushing is necessary in the disabled cache case so we can 292 * just keep the noop functions in local_flush_..() and __flush_..() 293 */ 294 if (unlikely(cache_disabled)) 295 goto skip; 296 297 if (boot_cpu_data.family == CPU_FAMILY_SH2) { 298 extern void __weak sh2_cache_init(void); 299 300 sh2_cache_init(); 301 } 302 303 if (boot_cpu_data.family == CPU_FAMILY_SH2A) { 304 extern void __weak sh2a_cache_init(void); 305 306 sh2a_cache_init(); 307 } 308 309 if (boot_cpu_data.family == CPU_FAMILY_SH3) { 310 extern void __weak sh3_cache_init(void); 311 312 sh3_cache_init(); 313 314 if ((boot_cpu_data.type == CPU_SH7705) && 315 (boot_cpu_data.dcache.sets == 512)) { 316 extern void __weak sh7705_cache_init(void); 317 318 sh7705_cache_init(); 319 } 320 } 321 322 if ((boot_cpu_data.family == CPU_FAMILY_SH4) || 323 (boot_cpu_data.family == CPU_FAMILY_SH4A) || 324 (boot_cpu_data.family == CPU_FAMILY_SH4AL_DSP)) { 325 extern void __weak sh4_cache_init(void); 326 327 sh4_cache_init(); 328 } 329 330 if (boot_cpu_data.family == CPU_FAMILY_SH5) { 331 extern void __weak sh5_cache_init(void); 332 333 sh5_cache_init(); 334 } 335 336skip: 337 emit_cache_params(); 338}