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