sh: Support for extended ASIDs on PTEAEX-capable SH-X3 cores.
authorPaul Mundt <lethal@linux-sh.org>
Tue, 17 Mar 2009 08:49:49 +0000 (17:49 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 17 Mar 2009 08:49:49 +0000 (17:49 +0900)
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 <lethal@linux-sh.org>
arch/sh/Kconfig
arch/sh/Kconfig.cpu
arch/sh/include/asm/cpu-features.h
arch/sh/include/asm/mmu_context.h
arch/sh/include/asm/mmu_context_32.h
arch/sh/include/cpu-sh4/cpu/mmu_context.h
arch/sh/kernel/cpu/sh4/probe.c
arch/sh/kernel/setup.c
arch/sh/mm/Makefile_32
arch/sh/mm/tlb-pteaex.c [new file with mode: 0644]

index a0c879d..6c56495 100644 (file)
@@ -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
 
index 0e27fe3..c7d7043 100644 (file)
@@ -104,6 +104,9 @@ config CPU_HAS_SR_RB
 config CPU_HAS_PTEA
        bool
 
+config CPU_HAS_PTEAEX
+       bool
+
 config CPU_HAS_DSP
        bool
 
index 86308aa..694abe4 100644 (file)
@@ -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 */
index 5d9157b..2a9c55f 100644 (file)
  *    (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)
 
index f4f9aeb..8ef800c 100644 (file)
@@ -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)
index 9ea8eb2..3ce7ef6 100644 (file)
 #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
 #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
index 2bd0ec9..3d3a3c4 100644 (file)
@@ -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;
index 61ab2a7..24c6025 100644 (file)
@@ -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)
index 469ff16..986a1e0 100644 (file)
@@ -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 (file)
index 0000000..5c9b2d7
--- /dev/null
@@ -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 <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+
+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();
+}