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