More Makefile cleanups, otherwise mainly noticeable are the netfilter fix
[davej-history.git] / arch / ppc / mm / 4xx_tlb.c
blob69bf88320d03401d6b8ae87a4055bcea88b80840
1 /*
3 * Copyright (c) 1998-1999 TiVo, Inc.
4 * Original implementation.
5 * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
6 * Minor rework.
8 * Module name: 4xx_tlb.c
10 * Description:
11 * Routines for manipulating the TLB on PowerPC 400-class processors.
15 #include <linux/mm.h>
17 #include <asm/processor.h>
18 #include <asm/io.h>
19 #include <asm/mmu.h>
20 #include <asm/pgtable.h>
21 #include <asm/system.h>
24 /* Preprocessor Defines */
26 #if !defined(TRUE) || TRUE != 1
27 #define TRUE 1
28 #endif
30 #if !defined(FALSE) || FALSE != 0
31 #define FALSE 0
32 #endif
35 /* Global Variables */
37 static int pinned = 0;
40 /* Function Prototypes */
42 static int PPC4xx_tlb_miss(struct pt_regs *, unsigned long, int);
44 extern void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
48 * ()
50 * Description:
51 * This routine...
53 * Input(s):
56 * Output(s):
59 * Returns:
63 static inline void
64 PPC4xx_tlb_write(unsigned long tag, unsigned long data, unsigned int index)
66 asm("tlbwe %0,%1,1" : : "r" (data), "r" (index));
67 asm("tlbwe %0,%1,0" : : "r" (tag), "r" (index));
71 * ()
73 * Description:
74 * This routine...
76 * Input(s):
79 * Output(s):
82 * Returns:
86 void
87 PPC4xx_flush_tlb_all(void)
89 int i;
90 unsigned long flags, pid;
92 save_flags(flags);
93 cli();
95 pid = mfspr(SPRN_PID);
96 mtspr(SPRN_PID, 0);
98 for (i = pinned; i < PPC4XX_TLB_SIZE; i++) {
99 PPC4xx_tlb_write(0, 0, i);
101 asm("sync;isync");
103 mtspr(SPRN_PID, pid);
104 restore_flags(flags);
108 * ()
110 * Description:
111 * This routine...
113 * Input(s):
116 * Output(s):
119 * Returns:
123 void
124 PPC4xx_dtlb_miss(struct pt_regs *regs)
126 unsigned long addr = mfspr(SPRN_DEAR);
127 int write = mfspr(SPRN_ESR) & ESR_DST;
129 if (PPC4xx_tlb_miss(regs, addr, write) < 0) {
130 sti();
131 do_page_fault(regs, addr, write);
132 cli();
138 * ()
140 * Description:
141 * This routine...
143 * Input(s):
146 * Output(s):
149 * Returns:
153 void
154 PPC4xx_itlb_miss(struct pt_regs *regs)
156 unsigned long addr = regs->nip;
158 if (PPC4xx_tlb_miss(regs, addr, 0) < 0) {
159 sti();
160 do_page_fault(regs, addr, 0);
161 cli();
166 * ()
168 * Description:
169 * This routine...
171 * Input(s):
174 * Output(s):
177 * Returns:
181 void
182 PPC4xx_tlb_pin(unsigned long va, unsigned long pa, int pagesz, int cache)
184 unsigned long tag, data;
185 unsigned long opid;
187 if (pinned >= PPC4XX_TLB_SIZE)
188 return;
190 opid = mfspr(SPRN_PID);
191 mtspr(SPRN_PID, 0);
193 data = (pa & TLB_RPN_MASK) | TLB_WR;
195 if (cache)
196 data |= (TLB_EX);
197 else
198 data |= (TLB_G | TLB_I);
200 tag = (va & TLB_EPN_MASK) | TLB_VALID | pagesz;
202 PPC4xx_tlb_write(tag, data, pinned++);
204 mtspr(SPRN_PID, opid);
205 return;
209 * ()
211 * Description:
212 * This routine...
214 * Input(s):
217 * Output(s):
220 * Returns:
224 void
225 PPC4xx_tlb_unpin(unsigned long va, unsigned long pa, int size)
227 /* XXX - To be implemented. */
231 * ()
233 * Description:
234 * This routine...
236 * Input(s):
239 * Output(s):
242 * Returns:
246 static inline void
247 PPC4xx_tlb_update(unsigned long addr, pte_t *pte)
249 unsigned long data, tag, rand;
250 int i, found = 1;
252 /* Construct the hardware TLB entry from the Linux-style PTE */
254 tag = tag = (addr & PAGE_MASK) | TLB_VALID | TLB_PAGESZ(PAGESZ_4K);
255 data = data = (pte_val(*pte) & PAGE_MASK) | TLB_EX | TLB_WR;
257 #if 0
258 if (pte_val(*pte) & _PAGE_HWWRITE)
259 data |= TLB_WR;
260 #endif
262 if (pte_val(*pte) & _PAGE_NO_CACHE)
263 data |= TLB_I;
265 if (pte_val(*pte) & _PAGE_GUARDED)
266 data |= TLB_G;
268 if (addr < KERNELBASE)
269 data |= TLB_ZSEL(1);
271 /* Attempt to match the new tag to an existing entry in the TLB. */
273 asm("tlbsx. %0,0,%2;"
274 "beq 1f;"
275 "li %1,0;1:" : "=r" (i), "=r" (found) : "r" (tag));
278 * If we found a match for the tag, reuse the entry index and update
279 * the tag and data portions. Otherwise, we did not find a match. Use
280 * the lower 5 bits of the lower time base register as a pseudo-random
281 * index into the TLB and replace the entry at that index.
284 if (found) {
285 PPC4xx_tlb_write(tag, data, i);
286 } else {
287 rand = mfspr(SPRN_TBLO) & (PPC4XX_TLB_SIZE - 1);
288 rand += pinned;
289 if (rand >= PPC4XX_TLB_SIZE)
290 rand -= pinned;
292 PPC4xx_tlb_write(tag, data, rand);
293 asm("isync;sync");
298 * ()
300 * Description:
301 * This routine...
303 * Input(s):
306 * Output(s):
309 * Returns:
313 static int
314 PPC4xx_tlb_miss(struct pt_regs *regs, unsigned long addr, int write)
316 unsigned long spid, ospid;
317 struct mm_struct *mm;
318 pgd_t *pgd;
319 pmd_t *pmd;
320 pte_t *pte;
322 if (!user_mode(regs) && (addr >= KERNELBASE)) {
323 mm = &init_mm;
324 spid = 0;
325 } else {
326 mm = current->mm;
327 spid = mfspr(SPRN_PID);
330 pgd = pgd_offset(mm, addr);
331 if (pgd_none(*pgd))
332 goto bad;
334 pmd = pmd_offset(pgd, addr);
335 if (pmd_none(*pmd))
336 goto bad;
338 pte = pte_offset(pmd, addr);
339 if (pte_none(*pte) || !pte_present(*pte))
340 goto bad;
342 if (write) {
343 if (!pte_write(*pte))
344 goto bad;
346 set_pte(pte, pte_mkdirty(*pte));
348 set_pte(pte, pte_mkyoung(*pte));
350 ospid = mfspr(SPRN_PID);
351 mtspr(SPRN_PID, spid);
352 PPC4xx_tlb_update(addr, pte);
353 mtspr(SPRN_PID, ospid);
355 return (0);
356 bad:
357 return (-1);