From: Paul Mundt Date: Tue, 17 Mar 2009 08:49:49 +0000 (+0900) Subject: sh: Support for extended ASIDs on PTEAEX-capable SH-X3 cores. X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=8263a67e169fdf0d06d172acbf6c03ae172a69d4;p=linux-beck.git sh: Support for extended ASIDs on PTEAEX-capable SH-X3 cores. This adds support for extended ASIDs (up to 16-bits) on newer SH-X3 cores that implement the PTAEX register and respective functionality. Presently only the 65nm SH7786 (90nm only supports legacy 8-bit ASIDs). The main change is in how the PTE is written out when loading the entry in to the TLB, as well as in how the TLB entry is selectively flushed. While SH-X2 extended mode splits out the memory-mapped U and I-TLB data arrays for extra bits, extended ASID mode splits out the address arrays. While we don't use the memory-mapped data array access, the address array accesses are necessary for selective TLB flushes, so these are implemented newly and replace the generic SH-4 implementation. With this, TLB flushes in switch_mm() are almost non-existent on newer parts. Signed-off-by: Paul Mundt --- diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index a0c879d17fd6..6c56495fd158 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -365,6 +365,7 @@ config CPU_SUBTYPE_SH7786 bool "Support SH7786 processor" select CPU_SH4A select CPU_SHX3 + select CPU_HAS_PTEAEX select ARCH_SPARSEMEM_ENABLE select SYS_SUPPORTS_NUMA diff --git a/arch/sh/Kconfig.cpu b/arch/sh/Kconfig.cpu index 0e27fe3b182b..c7d704381a6d 100644 --- a/arch/sh/Kconfig.cpu +++ b/arch/sh/Kconfig.cpu @@ -104,6 +104,9 @@ config CPU_HAS_SR_RB config CPU_HAS_PTEA bool +config CPU_HAS_PTEAEX + bool + config CPU_HAS_DSP bool diff --git a/arch/sh/include/asm/cpu-features.h b/arch/sh/include/asm/cpu-features.h index 86308aa39731..694abe490edb 100644 --- a/arch/sh/include/asm/cpu-features.h +++ b/arch/sh/include/asm/cpu-features.h @@ -21,5 +21,6 @@ #define CPU_HAS_LLSC 0x0040 /* movli.l/movco.l */ #define CPU_HAS_L2_CACHE 0x0080 /* Secondary cache / URAM */ #define CPU_HAS_OP32 0x0100 /* 32-bit instruction support */ +#define CPU_HAS_PTEAEX 0x0200 /* PTE ASID Extension support */ #endif /* __ASM_SH_CPU_FEATURES_H */ diff --git a/arch/sh/include/asm/mmu_context.h b/arch/sh/include/asm/mmu_context.h index 5d9157bd474d..2a9c55f1a83f 100644 --- a/arch/sh/include/asm/mmu_context.h +++ b/arch/sh/include/asm/mmu_context.h @@ -19,13 +19,18 @@ * (a) TLB cache version (or round, cycle whatever expression you like) * (b) ASID (Address Space IDentifier) */ +#ifdef CONFIG_CPU_HAS_PTEAEX +#define MMU_CONTEXT_ASID_MASK 0x0000ffff +#else #define MMU_CONTEXT_ASID_MASK 0x000000ff -#define MMU_CONTEXT_VERSION_MASK 0xffffff00 -#define MMU_CONTEXT_FIRST_VERSION 0x00000100 -#define NO_CONTEXT 0UL +#endif -/* ASID is 8-bit value, so it can't be 0x100 */ -#define MMU_NO_ASID 0x100 +#define MMU_CONTEXT_VERSION_MASK (~0UL & ~MMU_CONTEXT_ASID_MASK) +#define MMU_CONTEXT_FIRST_VERSION (MMU_CONTEXT_ASID_MASK + 1) + +/* Impossible ASID value, to differentiate from NO_CONTEXT. */ +#define MMU_NO_ASID MMU_CONTEXT_FIRST_VERSION +#define NO_CONTEXT 0UL #define asid_cache(cpu) (cpu_data[cpu].asid_cache) diff --git a/arch/sh/include/asm/mmu_context_32.h b/arch/sh/include/asm/mmu_context_32.h index f4f9aebd68b7..8ef800c549ab 100644 --- a/arch/sh/include/asm/mmu_context_32.h +++ b/arch/sh/include/asm/mmu_context_32.h @@ -10,6 +10,17 @@ static inline void destroy_context(struct mm_struct *mm) /* Do nothing */ } +#ifdef CONFIG_CPU_HAS_PTEAEX +static inline void set_asid(unsigned long asid) +{ + __raw_writel(asid, MMU_PTEAEX); +} + +static inline unsigned long get_asid(void) +{ + return __raw_readl(MMU_PTEAEX) & MMU_CONTEXT_ASID_MASK; +} +#else static inline void set_asid(unsigned long asid) { unsigned long __dummy; @@ -33,6 +44,7 @@ static inline unsigned long get_asid(void) asid &= MMU_CONTEXT_ASID_MASK; return asid; } +#endif /* CONFIG_CPU_HAS_PTEAEX */ /* MMU_TTB is used for optimizing the fault handling. */ static inline void set_TTB(pgd_t *pgd) diff --git a/arch/sh/include/cpu-sh4/cpu/mmu_context.h b/arch/sh/include/cpu-sh4/cpu/mmu_context.h index 9ea8eb27b18e..3ce7ef6c2978 100644 --- a/arch/sh/include/cpu-sh4/cpu/mmu_context.h +++ b/arch/sh/include/cpu-sh4/cpu/mmu_context.h @@ -14,28 +14,35 @@ #define MMU_PTEL 0xFF000004 /* Page table entry register LOW */ #define MMU_TTB 0xFF000008 /* Translation table base register */ #define MMU_TEA 0xFF00000C /* TLB Exception Address */ -#define MMU_PTEA 0xFF000034 /* Page table entry assistance register */ +#define MMU_PTEA 0xFF000034 /* PTE assistance register */ +#define MMU_PTEAEX 0xFF00007C /* PTE ASID extension register */ #define MMUCR 0xFF000010 /* MMU Control Register */ -#define MMU_ITLB_ADDRESS_ARRAY 0xF2000000 #define MMU_UTLB_ADDRESS_ARRAY 0xF6000000 +#define MMU_UTLB_ADDRESS_ARRAY2 0xF6800000 #define MMU_PAGE_ASSOC_BIT 0x80 #define MMUCR_TI (1<<2) -#ifdef CONFIG_X2TLB -#define MMUCR_ME (1 << 7) -#else -#define MMUCR_ME (0) -#endif - #if defined(CONFIG_32BIT) && defined(CONFIG_CPU_SUBTYPE_ST40) #define MMUCR_SE (1 << 4) #else #define MMUCR_SE (0) #endif +#ifdef CONFIG_CPU_HAS_PTEAEX +#define MMUCR_AEX (1 << 6) +#else +#define MMUCR_AEX (0) +#endif + +#ifdef CONFIG_X2TLB +#define MMUCR_ME (1 << 7) +#else +#define MMUCR_ME (0) +#endif + #ifdef CONFIG_SH_STORE_QUEUES #define MMUCR_SQMD (1 << 9) #else @@ -43,17 +50,7 @@ #endif #define MMU_NTLB_ENTRIES 64 -#define MMU_CONTROL_INIT (0x05|MMUCR_SQMD|MMUCR_ME|MMUCR_SE) - -#define MMU_ITLB_DATA_ARRAY 0xF3000000 -#define MMU_UTLB_DATA_ARRAY 0xF7000000 - -#define MMU_UTLB_ENTRIES 64 -#define MMU_U_ENTRY_SHIFT 8 -#define MMU_UTLB_VALID 0x100 -#define MMU_ITLB_ENTRIES 4 -#define MMU_I_ENTRY_SHIFT 8 -#define MMU_ITLB_VALID 0x100 +#define MMU_CONTROL_INIT (0x05|MMUCR_SQMD|MMUCR_ME|MMUCR_SE|MMUCR_AEX) #define TRA 0xff000020 #define EXPEVT 0xff000024 diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c index 2bd0ec962639..3d3a3c4425a9 100644 --- a/arch/sh/kernel/cpu/sh4/probe.c +++ b/arch/sh/kernel/cpu/sh4/probe.c @@ -134,7 +134,7 @@ int __init detect_cpu_and_cache_system(void) boot_cpu_data.icache.ways = 4; boot_cpu_data.dcache.ways = 4; boot_cpu_data.flags |= CPU_HAS_FPU | CPU_HAS_PERF_COUNTER | - CPU_HAS_LLSC; + CPU_HAS_LLSC | CPU_HAS_PTEAEX; break; case 0x3008: boot_cpu_data.icache.ways = 4; diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 61ab2a7f8647..24c60251f680 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -449,7 +449,7 @@ EXPORT_SYMBOL(get_cpu_subtype); /* Symbolic CPU flags, keep in sync with asm/cpu-features.h */ static const char *cpu_flags[] = { "none", "fpu", "p2flush", "mmuassoc", "dsp", "perfctr", - "ptea", "llsc", "l2", "op32", NULL + "ptea", "llsc", "l2", "op32", "pteaex", NULL }; static void show_cpuflags(struct seq_file *m, struct sh_cpuinfo *c) diff --git a/arch/sh/mm/Makefile_32 b/arch/sh/mm/Makefile_32 index 469ff1672451..986a1e055834 100644 --- a/arch/sh/mm/Makefile_32 +++ b/arch/sh/mm/Makefile_32 @@ -25,8 +25,10 @@ obj-$(CONFIG_CPU_SH4) += cache-debugfs.o endif ifdef CONFIG_MMU -obj-$(CONFIG_CPU_SH3) += tlb-sh3.o -obj-$(CONFIG_CPU_SH4) += tlb-sh4.o +tlb-$(CONFIG_CPU_SH3) := tlb-sh3.o +tlb-$(CONFIG_CPU_SH4) := tlb-sh4.o +tlb-$(CONFIG_CPU_HAS_PTEAEX) := tlb-pteaex.o +obj-y += $(tlb-y) ifndef CONFIG_CACHE_OFF obj-$(CONFIG_CPU_SH4) += pg-sh4.o obj-$(CONFIG_SH7705_CACHE_32KB) += pg-sh7705.o diff --git a/arch/sh/mm/tlb-pteaex.c b/arch/sh/mm/tlb-pteaex.c new file mode 100644 index 000000000000..5c9b2d781e08 --- /dev/null +++ b/arch/sh/mm/tlb-pteaex.c @@ -0,0 +1,99 @@ +/* + * arch/sh/mm/tlb-pteaex.c + * + * TLB operations for SH-X3 CPUs featuring PTE ASID Extensions. + * + * Copyright (C) 2009 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include + +void update_mmu_cache(struct vm_area_struct * vma, + unsigned long address, pte_t pte) +{ + unsigned long flags; + unsigned long pteval; + unsigned long vpn; + + /* Ptrace may call this routine. */ + if (vma && current->active_mm != vma->vm_mm) + return; + +#ifndef CONFIG_CACHE_OFF + { + unsigned long pfn = pte_pfn(pte); + + if (pfn_valid(pfn)) { + struct page *page = pfn_to_page(pfn); + + if (!test_bit(PG_mapped, &page->flags)) { + unsigned long phys = pte_val(pte) & PTE_PHYS_MASK; + __flush_wback_region((void *)P1SEGADDR(phys), + PAGE_SIZE); + __set_bit(PG_mapped, &page->flags); + } + } + } +#endif + + local_irq_save(flags); + + /* Set PTEH register */ + vpn = address & MMU_VPN_MASK; + __raw_writel(vpn, MMU_PTEH); + + /* Set PTEAEX */ + __raw_writel(get_asid(), MMU_PTEAEX); + + pteval = pte.pte_low; + + /* Set PTEA register */ +#ifdef CONFIG_X2TLB + /* + * For the extended mode TLB this is trivial, only the ESZ and + * EPR bits need to be written out to PTEA, with the remainder of + * the protection bits (with the exception of the compat-mode SZ + * and PR bits, which are cleared) being written out in PTEL. + */ + __raw_writel(pte.pte_high, MMU_PTEA); +#else + /* TODO: make this look less hacky */ + __raw_writel(((pteval >> 28) & 0xe) | (pteval & 0x1), MMU_PTEA); +#endif + + /* Set PTEL register */ + pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */ +#ifdef CONFIG_CACHE_WRITETHROUGH + pteval |= _PAGE_WT; +#endif + /* conveniently, we want all the software flags to be 0 anyway */ + __raw_writel(pteval, MMU_PTEL); + + /* Load the TLB */ + asm volatile("ldtlb": /* no output */ : /* no input */ : "memory"); + local_irq_restore(flags); +} + +/* + * While SH-X2 extended TLB mode splits out the memory-mapped I/UTLB + * data arrays, SH-X3 cores with PTEAEX split out the memory-mapped + * address arrays. In compat mode the second array is inaccessible, while + * in extended mode, the legacy 8-bit ASID field in address array 1 has + * undefined behaviour. + */ +void __uses_jump_to_uncached local_flush_tlb_one(unsigned long asid, + unsigned long page) +{ + jump_to_uncached(); + __raw_writel(page, MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT); + __raw_writel(asid, MMU_UTLB_ADDRESS_ARRAY2 | MMU_PAGE_ASSOC_BIT); + back_to_cached(); +}