blob: b36a9c986a58d065915600e8672236ed8bf950e6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/sh/mm/cache-sh4.c
3 *
4 * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
Paul Mundtd10040f2007-09-24 16:38:25 +09005 * Copyright (C) 2001 - 2007 Paul Mundt
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Copyright (C) 2003 Richard Curnow
Chris Smith09b5a102008-07-02 15:17:11 +09007 * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 *
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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/mm.h>
Paul Mundt52e27782006-11-21 11:09:41 +090015#include <linux/io.h>
16#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <asm/mmu_context.h>
18#include <asm/cacheflush.h>
19
Paul Mundt28ccf7f2006-09-27 18:30:07 +090020/*
21 * The maximum number of pages we support up to when doing ranged dcache
22 * flushing. Anything exceeding this will simply flush the dcache in its
23 * entirety.
24 */
25#define MAX_DCACHE_PAGES 64 /* XXX: Tune for ways */
Chris Smith09b5a102008-07-02 15:17:11 +090026#define MAX_ICACHE_PAGES 32
Paul Mundt28ccf7f2006-09-27 18:30:07 +090027
Matt Flemingce3f7cb2009-09-01 13:32:48 +090028static void __flush_dcache_segment_writethrough(unsigned long start,
29 unsigned long extent);
Richard Curnowb638d0b2006-09-27 14:09:26 +090030static void __flush_dcache_segment_1way(unsigned long start,
31 unsigned long extent);
32static void __flush_dcache_segment_2way(unsigned long start,
33 unsigned long extent);
34static void __flush_dcache_segment_4way(unsigned long start,
35 unsigned long extent);
Richard Curnowb638d0b2006-09-27 14:09:26 +090036static void __flush_cache_4096(unsigned long addr, unsigned long phys,
Paul Mundta2527102006-09-27 11:29:55 +090037 unsigned long exec_offset);
Richard Curnowb638d0b2006-09-27 14:09:26 +090038
39/*
40 * This is initialised here to ensure that it is not placed in the BSS. If
41 * that were to happen, note that cache_init gets called before the BSS is
42 * cleared, so this would get nulled out which would be hopeless.
43 */
44static void (*__flush_dcache_segment_fn)(unsigned long, unsigned long) =
45 (void (*)(unsigned long, unsigned long))0xdeadbeef;
46
47static void compute_alias(struct cache_info *c)
48{
49 c->alias_mask = ((c->sets - 1) << c->entry_shift) & ~(PAGE_SIZE - 1);
Paul Mundtd10040f2007-09-24 16:38:25 +090050 c->n_aliases = c->alias_mask ? (c->alias_mask >> PAGE_SHIFT) + 1 : 0;
Richard Curnowb638d0b2006-09-27 14:09:26 +090051}
52
53static void __init emit_cache_params(void)
54{
55 printk("PVR=%08x CVR=%08x PRR=%08x\n",
56 ctrl_inl(CCN_PVR),
57 ctrl_inl(CCN_CVR),
58 ctrl_inl(CCN_PRR));
59 printk("I-cache : n_ways=%d n_sets=%d way_incr=%d\n",
Paul Mundt7ec9d6f2007-09-21 18:05:20 +090060 boot_cpu_data.icache.ways,
61 boot_cpu_data.icache.sets,
62 boot_cpu_data.icache.way_incr);
Richard Curnowb638d0b2006-09-27 14:09:26 +090063 printk("I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n",
Paul Mundt7ec9d6f2007-09-21 18:05:20 +090064 boot_cpu_data.icache.entry_mask,
65 boot_cpu_data.icache.alias_mask,
66 boot_cpu_data.icache.n_aliases);
Richard Curnowb638d0b2006-09-27 14:09:26 +090067 printk("D-cache : n_ways=%d n_sets=%d way_incr=%d\n",
Paul Mundt7ec9d6f2007-09-21 18:05:20 +090068 boot_cpu_data.dcache.ways,
69 boot_cpu_data.dcache.sets,
70 boot_cpu_data.dcache.way_incr);
Richard Curnowb638d0b2006-09-27 14:09:26 +090071 printk("D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n",
Paul Mundt7ec9d6f2007-09-21 18:05:20 +090072 boot_cpu_data.dcache.entry_mask,
73 boot_cpu_data.dcache.alias_mask,
74 boot_cpu_data.dcache.n_aliases);
Richard Curnowb638d0b2006-09-27 14:09:26 +090075
Paul Mundtab27f622007-09-24 17:00:45 +090076 /*
77 * Emit Secondary Cache parameters if the CPU has a probed L2.
78 */
79 if (boot_cpu_data.flags & CPU_HAS_L2_CACHE) {
80 printk("S-cache : n_ways=%d n_sets=%d way_incr=%d\n",
81 boot_cpu_data.scache.ways,
82 boot_cpu_data.scache.sets,
83 boot_cpu_data.scache.way_incr);
84 printk("S-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n",
85 boot_cpu_data.scache.entry_mask,
86 boot_cpu_data.scache.alias_mask,
87 boot_cpu_data.scache.n_aliases);
88 }
89
Richard Curnowb638d0b2006-09-27 14:09:26 +090090 if (!__flush_dcache_segment_fn)
91 panic("unknown number of cache ways\n");
92}
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
94/*
95 * SH-4 has virtually indexed and physically tagged cache.
96 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070097void __init p3_cache_init(void)
98{
Matt Flemingce3f7cb2009-09-01 13:32:48 +090099 unsigned int wt_enabled = !!(__raw_readl(CCR) & CCR_CACHE_WT);
100
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900101 compute_alias(&boot_cpu_data.icache);
102 compute_alias(&boot_cpu_data.dcache);
Paul Mundtab27f622007-09-24 17:00:45 +0900103 compute_alias(&boot_cpu_data.scache);
Richard Curnowb638d0b2006-09-27 14:09:26 +0900104
Matt Flemingce3f7cb2009-09-01 13:32:48 +0900105 if (wt_enabled) {
106 __flush_dcache_segment_fn = __flush_dcache_segment_writethrough;
107 goto out;
108 }
109
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900110 switch (boot_cpu_data.dcache.ways) {
Richard Curnowb638d0b2006-09-27 14:09:26 +0900111 case 1:
112 __flush_dcache_segment_fn = __flush_dcache_segment_1way;
113 break;
114 case 2:
115 __flush_dcache_segment_fn = __flush_dcache_segment_2way;
116 break;
117 case 4:
118 __flush_dcache_segment_fn = __flush_dcache_segment_4way;
119 break;
120 default:
121 __flush_dcache_segment_fn = NULL;
122 break;
123 }
124
Matt Flemingce3f7cb2009-09-01 13:32:48 +0900125out:
Richard Curnowb638d0b2006-09-27 14:09:26 +0900126 emit_cache_params();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127}
128
129/*
130 * Write back the dirty D-caches, but not invalidate them.
131 *
132 * START: Virtual Address (U0, P1, or P3)
133 * SIZE: Size of the region.
134 */
135void __flush_wback_region(void *start, int size)
136{
137 unsigned long v;
138 unsigned long begin, end;
139
140 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
141 end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
142 & ~(L1_CACHE_BYTES-1);
143 for (v = begin; v < end; v+=L1_CACHE_BYTES) {
144 asm volatile("ocbwb %0"
145 : /* no output */
146 : "m" (__m(v)));
147 }
148}
149
150/*
151 * Write back the dirty D-caches and invalidate them.
152 *
153 * START: Virtual Address (U0, P1, or P3)
154 * SIZE: Size of the region.
155 */
156void __flush_purge_region(void *start, int size)
157{
158 unsigned long v;
159 unsigned long begin, end;
160
161 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
162 end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
163 & ~(L1_CACHE_BYTES-1);
164 for (v = begin; v < end; v+=L1_CACHE_BYTES) {
165 asm volatile("ocbp %0"
166 : /* no output */
167 : "m" (__m(v)));
168 }
169}
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171/*
172 * No write back please
173 */
174void __flush_invalidate_region(void *start, int size)
175{
176 unsigned long v;
177 unsigned long begin, end;
178
179 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
180 end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
181 & ~(L1_CACHE_BYTES-1);
182 for (v = begin; v < end; v+=L1_CACHE_BYTES) {
183 asm volatile("ocbi %0"
184 : /* no output */
185 : "m" (__m(v)));
186 }
187}
188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189/*
190 * Write back the range of D-cache, and purge the I-cache.
191 *
Chris Smith09b5a102008-07-02 15:17:11 +0900192 * Called from kernel/module.c:sys_init_module and routine for a.out format,
193 * signal handler code and kprobes code
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 */
195void flush_icache_range(unsigned long start, unsigned long end)
196{
Chris Smith09b5a102008-07-02 15:17:11 +0900197 int icacheaddr;
198 unsigned long flags, v;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 int i;
200
Chris Smith09b5a102008-07-02 15:17:11 +0900201 /* If there are too many pages then just blow the caches */
202 if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
203 flush_cache_all();
204 } else {
205 /* selectively flush d-cache then invalidate the i-cache */
206 /* this is inefficient, so only use for small ranges */
207 start &= ~(L1_CACHE_BYTES-1);
208 end += L1_CACHE_BYTES-1;
209 end &= ~(L1_CACHE_BYTES-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
Chris Smith09b5a102008-07-02 15:17:11 +0900211 local_irq_save(flags);
212 jump_to_uncached();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Chris Smith09b5a102008-07-02 15:17:11 +0900214 for (v = start; v < end; v+=L1_CACHE_BYTES) {
215 asm volatile("ocbwb %0"
216 : /* no output */
217 : "m" (__m(v)));
Richard Curnowb638d0b2006-09-27 14:09:26 +0900218
Chris Smith09b5a102008-07-02 15:17:11 +0900219 icacheaddr = CACHE_IC_ADDRESS_ARRAY | (
220 v & cpu_data->icache.entry_mask);
Richard Curnowb638d0b2006-09-27 14:09:26 +0900221
Chris Smith09b5a102008-07-02 15:17:11 +0900222 for (i = 0; i < cpu_data->icache.ways;
223 i++, icacheaddr += cpu_data->icache.way_incr)
224 /* Clear i-cache line valid-bit */
225 ctrl_outl(0, icacheaddr);
226 }
227
228 back_to_cached();
229 local_irq_restore(flags);
230 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231}
232
233static inline void flush_cache_4096(unsigned long start,
234 unsigned long phys)
235{
Paul Mundt33573c02006-09-27 18:37:30 +0900236 unsigned long flags, exec_offset = 0;
237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 /*
Richard Curnowb638d0b2006-09-27 14:09:26 +0900239 * All types of SH-4 require PC to be in P2 to operate on the I-cache.
240 * Some types of SH-4 require PC to be in P2 to operate on the D-cache.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 */
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900242 if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) ||
Paul Mundt33573c02006-09-27 18:37:30 +0900243 (start < CACHE_OC_ADDRESS_ARRAY))
Paul Mundt510c72ad2006-11-27 12:06:26 +0900244 exec_offset = 0x20000000;
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900245
Paul Mundt33573c02006-09-27 18:37:30 +0900246 local_irq_save(flags);
247 __flush_cache_4096(start | SH_CACHE_ASSOC,
248 P1SEGADDR(phys), exec_offset);
249 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250}
251
252/*
253 * Write back & invalidate the D-cache of the page.
254 * (To avoid "alias" issues)
255 */
256void flush_dcache_page(struct page *page)
257{
Paul Mundt39e688a2007-03-05 19:46:47 +0900258 if (test_bit(PG_mapped, &page->flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 unsigned long phys = PHYSADDR(page_address(page));
Richard Curnowb638d0b2006-09-27 14:09:26 +0900260 unsigned long addr = CACHE_OC_ADDRESS_ARRAY;
261 int i, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263 /* Loop all the D-cache */
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900264 n = boot_cpu_data.dcache.n_aliases;
Paul Mundt510c72ad2006-11-27 12:06:26 +0900265 for (i = 0; i < n; i++, addr += 4096)
Richard Curnowb638d0b2006-09-27 14:09:26 +0900266 flush_cache_4096(addr, phys);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
Paul Mundtfdfc74f2006-09-27 14:05:52 +0900268
269 wmb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900272/* TODO: Selective icache invalidation through IC address array.. */
Paul Mundt205a3b42008-09-05 18:00:29 +0900273static void __uses_jump_to_uncached flush_icache_all(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
275 unsigned long flags, ccr;
276
277 local_irq_save(flags);
Stuart Menefycbaa1182007-11-30 17:06:36 +0900278 jump_to_uncached();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
280 /* Flush I-cache */
281 ccr = ctrl_inl(CCR);
282 ccr |= CCR_CACHE_ICI;
283 ctrl_outl(ccr, CCR);
284
Paul Mundt29847622006-09-27 14:57:44 +0900285 /*
Stuart Menefycbaa1182007-11-30 17:06:36 +0900286 * back_to_cached() will take care of the barrier for us, don't add
Paul Mundt29847622006-09-27 14:57:44 +0900287 * another one!
288 */
289
Stuart Menefycbaa1182007-11-30 17:06:36 +0900290 back_to_cached();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 local_irq_restore(flags);
292}
293
Paul Mundta2527102006-09-27 11:29:55 +0900294void flush_dcache_all(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900296 (*__flush_dcache_segment_fn)(0UL, boot_cpu_data.dcache.way_size);
Paul Mundtfdfc74f2006-09-27 14:05:52 +0900297 wmb();
Paul Mundta2527102006-09-27 11:29:55 +0900298}
299
300void flush_cache_all(void)
301{
302 flush_dcache_all();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 flush_icache_all();
304}
305
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900306static void __flush_cache_mm(struct mm_struct *mm, unsigned long start,
307 unsigned long end)
308{
309 unsigned long d = 0, p = start & PAGE_MASK;
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900310 unsigned long alias_mask = boot_cpu_data.dcache.alias_mask;
311 unsigned long n_aliases = boot_cpu_data.dcache.n_aliases;
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900312 unsigned long select_bit;
313 unsigned long all_aliases_mask;
314 unsigned long addr_offset;
315 pgd_t *dir;
316 pmd_t *pmd;
317 pud_t *pud;
318 pte_t *pte;
319 int i;
320
321 dir = pgd_offset(mm, p);
322 pud = pud_offset(dir, p);
323 pmd = pmd_offset(pud, p);
324 end = PAGE_ALIGN(end);
325
326 all_aliases_mask = (1 << n_aliases) - 1;
327
328 do {
329 if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) {
330 p &= PMD_MASK;
331 p += PMD_SIZE;
332 pmd++;
333
334 continue;
335 }
336
337 pte = pte_offset_kernel(pmd, p);
338
339 do {
340 unsigned long phys;
341 pte_t entry = *pte;
342
343 if (!(pte_val(entry) & _PAGE_PRESENT)) {
344 pte++;
345 p += PAGE_SIZE;
346 continue;
347 }
348
349 phys = pte_val(entry) & PTE_PHYS_MASK;
350
351 if ((p ^ phys) & alias_mask) {
352 d |= 1 << ((p & alias_mask) >> PAGE_SHIFT);
353 d |= 1 << ((phys & alias_mask) >> PAGE_SHIFT);
354
355 if (d == all_aliases_mask)
356 goto loop_exit;
357 }
358
359 pte++;
360 p += PAGE_SIZE;
361 } while (p < end && ((unsigned long)pte & ~PAGE_MASK));
362 pmd++;
363 } while (p < end);
364
365loop_exit:
366 addr_offset = 0;
367 select_bit = 1;
368
369 for (i = 0; i < n_aliases; i++) {
370 if (d & select_bit) {
371 (*__flush_dcache_segment_fn)(addr_offset, PAGE_SIZE);
372 wmb();
373 }
374
375 select_bit <<= 1;
376 addr_offset += PAGE_SIZE;
377 }
378}
379
380/*
381 * Note : (RPC) since the caches are physically tagged, the only point
382 * of flush_cache_mm for SH-4 is to get rid of aliases from the
383 * D-cache. The assumption elsewhere, e.g. flush_cache_range, is that
384 * lines can stay resident so long as the virtual address they were
385 * accessed with (hence cache set) is in accord with the physical
386 * address (i.e. tag). It's no different here. So I reckon we don't
387 * need to flush the I-cache, since aliases don't matter for that. We
388 * should try that.
389 *
390 * Caller takes mm->mmap_sem.
391 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392void flush_cache_mm(struct mm_struct *mm)
393{
Richard Curnowb638d0b2006-09-27 14:09:26 +0900394 /*
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900395 * If cache is only 4k-per-way, there are never any 'aliases'. Since
396 * the cache is physically tagged, the data can just be left in there.
Richard Curnowb638d0b2006-09-27 14:09:26 +0900397 */
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900398 if (boot_cpu_data.dcache.n_aliases == 0)
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900399 return;
400
401 /*
402 * Don't bother groveling around the dcache for the VMA ranges
403 * if there are too many PTEs to make it worthwhile.
404 */
405 if (mm->nr_ptes >= MAX_DCACHE_PAGES)
406 flush_dcache_all();
407 else {
408 struct vm_area_struct *vma;
409
410 /*
411 * In this case there are reasonably sized ranges to flush,
412 * iterate through the VMA list and take care of any aliases.
413 */
414 for (vma = mm->mmap; vma; vma = vma->vm_next)
415 __flush_cache_mm(mm, vma->vm_start, vma->vm_end);
416 }
417
418 /* Only touch the icache if one of the VMAs has VM_EXEC set. */
419 if (mm->exec_vm)
420 flush_icache_all();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421}
422
423/*
424 * Write back and invalidate I/D-caches for the page.
425 *
426 * ADDR: Virtual Address (U0 address)
427 * PFN: Physical page number
428 */
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900429void flush_cache_page(struct vm_area_struct *vma, unsigned long address,
430 unsigned long pfn)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
432 unsigned long phys = pfn << PAGE_SHIFT;
Richard Curnowb638d0b2006-09-27 14:09:26 +0900433 unsigned int alias_mask;
434
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900435 alias_mask = boot_cpu_data.dcache.alias_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 /* We only need to flush D-cache when we have alias */
Richard Curnowb638d0b2006-09-27 14:09:26 +0900438 if ((address^phys) & alias_mask) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 /* Loop 4K of the D-cache */
440 flush_cache_4096(
Richard Curnowb638d0b2006-09-27 14:09:26 +0900441 CACHE_OC_ADDRESS_ARRAY | (address & alias_mask),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 phys);
443 /* Loop another 4K of the D-cache */
444 flush_cache_4096(
Richard Curnowb638d0b2006-09-27 14:09:26 +0900445 CACHE_OC_ADDRESS_ARRAY | (phys & alias_mask),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 phys);
447 }
448
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900449 alias_mask = boot_cpu_data.icache.alias_mask;
Richard Curnowb638d0b2006-09-27 14:09:26 +0900450 if (vma->vm_flags & VM_EXEC) {
451 /*
452 * Evict entries from the portion of the cache from which code
453 * may have been executed at this address (virtual). There's
454 * no need to evict from the portion corresponding to the
455 * physical address as for the D-cache, because we know the
456 * kernel has never executed the code through its identity
457 * translation.
458 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 flush_cache_4096(
Richard Curnowb638d0b2006-09-27 14:09:26 +0900460 CACHE_IC_ADDRESS_ARRAY | (address & alias_mask),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 phys);
Richard Curnowb638d0b2006-09-27 14:09:26 +0900462 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
465/*
466 * Write back and invalidate D-caches.
467 *
468 * START, END: Virtual Address (U0 address)
469 *
470 * NOTE: We need to flush the _physical_ page entry.
471 * Flushing the cache lines for U0 only isn't enough.
472 * We need to flush for P1 too, which may contain aliases.
473 */
474void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
475 unsigned long end)
476{
Richard Curnowb638d0b2006-09-27 14:09:26 +0900477 /*
478 * If cache is only 4k-per-way, there are never any 'aliases'. Since
479 * the cache is physically tagged, the data can just be left in there.
480 */
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900481 if (boot_cpu_data.dcache.n_aliases == 0)
Richard Curnowb638d0b2006-09-27 14:09:26 +0900482 return;
483
Paul Mundta2527102006-09-27 11:29:55 +0900484 /*
485 * Don't bother with the lookup and alias check if we have a
486 * wide range to cover, just blow away the dcache in its
487 * entirety instead. -- PFM.
488 */
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900489 if (((end - start) >> PAGE_SHIFT) >= MAX_DCACHE_PAGES)
Paul Mundta2527102006-09-27 11:29:55 +0900490 flush_dcache_all();
Paul Mundt28ccf7f2006-09-27 18:30:07 +0900491 else
492 __flush_cache_mm(vma->vm_mm, start, end);
Richard Curnowb638d0b2006-09-27 14:09:26 +0900493
494 if (vma->vm_flags & VM_EXEC) {
495 /*
496 * TODO: Is this required??? Need to look at how I-cache
497 * coherency is assured when new programs are loaded to see if
498 * this matters.
499 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 flush_icache_all();
Richard Curnowb638d0b2006-09-27 14:09:26 +0900501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502}
503
504/*
505 * flush_icache_user_range
506 * @vma: VMA of the process
507 * @page: page
508 * @addr: U0 address
509 * @len: length of the range (< page size)
510 */
511void flush_icache_user_range(struct vm_area_struct *vma,
512 struct page *page, unsigned long addr, int len)
513{
514 flush_cache_page(vma, addr, page_to_pfn(page));
Paul Mundtfdfc74f2006-09-27 14:05:52 +0900515 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
Richard Curnowb638d0b2006-09-27 14:09:26 +0900518/**
519 * __flush_cache_4096
520 *
521 * @addr: address in memory mapped cache array
522 * @phys: P1 address to flush (has to match tags if addr has 'A' bit
523 * set i.e. associative write)
524 * @exec_offset: set to 0x20000000 if flush has to be executed from P2
525 * region else 0x0
526 *
527 * The offset into the cache array implied by 'addr' selects the
528 * 'colour' of the virtual address range that will be flushed. The
529 * operation (purge/write-back) is selected by the lower 2 bits of
530 * 'phys'.
531 */
532static void __flush_cache_4096(unsigned long addr, unsigned long phys,
533 unsigned long exec_offset)
534{
535 int way_count;
536 unsigned long base_addr = addr;
537 struct cache_info *dcache;
538 unsigned long way_incr;
539 unsigned long a, ea, p;
540 unsigned long temp_pc;
541
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900542 dcache = &boot_cpu_data.dcache;
Richard Curnowb638d0b2006-09-27 14:09:26 +0900543 /* Write this way for better assembly. */
544 way_count = dcache->ways;
545 way_incr = dcache->way_incr;
546
547 /*
548 * Apply exec_offset (i.e. branch to P2 if required.).
549 *
550 * FIXME:
551 *
552 * If I write "=r" for the (temp_pc), it puts this in r6 hence
553 * trashing exec_offset before it's been added on - why? Hence
554 * "=&r" as a 'workaround'
555 */
556 asm volatile("mov.l 1f, %0\n\t"
557 "add %1, %0\n\t"
558 "jmp @%0\n\t"
559 "nop\n\t"
560 ".balign 4\n\t"
561 "1: .long 2f\n\t"
562 "2:\n" : "=&r" (temp_pc) : "r" (exec_offset));
563
564 /*
565 * We know there will be >=1 iteration, so write as do-while to avoid
566 * pointless nead-of-loop check for 0 iterations.
567 */
568 do {
569 ea = base_addr + PAGE_SIZE;
570 a = base_addr;
571 p = phys;
572
573 do {
574 *(volatile unsigned long *)a = p;
575 /*
576 * Next line: intentionally not p+32, saves an add, p
577 * will do since only the cache tag bits need to
578 * match.
579 */
580 *(volatile unsigned long *)(a+32) = p;
581 a += 64;
582 p += 64;
583 } while (a < ea);
584
585 base_addr += way_incr;
586 } while (--way_count != 0);
587}
588
589/*
590 * Break the 1, 2 and 4 way variants of this out into separate functions to
591 * avoid nearly all the overhead of having the conditional stuff in the function
592 * bodies (+ the 1 and 2 way cases avoid saving any registers too).
Stuart Menefya5cf9e242009-08-24 17:36:24 +0900593 *
594 * We want to eliminate unnecessary bus transactions, so this code uses
595 * a non-obvious technique.
596 *
597 * Loop over a cache way sized block of, one cache line at a time. For each
598 * line, use movca.a to cause the current cache line contents to be written
599 * back, but without reading anything from main memory. However this has the
600 * side effect that the cache is now caching that memory location. So follow
601 * this with a cache invalidate to mark the cache line invalid. And do all
602 * this with interrupts disabled, to avoid the cache line being accidently
603 * evicted while it is holding garbage.
Stuart Menefyffad9d72009-08-24 18:39:39 +0900604 *
605 * This also breaks in a number of circumstances:
606 * - if there are modifications to the region of memory just above
607 * empty_zero_page (for example because a breakpoint has been placed
608 * there), then these can be lost.
609 *
610 * This is because the the memory address which the cache temporarily
611 * caches in the above description is empty_zero_page. So the
612 * movca.l hits the cache (it is assumed that it misses, or at least
613 * isn't dirty), modifies the line and then invalidates it, losing the
614 * required change.
615 *
616 * - If caches are disabled or configured in write-through mode, then
617 * the movca.l writes garbage directly into memory.
Richard Curnowb638d0b2006-09-27 14:09:26 +0900618 */
Matt Flemingce3f7cb2009-09-01 13:32:48 +0900619static void __flush_dcache_segment_writethrough(unsigned long start,
620 unsigned long extent_per_way)
621{
622 unsigned long addr;
623 int i;
624
625 addr = CACHE_OC_ADDRESS_ARRAY | (start & cpu_data->dcache.entry_mask);
626
627 while (extent_per_way) {
628 for (i = 0; i < cpu_data->dcache.ways; i++)
629 __raw_writel(0, addr + cpu_data->dcache.way_incr * i);
630
631 addr += cpu_data->dcache.linesz;
632 extent_per_way -= cpu_data->dcache.linesz;
633 }
634}
635
Richard Curnowb638d0b2006-09-27 14:09:26 +0900636static void __flush_dcache_segment_1way(unsigned long start,
637 unsigned long extent_per_way)
638{
639 unsigned long orig_sr, sr_with_bl;
640 unsigned long base_addr;
641 unsigned long way_incr, linesz, way_size;
642 struct cache_info *dcache;
643 register unsigned long a0, a0e;
644
645 asm volatile("stc sr, %0" : "=r" (orig_sr));
646 sr_with_bl = orig_sr | (1<<28);
647 base_addr = ((unsigned long)&empty_zero_page[0]);
648
649 /*
650 * The previous code aligned base_addr to 16k, i.e. the way_size of all
651 * existing SH-4 D-caches. Whilst I don't see a need to have this
652 * aligned to any better than the cache line size (which it will be
653 * anyway by construction), let's align it to at least the way_size of
654 * any existing or conceivable SH-4 D-cache. -- RPC
655 */
656 base_addr = ((base_addr >> 16) << 16);
657 base_addr |= start;
658
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900659 dcache = &boot_cpu_data.dcache;
Richard Curnowb638d0b2006-09-27 14:09:26 +0900660 linesz = dcache->linesz;
661 way_incr = dcache->way_incr;
662 way_size = dcache->way_size;
663
664 a0 = base_addr;
665 a0e = base_addr + extent_per_way;
666 do {
667 asm volatile("ldc %0, sr" : : "r" (sr_with_bl));
668 asm volatile("movca.l r0, @%0\n\t"
669 "ocbi @%0" : : "r" (a0));
670 a0 += linesz;
671 asm volatile("movca.l r0, @%0\n\t"
672 "ocbi @%0" : : "r" (a0));
673 a0 += linesz;
674 asm volatile("movca.l r0, @%0\n\t"
675 "ocbi @%0" : : "r" (a0));
676 a0 += linesz;
677 asm volatile("movca.l r0, @%0\n\t"
678 "ocbi @%0" : : "r" (a0));
679 asm volatile("ldc %0, sr" : : "r" (orig_sr));
680 a0 += linesz;
681 } while (a0 < a0e);
682}
683
684static void __flush_dcache_segment_2way(unsigned long start,
685 unsigned long extent_per_way)
686{
687 unsigned long orig_sr, sr_with_bl;
688 unsigned long base_addr;
689 unsigned long way_incr, linesz, way_size;
690 struct cache_info *dcache;
691 register unsigned long a0, a1, a0e;
692
693 asm volatile("stc sr, %0" : "=r" (orig_sr));
694 sr_with_bl = orig_sr | (1<<28);
695 base_addr = ((unsigned long)&empty_zero_page[0]);
696
697 /* See comment under 1-way above */
698 base_addr = ((base_addr >> 16) << 16);
699 base_addr |= start;
700
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900701 dcache = &boot_cpu_data.dcache;
Richard Curnowb638d0b2006-09-27 14:09:26 +0900702 linesz = dcache->linesz;
703 way_incr = dcache->way_incr;
704 way_size = dcache->way_size;
705
706 a0 = base_addr;
707 a1 = a0 + way_incr;
708 a0e = base_addr + extent_per_way;
709 do {
710 asm volatile("ldc %0, sr" : : "r" (sr_with_bl));
711 asm volatile("movca.l r0, @%0\n\t"
712 "movca.l r0, @%1\n\t"
713 "ocbi @%0\n\t"
714 "ocbi @%1" : :
715 "r" (a0), "r" (a1));
716 a0 += linesz;
717 a1 += linesz;
718 asm volatile("movca.l r0, @%0\n\t"
719 "movca.l r0, @%1\n\t"
720 "ocbi @%0\n\t"
721 "ocbi @%1" : :
722 "r" (a0), "r" (a1));
723 a0 += linesz;
724 a1 += linesz;
725 asm volatile("movca.l r0, @%0\n\t"
726 "movca.l r0, @%1\n\t"
727 "ocbi @%0\n\t"
728 "ocbi @%1" : :
729 "r" (a0), "r" (a1));
730 a0 += linesz;
731 a1 += linesz;
732 asm volatile("movca.l r0, @%0\n\t"
733 "movca.l r0, @%1\n\t"
734 "ocbi @%0\n\t"
735 "ocbi @%1" : :
736 "r" (a0), "r" (a1));
737 asm volatile("ldc %0, sr" : : "r" (orig_sr));
738 a0 += linesz;
739 a1 += linesz;
740 } while (a0 < a0e);
741}
742
743static void __flush_dcache_segment_4way(unsigned long start,
744 unsigned long extent_per_way)
745{
746 unsigned long orig_sr, sr_with_bl;
747 unsigned long base_addr;
748 unsigned long way_incr, linesz, way_size;
749 struct cache_info *dcache;
750 register unsigned long a0, a1, a2, a3, a0e;
751
752 asm volatile("stc sr, %0" : "=r" (orig_sr));
753 sr_with_bl = orig_sr | (1<<28);
754 base_addr = ((unsigned long)&empty_zero_page[0]);
755
756 /* See comment under 1-way above */
757 base_addr = ((base_addr >> 16) << 16);
758 base_addr |= start;
759
Paul Mundt7ec9d6f2007-09-21 18:05:20 +0900760 dcache = &boot_cpu_data.dcache;
Richard Curnowb638d0b2006-09-27 14:09:26 +0900761 linesz = dcache->linesz;
762 way_incr = dcache->way_incr;
763 way_size = dcache->way_size;
764
765 a0 = base_addr;
766 a1 = a0 + way_incr;
767 a2 = a1 + way_incr;
768 a3 = a2 + way_incr;
769 a0e = base_addr + extent_per_way;
770 do {
771 asm volatile("ldc %0, sr" : : "r" (sr_with_bl));
772 asm volatile("movca.l r0, @%0\n\t"
773 "movca.l r0, @%1\n\t"
774 "movca.l r0, @%2\n\t"
775 "movca.l r0, @%3\n\t"
776 "ocbi @%0\n\t"
777 "ocbi @%1\n\t"
778 "ocbi @%2\n\t"
779 "ocbi @%3\n\t" : :
780 "r" (a0), "r" (a1), "r" (a2), "r" (a3));
781 a0 += linesz;
782 a1 += linesz;
783 a2 += linesz;
784 a3 += linesz;
785 asm volatile("movca.l r0, @%0\n\t"
786 "movca.l r0, @%1\n\t"
787 "movca.l r0, @%2\n\t"
788 "movca.l r0, @%3\n\t"
789 "ocbi @%0\n\t"
790 "ocbi @%1\n\t"
791 "ocbi @%2\n\t"
792 "ocbi @%3\n\t" : :
793 "r" (a0), "r" (a1), "r" (a2), "r" (a3));
794 a0 += linesz;
795 a1 += linesz;
796 a2 += linesz;
797 a3 += linesz;
798 asm volatile("movca.l r0, @%0\n\t"
799 "movca.l r0, @%1\n\t"
800 "movca.l r0, @%2\n\t"
801 "movca.l r0, @%3\n\t"
802 "ocbi @%0\n\t"
803 "ocbi @%1\n\t"
804 "ocbi @%2\n\t"
805 "ocbi @%3\n\t" : :
806 "r" (a0), "r" (a1), "r" (a2), "r" (a3));
807 a0 += linesz;
808 a1 += linesz;
809 a2 += linesz;
810 a3 += linesz;
811 asm volatile("movca.l r0, @%0\n\t"
812 "movca.l r0, @%1\n\t"
813 "movca.l r0, @%2\n\t"
814 "movca.l r0, @%3\n\t"
815 "ocbi @%0\n\t"
816 "ocbi @%1\n\t"
817 "ocbi @%2\n\t"
818 "ocbi @%3\n\t" : :
819 "r" (a0), "r" (a1), "r" (a2), "r" (a3));
820 asm volatile("ldc %0, sr" : : "r" (orig_sr));
821 a0 += linesz;
822 a1 += linesz;
823 a2 += linesz;
824 a3 += linesz;
825 } while (a0 < a0e);
826}