162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Switch an MMU context. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 562306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 662306a36Sopenharmony_ci * for more details. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2001 - 2013 Tensilica Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#ifndef _XTENSA_MMU_CONTEXT_H 1262306a36Sopenharmony_ci#define _XTENSA_MMU_CONTEXT_H 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#ifndef CONFIG_MMU 1562306a36Sopenharmony_ci#include <asm/nommu_context.h> 1662306a36Sopenharmony_ci#else 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/stringify.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/mm_types.h> 2162306a36Sopenharmony_ci#include <linux/pgtable.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/vectors.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/cacheflush.h> 2662306a36Sopenharmony_ci#include <asm/tlbflush.h> 2762306a36Sopenharmony_ci#include <asm-generic/mm_hooks.h> 2862306a36Sopenharmony_ci#include <asm-generic/percpu.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#if (XCHAL_HAVE_TLBS != 1) 3162306a36Sopenharmony_ci# error "Linux must have an MMU!" 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciDECLARE_PER_CPU(unsigned long, asid_cache); 3562306a36Sopenharmony_ci#define cpu_asid_cache(cpu) per_cpu(asid_cache, cpu) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * NO_CONTEXT is the invalid ASID value that we don't ever assign to 3962306a36Sopenharmony_ci * any user or kernel context. We use the reserved values in the 4062306a36Sopenharmony_ci * ASID_INSERT macro below. 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * 0 invalid 4362306a36Sopenharmony_ci * 1 kernel 4462306a36Sopenharmony_ci * 2 reserved 4562306a36Sopenharmony_ci * 3 reserved 4662306a36Sopenharmony_ci * 4...255 available 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define NO_CONTEXT 0 5062306a36Sopenharmony_ci#define ASID_USER_FIRST 4 5162306a36Sopenharmony_ci#define ASID_MASK ((1 << XCHAL_MMU_ASID_BITS) - 1) 5262306a36Sopenharmony_ci#define ASID_INSERT(x) (0x03020001 | (((x) & ASID_MASK) << 8)) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_civoid init_mmu(void); 5562306a36Sopenharmony_civoid init_kio(void); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic inline void set_rasid_register (unsigned long val) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci __asm__ __volatile__ (" wsr %0, rasid\n\t" 6062306a36Sopenharmony_ci " isync\n" : : "a" (val)); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic inline unsigned long get_rasid_register (void) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci unsigned long tmp; 6662306a36Sopenharmony_ci __asm__ __volatile__ (" rsr %0, rasid\n\t" : "=a" (tmp)); 6762306a36Sopenharmony_ci return tmp; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline void get_new_mmu_context(struct mm_struct *mm, unsigned int cpu) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci unsigned long asid = cpu_asid_cache(cpu); 7362306a36Sopenharmony_ci if ((++asid & ASID_MASK) == 0) { 7462306a36Sopenharmony_ci /* 7562306a36Sopenharmony_ci * Start new asid cycle; continue counting with next 7662306a36Sopenharmony_ci * incarnation bits; skipping over 0, 1, 2, 3. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci local_flush_tlb_all(); 7962306a36Sopenharmony_ci asid += ASID_USER_FIRST; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci cpu_asid_cache(cpu) = asid; 8262306a36Sopenharmony_ci mm->context.asid[cpu] = asid; 8362306a36Sopenharmony_ci mm->context.cpu = cpu; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline void get_mmu_context(struct mm_struct *mm, unsigned int cpu) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * Check if our ASID is of an older version and thus invalid. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (mm) { 9362306a36Sopenharmony_ci unsigned long asid = mm->context.asid[cpu]; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (asid == NO_CONTEXT || 9662306a36Sopenharmony_ci ((asid ^ cpu_asid_cache(cpu)) & ~ASID_MASK)) 9762306a36Sopenharmony_ci get_new_mmu_context(mm, cpu); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic inline void activate_context(struct mm_struct *mm, unsigned int cpu) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci get_mmu_context(mm, cpu); 10462306a36Sopenharmony_ci set_rasid_register(ASID_INSERT(mm->context.asid[cpu])); 10562306a36Sopenharmony_ci invalidate_page_directory(); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * Initialize the context related info for a new mm_struct 11062306a36Sopenharmony_ci * instance. Valid cpu values are 0..(NR_CPUS-1), so initializing 11162306a36Sopenharmony_ci * to -1 says the process has never run on any core. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define init_new_context init_new_context 11562306a36Sopenharmony_cistatic inline int init_new_context(struct task_struct *tsk, 11662306a36Sopenharmony_ci struct mm_struct *mm) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci int cpu; 11962306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 12062306a36Sopenharmony_ci mm->context.asid[cpu] = NO_CONTEXT; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci mm->context.cpu = -1; 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 12762306a36Sopenharmony_ci struct task_struct *tsk) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci unsigned int cpu = smp_processor_id(); 13062306a36Sopenharmony_ci int migrated = next->context.cpu != cpu; 13162306a36Sopenharmony_ci /* Flush the icache if we migrated to a new core. */ 13262306a36Sopenharmony_ci if (migrated) { 13362306a36Sopenharmony_ci __invalidate_icache_all(); 13462306a36Sopenharmony_ci next->context.cpu = cpu; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci if (migrated || prev != next) 13762306a36Sopenharmony_ci activate_context(next, cpu); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * Destroy context related info for an mm_struct that is about 14262306a36Sopenharmony_ci * to be put to rest. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci#define destroy_context destroy_context 14562306a36Sopenharmony_cistatic inline void destroy_context(struct mm_struct *mm) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci invalidate_page_directory(); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#include <asm-generic/mmu_context.h> 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#endif /* CONFIG_MMU */ 15462306a36Sopenharmony_ci#endif /* _XTENSA_MMU_CONTEXT_H */ 155