11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * arch/sh/mm/cache-sh7705.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1999, 2000 Niibe Yutaka 51da177e4SLinus Torvalds * Copyright (C) 2004 Alex Song 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 81da177e4SLinus Torvalds * License. See the file "COPYING" in the main directory of this archive 91da177e4SLinus Torvalds * for more details. 1039e688a9SPaul Mundt * 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds #include <linux/init.h> 131da177e4SLinus Torvalds #include <linux/mman.h> 141da177e4SLinus Torvalds #include <linux/mm.h> 151da177e4SLinus Torvalds #include <linux/threads.h> 161da177e4SLinus Torvalds #include <asm/addrspace.h> 171da177e4SLinus Torvalds #include <asm/page.h> 181da177e4SLinus Torvalds #include <asm/pgtable.h> 191da177e4SLinus Torvalds #include <asm/processor.h> 201da177e4SLinus Torvalds #include <asm/cache.h> 211da177e4SLinus Torvalds #include <asm/io.h> 221da177e4SLinus Torvalds #include <asm/uaccess.h> 231da177e4SLinus Torvalds #include <asm/pgalloc.h> 241da177e4SLinus Torvalds #include <asm/mmu_context.h> 251da177e4SLinus Torvalds #include <asm/cacheflush.h> 261da177e4SLinus Torvalds 270f08f338SPaul Mundt /* 280f08f338SPaul Mundt * The 32KB cache on the SH7705 suffers from the same synonym problem 290f08f338SPaul Mundt * as SH4 CPUs 300f08f338SPaul Mundt */ 311da177e4SLinus Torvalds static inline void cache_wback_all(void) 321da177e4SLinus Torvalds { 331da177e4SLinus Torvalds unsigned long ways, waysize, addrstart; 341da177e4SLinus Torvalds 3511c19656SPaul Mundt ways = current_cpu_data.dcache.ways; 3611c19656SPaul Mundt waysize = current_cpu_data.dcache.sets; 3711c19656SPaul Mundt waysize <<= current_cpu_data.dcache.entry_shift; 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds addrstart = CACHE_OC_ADDRESS_ARRAY; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds do { 421da177e4SLinus Torvalds unsigned long addr; 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds for (addr = addrstart; 451da177e4SLinus Torvalds addr < addrstart + waysize; 4611c19656SPaul Mundt addr += current_cpu_data.dcache.linesz) { 471da177e4SLinus Torvalds unsigned long data; 481da177e4SLinus Torvalds int v = SH_CACHE_UPDATED | SH_CACHE_VALID; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds data = ctrl_inl(addr); 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds if ((data & v) == v) 531da177e4SLinus Torvalds ctrl_outl(data & ~v, addr); 5439e688a9SPaul Mundt 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds 5711c19656SPaul Mundt addrstart += current_cpu_data.dcache.way_incr; 581da177e4SLinus Torvalds } while (--ways); 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* 621da177e4SLinus Torvalds * Write back the range of D-cache, and purge the I-cache. 631da177e4SLinus Torvalds * 641da177e4SLinus Torvalds * Called from kernel/module.c:sys_init_module and routine for a.out format. 651da177e4SLinus Torvalds */ 661da177e4SLinus Torvalds void flush_icache_range(unsigned long start, unsigned long end) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds __flush_wback_region((void *)start, end - start); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* 721da177e4SLinus Torvalds * Writeback&Invalidate the D-cache of the page 731da177e4SLinus Torvalds */ 74cbaa118eSStuart Menefy static void __uses_jump_to_uncached __flush_dcache_page(unsigned long phys) 751da177e4SLinus Torvalds { 761da177e4SLinus Torvalds unsigned long ways, waysize, addrstart; 771da177e4SLinus Torvalds unsigned long flags; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds phys |= SH_CACHE_VALID; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds /* 821da177e4SLinus Torvalds * Here, phys is the physical address of the page. We check all the 831da177e4SLinus Torvalds * tags in the cache for those with the same page number as this page 841da177e4SLinus Torvalds * (by masking off the lowest 2 bits of the 19-bit tag; these bits are 851da177e4SLinus Torvalds * derived from the offset within in the 4k page). Matching valid 861da177e4SLinus Torvalds * entries are invalidated. 871da177e4SLinus Torvalds * 881da177e4SLinus Torvalds * Since 2 bits of the cache index are derived from the virtual page 891da177e4SLinus Torvalds * number, knowing this would reduce the number of cache entries to be 901da177e4SLinus Torvalds * searched by a factor of 4. However this function exists to deal with 911da177e4SLinus Torvalds * potential cache aliasing, therefore the optimisation is probably not 921da177e4SLinus Torvalds * possible. 931da177e4SLinus Torvalds */ 941da177e4SLinus Torvalds local_irq_save(flags); 95cbaa118eSStuart Menefy jump_to_uncached(); 961da177e4SLinus Torvalds 9711c19656SPaul Mundt ways = current_cpu_data.dcache.ways; 9811c19656SPaul Mundt waysize = current_cpu_data.dcache.sets; 9911c19656SPaul Mundt waysize <<= current_cpu_data.dcache.entry_shift; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds addrstart = CACHE_OC_ADDRESS_ARRAY; 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds do { 1041da177e4SLinus Torvalds unsigned long addr; 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds for (addr = addrstart; 1071da177e4SLinus Torvalds addr < addrstart + waysize; 10811c19656SPaul Mundt addr += current_cpu_data.dcache.linesz) { 1091da177e4SLinus Torvalds unsigned long data; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds data = ctrl_inl(addr) & (0x1ffffC00 | SH_CACHE_VALID); 1121da177e4SLinus Torvalds if (data == phys) { 1131da177e4SLinus Torvalds data &= ~(SH_CACHE_VALID | SH_CACHE_UPDATED); 1141da177e4SLinus Torvalds ctrl_outl(data, addr); 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 11811c19656SPaul Mundt addrstart += current_cpu_data.dcache.way_incr; 1191da177e4SLinus Torvalds } while (--ways); 1201da177e4SLinus Torvalds 121cbaa118eSStuart Menefy back_to_cached(); 1221da177e4SLinus Torvalds local_irq_restore(flags); 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /* 1261da177e4SLinus Torvalds * Write back & invalidate the D-cache of the page. 1271da177e4SLinus Torvalds * (To avoid "alias" issues) 1281da177e4SLinus Torvalds */ 1291da177e4SLinus Torvalds void flush_dcache_page(struct page *page) 1301da177e4SLinus Torvalds { 13139e688a9SPaul Mundt if (test_bit(PG_mapped, &page->flags)) 1321da177e4SLinus Torvalds __flush_dcache_page(PHYSADDR(page_address(page))); 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 135cbaa118eSStuart Menefy void __uses_jump_to_uncached flush_cache_all(void) 1361da177e4SLinus Torvalds { 1371da177e4SLinus Torvalds unsigned long flags; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds local_irq_save(flags); 140cbaa118eSStuart Menefy jump_to_uncached(); 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds cache_wback_all(); 143cbaa118eSStuart Menefy back_to_cached(); 1441da177e4SLinus Torvalds local_irq_restore(flags); 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds void flush_cache_mm(struct mm_struct *mm) 1481da177e4SLinus Torvalds { 1491da177e4SLinus Torvalds /* Is there any good way? */ 1501da177e4SLinus Torvalds /* XXX: possibly call flush_cache_range for each vm area */ 1511da177e4SLinus Torvalds flush_cache_all(); 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds /* 1551da177e4SLinus Torvalds * Write back and invalidate D-caches. 1561da177e4SLinus Torvalds * 1571da177e4SLinus Torvalds * START, END: Virtual Address (U0 address) 1581da177e4SLinus Torvalds * 1591da177e4SLinus Torvalds * NOTE: We need to flush the _physical_ page entry. 1601da177e4SLinus Torvalds * Flushing the cache lines for U0 only isn't enough. 1611da177e4SLinus Torvalds * We need to flush for P1 too, which may contain aliases. 1621da177e4SLinus Torvalds */ 1631da177e4SLinus Torvalds void flush_cache_range(struct vm_area_struct *vma, unsigned long start, 1641da177e4SLinus Torvalds unsigned long end) 1651da177e4SLinus Torvalds { 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds /* 1681da177e4SLinus Torvalds * We could call flush_cache_page for the pages of these range, 1691da177e4SLinus Torvalds * but it's not efficient (scan the caches all the time...). 1701da177e4SLinus Torvalds * 1711da177e4SLinus Torvalds * We can't use A-bit magic, as there's the case we don't have 1721da177e4SLinus Torvalds * valid entry on TLB. 1731da177e4SLinus Torvalds */ 1741da177e4SLinus Torvalds flush_cache_all(); 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds /* 1781da177e4SLinus Torvalds * Write back and invalidate I/D-caches for the page. 1791da177e4SLinus Torvalds * 1801da177e4SLinus Torvalds * ADDRESS: Virtual Address (U0 address) 1811da177e4SLinus Torvalds */ 1820f08f338SPaul Mundt void flush_cache_page(struct vm_area_struct *vma, unsigned long address, 1830f08f338SPaul Mundt unsigned long pfn) 1841da177e4SLinus Torvalds { 1851da177e4SLinus Torvalds __flush_dcache_page(pfn << PAGE_SHIFT); 1861da177e4SLinus Torvalds } 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds /* 1891da177e4SLinus Torvalds * This is called when a page-cache page is about to be mapped into a 1901da177e4SLinus Torvalds * user process' address space. It offers an opportunity for a 1911da177e4SLinus Torvalds * port to ensure d-cache/i-cache coherency if necessary. 1921da177e4SLinus Torvalds * 1931da177e4SLinus Torvalds * Not entirely sure why this is necessary on SH3 with 32K cache but 1941da177e4SLinus Torvalds * without it we get occasional "Memory fault" when loading a program. 1951da177e4SLinus Torvalds */ 1961da177e4SLinus Torvalds void flush_icache_page(struct vm_area_struct *vma, struct page *page) 1971da177e4SLinus Torvalds { 1981da177e4SLinus Torvalds __flush_purge_region(page_address(page), PAGE_SIZE); 1991da177e4SLinus Torvalds } 200