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