diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-02 19:41:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-02 19:41:08 -0700 |
commit | c9297d284126b80c9cfd72c690e0da531c99fc48 (patch) | |
tree | bec7ce343f2a37def4e99ec556deb3998dbeb041 /arch/nds32/mm/cacheflush.c | |
parent | 17e3cd222af1c72a750cd83565bb8dfc7bc12335 (diff) | |
parent | 6fc61ee69433e7e0433cabd36f78bb5fb3b26524 (diff) |
Merge tag 'nds32-for-linus-4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/greentime/linux
Pull nds32 architecture support from Greentime Hu:
"This contains the core nds32 Linux port (including interrupt
controller driver and timer driver), which has been through seven
rounds of review on mailing list.
It is able to boot to shell and passes most LTP-2017 testsuites in
nds32 AE3XX platform:
Total Tests: 1901
Total Skipped Tests: 618
Total Failures: 78"
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
* tag 'nds32-for-linus-4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/greentime/linux: (44 commits)
nds32: To use the generic dump_stack()
nds32: fix building failed if using elf toolchain.
nios2: add ioremap_nocache declaration before include asm-generic/io.h.
nds32: fix building failed if using older version gcc.
dt-bindings: timer: Add andestech atcpit100 timer binding doc
clocksource/drivers/atcpit100: VDSO support
clocksource/drivers/atcpit100: Add andestech atcpit100 timer
net: faraday add nds32 support.
irqchip: Andestech Internal Vector Interrupt Controller driver
dt-bindings: interrupt-controller: Andestech Internal Vector Interrupt Controller
dt-bindings: nds32 SoC Bindings
dt-bindings: nds32 L2 cache controller Bindings
dt-bindings: nds32 CPU Bindings
MAINTAINERS: Add nds32
nds32: Build infrastructure
nds32: defconfig
nds32: Miscellaneous header files
nds32: Device tree support
nds32: Generic timers support
nds32: Loadable modules
...
Diffstat (limited to 'arch/nds32/mm/cacheflush.c')
-rw-r--r-- | arch/nds32/mm/cacheflush.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/arch/nds32/mm/cacheflush.c b/arch/nds32/mm/cacheflush.c new file mode 100644 index 000000000000..6eb786a399a2 --- /dev/null +++ b/arch/nds32/mm/cacheflush.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2005-2017 Andes Technology Corporation + +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/pagemap.h> +#include <linux/module.h> +#include <asm/cacheflush.h> +#include <asm/proc-fns.h> +#include <asm/shmparam.h> +#include <asm/cache_info.h> + +extern struct cache_info L1_cache_info[2]; + +#ifndef CONFIG_CPU_CACHE_ALIASING +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, + pte_t * pte) +{ + struct page *page; + unsigned long pfn = pte_pfn(*pte); + unsigned long flags; + + if (!pfn_valid(pfn)) + return; + + if (vma->vm_mm == current->active_mm) { + local_irq_save(flags); + __nds32__mtsr_dsb(addr, NDS32_SR_TLB_VPN); + __nds32__tlbop_rwr(*pte); + __nds32__isb(); + local_irq_restore(flags); + } + page = pfn_to_page(pfn); + + if ((test_and_clear_bit(PG_dcache_dirty, &page->flags)) || + (vma->vm_flags & VM_EXEC)) { + + if (!PageHighMem(page)) { + cpu_cache_wbinval_page((unsigned long) + page_address(page), + vma->vm_flags & VM_EXEC); + } else { + unsigned long kaddr = (unsigned long)kmap_atomic(page); + cpu_cache_wbinval_page(kaddr, vma->vm_flags & VM_EXEC); + kunmap_atomic((void *)kaddr); + } + } +} +#else +extern pte_t va_present(struct mm_struct *mm, unsigned long addr); + +static inline unsigned long aliasing(unsigned long addr, unsigned long page) +{ + return ((addr & PAGE_MASK) ^ page) & (SHMLBA - 1); +} + +static inline unsigned long kremap0(unsigned long uaddr, unsigned long pa) +{ + unsigned long kaddr, pte; + +#define BASE_ADDR0 0xffffc000 + kaddr = BASE_ADDR0 | (uaddr & L1_cache_info[DCACHE].aliasing_mask); + pte = (pa | PAGE_KERNEL); + __nds32__mtsr_dsb(kaddr, NDS32_SR_TLB_VPN); + __nds32__tlbop_rwlk(pte); + __nds32__isb(); + return kaddr; +} + +static inline void kunmap01(unsigned long kaddr) +{ + __nds32__tlbop_unlk(kaddr); + __nds32__tlbop_inv(kaddr); + __nds32__isb(); +} + +static inline unsigned long kremap1(unsigned long uaddr, unsigned long pa) +{ + unsigned long kaddr, pte; + +#define BASE_ADDR1 0xffff8000 + kaddr = BASE_ADDR1 | (uaddr & L1_cache_info[DCACHE].aliasing_mask); + pte = (pa | PAGE_KERNEL); + __nds32__mtsr_dsb(kaddr, NDS32_SR_TLB_VPN); + __nds32__tlbop_rwlk(pte); + __nds32__isb(); + return kaddr; +} + +void flush_cache_mm(struct mm_struct *mm) +{ + unsigned long flags; + + local_irq_save(flags); + cpu_dcache_wbinval_all(); + cpu_icache_inval_all(); + local_irq_restore(flags); +} + +void flush_cache_dup_mm(struct mm_struct *mm) +{ +} + +void flush_cache_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end) +{ + unsigned long flags; + + if ((end - start) > 8 * PAGE_SIZE) { + cpu_dcache_wbinval_all(); + if (vma->vm_flags & VM_EXEC) + cpu_icache_inval_all(); + return; + } + local_irq_save(flags); + while (start < end) { + if (va_present(vma->vm_mm, start)) + cpu_cache_wbinval_page(start, vma->vm_flags & VM_EXEC); + start += PAGE_SIZE; + } + local_irq_restore(flags); + return; +} + +void flush_cache_page(struct vm_area_struct *vma, + unsigned long addr, unsigned long pfn) +{ + unsigned long vto, flags; + + local_irq_save(flags); + vto = kremap0(addr, pfn << PAGE_SHIFT); + cpu_cache_wbinval_page(vto, vma->vm_flags & VM_EXEC); + kunmap01(vto); + local_irq_restore(flags); +} + +void flush_cache_vmap(unsigned long start, unsigned long end) +{ + cpu_dcache_wbinval_all(); + cpu_icache_inval_all(); +} + +void flush_cache_vunmap(unsigned long start, unsigned long end) +{ + cpu_dcache_wbinval_all(); + cpu_icache_inval_all(); +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + unsigned long vto, vfrom, flags, kto, kfrom, pfrom, pto; + kto = ((unsigned long)page_address(to) & PAGE_MASK); + kfrom = ((unsigned long)page_address(from) & PAGE_MASK); + pto = page_to_phys(to); + pfrom = page_to_phys(from); + + if (aliasing(vaddr, (unsigned long)kfrom)) + cpu_dcache_wb_page((unsigned long)kfrom); + if (aliasing(vaddr, (unsigned long)kto)) + cpu_dcache_inval_page((unsigned long)kto); + local_irq_save(flags); + vto = kremap0(vaddr, pto); + vfrom = kremap1(vaddr, pfrom); + copy_page((void *)vto, (void *)vfrom); + kunmap01(vfrom); + kunmap01(vto); + local_irq_restore(flags); +} + +EXPORT_SYMBOL(copy_user_highpage); + +void clear_user_highpage(struct page *page, unsigned long vaddr) +{ + unsigned long vto, flags, kto; + + kto = ((unsigned long)page_address(page) & PAGE_MASK); + + local_irq_save(flags); + if (aliasing(kto, vaddr) && kto != 0) { + cpu_dcache_inval_page(kto); + cpu_icache_inval_page(kto); + } + vto = kremap0(vaddr, page_to_phys(page)); + clear_page((void *)vto); + kunmap01(vto); + local_irq_restore(flags); +} + +EXPORT_SYMBOL(clear_user_highpage); + +void flush_dcache_page(struct page *page) +{ + struct address_space *mapping; + + mapping = page_mapping(page); + if (mapping && !mapping_mapped(mapping)) + set_bit(PG_dcache_dirty, &page->flags); + else { + int i, pc; + unsigned long vto, kaddr, flags; + kaddr = (unsigned long)page_address(page); + cpu_dcache_wbinval_page(kaddr); + pc = CACHE_SET(DCACHE) * CACHE_LINE_SIZE(DCACHE) / PAGE_SIZE; + local_irq_save(flags); + for (i = 0; i < pc; i++) { + vto = + kremap0(kaddr + i * PAGE_SIZE, page_to_phys(page)); + cpu_dcache_wbinval_page(vto); + kunmap01(vto); + } + local_irq_restore(flags); + } +} + +void copy_to_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long vaddr, void *dst, void *src, int len) +{ + unsigned long line_size, start, end, vto, flags; + + local_irq_save(flags); + vto = kremap0(vaddr, page_to_phys(page)); + dst = (void *)(vto | (vaddr & (PAGE_SIZE - 1))); + memcpy(dst, src, len); + if (vma->vm_flags & VM_EXEC) { + line_size = L1_cache_info[DCACHE].line_size; + start = (unsigned long)dst & ~(line_size - 1); + end = + ((unsigned long)dst + len + line_size - 1) & ~(line_size - + 1); + cpu_cache_wbinval_range(start, end, 1); + } + kunmap01(vto); + local_irq_restore(flags); +} + +void copy_from_user_page(struct vm_area_struct *vma, struct page *page, + unsigned long vaddr, void *dst, void *src, int len) +{ + unsigned long vto, flags; + + local_irq_save(flags); + vto = kremap0(vaddr, page_to_phys(page)); + src = (void *)(vto | (vaddr & (PAGE_SIZE - 1))); + memcpy(dst, src, len); + kunmap01(vto); + local_irq_restore(flags); +} + +void flush_anon_page(struct vm_area_struct *vma, + struct page *page, unsigned long vaddr) +{ + unsigned long flags; + if (!PageAnon(page)) + return; + + if (vma->vm_mm != current->active_mm) + return; + + local_irq_save(flags); + if (vma->vm_flags & VM_EXEC) + cpu_icache_inval_page(vaddr & PAGE_MASK); + cpu_dcache_wbinval_page((unsigned long)page_address(page)); + local_irq_restore(flags); +} + +void flush_kernel_dcache_page(struct page *page) +{ + unsigned long flags; + local_irq_save(flags); + cpu_dcache_wbinval_page((unsigned long)page_address(page)); + local_irq_restore(flags); +} + +void flush_icache_range(unsigned long start, unsigned long end) +{ + unsigned long line_size, flags; + line_size = L1_cache_info[DCACHE].line_size; + start = start & ~(line_size - 1); + end = (end + line_size - 1) & ~(line_size - 1); + local_irq_save(flags); + cpu_cache_wbinval_range(start, end, 1); + local_irq_restore(flags); +} + +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + unsigned long flags; + local_irq_save(flags); + cpu_cache_wbinval_page((unsigned long)page_address(page), + vma->vm_flags & VM_EXEC); + local_irq_restore(flags); +} + +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, + pte_t * pte) +{ + struct page *page; + unsigned long flags; + unsigned long pfn = pte_pfn(*pte); + + if (!pfn_valid(pfn)) + return; + + if (vma->vm_mm == current->active_mm) { + local_irq_save(flags); + __nds32__mtsr_dsb(addr, NDS32_SR_TLB_VPN); + __nds32__tlbop_rwr(*pte); + __nds32__isb(); + local_irq_restore(flags); + } + + page = pfn_to_page(pfn); + if (test_and_clear_bit(PG_dcache_dirty, &page->flags) || + (vma->vm_flags & VM_EXEC)) { + local_irq_save(flags); + cpu_dcache_wbinval_page((unsigned long)page_address(page)); + local_irq_restore(flags); + } +} +#endif |