File: /usr/src/linux/arch/ppc/mm/4xx_tlb.c
1 /*
2 * BK Id: SCCS/s.4xx_tlb.c 1.5 05/17/01 18:14:23 cort
3 */
4 /*
5 *
6 * Copyright (c) 1998-1999 TiVo, Inc.
7 * Original implementation.
8 * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
9 * Minor rework.
10 *
11 * Module name: 4xx_tlb.c
12 *
13 * Description:
14 * Routines for manipulating the TLB on PowerPC 400-class processors.
15 *
16 */
17
18 #include <linux/mm.h>
19
20 #include <asm/processor.h>
21 #include <asm/io.h>
22 #include <asm/mmu.h>
23 #include <asm/pgtable.h>
24 #include <asm/system.h>
25
26
27 /* Preprocessor Defines */
28
29 #if !defined(TRUE) || TRUE != 1
30 #define TRUE 1
31 #endif
32
33 #if !defined(FALSE) || FALSE != 0
34 #define FALSE 0
35 #endif
36
37
38 /* Global Variables */
39
40 static int pinned = 0;
41
42
43 /* Function Prototypes */
44
45 static int PPC4xx_tlb_miss(struct pt_regs *, unsigned long, int);
46
47 extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
48
49
50 /*
51 * ()
52 *
53 * Description:
54 * This routine...
55 *
56 * Input(s):
57 *
58 *
59 * Output(s):
60 *
61 *
62 * Returns:
63 *
64 *
65 */
66 static inline void
67 PPC4xx_tlb_write(unsigned long tag, unsigned long data, unsigned int index)
68 {
69 asm("tlbwe %0,%1,1" : : "r" (data), "r" (index));
70 asm("tlbwe %0,%1,0" : : "r" (tag), "r" (index));
71 }
72
73 /*
74 * ()
75 *
76 * Description:
77 * This routine...
78 *
79 * Input(s):
80 *
81 *
82 * Output(s):
83 *
84 *
85 * Returns:
86 *
87 *
88 */
89 void
90 PPC4xx_flush_tlb_all(void)
91 {
92 int i;
93 unsigned long flags, pid;
94
95 save_flags(flags);
96 cli();
97
98 pid = mfspr(SPRN_PID);
99 mtspr(SPRN_PID, 0);
100
101 for (i = pinned; i < PPC4XX_TLB_SIZE; i++) {
102 PPC4xx_tlb_write(0, 0, i);
103 }
104 asm("sync;isync");
105
106 mtspr(SPRN_PID, pid);
107 restore_flags(flags);
108 }
109
110 /*
111 * ()
112 *
113 * Description:
114 * This routine...
115 *
116 * Input(s):
117 *
118 *
119 * Output(s):
120 *
121 *
122 * Returns:
123 *
124 *
125 */
126 void
127 PPC4xx_dtlb_miss(struct pt_regs *regs)
128 {
129 unsigned long addr = mfspr(SPRN_DEAR);
130 int write = mfspr(SPRN_ESR) & ESR_DST;
131
132 if (PPC4xx_tlb_miss(regs, addr, write) < 0) {
133 sti();
134 do_page_fault(regs, addr, write);
135 cli();
136 }
137
138 }
139
140 /*
141 * ()
142 *
143 * Description:
144 * This routine...
145 *
146 * Input(s):
147 *
148 *
149 * Output(s):
150 *
151 *
152 * Returns:
153 *
154 *
155 */
156 void
157 PPC4xx_itlb_miss(struct pt_regs *regs)
158 {
159 unsigned long addr = regs->nip;
160
161 if (PPC4xx_tlb_miss(regs, addr, 0) < 0) {
162 sti();
163 do_page_fault(regs, addr, 0);
164 cli();
165 }
166 }
167
168 /*
169 * ()
170 *
171 * Description:
172 * This routine...
173 *
174 * Input(s):
175 *
176 *
177 * Output(s):
178 *
179 *
180 * Returns:
181 *
182 *
183 */
184 void
185 PPC4xx_tlb_pin(unsigned long va, unsigned long pa, int pagesz, int cache)
186 {
187 unsigned long tag, data;
188 unsigned long opid;
189
190 if (pinned >= PPC4XX_TLB_SIZE)
191 return;
192
193 opid = mfspr(SPRN_PID);
194 mtspr(SPRN_PID, 0);
195
196 data = (pa & TLB_RPN_MASK) | TLB_WR;
197
198 if (cache)
199 data |= (TLB_EX);
200 else
201 data |= (TLB_G | TLB_I);
202
203 tag = (va & TLB_EPN_MASK) | TLB_VALID | pagesz;
204
205 PPC4xx_tlb_write(tag, data, pinned++);
206
207 mtspr(SPRN_PID, opid);
208 return;
209 }
210
211 /*
212 * ()
213 *
214 * Description:
215 * This routine...
216 *
217 * Input(s):
218 *
219 *
220 * Output(s):
221 *
222 *
223 * Returns:
224 *
225 *
226 */
227 void
228 PPC4xx_tlb_unpin(unsigned long va, unsigned long pa, int size)
229 {
230 /* XXX - To be implemented. */
231 }
232
233 /*
234 * ()
235 *
236 * Description:
237 * This routine...
238 *
239 * Input(s):
240 *
241 *
242 * Output(s):
243 *
244 *
245 * Returns:
246 *
247 *
248 */
249 static inline void
250 PPC4xx_tlb_update(unsigned long addr, pte_t *pte)
251 {
252 unsigned long data, tag, rand;
253 int i, found = 1;
254
255 /* Construct the hardware TLB entry from the Linux-style PTE */
256
257 tag = tag = (addr & PAGE_MASK) | TLB_VALID | TLB_PAGESZ(PAGESZ_4K);
258 data = data = (pte_val(*pte) & PAGE_MASK) | TLB_EX | TLB_WR;
259
260 #if 0
261 if (pte_val(*pte) & _PAGE_HWWRITE)
262 data |= TLB_WR;
263 #endif
264
265 if (pte_val(*pte) & _PAGE_NO_CACHE)
266 data |= TLB_I;
267
268 if (pte_val(*pte) & _PAGE_GUARDED)
269 data |= TLB_G;
270
271 if (addr < KERNELBASE)
272 data |= TLB_ZSEL(1);
273
274 /* Attempt to match the new tag to an existing entry in the TLB. */
275
276 asm("tlbsx. %0,0,%2;"
277 "beq 1f;"
278 "li %1,0;1:" : "=r" (i), "=r" (found) : "r" (tag));
279
280 /*
281 * If we found a match for the tag, reuse the entry index and update
282 * the tag and data portions. Otherwise, we did not find a match. Use
283 * the lower 5 bits of the lower time base register as a pseudo-random
284 * index into the TLB and replace the entry at that index.
285 */
286
287 if (found) {
288 PPC4xx_tlb_write(tag, data, i);
289 } else {
290 rand = mfspr(SPRN_TBLO) & (PPC4XX_TLB_SIZE - 1);
291 rand += pinned;
292 if (rand >= PPC4XX_TLB_SIZE)
293 rand -= pinned;
294
295 PPC4xx_tlb_write(tag, data, rand);
296 asm("isync;sync");
297 }
298 }
299
300 /*
301 * ()
302 *
303 * Description:
304 * This routine...
305 *
306 * Input(s):
307 *
308 *
309 * Output(s):
310 *
311 *
312 * Returns:
313 *
314 *
315 */
316 static int
317 PPC4xx_tlb_miss(struct pt_regs *regs, unsigned long addr, int write)
318 {
319 unsigned long spid, ospid;
320 struct mm_struct *mm;
321 pgd_t *pgd;
322 pmd_t *pmd;
323 pte_t *pte;
324
325 if (!user_mode(regs) && (addr >= KERNELBASE)) {
326 mm = &init_mm;
327 spid = 0;
328 } else {
329 mm = current->mm;
330 spid = mfspr(SPRN_PID);
331 }
332
333 pgd = pgd_offset(mm, addr);
334 if (pgd_none(*pgd))
335 goto bad;
336
337 pmd = pmd_offset(pgd, addr);
338 if (pmd_none(*pmd))
339 goto bad;
340
341 pte = pte_offset(pmd, addr);
342 if (pte_none(*pte) || !pte_present(*pte))
343 goto bad;
344
345 if (write) {
346 if (!pte_write(*pte))
347 goto bad;
348
349 set_pte(pte, pte_mkdirty(*pte));
350 }
351 set_pte(pte, pte_mkyoung(*pte));
352
353 ospid = mfspr(SPRN_PID);
354 mtspr(SPRN_PID, spid);
355 PPC4xx_tlb_update(addr, pte);
356 mtspr(SPRN_PID, ospid);
357
358 return (0);
359 bad:
360 return (-1);
361 }
362