xref: /openbmc/linux/arch/mips/mm/c-r3k.c (revision c86df6c0)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * r2300.c: R2000 and R3000 specific mmu/cache code.
41da177e4SLinus Torvalds  *
579add627SJustin P. Mattock  * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * with a lot of changes to make this thing work for R3000s
81da177e4SLinus Torvalds  * Tx39XX R4k style caches added. HK
91da177e4SLinus Torvalds  * Copyright (C) 1998, 1999, 2000 Harald Koerfgen
101da177e4SLinus Torvalds  * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
1121b2aecaSMaciej W. Rozycki  * Copyright (C) 2001, 2004, 2007  Maciej W. Rozycki
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
15631330f5SRalf Baechle #include <linux/smp.h>
161da177e4SLinus Torvalds #include <linux/mm.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include <asm/page.h>
191da177e4SLinus Torvalds #include <asm/mmu_context.h>
201da177e4SLinus Torvalds #include <asm/isadep.h>
211da177e4SLinus Torvalds #include <asm/io.h>
221da177e4SLinus Torvalds #include <asm/bootinfo.h>
231da177e4SLinus Torvalds #include <asm/cpu.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds static unsigned long icache_size, dcache_size;		/* Size in bytes */
261da177e4SLinus Torvalds static unsigned long icache_lsize, dcache_lsize;	/* Size in bytes */
271da177e4SLinus Torvalds 
r3k_cache_size(unsigned long ca_flags)28078a55fcSPaul Gortmaker unsigned long r3k_cache_size(unsigned long ca_flags)
291da177e4SLinus Torvalds {
301da177e4SLinus Torvalds 	unsigned long flags, status, dummy, size;
311da177e4SLinus Torvalds 	volatile unsigned long *p;
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds 	p = (volatile unsigned long *) KSEG0;
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds 	flags = read_c0_status();
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 	/* isolate cache space */
381da177e4SLinus Torvalds 	write_c0_status((ca_flags|flags)&~ST0_IEC);
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds 	*p = 0xa5a55a5a;
411da177e4SLinus Torvalds 	dummy = *p;
421da177e4SLinus Torvalds 	status = read_c0_status();
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds 	if (dummy != 0xa5a55a5a || (status & ST0_CM)) {
451da177e4SLinus Torvalds 		size = 0;
461da177e4SLinus Torvalds 	} else {
471da177e4SLinus Torvalds 		for (size = 128; size <= 0x40000; size <<= 1)
481da177e4SLinus Torvalds 			*(p + size) = 0;
491da177e4SLinus Torvalds 		*p = -1;
501da177e4SLinus Torvalds 		for (size = 128;
511da177e4SLinus Torvalds 		     (size <= 0x40000) && (*(p + size) == 0);
521da177e4SLinus Torvalds 		     size <<= 1)
531da177e4SLinus Torvalds 			;
541da177e4SLinus Torvalds 		if (size > 0x40000)
551da177e4SLinus Torvalds 			size = 0;
561da177e4SLinus Torvalds 	}
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	write_c0_status(flags);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	return size * sizeof(*p);
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds 
r3k_cache_lsize(unsigned long ca_flags)63078a55fcSPaul Gortmaker unsigned long r3k_cache_lsize(unsigned long ca_flags)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds 	unsigned long flags, status, lsize, i;
661da177e4SLinus Torvalds 	volatile unsigned long *p;
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds 	p = (volatile unsigned long *) KSEG0;
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	flags = read_c0_status();
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	/* isolate cache space */
731da177e4SLinus Torvalds 	write_c0_status((ca_flags|flags)&~ST0_IEC);
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	for (i = 0; i < 128; i++)
761da177e4SLinus Torvalds 		*(p + i) = 0;
771da177e4SLinus Torvalds 	*(volatile unsigned char *)p = 0;
781da177e4SLinus Torvalds 	for (lsize = 1; lsize < 128; lsize <<= 1) {
791da177e4SLinus Torvalds 		*(p + lsize);
801da177e4SLinus Torvalds 		status = read_c0_status();
811da177e4SLinus Torvalds 		if (!(status & ST0_CM))
821da177e4SLinus Torvalds 			break;
831da177e4SLinus Torvalds 	}
841da177e4SLinus Torvalds 	for (i = 0; i < 128; i += lsize)
851da177e4SLinus Torvalds 		*(volatile unsigned char *)(p + i) = 0;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	write_c0_status(flags);
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	return lsize * sizeof(*p);
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
r3k_probe_cache(void)92078a55fcSPaul Gortmaker static void r3k_probe_cache(void)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	dcache_size = r3k_cache_size(ST0_ISC);
951da177e4SLinus Torvalds 	if (dcache_size)
961da177e4SLinus Torvalds 		dcache_lsize = r3k_cache_lsize(ST0_ISC);
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	icache_size = r3k_cache_size(ST0_ISC|ST0_SWC);
991da177e4SLinus Torvalds 	if (icache_size)
1001da177e4SLinus Torvalds 		icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC);
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
r3k_flush_icache_range(unsigned long start,unsigned long end)1031da177e4SLinus Torvalds static void r3k_flush_icache_range(unsigned long start, unsigned long end)
1041da177e4SLinus Torvalds {
1051da177e4SLinus Torvalds 	unsigned long size, i, flags;
1061da177e4SLinus Torvalds 	volatile unsigned char *p;
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds 	size = end - start;
1091da177e4SLinus Torvalds 	if (size > icache_size || KSEGX(start) != KSEG0) {
1101da177e4SLinus Torvalds 		start = KSEG0;
1111da177e4SLinus Torvalds 		size = icache_size;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 	p = (char *)start;
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds 	flags = read_c0_status();
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	/* isolate cache space */
1181da177e4SLinus Torvalds 	write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	for (i = 0; i < size; i += 0x080) {
1211da177e4SLinus Torvalds 		asm(	"sb\t$0, 0x000(%0)\n\t"
1221da177e4SLinus Torvalds 			"sb\t$0, 0x004(%0)\n\t"
1231da177e4SLinus Torvalds 			"sb\t$0, 0x008(%0)\n\t"
1241da177e4SLinus Torvalds 			"sb\t$0, 0x00c(%0)\n\t"
1251da177e4SLinus Torvalds 			"sb\t$0, 0x010(%0)\n\t"
1261da177e4SLinus Torvalds 			"sb\t$0, 0x014(%0)\n\t"
1271da177e4SLinus Torvalds 			"sb\t$0, 0x018(%0)\n\t"
1281da177e4SLinus Torvalds 			"sb\t$0, 0x01c(%0)\n\t"
1291da177e4SLinus Torvalds 			"sb\t$0, 0x020(%0)\n\t"
1301da177e4SLinus Torvalds 			"sb\t$0, 0x024(%0)\n\t"
1311da177e4SLinus Torvalds 			"sb\t$0, 0x028(%0)\n\t"
1321da177e4SLinus Torvalds 			"sb\t$0, 0x02c(%0)\n\t"
1331da177e4SLinus Torvalds 			"sb\t$0, 0x030(%0)\n\t"
1341da177e4SLinus Torvalds 			"sb\t$0, 0x034(%0)\n\t"
1351da177e4SLinus Torvalds 			"sb\t$0, 0x038(%0)\n\t"
1361da177e4SLinus Torvalds 			"sb\t$0, 0x03c(%0)\n\t"
1371da177e4SLinus Torvalds 			"sb\t$0, 0x040(%0)\n\t"
1381da177e4SLinus Torvalds 			"sb\t$0, 0x044(%0)\n\t"
1391da177e4SLinus Torvalds 			"sb\t$0, 0x048(%0)\n\t"
1401da177e4SLinus Torvalds 			"sb\t$0, 0x04c(%0)\n\t"
1411da177e4SLinus Torvalds 			"sb\t$0, 0x050(%0)\n\t"
1421da177e4SLinus Torvalds 			"sb\t$0, 0x054(%0)\n\t"
1431da177e4SLinus Torvalds 			"sb\t$0, 0x058(%0)\n\t"
1441da177e4SLinus Torvalds 			"sb\t$0, 0x05c(%0)\n\t"
1451da177e4SLinus Torvalds 			"sb\t$0, 0x060(%0)\n\t"
1461da177e4SLinus Torvalds 			"sb\t$0, 0x064(%0)\n\t"
1471da177e4SLinus Torvalds 			"sb\t$0, 0x068(%0)\n\t"
1481da177e4SLinus Torvalds 			"sb\t$0, 0x06c(%0)\n\t"
1491da177e4SLinus Torvalds 			"sb\t$0, 0x070(%0)\n\t"
1501da177e4SLinus Torvalds 			"sb\t$0, 0x074(%0)\n\t"
1511da177e4SLinus Torvalds 			"sb\t$0, 0x078(%0)\n\t"
1521da177e4SLinus Torvalds 			"sb\t$0, 0x07c(%0)\n\t"
1531da177e4SLinus Torvalds 			: : "r" (p) );
1541da177e4SLinus Torvalds 		p += 0x080;
1551da177e4SLinus Torvalds 	}
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	write_c0_status(flags);
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
r3k_flush_dcache_range(unsigned long start,unsigned long end)1601da177e4SLinus Torvalds static void r3k_flush_dcache_range(unsigned long start, unsigned long end)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds 	unsigned long size, i, flags;
1631da177e4SLinus Torvalds 	volatile unsigned char *p;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	size = end - start;
1661da177e4SLinus Torvalds 	if (size > dcache_size || KSEGX(start) != KSEG0) {
1671da177e4SLinus Torvalds 		start = KSEG0;
1681da177e4SLinus Torvalds 		size = dcache_size;
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds 	p = (char *)start;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	flags = read_c0_status();
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds 	/* isolate cache space */
1751da177e4SLinus Torvalds 	write_c0_status((ST0_ISC|flags)&~ST0_IEC);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	for (i = 0; i < size; i += 0x080) {
1781da177e4SLinus Torvalds 		asm(	"sb\t$0, 0x000(%0)\n\t"
1791da177e4SLinus Torvalds 			"sb\t$0, 0x004(%0)\n\t"
1801da177e4SLinus Torvalds 			"sb\t$0, 0x008(%0)\n\t"
1811da177e4SLinus Torvalds 			"sb\t$0, 0x00c(%0)\n\t"
1821da177e4SLinus Torvalds 			"sb\t$0, 0x010(%0)\n\t"
1831da177e4SLinus Torvalds 			"sb\t$0, 0x014(%0)\n\t"
1841da177e4SLinus Torvalds 			"sb\t$0, 0x018(%0)\n\t"
1851da177e4SLinus Torvalds 			"sb\t$0, 0x01c(%0)\n\t"
1861da177e4SLinus Torvalds 			"sb\t$0, 0x020(%0)\n\t"
1871da177e4SLinus Torvalds 			"sb\t$0, 0x024(%0)\n\t"
1881da177e4SLinus Torvalds 			"sb\t$0, 0x028(%0)\n\t"
1891da177e4SLinus Torvalds 			"sb\t$0, 0x02c(%0)\n\t"
1901da177e4SLinus Torvalds 			"sb\t$0, 0x030(%0)\n\t"
1911da177e4SLinus Torvalds 			"sb\t$0, 0x034(%0)\n\t"
1921da177e4SLinus Torvalds 			"sb\t$0, 0x038(%0)\n\t"
1931da177e4SLinus Torvalds 			"sb\t$0, 0x03c(%0)\n\t"
1941da177e4SLinus Torvalds 			"sb\t$0, 0x040(%0)\n\t"
1951da177e4SLinus Torvalds 			"sb\t$0, 0x044(%0)\n\t"
1961da177e4SLinus Torvalds 			"sb\t$0, 0x048(%0)\n\t"
1971da177e4SLinus Torvalds 			"sb\t$0, 0x04c(%0)\n\t"
1981da177e4SLinus Torvalds 			"sb\t$0, 0x050(%0)\n\t"
1991da177e4SLinus Torvalds 			"sb\t$0, 0x054(%0)\n\t"
2001da177e4SLinus Torvalds 			"sb\t$0, 0x058(%0)\n\t"
2011da177e4SLinus Torvalds 			"sb\t$0, 0x05c(%0)\n\t"
2021da177e4SLinus Torvalds 			"sb\t$0, 0x060(%0)\n\t"
2031da177e4SLinus Torvalds 			"sb\t$0, 0x064(%0)\n\t"
2041da177e4SLinus Torvalds 			"sb\t$0, 0x068(%0)\n\t"
2051da177e4SLinus Torvalds 			"sb\t$0, 0x06c(%0)\n\t"
2061da177e4SLinus Torvalds 			"sb\t$0, 0x070(%0)\n\t"
2071da177e4SLinus Torvalds 			"sb\t$0, 0x074(%0)\n\t"
2081da177e4SLinus Torvalds 			"sb\t$0, 0x078(%0)\n\t"
2091da177e4SLinus Torvalds 			"sb\t$0, 0x07c(%0)\n\t"
2101da177e4SLinus Torvalds 			: : "r" (p) );
2111da177e4SLinus Torvalds 		p += 0x080;
2121da177e4SLinus Torvalds 	}
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 	write_c0_status(flags);
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
r3k_flush_cache_all(void)2171da177e4SLinus Torvalds static inline void r3k_flush_cache_all(void)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
r3k___flush_cache_all(void)2211da177e4SLinus Torvalds static inline void r3k___flush_cache_all(void)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	r3k_flush_dcache_range(KSEG0, KSEG0 + dcache_size);
2241da177e4SLinus Torvalds 	r3k_flush_icache_range(KSEG0, KSEG0 + icache_size);
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
r3k_flush_cache_mm(struct mm_struct * mm)2271da177e4SLinus Torvalds static void r3k_flush_cache_mm(struct mm_struct *mm)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds 
r3k_flush_cache_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)2311da177e4SLinus Torvalds static void r3k_flush_cache_range(struct vm_area_struct *vma,
2321da177e4SLinus Torvalds 				  unsigned long start, unsigned long end)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds }
2351da177e4SLinus Torvalds 
r3k_flush_cache_page(struct vm_area_struct * vma,unsigned long addr,unsigned long pfn)23621b2aecaSMaciej W. Rozycki static void r3k_flush_cache_page(struct vm_area_struct *vma,
23721b2aecaSMaciej W. Rozycki 				 unsigned long addr, unsigned long pfn)
2381da177e4SLinus Torvalds {
23921b2aecaSMaciej W. Rozycki 	unsigned long kaddr = KSEG0ADDR(pfn << PAGE_SHIFT);
24021b2aecaSMaciej W. Rozycki 	int exec = vma->vm_flags & VM_EXEC;
24121b2aecaSMaciej W. Rozycki 	struct mm_struct *mm = vma->vm_mm;
24221b2aecaSMaciej W. Rozycki 	pmd_t *pmdp;
24321b2aecaSMaciej W. Rozycki 	pte_t *ptep;
24421b2aecaSMaciej W. Rozycki 
245ff4dd232SPaul Burton 	pr_debug("cpage[%08llx,%08lx]\n",
24621b2aecaSMaciej W. Rozycki 		 cpu_context(smp_processor_id(), mm), addr);
24721b2aecaSMaciej W. Rozycki 
24821b2aecaSMaciej W. Rozycki 	/* No ASID => no such page in the cache.  */
24921b2aecaSMaciej W. Rozycki 	if (cpu_context(smp_processor_id(), mm) == 0)
25021b2aecaSMaciej W. Rozycki 		return;
25121b2aecaSMaciej W. Rozycki 
252e05c7b1fSMike Rapoport 	pmdp = pmd_off(mm, addr);
253e05c7b1fSMike Rapoport 	ptep = pte_offset_kernel(pmdp, addr);
25421b2aecaSMaciej W. Rozycki 
25521b2aecaSMaciej W. Rozycki 	/* Invalid => no such page in the cache.  */
25621b2aecaSMaciej W. Rozycki 	if (!(pte_val(*ptep) & _PAGE_PRESENT))
25721b2aecaSMaciej W. Rozycki 		return;
25821b2aecaSMaciej W. Rozycki 
25921b2aecaSMaciej W. Rozycki 	r3k_flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
26021b2aecaSMaciej W. Rozycki 	if (exec)
26121b2aecaSMaciej W. Rozycki 		r3k_flush_icache_range(kaddr, kaddr + PAGE_SIZE);
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
r3k_flush_data_cache_page(unsigned long addr)2641da177e4SLinus Torvalds static void r3k_flush_data_cache_page(unsigned long addr)
2651da177e4SLinus Torvalds {
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
r3k_flush_kernel_vmap_range(unsigned long vaddr,int size)268d9cdc901SRalf Baechle static void r3k_flush_kernel_vmap_range(unsigned long vaddr, int size)
269d9cdc901SRalf Baechle {
270d9cdc901SRalf Baechle 	BUG();
271d9cdc901SRalf Baechle }
272d9cdc901SRalf Baechle 
r3k_dma_cache_wback_inv(unsigned long start,unsigned long size)2731da177e4SLinus Torvalds static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	/* Catch bad driver code */
2761da177e4SLinus Torvalds 	BUG_ON(size == 0);
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds 	iob();
2791da177e4SLinus Torvalds 	r3k_flush_dcache_range(start, start + size);
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
r3k_cache_init(void)282078a55fcSPaul Gortmaker void r3k_cache_init(void)
2831da177e4SLinus Torvalds {
2841da177e4SLinus Torvalds 	extern void build_clear_page(void);
2851da177e4SLinus Torvalds 	extern void build_copy_page(void);
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	r3k_probe_cache();
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 	flush_cache_all = r3k_flush_cache_all;
2901da177e4SLinus Torvalds 	__flush_cache_all = r3k___flush_cache_all;
2911da177e4SLinus Torvalds 	flush_cache_mm = r3k_flush_cache_mm;
2921da177e4SLinus Torvalds 	flush_cache_range = r3k_flush_cache_range;
2931da177e4SLinus Torvalds 	flush_cache_page = r3k_flush_cache_page;
2941da177e4SLinus Torvalds 	flush_icache_range = r3k_flush_icache_range;
295e0cee3eeSThomas Bogendoerfer 	local_flush_icache_range = r3k_flush_icache_range;
29601882b4dSJames Hogan 	__flush_icache_user_range = r3k_flush_icache_range;
29701882b4dSJames Hogan 	__local_flush_icache_user_range = r3k_flush_icache_range;
2981da177e4SLinus Torvalds 
299d9cdc901SRalf Baechle 	__flush_kernel_vmap_range = r3k_flush_kernel_vmap_range;
300d9cdc901SRalf Baechle 
3011da177e4SLinus Torvalds 	flush_data_cache_page = r3k_flush_data_cache_page;
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	_dma_cache_wback_inv = r3k_dma_cache_wback_inv;
3041da177e4SLinus Torvalds 	_dma_cache_wback = r3k_dma_cache_wback_inv;
3051da177e4SLinus Torvalds 	_dma_cache_inv = r3k_dma_cache_wback_inv;
3061da177e4SLinus Torvalds 
307bea176fbSOleksij Rempel 	pr_info("Primary instruction cache %ldkB, linesize %ld bytes.\n",
3081da177e4SLinus Torvalds 		icache_size >> 10, icache_lsize);
309bea176fbSOleksij Rempel 	pr_info("Primary data cache %ldkB, linesize %ld bytes.\n",
3101da177e4SLinus Torvalds 		dcache_size >> 10, dcache_lsize);
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	build_clear_page();
3131da177e4SLinus Torvalds 	build_copy_page();
3141da177e4SLinus Torvalds }
315