|  | // 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); |