Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6
[linux-2.6] / include / asm-mips / mmu_context.h
1 /*
2  * Switch a MMU context.
3  *
4  * This file is subject to the terms and conditions of the GNU General Public
5  * License.  See the file "COPYING" in the main directory of this archive
6  * for more details.
7  *
8  * Copyright (C) 1996, 1997, 1998, 1999 by Ralf Baechle
9  * Copyright (C) 1999 Silicon Graphics, Inc.
10  */
11 #ifndef _ASM_MMU_CONTEXT_H
12 #define _ASM_MMU_CONTEXT_H
13
14 #include <linux/config.h>
15 #include <linux/errno.h>
16 #include <linux/sched.h>
17 #include <linux/slab.h>
18 #include <asm/cacheflush.h>
19 #include <asm/tlbflush.h>
20
21 /*
22  * For the fast tlb miss handlers, we keep a per cpu array of pointers
23  * to the current pgd for each processor. Also, the proc. id is stuffed
24  * into the context register.
25  */
26 extern unsigned long pgd_current[];
27
28 #define TLBMISS_HANDLER_SETUP_PGD(pgd) \
29         pgd_current[smp_processor_id()] = (unsigned long)(pgd)
30
31 #ifdef CONFIG_32BIT
32 #define TLBMISS_HANDLER_SETUP()                                         \
33         write_c0_context((unsigned long) smp_processor_id() << 25);     \
34         TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir)
35 #endif
36 #if defined(CONFIG_64BIT) && !defined(CONFIG_BUILD_ELF64)
37 #define TLBMISS_HANDLER_SETUP()                                         \
38         write_c0_context((unsigned long) &pgd_current[smp_processor_id()] << 23); \
39         TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir)
40 #endif
41 #if defined(CONFIG_64BIT) && defined(CONFIG_BUILD_ELF64)
42 #define TLBMISS_HANDLER_SETUP()                                         \
43         write_c0_context((unsigned long) smp_processor_id() << 26);     \
44         TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir)
45 #endif
46
47 #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
48
49 #define ASID_INC        0x40
50 #define ASID_MASK       0xfc0
51
52 #elif defined(CONFIG_CPU_R8000)
53
54 #define ASID_INC        0x10
55 #define ASID_MASK       0xff0
56
57 #elif defined(CONFIG_CPU_RM9000)
58
59 #define ASID_INC        0x1
60 #define ASID_MASK       0xfff
61
62 #else /* FIXME: not correct for R6000 */
63
64 #define ASID_INC        0x1
65 #define ASID_MASK       0xff
66
67 #endif
68
69 #define cpu_context(cpu, mm)    ((mm)->context[cpu])
70 #define cpu_asid(cpu, mm)       (cpu_context((cpu), (mm)) & ASID_MASK)
71 #define asid_cache(cpu)         (cpu_data[cpu].asid_cache)
72
73 static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
74 {
75 }
76
77 /*
78  *  All unused by hardware upper bits will be considered
79  *  as a software asid extension.
80  */
81 #define ASID_VERSION_MASK  ((unsigned long)~(ASID_MASK|(ASID_MASK-1)))
82 #define ASID_FIRST_VERSION ((unsigned long)(~ASID_VERSION_MASK) + 1)
83
84 static inline void
85 get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
86 {
87         unsigned long asid = asid_cache(cpu);
88
89         if (! ((asid += ASID_INC) & ASID_MASK) ) {
90                 if (cpu_has_vtag_icache)
91                         flush_icache_all();
92                 local_flush_tlb_all();  /* start new asid cycle */
93                 if (!asid)              /* fix version if needed */
94                         asid = ASID_FIRST_VERSION;
95         }
96         cpu_context(cpu, mm) = asid_cache(cpu) = asid;
97 }
98
99 /*
100  * Initialize the context related info for a new mm_struct
101  * instance.
102  */
103 static inline int
104 init_new_context(struct task_struct *tsk, struct mm_struct *mm)
105 {
106         int i;
107
108         for (i = 0; i < num_online_cpus(); i++)
109                 cpu_context(i, mm) = 0;
110
111         return 0;
112 }
113
114 static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
115                              struct task_struct *tsk)
116 {
117         unsigned int cpu = smp_processor_id();
118         unsigned long flags;
119
120         local_irq_save(flags);
121
122         /* Check if our ASID is of an older version and thus invalid */
123         if ((cpu_context(cpu, next) ^ asid_cache(cpu)) & ASID_VERSION_MASK)
124                 get_new_mmu_context(next, cpu);
125
126         write_c0_entryhi(cpu_context(cpu, next));
127         TLBMISS_HANDLER_SETUP_PGD(next->pgd);
128
129         /*
130          * Mark current->active_mm as not "active" anymore.
131          * We don't want to mislead possible IPI tlb flush routines.
132          */
133         cpu_clear(cpu, prev->cpu_vm_mask);
134         cpu_set(cpu, next->cpu_vm_mask);
135
136         local_irq_restore(flags);
137 }
138
139 /*
140  * Destroy context related info for an mm_struct that is about
141  * to be put to rest.
142  */
143 static inline void destroy_context(struct mm_struct *mm)
144 {
145 }
146
147 #define deactivate_mm(tsk,mm)   do { } while (0)
148
149 /*
150  * After we have set current->mm to a new value, this activates
151  * the context for the new mm so we see the new mappings.
152  */
153 static inline void
154 activate_mm(struct mm_struct *prev, struct mm_struct *next)
155 {
156         unsigned long flags;
157         unsigned int cpu = smp_processor_id();
158
159         local_irq_save(flags);
160
161         /* Unconditionally get a new ASID.  */
162         get_new_mmu_context(next, cpu);
163
164         write_c0_entryhi(cpu_context(cpu, next));
165         TLBMISS_HANDLER_SETUP_PGD(next->pgd);
166
167         /* mark mmu ownership change */
168         cpu_clear(cpu, prev->cpu_vm_mask);
169         cpu_set(cpu, next->cpu_vm_mask);
170
171         local_irq_restore(flags);
172 }
173
174 /*
175  * If mm is currently active_mm, we can't really drop it.  Instead,
176  * we will get a new one for it.
177  */
178 static inline void
179 drop_mmu_context(struct mm_struct *mm, unsigned cpu)
180 {
181         unsigned long flags;
182
183         local_irq_save(flags);
184
185         if (cpu_isset(cpu, mm->cpu_vm_mask))  {
186                 get_new_mmu_context(mm, cpu);
187                 write_c0_entryhi(cpu_asid(cpu, mm));
188         } else {
189                 /* will get a new context next time */
190                 cpu_context(cpu, mm) = 0;
191         }
192
193         local_irq_restore(flags);
194 }
195
196 #endif /* _ASM_MMU_CONTEXT_H */