Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/sh/mm/cache-sh4.c | |
3 | * | |
4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka | |
d10040f7 | 5 | * Copyright (C) 2001 - 2007 Paul Mundt |
1da177e4 | 6 | * Copyright (C) 2003 Richard Curnow |
09b5a10c | 7 | * Copyright (c) 2007 STMicroelectronics (R&D) Ltd. |
1da177e4 LT |
8 | * |
9 | * This file is subject to the terms and conditions of the GNU General Public | |
10 | * License. See the file "COPYING" in the main directory of this archive | |
11 | * for more details. | |
12 | */ | |
1da177e4 | 13 | #include <linux/init.h> |
1da177e4 | 14 | #include <linux/mm.h> |
52e27782 PM |
15 | #include <linux/io.h> |
16 | #include <linux/mutex.h> | |
2277ab4a | 17 | #include <linux/fs.h> |
1da177e4 LT |
18 | #include <asm/mmu_context.h> |
19 | #include <asm/cacheflush.h> | |
20 | ||
28ccf7f9 PM |
21 | /* |
22 | * The maximum number of pages we support up to when doing ranged dcache | |
23 | * flushing. Anything exceeding this will simply flush the dcache in its | |
24 | * entirety. | |
25 | */ | |
26 | #define MAX_DCACHE_PAGES 64 /* XXX: Tune for ways */ | |
09b5a10c | 27 | #define MAX_ICACHE_PAGES 32 |
28ccf7f9 | 28 | |
b638d0b9 | 29 | static void __flush_cache_4096(unsigned long addr, unsigned long phys, |
a252710f | 30 | unsigned long exec_offset); |
b638d0b9 | 31 | |
1da177e4 LT |
32 | /* |
33 | * Write back the range of D-cache, and purge the I-cache. | |
34 | * | |
09b5a10c CS |
35 | * Called from kernel/module.c:sys_init_module and routine for a.out format, |
36 | * signal handler code and kprobes code | |
1da177e4 | 37 | */ |
f26b2a56 | 38 | static void sh4_flush_icache_range(void *args) |
1da177e4 | 39 | { |
f26b2a56 | 40 | struct flusher_data *data = args; |
f26b2a56 | 41 | unsigned long start, end; |
983f4c51 | 42 | unsigned long flags, v; |
1da177e4 LT |
43 | int i; |
44 | ||
f26b2a56 PM |
45 | start = data->addr1; |
46 | end = data->addr2; | |
47 | ||
682f88ab PM |
48 | /* If there are too many pages then just blow away the caches */ |
49 | if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) { | |
50 | local_flush_cache_all(NULL); | |
51 | return; | |
52 | } | |
53 | ||
54 | /* | |
55 | * Selectively flush d-cache then invalidate the i-cache. | |
56 | * This is inefficient, so only use this for small ranges. | |
57 | */ | |
58 | start &= ~(L1_CACHE_BYTES-1); | |
59 | end += L1_CACHE_BYTES-1; | |
60 | end &= ~(L1_CACHE_BYTES-1); | |
983f4c51 | 61 | |
682f88ab PM |
62 | local_irq_save(flags); |
63 | jump_to_uncached(); | |
983f4c51 | 64 | |
682f88ab PM |
65 | for (v = start; v < end; v += L1_CACHE_BYTES) { |
66 | unsigned long icacheaddr; | |
983f4c51 | 67 | |
682f88ab | 68 | __ocbwb(v); |
983f4c51 | 69 | |
682f88ab PM |
70 | icacheaddr = CACHE_IC_ADDRESS_ARRAY | (v & |
71 | cpu_data->icache.entry_mask); | |
09b5a10c | 72 | |
682f88ab PM |
73 | /* Clear i-cache line valid-bit */ |
74 | for (i = 0; i < cpu_data->icache.ways; i++) { | |
75 | __raw_writel(0, icacheaddr); | |
76 | icacheaddr += cpu_data->icache.way_incr; | |
77 | } | |
09b5a10c | 78 | } |
682f88ab PM |
79 | |
80 | back_to_cached(); | |
81 | local_irq_restore(flags); | |
1da177e4 LT |
82 | } |
83 | ||
84 | static inline void flush_cache_4096(unsigned long start, | |
85 | unsigned long phys) | |
86 | { | |
983f4c51 | 87 | unsigned long flags, exec_offset = 0; |
33573c0e | 88 | |
1da177e4 | 89 | /* |
b638d0b9 RC |
90 | * All types of SH-4 require PC to be in P2 to operate on the I-cache. |
91 | * Some types of SH-4 require PC to be in P2 to operate on the D-cache. | |
1da177e4 | 92 | */ |
7ec9d6f8 | 93 | if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) || |
33573c0e | 94 | (start < CACHE_OC_ADDRESS_ARRAY)) |
510c72ad | 95 | exec_offset = 0x20000000; |
33573c0e | 96 | |
983f4c51 | 97 | local_irq_save(flags); |
33573c0e PM |
98 | __flush_cache_4096(start | SH_CACHE_ASSOC, |
99 | P1SEGADDR(phys), exec_offset); | |
983f4c51 | 100 | local_irq_restore(flags); |
1da177e4 LT |
101 | } |
102 | ||
103 | /* | |
104 | * Write back & invalidate the D-cache of the page. | |
105 | * (To avoid "alias" issues) | |
106 | */ | |
e76a0136 | 107 | static void sh4_flush_dcache_page(void *arg) |
1da177e4 | 108 | { |
e76a0136 | 109 | struct page *page = arg; |
c139a595 | 110 | #ifndef CONFIG_SMP |
2277ab4a PM |
111 | struct address_space *mapping = page_mapping(page); |
112 | ||
2277ab4a PM |
113 | if (mapping && !mapping_mapped(mapping)) |
114 | set_bit(PG_dcache_dirty, &page->flags); | |
115 | else | |
116 | #endif | |
117 | { | |
31c9efde | 118 | unsigned long phys = page_to_phys(page); |
b638d0b9 RC |
119 | unsigned long addr = CACHE_OC_ADDRESS_ARRAY; |
120 | int i, n; | |
1da177e4 LT |
121 | |
122 | /* Loop all the D-cache */ | |
31c9efde | 123 | n = boot_cpu_data.dcache.way_incr >> 12; |
510c72ad | 124 | for (i = 0; i < n; i++, addr += 4096) |
b638d0b9 | 125 | flush_cache_4096(addr, phys); |
1da177e4 | 126 | } |
fdfc74f9 PM |
127 | |
128 | wmb(); | |
1da177e4 LT |
129 | } |
130 | ||
28ccf7f9 | 131 | /* TODO: Selective icache invalidation through IC address array.. */ |
205a3b43 | 132 | static void __uses_jump_to_uncached flush_icache_all(void) |
1da177e4 | 133 | { |
983f4c51 | 134 | unsigned long flags, ccr; |
1da177e4 | 135 | |
983f4c51 | 136 | local_irq_save(flags); |
cbaa118e | 137 | jump_to_uncached(); |
1da177e4 LT |
138 | |
139 | /* Flush I-cache */ | |
140 | ccr = ctrl_inl(CCR); | |
141 | ccr |= CCR_CACHE_ICI; | |
142 | ctrl_outl(ccr, CCR); | |
143 | ||
29847622 | 144 | /* |
cbaa118e | 145 | * back_to_cached() will take care of the barrier for us, don't add |
29847622 PM |
146 | * another one! |
147 | */ | |
983f4c51 | 148 | |
cbaa118e | 149 | back_to_cached(); |
983f4c51 | 150 | local_irq_restore(flags); |
1da177e4 LT |
151 | } |
152 | ||
bd6df574 | 153 | static void flush_dcache_all(void) |
1da177e4 | 154 | { |
bd6df574 PM |
155 | unsigned long addr, end_addr, entry_offset; |
156 | ||
157 | end_addr = CACHE_OC_ADDRESS_ARRAY + | |
158 | (current_cpu_data.dcache.sets << | |
159 | current_cpu_data.dcache.entry_shift) * | |
160 | current_cpu_data.dcache.ways; | |
161 | ||
162 | entry_offset = 1 << current_cpu_data.dcache.entry_shift; | |
163 | ||
164 | for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; ) { | |
165 | __raw_writel(0, addr); addr += entry_offset; | |
166 | __raw_writel(0, addr); addr += entry_offset; | |
167 | __raw_writel(0, addr); addr += entry_offset; | |
168 | __raw_writel(0, addr); addr += entry_offset; | |
169 | __raw_writel(0, addr); addr += entry_offset; | |
170 | __raw_writel(0, addr); addr += entry_offset; | |
171 | __raw_writel(0, addr); addr += entry_offset; | |
172 | __raw_writel(0, addr); addr += entry_offset; | |
173 | } | |
a252710f PM |
174 | } |
175 | ||
f26b2a56 | 176 | static void sh4_flush_cache_all(void *unused) |
a252710f PM |
177 | { |
178 | flush_dcache_all(); | |
1da177e4 LT |
179 | flush_icache_all(); |
180 | } | |
181 | ||
28ccf7f9 PM |
182 | /* |
183 | * Note : (RPC) since the caches are physically tagged, the only point | |
184 | * of flush_cache_mm for SH-4 is to get rid of aliases from the | |
185 | * D-cache. The assumption elsewhere, e.g. flush_cache_range, is that | |
186 | * lines can stay resident so long as the virtual address they were | |
187 | * accessed with (hence cache set) is in accord with the physical | |
654d364e | 188 | * address (i.e. tag). It's no different here. |
28ccf7f9 PM |
189 | * |
190 | * Caller takes mm->mmap_sem. | |
191 | */ | |
f26b2a56 | 192 | static void sh4_flush_cache_mm(void *arg) |
1da177e4 | 193 | { |
f26b2a56 PM |
194 | struct mm_struct *mm = arg; |
195 | ||
e7b8b7f1 PM |
196 | if (cpu_context(smp_processor_id(), mm) == NO_CONTEXT) |
197 | return; | |
198 | ||
654d364e | 199 | flush_dcache_all(); |
1da177e4 LT |
200 | } |
201 | ||
202 | /* | |
203 | * Write back and invalidate I/D-caches for the page. | |
204 | * | |
205 | * ADDR: Virtual Address (U0 address) | |
206 | * PFN: Physical page number | |
207 | */ | |
f26b2a56 | 208 | static void sh4_flush_cache_page(void *args) |
1da177e4 | 209 | { |
f26b2a56 PM |
210 | struct flusher_data *data = args; |
211 | struct vm_area_struct *vma; | |
212 | unsigned long address, pfn, phys; | |
b638d0b9 RC |
213 | unsigned int alias_mask; |
214 | ||
f26b2a56 PM |
215 | vma = data->vma; |
216 | address = data->addr1; | |
217 | pfn = data->addr2; | |
218 | phys = pfn << PAGE_SHIFT; | |
219 | ||
e7b8b7f1 PM |
220 | if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT) |
221 | return; | |
222 | ||
7ec9d6f8 | 223 | alias_mask = boot_cpu_data.dcache.alias_mask; |
1da177e4 LT |
224 | |
225 | /* We only need to flush D-cache when we have alias */ | |
b638d0b9 | 226 | if ((address^phys) & alias_mask) { |
1da177e4 LT |
227 | /* Loop 4K of the D-cache */ |
228 | flush_cache_4096( | |
b638d0b9 | 229 | CACHE_OC_ADDRESS_ARRAY | (address & alias_mask), |
1da177e4 LT |
230 | phys); |
231 | /* Loop another 4K of the D-cache */ | |
232 | flush_cache_4096( | |
b638d0b9 | 233 | CACHE_OC_ADDRESS_ARRAY | (phys & alias_mask), |
1da177e4 LT |
234 | phys); |
235 | } | |
236 | ||
7ec9d6f8 | 237 | alias_mask = boot_cpu_data.icache.alias_mask; |
b638d0b9 RC |
238 | if (vma->vm_flags & VM_EXEC) { |
239 | /* | |
240 | * Evict entries from the portion of the cache from which code | |
241 | * may have been executed at this address (virtual). There's | |
242 | * no need to evict from the portion corresponding to the | |
243 | * physical address as for the D-cache, because we know the | |
244 | * kernel has never executed the code through its identity | |
245 | * translation. | |
246 | */ | |
1da177e4 | 247 | flush_cache_4096( |
b638d0b9 | 248 | CACHE_IC_ADDRESS_ARRAY | (address & alias_mask), |
1da177e4 | 249 | phys); |
b638d0b9 | 250 | } |
1da177e4 LT |
251 | } |
252 | ||
253 | /* | |
254 | * Write back and invalidate D-caches. | |
255 | * | |
256 | * START, END: Virtual Address (U0 address) | |
257 | * | |
258 | * NOTE: We need to flush the _physical_ page entry. | |
259 | * Flushing the cache lines for U0 only isn't enough. | |
260 | * We need to flush for P1 too, which may contain aliases. | |
261 | */ | |
f26b2a56 | 262 | static void sh4_flush_cache_range(void *args) |
1da177e4 | 263 | { |
f26b2a56 PM |
264 | struct flusher_data *data = args; |
265 | struct vm_area_struct *vma; | |
266 | unsigned long start, end; | |
267 | ||
268 | vma = data->vma; | |
269 | start = data->addr1; | |
270 | end = data->addr2; | |
271 | ||
e7b8b7f1 PM |
272 | if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT) |
273 | return; | |
274 | ||
b638d0b9 RC |
275 | /* |
276 | * If cache is only 4k-per-way, there are never any 'aliases'. Since | |
277 | * the cache is physically tagged, the data can just be left in there. | |
278 | */ | |
7ec9d6f8 | 279 | if (boot_cpu_data.dcache.n_aliases == 0) |
b638d0b9 RC |
280 | return; |
281 | ||
654d364e | 282 | flush_dcache_all(); |
b638d0b9 | 283 | |
654d364e | 284 | if (vma->vm_flags & VM_EXEC) |
1da177e4 LT |
285 | flush_icache_all(); |
286 | } | |
287 | ||
b638d0b9 RC |
288 | /** |
289 | * __flush_cache_4096 | |
290 | * | |
291 | * @addr: address in memory mapped cache array | |
292 | * @phys: P1 address to flush (has to match tags if addr has 'A' bit | |
293 | * set i.e. associative write) | |
294 | * @exec_offset: set to 0x20000000 if flush has to be executed from P2 | |
295 | * region else 0x0 | |
296 | * | |
297 | * The offset into the cache array implied by 'addr' selects the | |
298 | * 'colour' of the virtual address range that will be flushed. The | |
299 | * operation (purge/write-back) is selected by the lower 2 bits of | |
300 | * 'phys'. | |
301 | */ | |
302 | static void __flush_cache_4096(unsigned long addr, unsigned long phys, | |
303 | unsigned long exec_offset) | |
304 | { | |
305 | int way_count; | |
306 | unsigned long base_addr = addr; | |
307 | struct cache_info *dcache; | |
308 | unsigned long way_incr; | |
309 | unsigned long a, ea, p; | |
310 | unsigned long temp_pc; | |
311 | ||
7ec9d6f8 | 312 | dcache = &boot_cpu_data.dcache; |
b638d0b9 RC |
313 | /* Write this way for better assembly. */ |
314 | way_count = dcache->ways; | |
315 | way_incr = dcache->way_incr; | |
316 | ||
317 | /* | |
318 | * Apply exec_offset (i.e. branch to P2 if required.). | |
319 | * | |
320 | * FIXME: | |
321 | * | |
322 | * If I write "=r" for the (temp_pc), it puts this in r6 hence | |
323 | * trashing exec_offset before it's been added on - why? Hence | |
324 | * "=&r" as a 'workaround' | |
325 | */ | |
326 | asm volatile("mov.l 1f, %0\n\t" | |
327 | "add %1, %0\n\t" | |
328 | "jmp @%0\n\t" | |
329 | "nop\n\t" | |
330 | ".balign 4\n\t" | |
331 | "1: .long 2f\n\t" | |
332 | "2:\n" : "=&r" (temp_pc) : "r" (exec_offset)); | |
333 | ||
334 | /* | |
335 | * We know there will be >=1 iteration, so write as do-while to avoid | |
336 | * pointless nead-of-loop check for 0 iterations. | |
337 | */ | |
338 | do { | |
339 | ea = base_addr + PAGE_SIZE; | |
340 | a = base_addr; | |
341 | p = phys; | |
342 | ||
343 | do { | |
344 | *(volatile unsigned long *)a = p; | |
345 | /* | |
346 | * Next line: intentionally not p+32, saves an add, p | |
347 | * will do since only the cache tag bits need to | |
348 | * match. | |
349 | */ | |
350 | *(volatile unsigned long *)(a+32) = p; | |
351 | a += 64; | |
352 | p += 64; | |
353 | } while (a < ea); | |
354 | ||
355 | base_addr += way_incr; | |
356 | } while (--way_count != 0); | |
357 | } | |
358 | ||
37443ef3 PM |
359 | extern void __weak sh4__flush_region_init(void); |
360 | ||
361 | /* | |
362 | * SH-4 has virtually indexed and physically tagged cache. | |
363 | */ | |
364 | void __init sh4_cache_init(void) | |
365 | { | |
366 | printk("PVR=%08x CVR=%08x PRR=%08x\n", | |
367 | ctrl_inl(CCN_PVR), | |
368 | ctrl_inl(CCN_CVR), | |
369 | ctrl_inl(CCN_PRR)); | |
370 | ||
f26b2a56 PM |
371 | local_flush_icache_range = sh4_flush_icache_range; |
372 | local_flush_dcache_page = sh4_flush_dcache_page; | |
373 | local_flush_cache_all = sh4_flush_cache_all; | |
374 | local_flush_cache_mm = sh4_flush_cache_mm; | |
375 | local_flush_cache_dup_mm = sh4_flush_cache_mm; | |
376 | local_flush_cache_page = sh4_flush_cache_page; | |
377 | local_flush_cache_range = sh4_flush_cache_range; | |
37443ef3 PM |
378 | |
379 | sh4__flush_region_init(); | |
380 | } |