Root/
Source at commit 694c7fbe86b8a9c91392e505afcb9fcfc91deccc created 13 years 12 days ago. By Maarten ter Huurne, MIPS: JZ4740: Add cpufreq support | |
---|---|
1 | /* |
2 | * r2300.c: R2000 and R3000 specific mmu/cache code. |
3 | * |
4 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) |
5 | * |
6 | * with a lot of changes to make this thing work for R3000s |
7 | * Tx39XX R4k style caches added. HK |
8 | * Copyright (C) 1998, 1999, 2000 Harald Koerfgen |
9 | * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov |
10 | * Copyright (C) 2001, 2004, 2007 Maciej W. Rozycki |
11 | */ |
12 | #include <linux/init.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/sched.h> |
15 | #include <linux/smp.h> |
16 | #include <linux/mm.h> |
17 | |
18 | #include <asm/page.h> |
19 | #include <asm/pgtable.h> |
20 | #include <asm/mmu_context.h> |
21 | #include <asm/isadep.h> |
22 | #include <asm/io.h> |
23 | #include <asm/bootinfo.h> |
24 | #include <asm/cpu.h> |
25 | |
26 | static unsigned long icache_size, dcache_size; /* Size in bytes */ |
27 | static unsigned long icache_lsize, dcache_lsize; /* Size in bytes */ |
28 | |
29 | unsigned long r3k_cache_size(unsigned long ca_flags) |
30 | { |
31 | unsigned long flags, status, dummy, size; |
32 | volatile unsigned long *p; |
33 | |
34 | p = (volatile unsigned long *) KSEG0; |
35 | |
36 | flags = read_c0_status(); |
37 | |
38 | /* isolate cache space */ |
39 | write_c0_status((ca_flags|flags)&~ST0_IEC); |
40 | |
41 | *p = 0xa5a55a5a; |
42 | dummy = *p; |
43 | status = read_c0_status(); |
44 | |
45 | if (dummy != 0xa5a55a5a || (status & ST0_CM)) { |
46 | size = 0; |
47 | } else { |
48 | for (size = 128; size <= 0x40000; size <<= 1) |
49 | *(p + size) = 0; |
50 | *p = -1; |
51 | for (size = 128; |
52 | (size <= 0x40000) && (*(p + size) == 0); |
53 | size <<= 1) |
54 | ; |
55 | if (size > 0x40000) |
56 | size = 0; |
57 | } |
58 | |
59 | write_c0_status(flags); |
60 | |
61 | return size * sizeof(*p); |
62 | } |
63 | |
64 | unsigned long r3k_cache_lsize(unsigned long ca_flags) |
65 | { |
66 | unsigned long flags, status, lsize, i; |
67 | volatile unsigned long *p; |
68 | |
69 | p = (volatile unsigned long *) KSEG0; |
70 | |
71 | flags = read_c0_status(); |
72 | |
73 | /* isolate cache space */ |
74 | write_c0_status((ca_flags|flags)&~ST0_IEC); |
75 | |
76 | for (i = 0; i < 128; i++) |
77 | *(p + i) = 0; |
78 | *(volatile unsigned char *)p = 0; |
79 | for (lsize = 1; lsize < 128; lsize <<= 1) { |
80 | *(p + lsize); |
81 | status = read_c0_status(); |
82 | if (!(status & ST0_CM)) |
83 | break; |
84 | } |
85 | for (i = 0; i < 128; i += lsize) |
86 | *(volatile unsigned char *)(p + i) = 0; |
87 | |
88 | write_c0_status(flags); |
89 | |
90 | return lsize * sizeof(*p); |
91 | } |
92 | |
93 | static void r3k_probe_cache(void) |
94 | { |
95 | dcache_size = r3k_cache_size(ST0_ISC); |
96 | if (dcache_size) |
97 | dcache_lsize = r3k_cache_lsize(ST0_ISC); |
98 | |
99 | icache_size = r3k_cache_size(ST0_ISC|ST0_SWC); |
100 | if (icache_size) |
101 | icache_lsize = r3k_cache_lsize(ST0_ISC|ST0_SWC); |
102 | } |
103 | |
104 | static void r3k_flush_icache_range(unsigned long start, unsigned long end) |
105 | { |
106 | unsigned long size, i, flags; |
107 | volatile unsigned char *p; |
108 | |
109 | size = end - start; |
110 | if (size > icache_size || KSEGX(start) != KSEG0) { |
111 | start = KSEG0; |
112 | size = icache_size; |
113 | } |
114 | p = (char *)start; |
115 | |
116 | flags = read_c0_status(); |
117 | |
118 | /* isolate cache space */ |
119 | write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC); |
120 | |
121 | for (i = 0; i < size; i += 0x080) { |
122 | asm( "sb\t$0, 0x000(%0)\n\t" |
123 | "sb\t$0, 0x004(%0)\n\t" |
124 | "sb\t$0, 0x008(%0)\n\t" |
125 | "sb\t$0, 0x00c(%0)\n\t" |
126 | "sb\t$0, 0x010(%0)\n\t" |
127 | "sb\t$0, 0x014(%0)\n\t" |
128 | "sb\t$0, 0x018(%0)\n\t" |
129 | "sb\t$0, 0x01c(%0)\n\t" |
130 | "sb\t$0, 0x020(%0)\n\t" |
131 | "sb\t$0, 0x024(%0)\n\t" |
132 | "sb\t$0, 0x028(%0)\n\t" |
133 | "sb\t$0, 0x02c(%0)\n\t" |
134 | "sb\t$0, 0x030(%0)\n\t" |
135 | "sb\t$0, 0x034(%0)\n\t" |
136 | "sb\t$0, 0x038(%0)\n\t" |
137 | "sb\t$0, 0x03c(%0)\n\t" |
138 | "sb\t$0, 0x040(%0)\n\t" |
139 | "sb\t$0, 0x044(%0)\n\t" |
140 | "sb\t$0, 0x048(%0)\n\t" |
141 | "sb\t$0, 0x04c(%0)\n\t" |
142 | "sb\t$0, 0x050(%0)\n\t" |
143 | "sb\t$0, 0x054(%0)\n\t" |
144 | "sb\t$0, 0x058(%0)\n\t" |
145 | "sb\t$0, 0x05c(%0)\n\t" |
146 | "sb\t$0, 0x060(%0)\n\t" |
147 | "sb\t$0, 0x064(%0)\n\t" |
148 | "sb\t$0, 0x068(%0)\n\t" |
149 | "sb\t$0, 0x06c(%0)\n\t" |
150 | "sb\t$0, 0x070(%0)\n\t" |
151 | "sb\t$0, 0x074(%0)\n\t" |
152 | "sb\t$0, 0x078(%0)\n\t" |
153 | "sb\t$0, 0x07c(%0)\n\t" |
154 | : : "r" (p) ); |
155 | p += 0x080; |
156 | } |
157 | |
158 | write_c0_status(flags); |
159 | } |
160 | |
161 | static void r3k_flush_dcache_range(unsigned long start, unsigned long end) |
162 | { |
163 | unsigned long size, i, flags; |
164 | volatile unsigned char *p; |
165 | |
166 | size = end - start; |
167 | if (size > dcache_size || KSEGX(start) != KSEG0) { |
168 | start = KSEG0; |
169 | size = dcache_size; |
170 | } |
171 | p = (char *)start; |
172 | |
173 | flags = read_c0_status(); |
174 | |
175 | /* isolate cache space */ |
176 | write_c0_status((ST0_ISC|flags)&~ST0_IEC); |
177 | |
178 | for (i = 0; i < size; i += 0x080) { |
179 | asm( "sb\t$0, 0x000(%0)\n\t" |
180 | "sb\t$0, 0x004(%0)\n\t" |
181 | "sb\t$0, 0x008(%0)\n\t" |
182 | "sb\t$0, 0x00c(%0)\n\t" |
183 | "sb\t$0, 0x010(%0)\n\t" |
184 | "sb\t$0, 0x014(%0)\n\t" |
185 | "sb\t$0, 0x018(%0)\n\t" |
186 | "sb\t$0, 0x01c(%0)\n\t" |
187 | "sb\t$0, 0x020(%0)\n\t" |
188 | "sb\t$0, 0x024(%0)\n\t" |
189 | "sb\t$0, 0x028(%0)\n\t" |
190 | "sb\t$0, 0x02c(%0)\n\t" |
191 | "sb\t$0, 0x030(%0)\n\t" |
192 | "sb\t$0, 0x034(%0)\n\t" |
193 | "sb\t$0, 0x038(%0)\n\t" |
194 | "sb\t$0, 0x03c(%0)\n\t" |
195 | "sb\t$0, 0x040(%0)\n\t" |
196 | "sb\t$0, 0x044(%0)\n\t" |
197 | "sb\t$0, 0x048(%0)\n\t" |
198 | "sb\t$0, 0x04c(%0)\n\t" |
199 | "sb\t$0, 0x050(%0)\n\t" |
200 | "sb\t$0, 0x054(%0)\n\t" |
201 | "sb\t$0, 0x058(%0)\n\t" |
202 | "sb\t$0, 0x05c(%0)\n\t" |
203 | "sb\t$0, 0x060(%0)\n\t" |
204 | "sb\t$0, 0x064(%0)\n\t" |
205 | "sb\t$0, 0x068(%0)\n\t" |
206 | "sb\t$0, 0x06c(%0)\n\t" |
207 | "sb\t$0, 0x070(%0)\n\t" |
208 | "sb\t$0, 0x074(%0)\n\t" |
209 | "sb\t$0, 0x078(%0)\n\t" |
210 | "sb\t$0, 0x07c(%0)\n\t" |
211 | : : "r" (p) ); |
212 | p += 0x080; |
213 | } |
214 | |
215 | write_c0_status(flags); |
216 | } |
217 | |
218 | static inline void r3k_flush_cache_all(void) |
219 | { |
220 | } |
221 | |
222 | static inline void r3k___flush_cache_all(void) |
223 | { |
224 | r3k_flush_dcache_range(KSEG0, KSEG0 + dcache_size); |
225 | r3k_flush_icache_range(KSEG0, KSEG0 + icache_size); |
226 | } |
227 | |
228 | static void r3k_flush_cache_mm(struct mm_struct *mm) |
229 | { |
230 | } |
231 | |
232 | static void r3k_flush_cache_range(struct vm_area_struct *vma, |
233 | unsigned long start, unsigned long end) |
234 | { |
235 | } |
236 | |
237 | static void r3k_flush_cache_page(struct vm_area_struct *vma, |
238 | unsigned long addr, unsigned long pfn) |
239 | { |
240 | unsigned long kaddr = KSEG0ADDR(pfn << PAGE_SHIFT); |
241 | int exec = vma->vm_flags & VM_EXEC; |
242 | struct mm_struct *mm = vma->vm_mm; |
243 | pgd_t *pgdp; |
244 | pud_t *pudp; |
245 | pmd_t *pmdp; |
246 | pte_t *ptep; |
247 | |
248 | pr_debug("cpage[%08lx,%08lx]\n", |
249 | cpu_context(smp_processor_id(), mm), addr); |
250 | |
251 | /* No ASID => no such page in the cache. */ |
252 | if (cpu_context(smp_processor_id(), mm) == 0) |
253 | return; |
254 | |
255 | pgdp = pgd_offset(mm, addr); |
256 | pudp = pud_offset(pgdp, addr); |
257 | pmdp = pmd_offset(pudp, addr); |
258 | ptep = pte_offset(pmdp, addr); |
259 | |
260 | /* Invalid => no such page in the cache. */ |
261 | if (!(pte_val(*ptep) & _PAGE_PRESENT)) |
262 | return; |
263 | |
264 | r3k_flush_dcache_range(kaddr, kaddr + PAGE_SIZE); |
265 | if (exec) |
266 | r3k_flush_icache_range(kaddr, kaddr + PAGE_SIZE); |
267 | } |
268 | |
269 | static void local_r3k_flush_data_cache_page(void *addr) |
270 | { |
271 | } |
272 | |
273 | static void r3k_flush_data_cache_page(unsigned long addr) |
274 | { |
275 | } |
276 | |
277 | static void r3k_flush_cache_sigtramp(unsigned long addr) |
278 | { |
279 | unsigned long flags; |
280 | |
281 | pr_debug("csigtramp[%08lx]\n", addr); |
282 | |
283 | flags = read_c0_status(); |
284 | |
285 | write_c0_status(flags&~ST0_IEC); |
286 | |
287 | /* Fill the TLB to avoid an exception with caches isolated. */ |
288 | asm( "lw\t$0, 0x000(%0)\n\t" |
289 | "lw\t$0, 0x004(%0)\n\t" |
290 | : : "r" (addr) ); |
291 | |
292 | write_c0_status((ST0_ISC|ST0_SWC|flags)&~ST0_IEC); |
293 | |
294 | asm( "sb\t$0, 0x000(%0)\n\t" |
295 | "sb\t$0, 0x004(%0)\n\t" |
296 | : : "r" (addr) ); |
297 | |
298 | write_c0_status(flags); |
299 | } |
300 | |
301 | static void r3k_flush_kernel_vmap_range(unsigned long vaddr, int size) |
302 | { |
303 | BUG(); |
304 | } |
305 | |
306 | static void r3k_dma_cache_wback_inv(unsigned long start, unsigned long size) |
307 | { |
308 | /* Catch bad driver code */ |
309 | BUG_ON(size == 0); |
310 | |
311 | iob(); |
312 | r3k_flush_dcache_range(start, start + size); |
313 | } |
314 | |
315 | void r3k_cache_init(void) |
316 | { |
317 | extern void build_clear_page(void); |
318 | extern void build_copy_page(void); |
319 | |
320 | r3k_probe_cache(); |
321 | |
322 | flush_cache_all = r3k_flush_cache_all; |
323 | __flush_cache_all = r3k___flush_cache_all; |
324 | flush_cache_mm = r3k_flush_cache_mm; |
325 | flush_cache_range = r3k_flush_cache_range; |
326 | flush_cache_page = r3k_flush_cache_page; |
327 | flush_icache_range = r3k_flush_icache_range; |
328 | local_flush_icache_range = r3k_flush_icache_range; |
329 | |
330 | __flush_kernel_vmap_range = r3k_flush_kernel_vmap_range; |
331 | |
332 | flush_cache_sigtramp = r3k_flush_cache_sigtramp; |
333 | local_flush_data_cache_page = local_r3k_flush_data_cache_page; |
334 | flush_data_cache_page = r3k_flush_data_cache_page; |
335 | |
336 | _dma_cache_wback_inv = r3k_dma_cache_wback_inv; |
337 | _dma_cache_wback = r3k_dma_cache_wback_inv; |
338 | _dma_cache_inv = r3k_dma_cache_wback_inv; |
339 | |
340 | printk("Primary instruction cache %ldkB, linesize %ld bytes.\n", |
341 | icache_size >> 10, icache_lsize); |
342 | printk("Primary data cache %ldkB, linesize %ld bytes.\n", |
343 | dcache_size >> 10, dcache_lsize); |
344 | |
345 | build_clear_page(); |
346 | build_copy_page(); |
347 | } |
348 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9