3 * Copyright (c) 1998-1999 TiVo, Inc.
4 * Original implementation.
5 * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
8 * Module name: 4xx_tlb.c
11 * Routines for manipulating the TLB on PowerPC 400-class processors.
17 #include <asm/processor.h>
20 #include <asm/pgtable.h>
21 #include <asm/system.h>
24 /* Preprocessor Defines */
26 #if !defined(TRUE) || TRUE != 1
30 #if !defined(FALSE) || FALSE != 0
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);
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
));
87 PPC4xx_flush_tlb_all(void)
90 unsigned long flags
, pid
;
95 pid
= mfspr(SPRN_PID
);
98 for (i
= pinned
; i
< PPC4XX_TLB_SIZE
; i
++) {
99 PPC4xx_tlb_write(0, 0, i
);
103 mtspr(SPRN_PID
, pid
);
104 restore_flags(flags
);
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) {
131 do_page_fault(regs
, addr
, write
);
154 PPC4xx_itlb_miss(struct pt_regs
*regs
)
156 unsigned long addr
= regs
->nip
;
158 if (PPC4xx_tlb_miss(regs
, addr
, 0) < 0) {
160 do_page_fault(regs
, addr
, 0);
182 PPC4xx_tlb_pin(unsigned long va
, unsigned long pa
, int pagesz
, int cache
)
184 unsigned long tag
, data
;
187 if (pinned
>= PPC4XX_TLB_SIZE
)
190 opid
= mfspr(SPRN_PID
);
193 data
= (pa
& TLB_RPN_MASK
) | TLB_WR
;
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
);
225 PPC4xx_tlb_unpin(unsigned long va
, unsigned long pa
, int size
)
227 /* XXX - To be implemented. */
247 PPC4xx_tlb_update(unsigned long addr
, pte_t
*pte
)
249 unsigned long data
, tag
, rand
;
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
;
258 if (pte_val(*pte
) & _PAGE_HWWRITE
)
262 if (pte_val(*pte
) & _PAGE_NO_CACHE
)
265 if (pte_val(*pte
) & _PAGE_GUARDED
)
268 if (addr
< KERNELBASE
)
271 /* Attempt to match the new tag to an existing entry in the TLB. */
273 asm("tlbsx. %0,0,%2;"
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.
285 PPC4xx_tlb_write(tag
, data
, i
);
287 rand
= mfspr(SPRN_TBLO
) & (PPC4XX_TLB_SIZE
- 1);
289 if (rand
>= PPC4XX_TLB_SIZE
)
292 PPC4xx_tlb_write(tag
, data
, rand
);
314 PPC4xx_tlb_miss(struct pt_regs
*regs
, unsigned long addr
, int write
)
316 unsigned long spid
, ospid
;
317 struct mm_struct
*mm
;
322 if (!user_mode(regs
) && (addr
>= KERNELBASE
)) {
327 spid
= mfspr(SPRN_PID
);
330 pgd
= pgd_offset(mm
, addr
);
334 pmd
= pmd_offset(pgd
, addr
);
338 pte
= pte_offset(pmd
, addr
);
339 if (pte_none(*pte
) || !pte_present(*pte
))
343 if (!pte_write(*pte
))
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
);