xref: /openbmc/linux/arch/csky/mm/tlb.c (revision 3b756ccd)
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 
flush_tlb_all(void)1900a9730eSGuo Ren void flush_tlb_all(void)
2000a9730eSGuo Ren {
2100a9730eSGuo Ren 	tlb_invalid_all();
2200a9730eSGuo Ren }
2300a9730eSGuo Ren 
flush_tlb_mm(struct mm_struct * mm)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 
flush_tlb_range(struct vm_area_struct * vma,unsigned long start,unsigned long end)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 
flush_tlb_kernel_range(unsigned long start,unsigned long end)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 
flush_tlb_page(struct vm_area_struct * vma,unsigned long addr)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 
flush_tlb_one(unsigned long addr)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