// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. #include #include #include #include #include #include /* * 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 asm volatile("tlbi.asids %0"::"r"(cpu_asid(mm))); #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 while (start < end) { asm volatile("tlbi.vas %0"::"r"(start | newpid)); start += 2*PAGE_SIZE; } sync_is(); #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 while (start < end) { asm volatile("tlbi.vaas %0"::"r"(start)); start += 2*PAGE_SIZE; } sync_is(); #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 asm volatile("tlbi.vas %0"::"r"(addr | newpid)); sync_is(); #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 asm volatile("tlbi.vaas %0"::"r"(addr)); sync_is(); #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);