xref: /openbmc/linux/arch/sh/mm/cache-sh7705.c (revision cbaa118e)
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