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 <lethal@linux-sh.org>
This commit is contained in:
parent
da78800632
commit
8263a67e16
10 changed files with 148 additions and 28 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -104,6 +104,9 @@ config CPU_HAS_SR_RB
|
|||
config CPU_HAS_PTEA
|
||||
bool
|
||||
|
||||
config CPU_HAS_PTEAEX
|
||||
bool
|
||||
|
||||
config CPU_HAS_DSP
|
||||
bool
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
99
arch/sh/mm/tlb-pteaex.c
Normal file
99
arch/sh/mm/tlb-pteaex.c
Normal file
|
@ -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();
|
||||
}
|
Loading…
Reference in a new issue