diff options
-rw-r--r-- | arch/nios2/Kconfig | 1 | ||||
-rw-r--r-- | arch/nios2/include/asm/pgtable.h | 1 | ||||
-rw-r--r-- | arch/nios2/include/asm/tlbflush.h | 19 | ||||
-rw-r--r-- | arch/nios2/kernel/nios2_ksyms.c | 12 | ||||
-rw-r--r-- | arch/nios2/mm/cacheflush.c | 7 | ||||
-rw-r--r-- | arch/nios2/mm/fault.c | 2 | ||||
-rw-r--r-- | arch/nios2/mm/tlb.c | 192 | ||||
-rw-r--r-- | arch/nios2/platform/Kconfig.platform | 9 |
8 files changed, 142 insertions, 101 deletions
diff --git a/arch/nios2/Kconfig b/arch/nios2/Kconfig index c3e913ef4f0c..4ef15a61b7bc 100644 --- a/arch/nios2/Kconfig +++ b/arch/nios2/Kconfig @@ -123,7 +123,6 @@ config NIOS2_CMDLINE_IGNORE_DTB config NIOS2_PASS_CMDLINE bool "Passed kernel command line from u-boot" - default n help Use bootargs env variable from u-boot for kernel command line. will override "Default kernel command string". diff --git a/arch/nios2/include/asm/pgtable.h b/arch/nios2/include/asm/pgtable.h index db4f7d179220..95237b7f6fc1 100644 --- a/arch/nios2/include/asm/pgtable.h +++ b/arch/nios2/include/asm/pgtable.h @@ -232,7 +232,6 @@ static inline void pte_clear(struct mm_struct *mm, pte_val(null) = (addr >> PAGE_SHIFT) & 0xf; set_pte_at(mm, addr, ptep, null); - flush_tlb_one(addr); } /* diff --git a/arch/nios2/include/asm/tlbflush.h b/arch/nios2/include/asm/tlbflush.h index e19652fca1c6..b4bf487b9832 100644 --- a/arch/nios2/include/asm/tlbflush.h +++ b/arch/nios2/include/asm/tlbflush.h @@ -26,21 +26,32 @@ struct mm_struct; * * - flush_tlb_all() flushes all processes TLB entries * - flush_tlb_mm(mm) flushes the specified mm context TLB entries - * - flush_tlb_page(vma, vmaddr) flushes one page * - flush_tlb_range(vma, start, end) flushes a range of pages + * - flush_tlb_page(vma, address) flushes a page * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages + * - flush_tlb_kernel_page(address) flushes a kernel page + * + * - reload_tlb_page(vma, address, pte) flushes the TLB for address like + * flush_tlb_page, then replaces it with a TLB for pte. */ extern void flush_tlb_all(void); extern void flush_tlb_mm(struct mm_struct *mm); extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); -extern void flush_tlb_one(unsigned long vaddr); static inline void flush_tlb_page(struct vm_area_struct *vma, - unsigned long addr) + unsigned long address) { - flush_tlb_one(addr); + flush_tlb_range(vma, address, address + PAGE_SIZE); } +static inline void flush_tlb_kernel_page(unsigned long address) +{ + flush_tlb_kernel_range(address, address + PAGE_SIZE); +} + +extern void reload_tlb_page(struct vm_area_struct *vma, unsigned long addr, + pte_t pte); + #endif /* _ASM_NIOS2_TLBFLUSH_H */ diff --git a/arch/nios2/kernel/nios2_ksyms.c b/arch/nios2/kernel/nios2_ksyms.c index bf2f55d10a4d..4e704046a150 100644 --- a/arch/nios2/kernel/nios2_ksyms.c +++ b/arch/nios2/kernel/nios2_ksyms.c @@ -9,12 +9,20 @@ #include <linux/export.h> #include <linux/string.h> +#include <asm/cacheflush.h> +#include <asm/pgtable.h> + /* string functions */ EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memmove); +/* memory management */ + +EXPORT_SYMBOL(empty_zero_page); +EXPORT_SYMBOL(flush_icache_range); + /* * libgcc functions - functions that are used internally by the * compiler... (prototypes are not correct though, but that @@ -31,3 +39,7 @@ DECLARE_EXPORT(__udivsi3); DECLARE_EXPORT(__umoddi3); DECLARE_EXPORT(__umodsi3); DECLARE_EXPORT(__muldi3); +DECLARE_EXPORT(__ucmpdi2); +DECLARE_EXPORT(__lshrdi3); +DECLARE_EXPORT(__ashldi3); +DECLARE_EXPORT(__ashrdi3); diff --git a/arch/nios2/mm/cacheflush.c b/arch/nios2/mm/cacheflush.c index 506f6e1c86d5..65de1bd6a760 100644 --- a/arch/nios2/mm/cacheflush.c +++ b/arch/nios2/mm/cacheflush.c @@ -198,12 +198,15 @@ void flush_dcache_page(struct page *page) EXPORT_SYMBOL(flush_dcache_page); void update_mmu_cache(struct vm_area_struct *vma, - unsigned long address, pte_t *pte) + unsigned long address, pte_t *ptep) { - unsigned long pfn = pte_pfn(*pte); + pte_t pte = *ptep; + unsigned long pfn = pte_pfn(pte); struct page *page; struct address_space *mapping; + reload_tlb_page(vma, address, pte); + if (!pfn_valid(pfn)) return; diff --git a/arch/nios2/mm/fault.c b/arch/nios2/mm/fault.c index eb65f17c158d..6a2e716b959f 100644 --- a/arch/nios2/mm/fault.c +++ b/arch/nios2/mm/fault.c @@ -270,7 +270,7 @@ vmalloc_fault: if (!pte_present(*pte_k)) goto no_context; - flush_tlb_one(address); + flush_tlb_kernel_page(address); return; } } diff --git a/arch/nios2/mm/tlb.c b/arch/nios2/mm/tlb.c index cf10326aab1c..7fea59e53f94 100644 --- a/arch/nios2/mm/tlb.c +++ b/arch/nios2/mm/tlb.c @@ -23,10 +23,6 @@ ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \ << PAGE_SHIFT) -/* Used as illegal PHYS_ADDR for TLB mappings - */ -#define MAX_PHYS_ADDR 0 - static void get_misc_and_pid(unsigned long *misc, unsigned long *pid) { *misc = RDCTL(CTL_TLBMISC); @@ -35,28 +31,23 @@ static void get_misc_and_pid(unsigned long *misc, unsigned long *pid) } /* - * All entries common to a mm share an asid. To effectively flush these - * entries, we just bump the asid. + * This provides a PTEADDR value for addr that will cause a TLB miss + * (fast TLB miss). TLB invalidation replaces entries with this value. */ -void flush_tlb_mm(struct mm_struct *mm) +static unsigned long pteaddr_invalid(unsigned long addr) { - if (current->mm == mm) - flush_tlb_all(); - else - memset(&mm->context, 0, sizeof(mm_context_t)); + return ((addr | 0xC0000000UL) >> PAGE_SHIFT) << 2; } /* * This one is only used for pages with the global bit set so we don't care * much about the ASID. */ -void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) +static void replace_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, unsigned long tlbacc) { unsigned int way; unsigned long org_misc, pid_misc; - pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); - /* remember pid/way until we return. */ get_misc_and_pid(&org_misc, &pid_misc); @@ -67,30 +58,48 @@ void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) unsigned long tlbmisc; unsigned long pid; - tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); + tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); WRCTL(CTL_TLBMISC, tlbmisc); + pteaddr = RDCTL(CTL_PTEADDR); + if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT)) + continue; + tlbmisc = RDCTL(CTL_TLBMISC); pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK; - if (((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) && - pid == mmu_pid) { - unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE + - ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + - (addr & TLB_INDEX_MASK); - pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", - vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT)); - - WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); - tlbmisc = pid_misc | TLBMISC_WE | - (way << TLBMISC_WAY_SHIFT); - WRCTL(CTL_TLBMISC, tlbmisc); - WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); - } + if (pid != mmu_pid) + continue; + + tlbmisc = (mmu_pid << TLBMISC_PID_SHIFT) | TLBMISC_WE | + (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); + if (tlbacc == 0) + WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); + WRCTL(CTL_TLBACC, tlbacc); + /* + * There should be only a single entry that maps a + * particular {address,pid} so break after a match. + */ + break; } WRCTL(CTL_TLBMISC, org_misc); } +static void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) +{ + pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); + + replace_tlb_one_pid(addr, mmu_pid, 0); +} + +static void reload_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, pte_t pte) +{ + pr_debug("Reload tlb-entry for vaddr=%#lx\n", addr); + + replace_tlb_one_pid(addr, mmu_pid, pte_val(pte)); +} + void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { @@ -102,19 +111,18 @@ void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, } } -void flush_tlb_kernel_range(unsigned long start, unsigned long end) +void reload_tlb_page(struct vm_area_struct *vma, unsigned long addr, pte_t pte) { - while (start < end) { - flush_tlb_one(start); - start += PAGE_SIZE; - } + unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context); + + reload_tlb_one_pid(addr, mmu_pid, pte); } /* * This one is only used for pages with the global bit set so we don't care * much about the ASID. */ -void flush_tlb_one(unsigned long addr) +static void flush_tlb_one(unsigned long addr) { unsigned int way; unsigned long org_misc, pid_misc; @@ -130,30 +138,33 @@ void flush_tlb_one(unsigned long addr) unsigned long pteaddr; unsigned long tlbmisc; - tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); + tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); WRCTL(CTL_TLBMISC, tlbmisc); - pteaddr = RDCTL(CTL_PTEADDR); - tlbmisc = RDCTL(CTL_TLBMISC); - if ((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) { - unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE + - ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + - (addr & TLB_INDEX_MASK); + pteaddr = RDCTL(CTL_PTEADDR); + if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT)) + continue; - pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", - vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT)); + pr_debug("Flush entry by writing way=%dl pid=%ld\n", + way, (pid_misc >> TLBMISC_PID_SHIFT)); - tlbmisc = pid_misc | TLBMISC_WE | - (way << TLBMISC_WAY_SHIFT); - WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); - WRCTL(CTL_TLBMISC, tlbmisc); - WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); - } + tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); + WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); + WRCTL(CTL_TLBACC, 0); } WRCTL(CTL_TLBMISC, org_misc); } +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + while (start < end) { + flush_tlb_one(start); + start += PAGE_SIZE; + } +} + void dump_tlb_line(unsigned long line) { unsigned int way; @@ -177,7 +188,7 @@ void dump_tlb_line(unsigned long line) tlbmisc = RDCTL(CTL_TLBMISC); tlbacc = RDCTL(CTL_TLBACC); - if ((tlbacc << PAGE_SHIFT) != (MAX_PHYS_ADDR & PAGE_MASK)) { + if ((tlbacc << PAGE_SHIFT) != 0) { pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n", way, (pteaddr << (PAGE_SHIFT-2)), @@ -203,8 +214,9 @@ void dump_tlb(void) dump_tlb_line(i); } -void flush_tlb_pid(unsigned long pid) +void flush_tlb_pid(unsigned long mmu_pid) { + unsigned long addr = 0; unsigned int line; unsigned int way; unsigned long org_misc, pid_misc; @@ -213,55 +225,65 @@ void flush_tlb_pid(unsigned long pid) get_misc_and_pid(&org_misc, &pid_misc); for (line = 0; line < cpuinfo.tlb_num_lines; line++) { - WRCTL(CTL_PTEADDR, line << 2); + WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); for (way = 0; way < cpuinfo.tlb_num_ways; way++) { - unsigned long pteaddr; unsigned long tlbmisc; - unsigned long tlbacc; + unsigned long pid; - tlbmisc = pid_misc | TLBMISC_RD | - (way << TLBMISC_WAY_SHIFT); + tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); WRCTL(CTL_TLBMISC, tlbmisc); - pteaddr = RDCTL(CTL_PTEADDR); tlbmisc = RDCTL(CTL_TLBMISC); - tlbacc = RDCTL(CTL_TLBACC); - - if (((tlbmisc>>TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK) - == pid) { - tlbmisc = pid_misc | TLBMISC_WE | - (way << TLBMISC_WAY_SHIFT); - WRCTL(CTL_TLBMISC, tlbmisc); - WRCTL(CTL_TLBACC, - (MAX_PHYS_ADDR >> PAGE_SHIFT)); - } + pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK; + if (pid != mmu_pid) + continue; + + tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); + WRCTL(CTL_TLBMISC, tlbmisc); + WRCTL(CTL_TLBACC, 0); } - WRCTL(CTL_TLBMISC, org_misc); + addr += PAGE_SIZE; + } + + WRCTL(CTL_TLBMISC, org_misc); +} + +/* + * All entries common to a mm share an asid. To effectively flush these + * entries, we just bump the asid. + */ +void flush_tlb_mm(struct mm_struct *mm) +{ + if (current->mm == mm) { + unsigned long mmu_pid = get_pid_from_context(&mm->context); + flush_tlb_pid(mmu_pid); + } else { + memset(&mm->context, 0, sizeof(mm_context_t)); } } void flush_tlb_all(void) { - int i; - unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE; + unsigned long addr = 0; + unsigned int line; unsigned int way; - unsigned long org_misc, pid_misc, tlbmisc; + unsigned long org_misc, pid_misc; /* remember pid/way until we return */ get_misc_and_pid(&org_misc, &pid_misc); - pid_misc |= TLBMISC_WE; + + /* Start at way 0, way is auto-incremented after each TLBACC write */ + WRCTL(CTL_TLBMISC, TLBMISC_WE); /* Map each TLB entry to physcal address 0 with no-access and a bad ptbase */ - for (way = 0; way < cpuinfo.tlb_num_ways; way++) { - tlbmisc = pid_misc | (way << TLBMISC_WAY_SHIFT); - for (i = 0; i < cpuinfo.tlb_num_lines; i++) { - WRCTL(CTL_PTEADDR, ((vaddr) >> PAGE_SHIFT) << 2); - WRCTL(CTL_TLBMISC, tlbmisc); - WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); - vaddr += 1UL << 12; - } + for (line = 0; line < cpuinfo.tlb_num_lines; line++) { + WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); + for (way = 0; way < cpuinfo.tlb_num_ways; way++) + WRCTL(CTL_TLBACC, 0); + + addr += PAGE_SIZE; } /* restore pid/way */ @@ -270,6 +292,10 @@ void flush_tlb_all(void) void set_mmu_pid(unsigned long pid) { - WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & TLBMISC_WAY) | - ((pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT)); + unsigned long tlbmisc; + + tlbmisc = RDCTL(CTL_TLBMISC); + tlbmisc = (tlbmisc & TLBMISC_WAY); + tlbmisc |= (pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT; + WRCTL(CTL_TLBMISC, tlbmisc); } diff --git a/arch/nios2/platform/Kconfig.platform b/arch/nios2/platform/Kconfig.platform index 74c1aaf588b8..c72074f8bdd9 100644 --- a/arch/nios2/platform/Kconfig.platform +++ b/arch/nios2/platform/Kconfig.platform @@ -17,7 +17,6 @@ comment "Device tree" config NIOS2_DTB_AT_PHYS_ADDR bool "DTB at physical address" - default n help When enabled you can select a physical address to load the dtb from. Normally this address is passed by a bootloader such as u-boot but @@ -37,7 +36,6 @@ config NIOS2_DTB_PHYS_ADDR config NIOS2_DTB_SOURCE_BOOL bool "Compile and link device tree into kernel image" - default n help This allows you to specify a dts (device tree source) file which will be compiled and linked into the kernel image. @@ -62,21 +60,18 @@ config NIOS2_ARCH_REVISION config NIOS2_HW_MUL_SUPPORT bool "Enable MUL instruction" - default n help Set to true if you configured the Nios II to include the MUL instruction. This will enable the -mhw-mul compiler flag. config NIOS2_HW_MULX_SUPPORT bool "Enable MULX instruction" - default n help Set to true if you configured the Nios II to include the MULX instruction. Enables the -mhw-mulx compiler flag. config NIOS2_HW_DIV_SUPPORT bool "Enable DIV instruction" - default n help Set to true if you configured the Nios II to include the DIV instruction. Enables the -mhw-div compiler flag. @@ -84,7 +79,6 @@ config NIOS2_HW_DIV_SUPPORT config NIOS2_BMX_SUPPORT bool "Enable BMX instructions" depends on NIOS2_ARCH_REVISION = 2 - default n help Set to true if you configured the Nios II R2 to include the BMX Bit Manipulation Extension instructions. Enables @@ -93,7 +87,6 @@ config NIOS2_BMX_SUPPORT config NIOS2_CDX_SUPPORT bool "Enable CDX instructions" depends on NIOS2_ARCH_REVISION = 2 - default n help Set to true if you configured the Nios II R2 to include the CDX Bit Manipulation Extension instructions. Enables @@ -101,13 +94,11 @@ config NIOS2_CDX_SUPPORT config NIOS2_FPU_SUPPORT bool "Custom floating point instr support" - default n help Enables the -mcustom-fpu-cfg=60-1 compiler flag. config NIOS2_CI_SWAB_SUPPORT bool "Byteswap custom instruction" - default n help Use the byteswap (endian converter) Nios II custom instruction provided by Altera and which can be enabled in QSYS builder. This accelerates |