199 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
 | 
						|
 | 
						|
#include <linux/init.h>
 | 
						|
#include <linux/mm.h>
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/sched.h>
 | 
						|
 | 
						|
#include <asm/mmu_context.h>
 | 
						|
#include <asm/setup.h>
 | 
						|
 | 
						|
/*
 | 
						|
 * One C-SKY MMU TLB entry contain two PFN/page entry, ie:
 | 
						|
 * 1VPN -> 2PFN
 | 
						|
 */
 | 
						|
#define TLB_ENTRY_SIZE		(PAGE_SIZE * 2)
 | 
						|
#define TLB_ENTRY_SIZE_MASK	(PAGE_MASK << 1)
 | 
						|
 | 
						|
void flush_tlb_all(void)
 | 
						|
{
 | 
						|
	tlb_invalid_all();
 | 
						|
}
 | 
						|
 | 
						|
void flush_tlb_mm(struct mm_struct *mm)
 | 
						|
{
 | 
						|
#ifdef CONFIG_CPU_HAS_TLBI
 | 
						|
	sync_is();
 | 
						|
	asm volatile(
 | 
						|
		"tlbi.asids %0	\n"
 | 
						|
		"sync.i		\n"
 | 
						|
		:
 | 
						|
		: "r" (cpu_asid(mm))
 | 
						|
		: "memory");
 | 
						|
#else
 | 
						|
	tlb_invalid_all();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * MMU operation regs only could invalid tlb entry in jtlb and we
 | 
						|
 * need change asid field to invalid I-utlb & D-utlb.
 | 
						|
 */
 | 
						|
#ifndef CONFIG_CPU_HAS_TLBI
 | 
						|
#define restore_asid_inv_utlb(oldpid, newpid) \
 | 
						|
do { \
 | 
						|
	if (oldpid == newpid) \
 | 
						|
		write_mmu_entryhi(oldpid + 1); \
 | 
						|
	write_mmu_entryhi(oldpid); \
 | 
						|
} while (0)
 | 
						|
#endif
 | 
						|
 | 
						|
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 | 
						|
			unsigned long end)
 | 
						|
{
 | 
						|
	unsigned long newpid = cpu_asid(vma->vm_mm);
 | 
						|
 | 
						|
	start &= TLB_ENTRY_SIZE_MASK;
 | 
						|
	end   += TLB_ENTRY_SIZE - 1;
 | 
						|
	end   &= TLB_ENTRY_SIZE_MASK;
 | 
						|
 | 
						|
#ifdef CONFIG_CPU_HAS_TLBI
 | 
						|
	sync_is();
 | 
						|
	while (start < end) {
 | 
						|
		asm volatile(
 | 
						|
			"tlbi.vas %0	\n"
 | 
						|
			:
 | 
						|
			: "r" (start | newpid)
 | 
						|
			: "memory");
 | 
						|
 | 
						|
		start += 2*PAGE_SIZE;
 | 
						|
	}
 | 
						|
	asm volatile("sync.i\n");
 | 
						|
#else
 | 
						|
	{
 | 
						|
	unsigned long flags, oldpid;
 | 
						|
 | 
						|
	local_irq_save(flags);
 | 
						|
	oldpid = read_mmu_entryhi() & ASID_MASK;
 | 
						|
	while (start < end) {
 | 
						|
		int idx;
 | 
						|
 | 
						|
		write_mmu_entryhi(start | newpid);
 | 
						|
		start += 2*PAGE_SIZE;
 | 
						|
		tlb_probe();
 | 
						|
		idx = read_mmu_index();
 | 
						|
		if (idx >= 0)
 | 
						|
			tlb_invalid_indexed();
 | 
						|
	}
 | 
						|
	restore_asid_inv_utlb(oldpid, newpid);
 | 
						|
	local_irq_restore(flags);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 | 
						|
{
 | 
						|
	start &= TLB_ENTRY_SIZE_MASK;
 | 
						|
	end   += TLB_ENTRY_SIZE - 1;
 | 
						|
	end   &= TLB_ENTRY_SIZE_MASK;
 | 
						|
 | 
						|
#ifdef CONFIG_CPU_HAS_TLBI
 | 
						|
	sync_is();
 | 
						|
	while (start < end) {
 | 
						|
		asm volatile(
 | 
						|
			"tlbi.vaas %0	\n"
 | 
						|
			:
 | 
						|
			: "r" (start)
 | 
						|
			: "memory");
 | 
						|
 | 
						|
		start += 2*PAGE_SIZE;
 | 
						|
	}
 | 
						|
	asm volatile("sync.i\n");
 | 
						|
#else
 | 
						|
	{
 | 
						|
	unsigned long flags, oldpid;
 | 
						|
 | 
						|
	local_irq_save(flags);
 | 
						|
	oldpid = read_mmu_entryhi() & ASID_MASK;
 | 
						|
	while (start < end) {
 | 
						|
		int idx;
 | 
						|
 | 
						|
		write_mmu_entryhi(start | oldpid);
 | 
						|
		start += 2*PAGE_SIZE;
 | 
						|
		tlb_probe();
 | 
						|
		idx = read_mmu_index();
 | 
						|
		if (idx >= 0)
 | 
						|
			tlb_invalid_indexed();
 | 
						|
	}
 | 
						|
	restore_asid_inv_utlb(oldpid, oldpid);
 | 
						|
	local_irq_restore(flags);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 | 
						|
{
 | 
						|
	int newpid = cpu_asid(vma->vm_mm);
 | 
						|
 | 
						|
	addr &= TLB_ENTRY_SIZE_MASK;
 | 
						|
 | 
						|
#ifdef CONFIG_CPU_HAS_TLBI
 | 
						|
	sync_is();
 | 
						|
	asm volatile(
 | 
						|
		"tlbi.vas %0	\n"
 | 
						|
		"sync.i		\n"
 | 
						|
		:
 | 
						|
		: "r" (addr | newpid)
 | 
						|
		: "memory");
 | 
						|
#else
 | 
						|
	{
 | 
						|
	int oldpid, idx;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	local_irq_save(flags);
 | 
						|
	oldpid = read_mmu_entryhi() & ASID_MASK;
 | 
						|
	write_mmu_entryhi(addr | newpid);
 | 
						|
	tlb_probe();
 | 
						|
	idx = read_mmu_index();
 | 
						|
	if (idx >= 0)
 | 
						|
		tlb_invalid_indexed();
 | 
						|
 | 
						|
	restore_asid_inv_utlb(oldpid, newpid);
 | 
						|
	local_irq_restore(flags);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void flush_tlb_one(unsigned long addr)
 | 
						|
{
 | 
						|
	addr &= TLB_ENTRY_SIZE_MASK;
 | 
						|
 | 
						|
#ifdef CONFIG_CPU_HAS_TLBI
 | 
						|
	sync_is();
 | 
						|
	asm volatile(
 | 
						|
		"tlbi.vaas %0	\n"
 | 
						|
		"sync.i		\n"
 | 
						|
		:
 | 
						|
		: "r" (addr)
 | 
						|
		: "memory");
 | 
						|
#else
 | 
						|
	{
 | 
						|
	int oldpid, idx;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	local_irq_save(flags);
 | 
						|
	oldpid = read_mmu_entryhi() & ASID_MASK;
 | 
						|
	write_mmu_entryhi(addr | oldpid);
 | 
						|
	tlb_probe();
 | 
						|
	idx = read_mmu_index();
 | 
						|
	if (idx >= 0)
 | 
						|
		tlb_invalid_indexed();
 | 
						|
 | 
						|
	restore_asid_inv_utlb(oldpid, oldpid);
 | 
						|
	local_irq_restore(flags);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 | 
						|
EXPORT_SYMBOL(flush_tlb_one);
 |