18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/init.h>
58c2ecf20Sopenharmony_ci#include <linux/mm.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <asm/mmu_context.h>
108c2ecf20Sopenharmony_ci#include <asm/setup.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/*
138c2ecf20Sopenharmony_ci * One C-SKY MMU TLB entry contain two PFN/page entry, ie:
148c2ecf20Sopenharmony_ci * 1VPN -> 2PFN
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#define TLB_ENTRY_SIZE		(PAGE_SIZE * 2)
178c2ecf20Sopenharmony_ci#define TLB_ENTRY_SIZE_MASK	(PAGE_MASK << 1)
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid flush_tlb_all(void)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	tlb_invalid_all();
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_civoid flush_tlb_mm(struct mm_struct *mm)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_TLBI
278c2ecf20Sopenharmony_ci	asm volatile("tlbi.asids %0"::"r"(cpu_asid(mm)));
288c2ecf20Sopenharmony_ci#else
298c2ecf20Sopenharmony_ci	tlb_invalid_all();
308c2ecf20Sopenharmony_ci#endif
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * MMU operation regs only could invalid tlb entry in jtlb and we
358c2ecf20Sopenharmony_ci * need change asid field to invalid I-utlb & D-utlb.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_HAS_TLBI
388c2ecf20Sopenharmony_ci#define restore_asid_inv_utlb(oldpid, newpid) \
398c2ecf20Sopenharmony_cido { \
408c2ecf20Sopenharmony_ci	if (oldpid == newpid) \
418c2ecf20Sopenharmony_ci		write_mmu_entryhi(oldpid + 1); \
428c2ecf20Sopenharmony_ci	write_mmu_entryhi(oldpid); \
438c2ecf20Sopenharmony_ci} while (0)
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_civoid flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
478c2ecf20Sopenharmony_ci			unsigned long end)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	unsigned long newpid = cpu_asid(vma->vm_mm);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	start &= TLB_ENTRY_SIZE_MASK;
528c2ecf20Sopenharmony_ci	end   += TLB_ENTRY_SIZE - 1;
538c2ecf20Sopenharmony_ci	end   &= TLB_ENTRY_SIZE_MASK;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_TLBI
568c2ecf20Sopenharmony_ci	while (start < end) {
578c2ecf20Sopenharmony_ci		asm volatile("tlbi.vas %0"::"r"(start | newpid));
588c2ecf20Sopenharmony_ci		start += 2*PAGE_SIZE;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	sync_is();
618c2ecf20Sopenharmony_ci#else
628c2ecf20Sopenharmony_ci	{
638c2ecf20Sopenharmony_ci	unsigned long flags, oldpid;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	local_irq_save(flags);
668c2ecf20Sopenharmony_ci	oldpid = read_mmu_entryhi() & ASID_MASK;
678c2ecf20Sopenharmony_ci	while (start < end) {
688c2ecf20Sopenharmony_ci		int idx;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		write_mmu_entryhi(start | newpid);
718c2ecf20Sopenharmony_ci		start += 2*PAGE_SIZE;
728c2ecf20Sopenharmony_ci		tlb_probe();
738c2ecf20Sopenharmony_ci		idx = read_mmu_index();
748c2ecf20Sopenharmony_ci		if (idx >= 0)
758c2ecf20Sopenharmony_ci			tlb_invalid_indexed();
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	restore_asid_inv_utlb(oldpid, newpid);
788c2ecf20Sopenharmony_ci	local_irq_restore(flags);
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci#endif
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_civoid flush_tlb_kernel_range(unsigned long start, unsigned long end)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	start &= TLB_ENTRY_SIZE_MASK;
868c2ecf20Sopenharmony_ci	end   += TLB_ENTRY_SIZE - 1;
878c2ecf20Sopenharmony_ci	end   &= TLB_ENTRY_SIZE_MASK;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_TLBI
908c2ecf20Sopenharmony_ci	while (start < end) {
918c2ecf20Sopenharmony_ci		asm volatile("tlbi.vaas %0"::"r"(start));
928c2ecf20Sopenharmony_ci		start += 2*PAGE_SIZE;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci	sync_is();
958c2ecf20Sopenharmony_ci#else
968c2ecf20Sopenharmony_ci	{
978c2ecf20Sopenharmony_ci	unsigned long flags, oldpid;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	local_irq_save(flags);
1008c2ecf20Sopenharmony_ci	oldpid = read_mmu_entryhi() & ASID_MASK;
1018c2ecf20Sopenharmony_ci	while (start < end) {
1028c2ecf20Sopenharmony_ci		int idx;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		write_mmu_entryhi(start | oldpid);
1058c2ecf20Sopenharmony_ci		start += 2*PAGE_SIZE;
1068c2ecf20Sopenharmony_ci		tlb_probe();
1078c2ecf20Sopenharmony_ci		idx = read_mmu_index();
1088c2ecf20Sopenharmony_ci		if (idx >= 0)
1098c2ecf20Sopenharmony_ci			tlb_invalid_indexed();
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	restore_asid_inv_utlb(oldpid, oldpid);
1128c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci#endif
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_civoid flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	int newpid = cpu_asid(vma->vm_mm);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	addr &= TLB_ENTRY_SIZE_MASK;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_TLBI
1248c2ecf20Sopenharmony_ci	asm volatile("tlbi.vas %0"::"r"(addr | newpid));
1258c2ecf20Sopenharmony_ci	sync_is();
1268c2ecf20Sopenharmony_ci#else
1278c2ecf20Sopenharmony_ci	{
1288c2ecf20Sopenharmony_ci	int oldpid, idx;
1298c2ecf20Sopenharmony_ci	unsigned long flags;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	local_irq_save(flags);
1328c2ecf20Sopenharmony_ci	oldpid = read_mmu_entryhi() & ASID_MASK;
1338c2ecf20Sopenharmony_ci	write_mmu_entryhi(addr | newpid);
1348c2ecf20Sopenharmony_ci	tlb_probe();
1358c2ecf20Sopenharmony_ci	idx = read_mmu_index();
1368c2ecf20Sopenharmony_ci	if (idx >= 0)
1378c2ecf20Sopenharmony_ci		tlb_invalid_indexed();
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	restore_asid_inv_utlb(oldpid, newpid);
1408c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci#endif
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_civoid flush_tlb_one(unsigned long addr)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	addr &= TLB_ENTRY_SIZE_MASK;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_TLBI
1508c2ecf20Sopenharmony_ci	asm volatile("tlbi.vaas %0"::"r"(addr));
1518c2ecf20Sopenharmony_ci	sync_is();
1528c2ecf20Sopenharmony_ci#else
1538c2ecf20Sopenharmony_ci	{
1548c2ecf20Sopenharmony_ci	int oldpid, idx;
1558c2ecf20Sopenharmony_ci	unsigned long flags;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	local_irq_save(flags);
1588c2ecf20Sopenharmony_ci	oldpid = read_mmu_entryhi() & ASID_MASK;
1598c2ecf20Sopenharmony_ci	write_mmu_entryhi(addr | oldpid);
1608c2ecf20Sopenharmony_ci	tlb_probe();
1618c2ecf20Sopenharmony_ci	idx = read_mmu_index();
1628c2ecf20Sopenharmony_ci	if (idx >= 0)
1638c2ecf20Sopenharmony_ci		tlb_invalid_indexed();
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	restore_asid_inv_utlb(oldpid, oldpid);
1668c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci#endif
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_tlb_one);
171