xref: /openbmc/linux/arch/sh/mm/cache-sh7705.c (revision 157efa29)
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>
152277ab4aSPaul Mundt #include <linux/fs.h>
16842ca547SMatthew Wilcox (Oracle) #include <linux/pagemap.h>
171da177e4SLinus Torvalds #include <linux/threads.h>
181da177e4SLinus Torvalds #include <asm/addrspace.h>
191da177e4SLinus Torvalds #include <asm/page.h>
201da177e4SLinus Torvalds #include <asm/processor.h>
211da177e4SLinus Torvalds #include <asm/cache.h>
221da177e4SLinus Torvalds #include <asm/io.h>
237c0f6ba6SLinus Torvalds #include <linux/uaccess.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  */
cache_wback_all(void)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 
509d56dd3bSPaul Mundt 			data = __raw_readl(addr);
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 			if ((data & v) == v)
539d56dd3bSPaul Mundt 				__raw_writel(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  */
sh7705_flush_icache_range(void * args)66f26b2a56SPaul Mundt static void sh7705_flush_icache_range(void *args)
671da177e4SLinus Torvalds {
68f26b2a56SPaul Mundt 	struct flusher_data *data = args;
69f26b2a56SPaul Mundt 	unsigned long start, end;
70f26b2a56SPaul Mundt 
71f26b2a56SPaul Mundt 	start = data->addr1;
72f26b2a56SPaul Mundt 	end = data->addr2;
73f26b2a56SPaul Mundt 
741da177e4SLinus Torvalds 	__flush_wback_region((void *)start, end - start);
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds /*
781da177e4SLinus Torvalds  * Writeback&Invalidate the D-cache of the page
791da177e4SLinus Torvalds  */
__flush_dcache_page(unsigned long phys)802dc2f8e0SPaul Mundt static void __flush_dcache_page(unsigned long phys)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	unsigned long ways, waysize, addrstart;
83983f4c51SPaul Mundt 	unsigned long flags;
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 	phys |= SH_CACHE_VALID;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	/*
881da177e4SLinus Torvalds 	 * Here, phys is the physical address of the page. We check all the
891da177e4SLinus Torvalds 	 * tags in the cache for those with the same page number as this page
901da177e4SLinus Torvalds 	 * (by masking off the lowest 2 bits of the 19-bit tag; these bits are
911da177e4SLinus Torvalds 	 * derived from the offset within in the 4k page). Matching valid
921da177e4SLinus Torvalds 	 * entries are invalidated.
931da177e4SLinus Torvalds 	 *
941da177e4SLinus Torvalds 	 * Since 2 bits of the cache index are derived from the virtual page
951da177e4SLinus Torvalds 	 * number, knowing this would reduce the number of cache entries to be
961da177e4SLinus Torvalds 	 * searched by a factor of 4. However this function exists to deal with
971da177e4SLinus Torvalds 	 * potential cache aliasing, therefore the optimisation is probably not
981da177e4SLinus Torvalds 	 * possible.
991da177e4SLinus Torvalds 	 */
100983f4c51SPaul Mundt 	local_irq_save(flags);
101cbaa118eSStuart Menefy 	jump_to_uncached();
1021da177e4SLinus Torvalds 
10311c19656SPaul Mundt 	ways = current_cpu_data.dcache.ways;
10411c19656SPaul Mundt 	waysize = current_cpu_data.dcache.sets;
10511c19656SPaul Mundt 	waysize <<= current_cpu_data.dcache.entry_shift;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	addrstart = CACHE_OC_ADDRESS_ARRAY;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	do {
1101da177e4SLinus Torvalds 		unsigned long addr;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 		for (addr = addrstart;
1131da177e4SLinus Torvalds 		     addr < addrstart + waysize;
11411c19656SPaul Mundt 		     addr += current_cpu_data.dcache.linesz) {
1151da177e4SLinus Torvalds 			unsigned long data;
1161da177e4SLinus Torvalds 
1179d56dd3bSPaul Mundt 			data = __raw_readl(addr) & (0x1ffffC00 | SH_CACHE_VALID);
1181da177e4SLinus Torvalds 		        if (data == phys) {
1191da177e4SLinus Torvalds 				data &= ~(SH_CACHE_VALID | SH_CACHE_UPDATED);
1209d56dd3bSPaul Mundt 				__raw_writel(data, addr);
1211da177e4SLinus Torvalds 			}
1221da177e4SLinus Torvalds 		}
1231da177e4SLinus Torvalds 
12411c19656SPaul Mundt 		addrstart += current_cpu_data.dcache.way_incr;
1251da177e4SLinus Torvalds 	} while (--ways);
1261da177e4SLinus Torvalds 
127cbaa118eSStuart Menefy 	back_to_cached();
128983f4c51SPaul Mundt 	local_irq_restore(flags);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds /*
1321da177e4SLinus Torvalds  * Write back & invalidate the D-cache of the page.
1331da177e4SLinus Torvalds  * (To avoid "alias" issues)
1341da177e4SLinus Torvalds  */
sh7705_flush_dcache_folio(void * arg)135*157efa29SMatthew Wilcox (Oracle) static void sh7705_flush_dcache_folio(void *arg)
1361da177e4SLinus Torvalds {
137*157efa29SMatthew Wilcox (Oracle) 	struct folio *folio = arg;
138*157efa29SMatthew Wilcox (Oracle) 	struct address_space *mapping = folio_flush_mapping(folio);
1392277ab4aSPaul Mundt 
1402277ab4aSPaul Mundt 	if (mapping && !mapping_mapped(mapping))
141*157efa29SMatthew Wilcox (Oracle) 		clear_bit(PG_dcache_clean, &folio->flags);
142*157efa29SMatthew Wilcox (Oracle) 	else {
143*157efa29SMatthew Wilcox (Oracle) 		unsigned long pfn = folio_pfn(folio);
144*157efa29SMatthew Wilcox (Oracle) 		unsigned int i, nr = folio_nr_pages(folio);
145*157efa29SMatthew Wilcox (Oracle) 
146*157efa29SMatthew Wilcox (Oracle) 		for (i = 0; i < nr; i++)
147*157efa29SMatthew Wilcox (Oracle) 			__flush_dcache_page((pfn + i) * PAGE_SIZE);
148*157efa29SMatthew Wilcox (Oracle) 	}
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds 
sh7705_flush_cache_all(void * args)1512dc2f8e0SPaul Mundt static void sh7705_flush_cache_all(void *args)
1521da177e4SLinus Torvalds {
153983f4c51SPaul Mundt 	unsigned long flags;
154983f4c51SPaul Mundt 
155983f4c51SPaul Mundt 	local_irq_save(flags);
156cbaa118eSStuart Menefy 	jump_to_uncached();
157983f4c51SPaul Mundt 
1581da177e4SLinus Torvalds 	cache_wback_all();
159cbaa118eSStuart Menefy 	back_to_cached();
160983f4c51SPaul Mundt 	local_irq_restore(flags);
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds /*
1641da177e4SLinus Torvalds  * Write back and invalidate I/D-caches for the page.
1651da177e4SLinus Torvalds  *
1661da177e4SLinus Torvalds  * ADDRESS: Virtual Address (U0 address)
1671da177e4SLinus Torvalds  */
sh7705_flush_cache_page(void * args)168f26b2a56SPaul Mundt static void sh7705_flush_cache_page(void *args)
1691da177e4SLinus Torvalds {
170f26b2a56SPaul Mundt 	struct flusher_data *data = args;
171f26b2a56SPaul Mundt 	unsigned long pfn = data->addr2;
172f26b2a56SPaul Mundt 
1731da177e4SLinus Torvalds 	__flush_dcache_page(pfn << PAGE_SHIFT);
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds /*
1771da177e4SLinus Torvalds  * This is called when a page-cache page is about to be mapped into a
1781da177e4SLinus Torvalds  * user process' address space.  It offers an opportunity for a
1791da177e4SLinus Torvalds  * port to ensure d-cache/i-cache coherency if necessary.
1801da177e4SLinus Torvalds  *
1811da177e4SLinus Torvalds  * Not entirely sure why this is necessary on SH3 with 32K cache but
1821da177e4SLinus Torvalds  * without it we get occasional "Memory fault" when loading a program.
1831da177e4SLinus Torvalds  */
sh7705_flush_icache_folio(void * arg)184*157efa29SMatthew Wilcox (Oracle) static void sh7705_flush_icache_folio(void *arg)
1851da177e4SLinus Torvalds {
186*157efa29SMatthew Wilcox (Oracle) 	struct folio *folio = arg;
187*157efa29SMatthew Wilcox (Oracle) 	__flush_purge_region(folio_address(folio), folio_size(folio));
1881da177e4SLinus Torvalds }
1890d051d90SPaul Mundt 
sh7705_cache_init(void)1900d051d90SPaul Mundt void __init sh7705_cache_init(void)
1910d051d90SPaul Mundt {
192f26b2a56SPaul Mundt 	local_flush_icache_range	= sh7705_flush_icache_range;
193*157efa29SMatthew Wilcox (Oracle) 	local_flush_dcache_folio	= sh7705_flush_dcache_folio;
194f26b2a56SPaul Mundt 	local_flush_cache_all		= sh7705_flush_cache_all;
195f26b2a56SPaul Mundt 	local_flush_cache_mm		= sh7705_flush_cache_all;
196f26b2a56SPaul Mundt 	local_flush_cache_dup_mm	= sh7705_flush_cache_all;
197f26b2a56SPaul Mundt 	local_flush_cache_range		= sh7705_flush_cache_all;
198f26b2a56SPaul Mundt 	local_flush_cache_page		= sh7705_flush_cache_page;
199*157efa29SMatthew Wilcox (Oracle) 	local_flush_icache_folio	= sh7705_flush_icache_folio;
2000d051d90SPaul Mundt }
201