Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-2.6] / arch / m32r / mm / mmu.S
1 /*
2  *  linux/arch/m32r/mm/mmu.S
3  *
4  *  Copyright (C) 2001 by Hiroyuki Kondo
5  */
6
7 #include <linux/linkage.h>
8 #include <asm/assembler.h>
9 #include <asm/smp.h>
10
11         .text
12 #ifdef CONFIG_MMU
13
14 #include <asm/mmu_context.h>
15 #include <asm/page.h>
16 #include <asm/pgtable.h>
17 #include <asm/m32r.h>
18
19 /*
20  * TLB Miss Exception handler
21  */
22         .balign 16
23 ENTRY(tme_handler)
24         .global tlb_entry_i_dat
25         .global tlb_entry_d_dat
26
27         SWITCH_TO_KERNEL_STACK
28
29 #if defined(CONFIG_ISA_M32R2)
30         st      r0, @-sp
31         st      r1, @-sp
32         st      r2, @-sp
33         st      r3, @-sp
34
35         seth    r3, #high(MMU_REG_BASE)
36         ld      r1, @(MESTS_offset, r3) ; r1: status     (MESTS reg.)
37         ld      r0, @(MDEVP_offset, r3) ; r0: PFN + ASID (MDEVP reg.)
38         st      r1, @(MESTS_offset, r3) ; clear status   (MESTS reg.)
39         and3    r1, r1, #(MESTS_IT)
40         bnez    r1, 1f                  ; instruction TLB miss?
41
42 ;; data TLB miss
43 ;;  input
44 ;;   r0: PFN + ASID (MDEVP reg.)
45 ;;   r1 - r3: free
46 ;;  output
47 ;;   r0: PFN + ASID
48 ;;   r1: TLB entry base address
49 ;;   r2: &tlb_entry_{i|d}_dat
50 ;;   r3: free
51
52 #ifndef CONFIG_SMP
53         seth    r2, #high(tlb_entry_d_dat)
54         or3     r2, r2, #low(tlb_entry_d_dat)
55 #else   /* CONFIG_SMP */
56         ldi     r1, #-8192
57         seth    r2, #high(tlb_entry_d_dat)
58         or3     r2, r2, #low(tlb_entry_d_dat)
59         and     r1, sp
60         ld      r1, @(16, r1)           ; current_thread_info->cpu
61         slli    r1, #2
62         add     r2, r1
63 #endif  /* !CONFIG_SMP */
64         seth    r1, #high(DTLB_BASE)
65         or3     r1, r1, #low(DTLB_BASE)
66         bra     2f
67
68         .balign 16
69         .fillinsn
70 1:
71 ;; instrucntion TLB miss
72 ;;  input
73 ;;   r0: MDEVP reg. (included ASID)
74 ;;   r1 - r3: free
75 ;;  output
76 ;;   r0: PFN + ASID
77 ;;   r1: TLB entry base address
78 ;;   r2: &tlb_entry_{i|d}_dat
79 ;;   r3: free
80         ldi     r3, #-4096
81         and3    r0, r0, #(MMU_CONTEXT_ASID_MASK)
82         mvfc    r1, bpc
83         and     r1, r3
84         or      r0, r1                  ; r0: PFN + ASID
85 #ifndef CONFIG_SMP
86         seth    r2, #high(tlb_entry_i_dat)
87         or3     r2, r2, #low(tlb_entry_i_dat)
88 #else   /* CONFIG_SMP */
89         ldi     r1, #-8192
90         seth    r2, #high(tlb_entry_i_dat)
91         or3     r2, r2, #low(tlb_entry_i_dat)
92         and     r1, sp
93         ld      r1, @(16, r1)           ; current_thread_info->cpu
94         slli    r1, #2
95         add     r2, r1
96 #endif  /* !CONFIG_SMP */
97         seth    r1, #high(ITLB_BASE)
98         or3     r1, r1, #low(ITLB_BASE)
99
100         .fillinsn
101 2:
102 ;; select TLB entry
103 ;;  input
104 ;;   r0: PFN + ASID
105 ;;   r1: TLB entry base address
106 ;;   r2: &tlb_entry_{i|d}_dat
107 ;;   r3: free
108 ;;  output
109 ;;   r0: PFN + ASID
110 ;;   r1: TLB entry address
111 ;;   r2, r3: free
112 #ifdef CONFIG_ISA_DUAL_ISSUE
113         ld      r3, @r2         ||      srli    r1, #3
114 #else
115         ld      r3, @r2
116         srli    r1, #3
117 #endif
118         add     r1, r3
119         ; tlb_entry_{d|i}_dat++;
120         addi    r3, #1
121         and3    r3, r3, #(NR_TLB_ENTRIES - 1)
122 #ifdef CONFIG_ISA_DUAL_ISSUE
123         st      r3, @r2         ||      slli    r1, #3
124 #else
125         st      r3, @r2
126         slli    r1, #3
127 #endif
128
129 ;; load pte
130 ;;  input
131 ;;   r0: PFN + ASID
132 ;;   r1: TLB entry address
133 ;;   r2, r3: free
134 ;;  output
135 ;;   r0: PFN + ASID
136 ;;   r1: TLB entry address
137 ;;   r2: pte_data
138 ;;   r3: free
139         ; pgd = *(unsigned long *)MPTB;
140         ld24    r2, #(-MPTB - 1)
141         srl3    r3, r0, #22
142 #ifdef CONFIG_ISA_DUAL_ISSUE
143         not     r2, r2              ||  slli    r3, #2  ; r3: pgd offset
144 #else
145         not     r2, r2
146         slli    r3, #2
147 #endif
148         ld      r2, @r2                 ; r2: pgd base addr (MPTB reg.)
149         or      r3, r2                  ; r3: pmd addr
150
151         ; pmd = pmd_offset(pgd, address);
152         ld      r3, @r3                 ; r3: pmd data
153         ldi     r2, #-4096
154         beqz    r3, 3f                  ; pmd_none(*pmd) ?
155
156         ; pte = pte_offset(pmd, address);
157         and     r2, r3                  ; r2: pte base addr
158         srl3    r3, r0, #10
159         and3    r3, r3, #0xffc          ; r3: pte offset
160         or      r3, r2
161         seth    r2, #0x8000
162         or      r3, r2                  ; r3: pte addr
163
164         ; pte_data = (unsigned long)pte_val(*pte);
165         ld      r2, @r3                 ; r2: pte data
166         and3    r3, r2, #2              ; _PAGE_PRESENT(=2) check
167         beqz    r3, 3f
168
169         .fillinsn
170 5:
171 ;; set tlb
172 ;;  input
173 ;;   r0: PFN + ASID
174 ;;   r1: TLB entry address
175 ;;   r2: pte_data
176 ;;   r3: free
177         st      r0, @r1                 ; set_tlb_tag(entry++, address);
178         st      r2, @+r1                ; set_tlb_data(entry, pte_data);
179
180         .fillinsn
181 6:
182         ld      r3, @sp+
183         ld      r2, @sp+
184         ld      r1, @sp+
185         ld      r0, @sp+
186         rte
187
188         .fillinsn
189 3:
190 ;; error
191 ;;  input
192 ;;   r0: PFN + ASID
193 ;;   r1: TLB entry address
194 ;;   r2, r3: free
195 ;;  output
196 ;;   r0: PFN + ASID
197 ;;   r1: TLB entry address
198 ;;   r2: pte_data
199 ;;   r3: free
200 #ifdef CONFIG_ISA_DUAL_ISSUE
201         bra     5b                  ||  ldi     r2, #2
202 #else
203         ldi     r2, #2          ; r2: pte_data = 0 | _PAGE_PRESENT(=2)
204         bra     5b
205 #endif
206
207 #elif defined (CONFIG_ISA_M32R)
208
209         st      sp, @-sp
210         st      r0, @-sp
211         st      r1, @-sp
212         st      r2, @-sp
213         st      r3, @-sp
214         st      r4, @-sp
215
216         seth    r3, #high(MMU_REG_BASE)
217         ld      r0, @(MDEVA_offset,r3)  ; r0: address  (MDEVA reg.)
218         mvfc    r2, bpc                 ; r2: bpc
219         ld      r1, @(MESTS_offset,r3)  ; r1: status   (MESTS reg.)
220         st      r1, @(MESTS_offset,r3)  ; clear status (MESTS reg.)
221         and3    r1, r1, #(MESTS_IT)
222         beqz    r1, 1f                  ; data TLB miss?
223
224 ;; instrucntion TLB miss
225         mv      r0, r2                  ; address = bpc;
226         ; entry = (unsigned long *)ITLB_BASE+tlb_entry_i*2;
227         seth    r3, #shigh(tlb_entry_i_dat)
228         ld      r4, @(low(tlb_entry_i_dat),r3)
229         sll3    r2, r4, #3
230         seth    r1, #high(ITLB_BASE)
231         or3     r1, r1, #low(ITLB_BASE)
232         add     r2, r1                  ; r2: entry
233         addi    r4, #1                  ; tlb_entry_i++;
234         and3    r4, r4, #(NR_TLB_ENTRIES-1)
235         st      r4, @(low(tlb_entry_i_dat),r3)
236         bra     2f
237         .fillinsn
238 1:
239 ;; data TLB miss
240         ; entry = (unsigned long *)DTLB_BASE+tlb_entry_d*2;
241         seth    r3, #shigh(tlb_entry_d_dat)
242         ld      r4, @(low(tlb_entry_d_dat),r3)
243         sll3    r2, r4, #3
244         seth    r1, #high(DTLB_BASE)
245         or3     r1, r1, #low(DTLB_BASE)
246         add     r2, r1                  ; r2: entry
247         addi    r4, #1                  ; tlb_entry_d++;
248         and3    r4, r4, #(NR_TLB_ENTRIES-1)
249         st      r4, @(low(tlb_entry_d_dat),r3)
250         .fillinsn
251 2:
252 ;; load pte
253 ; r0: address, r2: entry
254 ; r1,r3,r4: (free)
255         ; pgd = *(unsigned long *)MPTB;
256         ld24    r1, #(-MPTB-1)
257         not     r1, r1
258         ld      r1, @r1
259         srl3    r4, r0, #22
260         sll3    r3, r4, #2
261         add     r3, r1                  ; r3: pgd
262         ; pmd = pmd_offset(pgd, address);
263         ld      r1, @r3                 ; r1: pmd
264         beqz    r1, 3f                  ; pmd_none(*pmd) ?
265 ;
266         and3    r1, r1, #0xeff
267         ldi     r4, #611                ; _KERNPG_TABLE(=611)
268         bne     r1, r4, 3f              ; !pmd_bad(*pmd) ?
269
270         .fillinsn
271 4:
272         ; pte = pte_offset(pmd, address);
273         ld      r4, @r3                 ; r4: pte
274         ldi     r3, #-4096
275         and     r4, r3
276         srl3    r3, r0, #10
277         and3    r3, r3, #0xffc
278         add     r4, r3
279         seth    r3, #0x8000
280         add     r4, r3                  ; r4: pte
281         ; pte_data = (unsigned long)pte_val(*pte);
282         ld      r1, @r4                 ; r1: pte_data
283         and3    r3, r1, #2              ; _PAGE_PRESENT(=2) check
284         beqz    r3, 3f
285
286         .fillinsn
287 ;; set tlb
288 ; r0: address, r1: pte_data, r2: entry
289 ; r3,r4: (free)
290 5:
291         ldi     r3, #-4096              ; set_tlb_tag(entry++, address);
292         and     r3, r0
293         seth    r4, #shigh(MASID)
294         ld      r4, @(low(MASID),r4)    ; r4: MASID
295         and3    r4, r4, #(MMU_CONTEXT_ASID_MASK)
296         or      r3, r4
297         st      r3, @r2
298         st      r1, @(4,r2)             ; set_tlb_data(entry, pte_data);
299
300         ld      r4, @sp+
301         ld      r3, @sp+
302         ld      r2, @sp+
303         ld      r1, @sp+
304         ld      r0, @sp+
305         ld      sp, @sp+
306         rte
307
308         .fillinsn
309 3:
310         ldi     r1, #2                  ; r1: pte_data = 0 | _PAGE_PRESENT(=2)
311         bra     5b
312
313 #else
314 #error unknown isa configuration
315 #endif
316
317 ENTRY(init_tlb)
318 ;; Set MMU Register
319         seth    r0, #high(MMU_REG_BASE)  ; Set MMU_REG_BASE higher
320         or3     r0, r0, #low(MMU_REG_BASE)  ; Set MMU_REG_BASE lower
321         ldi     r1, #0
322         st      r1, @(MPSZ_offset,r0)   ; Set MPSZ Reg(Page size 4KB:0 16KB:1 64KB:2)
323         ldi     r1, #0
324         st      r1, @(MASID_offset,r0)  ; Set ASID Zero
325
326 ;; Set TLB
327         seth    r0, #high(ITLB_BASE)    ; Set ITLB_BASE higher
328         or3     r0, r0, #low(ITLB_BASE) ; Set ITLB_BASE lower
329         seth    r1, #high(DTLB_BASE)    ; Set DTLB_BASE higher
330         or3     r1, r1, #low(DTLB_BASE) ; Set DTLB_BASE lower
331         ldi     r2, #0
332         ldi     r3, #NR_TLB_ENTRIES
333         addi    r0, #-4
334         addi    r1, #-4
335 clear_tlb:
336         st      r2, @+r0                ; VPA <- 0
337         st      r2, @+r0                ; PPA <- 0
338         st      r2, @+r1                ; VPA <- 0
339         st      r2, @+r1                ; PPA <- 0
340         addi    r3, #-1
341         bnez    r3, clear_tlb
342 ;;
343         jmp     r14
344
345 ENTRY(m32r_itlb_entrys)
346 ENTRY(m32r_otlb_entrys)
347
348 #endif  /* CONFIG_MMU */
349
350         .end