100a9730eSGuo Ren // SPDX-License-Identifier: GPL-2.0 200a9730eSGuo Ren // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 300a9730eSGuo Ren 400a9730eSGuo Ren #include <linux/init.h> 500a9730eSGuo Ren #include <linux/mm.h> 600a9730eSGuo Ren #include <linux/module.h> 700a9730eSGuo Ren #include <linux/sched.h> 800a9730eSGuo Ren 900a9730eSGuo Ren #include <asm/mmu_context.h> 1000a9730eSGuo Ren #include <asm/setup.h> 1100a9730eSGuo Ren 124e562c11SGuo Ren /* 134e562c11SGuo Ren * One C-SKY MMU TLB entry contain two PFN/page entry, ie: 144e562c11SGuo Ren * 1VPN -> 2PFN 154e562c11SGuo Ren */ 164e562c11SGuo Ren #define TLB_ENTRY_SIZE (PAGE_SIZE * 2) 174e562c11SGuo Ren #define TLB_ENTRY_SIZE_MASK (PAGE_MASK << 1) 184e562c11SGuo Ren 1900a9730eSGuo Ren void flush_tlb_all(void) 2000a9730eSGuo Ren { 2100a9730eSGuo Ren tlb_invalid_all(); 2200a9730eSGuo Ren } 2300a9730eSGuo Ren 2400a9730eSGuo Ren void flush_tlb_mm(struct mm_struct *mm) 2500a9730eSGuo Ren { 264e562c11SGuo Ren #ifdef CONFIG_CPU_HAS_TLBI 27*3b756ccdSGuo Ren sync_is(); 28*3b756ccdSGuo Ren asm volatile( 29*3b756ccdSGuo Ren "tlbi.asids %0 \n" 30*3b756ccdSGuo Ren "sync.i \n" 31*3b756ccdSGuo Ren : 32*3b756ccdSGuo Ren : "r" (cpu_asid(mm)) 33*3b756ccdSGuo Ren : "memory"); 344e562c11SGuo Ren #else 3500a9730eSGuo Ren tlb_invalid_all(); 364e562c11SGuo Ren #endif 3700a9730eSGuo Ren } 3800a9730eSGuo Ren 394e562c11SGuo Ren /* 404e562c11SGuo Ren * MMU operation regs only could invalid tlb entry in jtlb and we 414e562c11SGuo Ren * need change asid field to invalid I-utlb & D-utlb. 424e562c11SGuo Ren */ 434e562c11SGuo Ren #ifndef CONFIG_CPU_HAS_TLBI 444e562c11SGuo Ren #define restore_asid_inv_utlb(oldpid, newpid) \ 454e562c11SGuo Ren do { \ 464e562c11SGuo Ren if (oldpid == newpid) \ 474e562c11SGuo Ren write_mmu_entryhi(oldpid + 1); \ 484e562c11SGuo Ren write_mmu_entryhi(oldpid); \ 494e562c11SGuo Ren } while (0) 504e562c11SGuo Ren #endif 514e562c11SGuo Ren 5200a9730eSGuo Ren void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 5300a9730eSGuo Ren unsigned long end) 5400a9730eSGuo Ren { 554e562c11SGuo Ren unsigned long newpid = cpu_asid(vma->vm_mm); 564e562c11SGuo Ren 574e562c11SGuo Ren start &= TLB_ENTRY_SIZE_MASK; 584e562c11SGuo Ren end += TLB_ENTRY_SIZE - 1; 594e562c11SGuo Ren end &= TLB_ENTRY_SIZE_MASK; 604e562c11SGuo Ren 614e562c11SGuo Ren #ifdef CONFIG_CPU_HAS_TLBI 62*3b756ccdSGuo Ren sync_is(); 634e562c11SGuo Ren while (start < end) { 64*3b756ccdSGuo Ren asm volatile( 65*3b756ccdSGuo Ren "tlbi.vas %0 \n" 66*3b756ccdSGuo Ren : 67*3b756ccdSGuo Ren : "r" (start | newpid) 68*3b756ccdSGuo Ren : "memory"); 69*3b756ccdSGuo Ren 704e562c11SGuo Ren start += 2*PAGE_SIZE; 714e562c11SGuo Ren } 72*3b756ccdSGuo Ren asm volatile("sync.i\n"); 734e562c11SGuo Ren #else 744e562c11SGuo Ren { 754e562c11SGuo Ren unsigned long flags, oldpid; 764e562c11SGuo Ren 774e562c11SGuo Ren local_irq_save(flags); 784e562c11SGuo Ren oldpid = read_mmu_entryhi() & ASID_MASK; 794e562c11SGuo Ren while (start < end) { 804e562c11SGuo Ren int idx; 814e562c11SGuo Ren 824e562c11SGuo Ren write_mmu_entryhi(start | newpid); 834e562c11SGuo Ren start += 2*PAGE_SIZE; 844e562c11SGuo Ren tlb_probe(); 854e562c11SGuo Ren idx = read_mmu_index(); 864e562c11SGuo Ren if (idx >= 0) 874e562c11SGuo Ren tlb_invalid_indexed(); 884e562c11SGuo Ren } 894e562c11SGuo Ren restore_asid_inv_utlb(oldpid, newpid); 904e562c11SGuo Ren local_irq_restore(flags); 914e562c11SGuo Ren } 924e562c11SGuo Ren #endif 9300a9730eSGuo Ren } 9400a9730eSGuo Ren 9500a9730eSGuo Ren void flush_tlb_kernel_range(unsigned long start, unsigned long end) 9600a9730eSGuo Ren { 974e562c11SGuo Ren start &= TLB_ENTRY_SIZE_MASK; 984e562c11SGuo Ren end += TLB_ENTRY_SIZE - 1; 994e562c11SGuo Ren end &= TLB_ENTRY_SIZE_MASK; 1004e562c11SGuo Ren 1014e562c11SGuo Ren #ifdef CONFIG_CPU_HAS_TLBI 102*3b756ccdSGuo Ren sync_is(); 1034e562c11SGuo Ren while (start < end) { 104*3b756ccdSGuo Ren asm volatile( 105*3b756ccdSGuo Ren "tlbi.vaas %0 \n" 106*3b756ccdSGuo Ren : 107*3b756ccdSGuo Ren : "r" (start) 108*3b756ccdSGuo Ren : "memory"); 109*3b756ccdSGuo Ren 1104e562c11SGuo Ren start += 2*PAGE_SIZE; 1114e562c11SGuo Ren } 112*3b756ccdSGuo Ren asm volatile("sync.i\n"); 1134e562c11SGuo Ren #else 1144e562c11SGuo Ren { 1154e562c11SGuo Ren unsigned long flags, oldpid; 1164e562c11SGuo Ren 1174e562c11SGuo Ren local_irq_save(flags); 1184e562c11SGuo Ren oldpid = read_mmu_entryhi() & ASID_MASK; 1194e562c11SGuo Ren while (start < end) { 1204e562c11SGuo Ren int idx; 1214e562c11SGuo Ren 1224e562c11SGuo Ren write_mmu_entryhi(start | oldpid); 1234e562c11SGuo Ren start += 2*PAGE_SIZE; 1244e562c11SGuo Ren tlb_probe(); 1254e562c11SGuo Ren idx = read_mmu_index(); 1264e562c11SGuo Ren if (idx >= 0) 1274e562c11SGuo Ren tlb_invalid_indexed(); 1284e562c11SGuo Ren } 1294e562c11SGuo Ren restore_asid_inv_utlb(oldpid, oldpid); 1304e562c11SGuo Ren local_irq_restore(flags); 1314e562c11SGuo Ren } 1324e562c11SGuo Ren #endif 13300a9730eSGuo Ren } 1349d35dc30SGuo Ren 1359d35dc30SGuo Ren void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) 13600a9730eSGuo Ren { 1374e562c11SGuo Ren int newpid = cpu_asid(vma->vm_mm); 1384e562c11SGuo Ren 1394e562c11SGuo Ren addr &= TLB_ENTRY_SIZE_MASK; 1404e562c11SGuo Ren 1414e562c11SGuo Ren #ifdef CONFIG_CPU_HAS_TLBI 1424e562c11SGuo Ren sync_is(); 143*3b756ccdSGuo Ren asm volatile( 144*3b756ccdSGuo Ren "tlbi.vas %0 \n" 145*3b756ccdSGuo Ren "sync.i \n" 146*3b756ccdSGuo Ren : 147*3b756ccdSGuo Ren : "r" (addr | newpid) 148*3b756ccdSGuo Ren : "memory"); 1494e562c11SGuo Ren #else 1504e562c11SGuo Ren { 1514e562c11SGuo Ren int oldpid, idx; 1524e562c11SGuo Ren unsigned long flags; 1534e562c11SGuo Ren 1544e562c11SGuo Ren local_irq_save(flags); 1554e562c11SGuo Ren oldpid = read_mmu_entryhi() & ASID_MASK; 1564e562c11SGuo Ren write_mmu_entryhi(addr | newpid); 1574e562c11SGuo Ren tlb_probe(); 1584e562c11SGuo Ren idx = read_mmu_index(); 1594e562c11SGuo Ren if (idx >= 0) 1604e562c11SGuo Ren tlb_invalid_indexed(); 1614e562c11SGuo Ren 1624e562c11SGuo Ren restore_asid_inv_utlb(oldpid, newpid); 1634e562c11SGuo Ren local_irq_restore(flags); 1644e562c11SGuo Ren } 1654e562c11SGuo Ren #endif 16600a9730eSGuo Ren } 16700a9730eSGuo Ren 1689d35dc30SGuo Ren void flush_tlb_one(unsigned long addr) 16900a9730eSGuo Ren { 1704e562c11SGuo Ren addr &= TLB_ENTRY_SIZE_MASK; 1714e562c11SGuo Ren 1724e562c11SGuo Ren #ifdef CONFIG_CPU_HAS_TLBI 1734e562c11SGuo Ren sync_is(); 174*3b756ccdSGuo Ren asm volatile( 175*3b756ccdSGuo Ren "tlbi.vaas %0 \n" 176*3b756ccdSGuo Ren "sync.i \n" 177*3b756ccdSGuo Ren : 178*3b756ccdSGuo Ren : "r" (addr) 179*3b756ccdSGuo Ren : "memory"); 1804e562c11SGuo Ren #else 1814e562c11SGuo Ren { 1824e562c11SGuo Ren int oldpid, idx; 1834e562c11SGuo Ren unsigned long flags; 1844e562c11SGuo Ren 1854e562c11SGuo Ren local_irq_save(flags); 1864e562c11SGuo Ren oldpid = read_mmu_entryhi() & ASID_MASK; 1874e562c11SGuo Ren write_mmu_entryhi(addr | oldpid); 1884e562c11SGuo Ren tlb_probe(); 1894e562c11SGuo Ren idx = read_mmu_index(); 1904e562c11SGuo Ren if (idx >= 0) 1914e562c11SGuo Ren tlb_invalid_indexed(); 1924e562c11SGuo Ren 1934e562c11SGuo Ren restore_asid_inv_utlb(oldpid, oldpid); 1944e562c11SGuo Ren local_irq_restore(flags); 1954e562c11SGuo Ren } 1964e562c11SGuo Ren #endif 19700a9730eSGuo Ren } 19800a9730eSGuo Ren EXPORT_SYMBOL(flush_tlb_one); 199