Linux-2.6.12-rc2
[linux-2.6] / arch / mips / mm / tlb-andes.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1997, 1998, 1999 Ralf Baechle (ralf@gnu.org)
7  * Copyright (C) 1999 Silicon Graphics, Inc.
8  * Copyright (C) 2000 Kanoj Sarcar (kanoj@sgi.com)
9  */
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/sched.h>
13 #include <linux/mm.h>
14 #include <asm/page.h>
15 #include <asm/pgtable.h>
16 #include <asm/system.h>
17 #include <asm/mmu_context.h>
18
19 extern void build_tlb_refill_handler(void);
20
21 #define NTLB_ENTRIES       64
22 #define NTLB_ENTRIES_HALF  32
23
24 void local_flush_tlb_all(void)
25 {
26         unsigned long flags;
27         unsigned long old_ctx;
28         unsigned long entry;
29
30         local_irq_save(flags);
31         /* Save old context and create impossible VPN2 value */
32         old_ctx = read_c0_entryhi() & ASID_MASK;
33         write_c0_entryhi(CKSEG0);
34         write_c0_entrylo0(0);
35         write_c0_entrylo1(0);
36
37         entry = read_c0_wired();
38
39         /* Blast 'em all away. */
40         while (entry < NTLB_ENTRIES) {
41                 write_c0_index(entry);
42                 tlb_write_indexed();
43                 entry++;
44         }
45         write_c0_entryhi(old_ctx);
46         local_irq_restore(flags);
47 }
48
49 void local_flush_tlb_mm(struct mm_struct *mm)
50 {
51         int cpu = smp_processor_id();
52         if (cpu_context(cpu, mm) != 0) {
53                 drop_mmu_context(mm,cpu);
54         }
55 }
56
57 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
58                            unsigned long end)
59 {
60         struct mm_struct *mm = vma->vm_mm;
61         int cpu = smp_processor_id();
62
63         if (cpu_context(cpu, mm) != 0) {
64                 unsigned long flags;
65                 int size;
66
67                 local_irq_save(flags);
68                 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
69                 size = (size + 1) >> 1;
70                 if (size <= NTLB_ENTRIES_HALF) {
71                         int oldpid = (read_c0_entryhi() & ASID_MASK);
72                         int newpid = (cpu_context(smp_processor_id(), mm)
73                                       & ASID_MASK);
74
75                         start &= (PAGE_MASK << 1);
76                         end += ((PAGE_SIZE << 1) - 1);
77                         end &= (PAGE_MASK << 1);
78                         while(start < end) {
79                                 int idx;
80
81                                 write_c0_entryhi(start | newpid);
82                                 start += (PAGE_SIZE << 1);
83                                 tlb_probe();
84                                 idx = read_c0_index();
85                                 write_c0_entrylo0(0);
86                                 write_c0_entrylo1(0);
87                                 write_c0_entryhi(CKSEG0);
88                                 if(idx < 0)
89                                         continue;
90                                 tlb_write_indexed();
91                         }
92                         write_c0_entryhi(oldpid);
93                 } else {
94                         drop_mmu_context(mm, cpu);
95                 }
96                 local_irq_restore(flags);
97         }
98 }
99
100 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
101 {
102         unsigned long flags;
103         int size;
104
105         size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
106         size = (size + 1) >> 1;
107
108         local_irq_save(flags);
109         if (size <= NTLB_ENTRIES_HALF) {
110                 int pid = read_c0_entryhi();
111
112                 start &= (PAGE_MASK << 1);
113                 end += ((PAGE_SIZE << 1) - 1);
114                 end &= (PAGE_MASK << 1);
115
116                 while (start < end) {
117                         int idx;
118
119                         write_c0_entryhi(start);
120                         start += (PAGE_SIZE << 1);
121                         tlb_probe();
122                         idx = read_c0_index();
123                         write_c0_entrylo0(0);
124                         write_c0_entrylo1(0);
125                         write_c0_entryhi(CKSEG0 + (idx << (PAGE_SHIFT+1)));
126                         if (idx < 0)
127                                 continue;
128                         tlb_write_indexed();
129                 }
130                 write_c0_entryhi(pid);
131         } else {
132                 local_flush_tlb_all();
133         }
134         local_irq_restore(flags);
135 }
136
137 void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
138 {
139         if (cpu_context(smp_processor_id(), vma->vm_mm) != 0) {
140                 unsigned long flags;
141                 int oldpid, newpid, idx;
142
143                 newpid = (cpu_context(smp_processor_id(), vma->vm_mm) &
144                           ASID_MASK);
145                 page &= (PAGE_MASK << 1);
146                 local_irq_save(flags);
147                 oldpid = (read_c0_entryhi() & ASID_MASK);
148                 write_c0_entryhi(page | newpid);
149                 tlb_probe();
150                 idx = read_c0_index();
151                 write_c0_entrylo0(0);
152                 write_c0_entrylo1(0);
153                 write_c0_entryhi(CKSEG0);
154                 if (idx < 0)
155                         goto finish;
156                 tlb_write_indexed();
157
158         finish:
159                 write_c0_entryhi(oldpid);
160                 local_irq_restore(flags);
161         }
162 }
163
164 /*
165  * This one is only used for pages with the global bit set so we don't care
166  * much about the ASID.
167  */
168 void local_flush_tlb_one(unsigned long page)
169 {
170         unsigned long flags;
171         int oldpid, idx;
172
173         local_irq_save(flags);
174         page &= (PAGE_MASK << 1);
175         oldpid = read_c0_entryhi() & 0xff;
176         write_c0_entryhi(page);
177         tlb_probe();
178         idx = read_c0_index();
179         write_c0_entrylo0(0);
180         write_c0_entrylo1(0);
181         if (idx >= 0) {
182                 /* Make sure all entries differ. */
183                 write_c0_entryhi(CKSEG0+(idx<<(PAGE_SHIFT+1)));
184                 tlb_write_indexed();
185         }
186         write_c0_entryhi(oldpid);
187
188         local_irq_restore(flags);
189 }
190
191 /* XXX Simplify this.  On the R10000 writing a TLB entry for an virtual
192    address that already exists will overwrite the old entry and not result
193    in TLB malfunction or TLB shutdown.  */
194 void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
195 {
196         unsigned long flags;
197         pgd_t *pgdp;
198         pmd_t *pmdp;
199         pte_t *ptep;
200         int idx, pid;
201
202         /*
203          * Handle debugger faulting in for debugee.
204          */
205         if (current->active_mm != vma->vm_mm)
206                 return;
207
208         pid = read_c0_entryhi() & ASID_MASK;
209
210         if ((pid != (cpu_context(smp_processor_id(), vma->vm_mm) & ASID_MASK))
211             || (cpu_context(smp_processor_id(), vma->vm_mm) == 0)) {
212                 printk(KERN_WARNING
213                        "%s: Wheee, bogus tlbpid mmpid=%d tlbpid=%d\n",
214                        __FUNCTION__, (int) (cpu_context(smp_processor_id(),
215                        vma->vm_mm) & ASID_MASK), pid);
216         }
217
218         local_irq_save(flags);
219         address &= (PAGE_MASK << 1);
220         write_c0_entryhi(address | (pid));
221         pgdp = pgd_offset(vma->vm_mm, address);
222         tlb_probe();
223         pmdp = pmd_offset(pgdp, address);
224         idx = read_c0_index();
225         ptep = pte_offset_map(pmdp, address);
226         write_c0_entrylo0(pte_val(*ptep++) >> 6);
227         write_c0_entrylo1(pte_val(*ptep) >> 6);
228         write_c0_entryhi(address | pid);
229         if (idx < 0) {
230                 tlb_write_random();
231         } else {
232                 tlb_write_indexed();
233         }
234         write_c0_entryhi(pid);
235         local_irq_restore(flags);
236 }
237
238 void __init tlb_init(void)
239 {
240         /*
241          * You should never change this register:
242          *   - On R4600 1.7 the tlbp never hits for pages smaller than
243          *     the value in the c0_pagemask register.
244          *   - The entire mm handling assumes the c0_pagemask register to
245          *     be set for 4kb pages.
246          */
247         write_c0_pagemask(PM_4K);
248         write_c0_wired(0);
249         write_c0_framemask(0);
250
251         /* From this point on the ARC firmware is dead.  */
252         local_flush_tlb_all();
253
254         /* Did I tell you that ARC SUCKS?  */
255
256         build_tlb_refill_handler();
257 }