sh: RTS7751R2D board updates.
[deliverable/linux.git] / arch / sh / mm / cache-sh4.c
CommitLineData
1da177e4
LT
1/*
2 * arch/sh/mm/cache-sh4.c
3 *
4 * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
a252710f 5 * Copyright (C) 2001, 2002, 2003, 2004, 2005 Paul Mundt
1da177e4
LT
6 * Copyright (C) 2003 Richard Curnow
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file "COPYING" in the main directory of this archive
10 * for more details.
11 */
12
1da177e4
LT
13#include <linux/init.h>
14#include <linux/mman.h>
15#include <linux/mm.h>
16#include <linux/threads.h>
17#include <asm/addrspace.h>
18#include <asm/page.h>
19#include <asm/pgtable.h>
20#include <asm/processor.h>
21#include <asm/cache.h>
22#include <asm/io.h>
23#include <asm/uaccess.h>
24#include <asm/pgalloc.h>
25#include <asm/mmu_context.h>
26#include <asm/cacheflush.h>
27
a252710f
PM
28extern void __flush_cache_4096(unsigned long addr, unsigned long phys,
29 unsigned long exec_offset);
1da177e4
LT
30extern void __flush_cache_4096_all(unsigned long start);
31static void __flush_cache_4096_all_ex(unsigned long start);
32extern void __flush_dcache_all(void);
33static void __flush_dcache_all_ex(void);
34
35/*
36 * SH-4 has virtually indexed and physically tagged cache.
37 */
38
39struct semaphore p3map_sem[4];
40
41void __init p3_cache_init(void)
42{
43 if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE))
44 panic("%s failed.", __FUNCTION__);
45
46 sema_init (&p3map_sem[0], 1);
47 sema_init (&p3map_sem[1], 1);
48 sema_init (&p3map_sem[2], 1);
49 sema_init (&p3map_sem[3], 1);
50}
51
52/*
53 * Write back the dirty D-caches, but not invalidate them.
54 *
55 * START: Virtual Address (U0, P1, or P3)
56 * SIZE: Size of the region.
57 */
58void __flush_wback_region(void *start, int size)
59{
60 unsigned long v;
61 unsigned long begin, end;
62
63 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
64 end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
65 & ~(L1_CACHE_BYTES-1);
66 for (v = begin; v < end; v+=L1_CACHE_BYTES) {
67 asm volatile("ocbwb %0"
68 : /* no output */
69 : "m" (__m(v)));
70 }
71}
72
73/*
74 * Write back the dirty D-caches and invalidate them.
75 *
76 * START: Virtual Address (U0, P1, or P3)
77 * SIZE: Size of the region.
78 */
79void __flush_purge_region(void *start, int size)
80{
81 unsigned long v;
82 unsigned long begin, end;
83
84 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
85 end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
86 & ~(L1_CACHE_BYTES-1);
87 for (v = begin; v < end; v+=L1_CACHE_BYTES) {
88 asm volatile("ocbp %0"
89 : /* no output */
90 : "m" (__m(v)));
91 }
92}
93
94
95/*
96 * No write back please
97 */
98void __flush_invalidate_region(void *start, int size)
99{
100 unsigned long v;
101 unsigned long begin, end;
102
103 begin = (unsigned long)start & ~(L1_CACHE_BYTES-1);
104 end = ((unsigned long)start + size + L1_CACHE_BYTES-1)
105 & ~(L1_CACHE_BYTES-1);
106 for (v = begin; v < end; v+=L1_CACHE_BYTES) {
107 asm volatile("ocbi %0"
108 : /* no output */
109 : "m" (__m(v)));
110 }
111}
112
113static void __flush_dcache_all_ex(void)
114{
115 unsigned long addr, end_addr, entry_offset;
116
a252710f
PM
117 end_addr = CACHE_OC_ADDRESS_ARRAY +
118 (cpu_data->dcache.sets << cpu_data->dcache.entry_shift) *
119 cpu_data->dcache.ways;
120
1da177e4 121 entry_offset = 1 << cpu_data->dcache.entry_shift;
a252710f
PM
122 for (addr = CACHE_OC_ADDRESS_ARRAY;
123 addr < end_addr;
124 addr += entry_offset) {
1da177e4
LT
125 ctrl_outl(0, addr);
126 }
127}
128
129static void __flush_cache_4096_all_ex(unsigned long start)
130{
131 unsigned long addr, entry_offset;
132 int i;
133
134 entry_offset = 1 << cpu_data->dcache.entry_shift;
a252710f
PM
135 for (i = 0; i < cpu_data->dcache.ways;
136 i++, start += cpu_data->dcache.way_incr) {
1da177e4
LT
137 for (addr = CACHE_OC_ADDRESS_ARRAY + start;
138 addr < CACHE_OC_ADDRESS_ARRAY + 4096 + start;
139 addr += entry_offset) {
140 ctrl_outl(0, addr);
141 }
142 }
143}
144
145void flush_cache_4096_all(unsigned long start)
146{
147 if (cpu_data->dcache.ways == 1)
148 __flush_cache_4096_all(start);
149 else
150 __flush_cache_4096_all_ex(start);
151}
152
153/*
154 * Write back the range of D-cache, and purge the I-cache.
155 *
156 * Called from kernel/module.c:sys_init_module and routine for a.out format.
157 */
158void flush_icache_range(unsigned long start, unsigned long end)
159{
160 flush_cache_all();
161}
162
163/*
a252710f 164 * Write back the D-cache and purge the I-cache for signal trampoline.
1da177e4
LT
165 * .. which happens to be the same behavior as flush_icache_range().
166 * So, we simply flush out a line.
167 */
168void flush_cache_sigtramp(unsigned long addr)
169{
170 unsigned long v, index;
a252710f 171 unsigned long flags;
1da177e4
LT
172 int i;
173
174 v = addr & ~(L1_CACHE_BYTES-1);
175 asm volatile("ocbwb %0"
176 : /* no output */
177 : "m" (__m(v)));
178
179 index = CACHE_IC_ADDRESS_ARRAY | (v & cpu_data->icache.entry_mask);
180
181 local_irq_save(flags);
182 jump_to_P2();
a252710f
PM
183 for (i = 0; i < cpu_data->icache.ways;
184 i++, index += cpu_data->icache.way_incr)
1da177e4
LT
185 ctrl_outl(0, index); /* Clear out Valid-bit */
186 back_to_P1();
187 local_irq_restore(flags);
188}
189
190static inline void flush_cache_4096(unsigned long start,
191 unsigned long phys)
192{
a252710f 193 unsigned long flags;
1da177e4
LT
194
195 /*
196 * SH7751, SH7751R, and ST40 have no restriction to handle cache.
197 * (While SH7750 must do that at P2 area.)
198 */
199 if ((cpu_data->flags & CPU_HAS_P2_FLUSH_BUG)
200 || start < CACHE_OC_ADDRESS_ARRAY) {
201 local_irq_save(flags);
a252710f
PM
202 __flush_cache_4096(start | SH_CACHE_ASSOC,
203 P1SEGADDR(phys), 0x20000000);
1da177e4
LT
204 local_irq_restore(flags);
205 } else {
a252710f
PM
206 __flush_cache_4096(start | SH_CACHE_ASSOC,
207 P1SEGADDR(phys), 0);
1da177e4
LT
208 }
209}
210
211/*
212 * Write back & invalidate the D-cache of the page.
213 * (To avoid "alias" issues)
214 */
215void flush_dcache_page(struct page *page)
216{
217 if (test_bit(PG_mapped, &page->flags)) {
218 unsigned long phys = PHYSADDR(page_address(page));
219
220 /* Loop all the D-cache */
221 flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys);
222 flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x1000, phys);
223 flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x2000, phys);
224 flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x3000, phys);
225 }
226}
227
228static inline void flush_icache_all(void)
229{
230 unsigned long flags, ccr;
231
232 local_irq_save(flags);
233 jump_to_P2();
234
235 /* Flush I-cache */
236 ccr = ctrl_inl(CCR);
237 ccr |= CCR_CACHE_ICI;
238 ctrl_outl(ccr, CCR);
239
240 back_to_P1();
241 local_irq_restore(flags);
242}
243
a252710f 244void flush_dcache_all(void)
1da177e4
LT
245{
246 if (cpu_data->dcache.ways == 1)
247 __flush_dcache_all();
248 else
249 __flush_dcache_all_ex();
a252710f
PM
250}
251
252void flush_cache_all(void)
253{
254 flush_dcache_all();
1da177e4
LT
255 flush_icache_all();
256}
257
258void flush_cache_mm(struct mm_struct *mm)
259{
1da177e4
LT
260 flush_cache_all();
261}
262
263/*
264 * Write back and invalidate I/D-caches for the page.
265 *
266 * ADDR: Virtual Address (U0 address)
267 * PFN: Physical page number
268 */
269void flush_cache_page(struct vm_area_struct *vma, unsigned long address, unsigned long pfn)
270{
271 unsigned long phys = pfn << PAGE_SHIFT;
272
273 /* We only need to flush D-cache when we have alias */
274 if ((address^phys) & CACHE_ALIAS) {
275 /* Loop 4K of the D-cache */
276 flush_cache_4096(
277 CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS),
278 phys);
279 /* Loop another 4K of the D-cache */
280 flush_cache_4096(
281 CACHE_OC_ADDRESS_ARRAY | (phys & CACHE_ALIAS),
282 phys);
283 }
284
285 if (vma->vm_flags & VM_EXEC)
286 /* Loop 4K (half) of the I-cache */
287 flush_cache_4096(
288 CACHE_IC_ADDRESS_ARRAY | (address & 0x1000),
289 phys);
290}
291
292/*
293 * Write back and invalidate D-caches.
294 *
295 * START, END: Virtual Address (U0 address)
296 *
297 * NOTE: We need to flush the _physical_ page entry.
298 * Flushing the cache lines for U0 only isn't enough.
299 * We need to flush for P1 too, which may contain aliases.
300 */
301void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
302 unsigned long end)
303{
304 unsigned long p = start & PAGE_MASK;
305 pgd_t *dir;
306 pmd_t *pmd;
a252710f 307 pud_t *pud;
1da177e4
LT
308 pte_t *pte;
309 pte_t entry;
310 unsigned long phys;
311 unsigned long d = 0;
312
a252710f
PM
313 /*
314 * Don't bother with the lookup and alias check if we have a
315 * wide range to cover, just blow away the dcache in its
316 * entirety instead. -- PFM.
317 */
318 if (((end - start) >> PAGE_SHIFT) >= 64) {
319 flush_dcache_all();
320
321 if (vma->vm_flags & VM_EXEC)
322 flush_icache_all();
323
324 return;
325 }
326
1da177e4 327 dir = pgd_offset(vma->vm_mm, p);
a252710f
PM
328 pud = pud_offset(dir, p);
329 pmd = pmd_offset(pud, p);
330 end = PAGE_ALIGN(end);
1da177e4
LT
331
332 do {
333 if (pmd_none(*pmd) || pmd_bad(*pmd)) {
334 p &= ~((1 << PMD_SHIFT) -1);
335 p += (1 << PMD_SHIFT);
336 pmd++;
337 continue;
338 }
339 pte = pte_offset_kernel(pmd, p);
340 do {
341 entry = *pte;
342 if ((pte_val(entry) & _PAGE_PRESENT)) {
343 phys = pte_val(entry)&PTE_PHYS_MASK;
344 if ((p^phys) & CACHE_ALIAS) {
a252710f 345 d |= 1 << ((p & CACHE_ALIAS)>>12);
1da177e4
LT
346 d |= 1 << ((phys & CACHE_ALIAS)>>12);
347 if (d == 0x0f)
348 goto loop_exit;
349 }
350 }
351 pte++;
352 p += PAGE_SIZE;
353 } while (p < end && ((unsigned long)pte & ~PAGE_MASK));
354 pmd++;
355 } while (p < end);
356 loop_exit:
357 if (d & 1)
358 flush_cache_4096_all(0);
359 if (d & 2)
360 flush_cache_4096_all(0x1000);
361 if (d & 4)
362 flush_cache_4096_all(0x2000);
363 if (d & 8)
364 flush_cache_4096_all(0x3000);
365 if (vma->vm_flags & VM_EXEC)
366 flush_icache_all();
367}
368
369/*
370 * flush_icache_user_range
371 * @vma: VMA of the process
372 * @page: page
373 * @addr: U0 address
374 * @len: length of the range (< page size)
375 */
376void flush_icache_user_range(struct vm_area_struct *vma,
377 struct page *page, unsigned long addr, int len)
378{
379 flush_cache_page(vma, addr, page_to_pfn(page));
380}
381
This page took 0.157843 seconds and 5 git commands to generate.