diff options
author | Paul Mundt <lethal@linux-sh.org> | 2011-01-13 01:06:28 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-01-13 01:06:28 -0500 |
commit | f43dc23d5ea91fca257be02138a255f02d98e806 (patch) | |
tree | b29722f6e965316e90ac97abf79923ced250dc21 /arch/sh/mm/cache-sh4.c | |
parent | f8e53553f452dcbf67cb89c8cba63a1cd6eb4cc0 (diff) | |
parent | 4162cf64973df51fc885825bc9ca4d055891c49f (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 into common/serial-rework
Conflicts:
arch/sh/kernel/cpu/sh2/setup-sh7619.c
arch/sh/kernel/cpu/sh2a/setup-mxg.c
arch/sh/kernel/cpu/sh2a/setup-sh7201.c
arch/sh/kernel/cpu/sh2a/setup-sh7203.c
arch/sh/kernel/cpu/sh2a/setup-sh7206.c
arch/sh/kernel/cpu/sh3/setup-sh7705.c
arch/sh/kernel/cpu/sh3/setup-sh770x.c
arch/sh/kernel/cpu/sh3/setup-sh7710.c
arch/sh/kernel/cpu/sh3/setup-sh7720.c
arch/sh/kernel/cpu/sh4/setup-sh4-202.c
arch/sh/kernel/cpu/sh4/setup-sh7750.c
arch/sh/kernel/cpu/sh4/setup-sh7760.c
arch/sh/kernel/cpu/sh4a/setup-sh7343.c
arch/sh/kernel/cpu/sh4a/setup-sh7366.c
arch/sh/kernel/cpu/sh4a/setup-sh7722.c
arch/sh/kernel/cpu/sh4a/setup-sh7723.c
arch/sh/kernel/cpu/sh4a/setup-sh7724.c
arch/sh/kernel/cpu/sh4a/setup-sh7763.c
arch/sh/kernel/cpu/sh4a/setup-sh7770.c
arch/sh/kernel/cpu/sh4a/setup-sh7780.c
arch/sh/kernel/cpu/sh4a/setup-sh7785.c
arch/sh/kernel/cpu/sh4a/setup-sh7786.c
arch/sh/kernel/cpu/sh4a/setup-shx3.c
arch/sh/kernel/cpu/sh5/setup-sh5.c
drivers/serial/sh-sci.c
drivers/serial/sh-sci.h
include/linux/serial_sci.h
Diffstat (limited to 'arch/sh/mm/cache-sh4.c')
-rw-r--r-- | arch/sh/mm/cache-sh4.c | 744 |
1 files changed, 181 insertions, 563 deletions
diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 5cfe08dbb59e..92eb98633ab0 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * arch/sh/mm/cache-sh4.c | 2 | * arch/sh/mm/cache-sh4.c |
3 | * | 3 | * |
4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka | 4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka |
5 | * Copyright (C) 2001 - 2007 Paul Mundt | 5 | * Copyright (C) 2001 - 2009 Paul Mundt |
6 | * Copyright (C) 2003 Richard Curnow | 6 | * Copyright (C) 2003 Richard Curnow |
7 | * Copyright (c) 2007 STMicroelectronics (R&D) Ltd. | 7 | * Copyright (c) 2007 STMicroelectronics (R&D) Ltd. |
8 | * | 8 | * |
@@ -14,6 +14,9 @@ | |||
14 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
15 | #include <linux/io.h> | 15 | #include <linux/io.h> |
16 | #include <linux/mutex.h> | 16 | #include <linux/mutex.h> |
17 | #include <linux/fs.h> | ||
18 | #include <linux/highmem.h> | ||
19 | #include <asm/pgtable.h> | ||
17 | #include <asm/mmu_context.h> | 20 | #include <asm/mmu_context.h> |
18 | #include <asm/cacheflush.h> | 21 | #include <asm/cacheflush.h> |
19 | 22 | ||
@@ -22,221 +25,80 @@ | |||
22 | * flushing. Anything exceeding this will simply flush the dcache in its | 25 | * flushing. Anything exceeding this will simply flush the dcache in its |
23 | * entirety. | 26 | * entirety. |
24 | */ | 27 | */ |
25 | #define MAX_DCACHE_PAGES 64 /* XXX: Tune for ways */ | ||
26 | #define MAX_ICACHE_PAGES 32 | 28 | #define MAX_ICACHE_PAGES 32 |
27 | 29 | ||
28 | static void __flush_dcache_segment_1way(unsigned long start, | 30 | static void __flush_cache_one(unsigned long addr, unsigned long phys, |
29 | unsigned long extent); | ||
30 | static void __flush_dcache_segment_2way(unsigned long start, | ||
31 | unsigned long extent); | ||
32 | static void __flush_dcache_segment_4way(unsigned long start, | ||
33 | unsigned long extent); | ||
34 | |||
35 | static void __flush_cache_4096(unsigned long addr, unsigned long phys, | ||
36 | unsigned long exec_offset); | 31 | unsigned long exec_offset); |
37 | 32 | ||
38 | /* | 33 | /* |
39 | * This is initialised here to ensure that it is not placed in the BSS. If | 34 | * Write back the range of D-cache, and purge the I-cache. |
40 | * that were to happen, note that cache_init gets called before the BSS is | 35 | * |
41 | * cleared, so this would get nulled out which would be hopeless. | 36 | * Called from kernel/module.c:sys_init_module and routine for a.out format, |
37 | * signal handler code and kprobes code | ||
42 | */ | 38 | */ |
43 | static void (*__flush_dcache_segment_fn)(unsigned long, unsigned long) = | 39 | static void sh4_flush_icache_range(void *args) |
44 | (void (*)(unsigned long, unsigned long))0xdeadbeef; | ||
45 | |||
46 | static void compute_alias(struct cache_info *c) | ||
47 | { | 40 | { |
48 | c->alias_mask = ((c->sets - 1) << c->entry_shift) & ~(PAGE_SIZE - 1); | 41 | struct flusher_data *data = args; |
49 | c->n_aliases = c->alias_mask ? (c->alias_mask >> PAGE_SHIFT) + 1 : 0; | 42 | unsigned long start, end; |
50 | } | 43 | unsigned long flags, v; |
44 | int i; | ||
51 | 45 | ||
52 | static void __init emit_cache_params(void) | 46 | start = data->addr1; |
53 | { | 47 | end = data->addr2; |
54 | printk("PVR=%08x CVR=%08x PRR=%08x\n", | 48 | |
55 | ctrl_inl(CCN_PVR), | 49 | /* If there are too many pages then just blow away the caches */ |
56 | ctrl_inl(CCN_CVR), | 50 | if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) { |
57 | ctrl_inl(CCN_PRR)); | 51 | local_flush_cache_all(NULL); |
58 | printk("I-cache : n_ways=%d n_sets=%d way_incr=%d\n", | 52 | return; |
59 | boot_cpu_data.icache.ways, | 53 | } |
60 | boot_cpu_data.icache.sets, | ||
61 | boot_cpu_data.icache.way_incr); | ||
62 | printk("I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | ||
63 | boot_cpu_data.icache.entry_mask, | ||
64 | boot_cpu_data.icache.alias_mask, | ||
65 | boot_cpu_data.icache.n_aliases); | ||
66 | printk("D-cache : n_ways=%d n_sets=%d way_incr=%d\n", | ||
67 | boot_cpu_data.dcache.ways, | ||
68 | boot_cpu_data.dcache.sets, | ||
69 | boot_cpu_data.dcache.way_incr); | ||
70 | printk("D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | ||
71 | boot_cpu_data.dcache.entry_mask, | ||
72 | boot_cpu_data.dcache.alias_mask, | ||
73 | boot_cpu_data.dcache.n_aliases); | ||
74 | 54 | ||
75 | /* | 55 | /* |
76 | * Emit Secondary Cache parameters if the CPU has a probed L2. | 56 | * Selectively flush d-cache then invalidate the i-cache. |
57 | * This is inefficient, so only use this for small ranges. | ||
77 | */ | 58 | */ |
78 | if (boot_cpu_data.flags & CPU_HAS_L2_CACHE) { | 59 | start &= ~(L1_CACHE_BYTES-1); |
79 | printk("S-cache : n_ways=%d n_sets=%d way_incr=%d\n", | 60 | end += L1_CACHE_BYTES-1; |
80 | boot_cpu_data.scache.ways, | 61 | end &= ~(L1_CACHE_BYTES-1); |
81 | boot_cpu_data.scache.sets, | ||
82 | boot_cpu_data.scache.way_incr); | ||
83 | printk("S-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | ||
84 | boot_cpu_data.scache.entry_mask, | ||
85 | boot_cpu_data.scache.alias_mask, | ||
86 | boot_cpu_data.scache.n_aliases); | ||
87 | } | ||
88 | 62 | ||
89 | if (!__flush_dcache_segment_fn) | 63 | local_irq_save(flags); |
90 | panic("unknown number of cache ways\n"); | 64 | jump_to_uncached(); |
91 | } | ||
92 | 65 | ||
93 | /* | 66 | for (v = start; v < end; v += L1_CACHE_BYTES) { |
94 | * SH-4 has virtually indexed and physically tagged cache. | 67 | unsigned long icacheaddr; |
95 | */ | 68 | int j, n; |
96 | void __init p3_cache_init(void) | ||
97 | { | ||
98 | compute_alias(&boot_cpu_data.icache); | ||
99 | compute_alias(&boot_cpu_data.dcache); | ||
100 | compute_alias(&boot_cpu_data.scache); | ||
101 | |||
102 | switch (boot_cpu_data.dcache.ways) { | ||
103 | case 1: | ||
104 | __flush_dcache_segment_fn = __flush_dcache_segment_1way; | ||
105 | break; | ||
106 | case 2: | ||
107 | __flush_dcache_segment_fn = __flush_dcache_segment_2way; | ||
108 | break; | ||
109 | case 4: | ||
110 | __flush_dcache_segment_fn = __flush_dcache_segment_4way; | ||
111 | break; | ||
112 | default: | ||
113 | __flush_dcache_segment_fn = NULL; | ||
114 | break; | ||
115 | } | ||
116 | 69 | ||
117 | emit_cache_params(); | 70 | __ocbwb(v); |
118 | } | ||
119 | 71 | ||
120 | /* | 72 | icacheaddr = CACHE_IC_ADDRESS_ARRAY | (v & |
121 | * Write back the dirty D-caches, but not invalidate them. | 73 | cpu_data->icache.entry_mask); |
122 | * | ||
123 | * START: Virtual Address (U0, P1, or P3) | ||
124 | * SIZE: Size of the region. | ||
125 | */ | ||
126 | void __flush_wback_region(void *start, int size) | ||
127 | { | ||
128 | unsigned long v; | ||
129 | unsigned long begin, end; | ||
130 | |||
131 | begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); | ||
132 | end = ((unsigned long)start + size + L1_CACHE_BYTES-1) | ||
133 | & ~(L1_CACHE_BYTES-1); | ||
134 | for (v = begin; v < end; v+=L1_CACHE_BYTES) { | ||
135 | asm volatile("ocbwb %0" | ||
136 | : /* no output */ | ||
137 | : "m" (__m(v))); | ||
138 | } | ||
139 | } | ||
140 | 74 | ||
141 | /* | 75 | /* Clear i-cache line valid-bit */ |
142 | * Write back the dirty D-caches and invalidate them. | 76 | n = boot_cpu_data.icache.n_aliases; |
143 | * | 77 | for (i = 0; i < cpu_data->icache.ways; i++) { |
144 | * START: Virtual Address (U0, P1, or P3) | 78 | for (j = 0; j < n; j++) |
145 | * SIZE: Size of the region. | 79 | __raw_writel(0, icacheaddr + (j * PAGE_SIZE)); |
146 | */ | 80 | icacheaddr += cpu_data->icache.way_incr; |
147 | void __flush_purge_region(void *start, int size) | 81 | } |
148 | { | ||
149 | unsigned long v; | ||
150 | unsigned long begin, end; | ||
151 | |||
152 | begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); | ||
153 | end = ((unsigned long)start + size + L1_CACHE_BYTES-1) | ||
154 | & ~(L1_CACHE_BYTES-1); | ||
155 | for (v = begin; v < end; v+=L1_CACHE_BYTES) { | ||
156 | asm volatile("ocbp %0" | ||
157 | : /* no output */ | ||
158 | : "m" (__m(v))); | ||
159 | } | 82 | } |
160 | } | ||
161 | 83 | ||
162 | /* | 84 | back_to_cached(); |
163 | * No write back please | 85 | local_irq_restore(flags); |
164 | */ | ||
165 | void __flush_invalidate_region(void *start, int size) | ||
166 | { | ||
167 | unsigned long v; | ||
168 | unsigned long begin, end; | ||
169 | |||
170 | begin = (unsigned long)start & ~(L1_CACHE_BYTES-1); | ||
171 | end = ((unsigned long)start + size + L1_CACHE_BYTES-1) | ||
172 | & ~(L1_CACHE_BYTES-1); | ||
173 | for (v = begin; v < end; v+=L1_CACHE_BYTES) { | ||
174 | asm volatile("ocbi %0" | ||
175 | : /* no output */ | ||
176 | : "m" (__m(v))); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * Write back the range of D-cache, and purge the I-cache. | ||
182 | * | ||
183 | * Called from kernel/module.c:sys_init_module and routine for a.out format, | ||
184 | * signal handler code and kprobes code | ||
185 | */ | ||
186 | void flush_icache_range(unsigned long start, unsigned long end) | ||
187 | { | ||
188 | int icacheaddr; | ||
189 | unsigned long flags, v; | ||
190 | int i; | ||
191 | |||
192 | /* If there are too many pages then just blow the caches */ | ||
193 | if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) { | ||
194 | flush_cache_all(); | ||
195 | } else { | ||
196 | /* selectively flush d-cache then invalidate the i-cache */ | ||
197 | /* this is inefficient, so only use for small ranges */ | ||
198 | start &= ~(L1_CACHE_BYTES-1); | ||
199 | end += L1_CACHE_BYTES-1; | ||
200 | end &= ~(L1_CACHE_BYTES-1); | ||
201 | |||
202 | local_irq_save(flags); | ||
203 | jump_to_uncached(); | ||
204 | |||
205 | for (v = start; v < end; v+=L1_CACHE_BYTES) { | ||
206 | asm volatile("ocbwb %0" | ||
207 | : /* no output */ | ||
208 | : "m" (__m(v))); | ||
209 | |||
210 | icacheaddr = CACHE_IC_ADDRESS_ARRAY | ( | ||
211 | v & cpu_data->icache.entry_mask); | ||
212 | |||
213 | for (i = 0; i < cpu_data->icache.ways; | ||
214 | i++, icacheaddr += cpu_data->icache.way_incr) | ||
215 | /* Clear i-cache line valid-bit */ | ||
216 | ctrl_outl(0, icacheaddr); | ||
217 | } | ||
218 | |||
219 | back_to_cached(); | ||
220 | local_irq_restore(flags); | ||
221 | } | ||
222 | } | 86 | } |
223 | 87 | ||
224 | static inline void flush_cache_4096(unsigned long start, | 88 | static inline void flush_cache_one(unsigned long start, unsigned long phys) |
225 | unsigned long phys) | ||
226 | { | 89 | { |
227 | unsigned long flags, exec_offset = 0; | 90 | unsigned long flags, exec_offset = 0; |
228 | 91 | ||
229 | /* | 92 | /* |
230 | * All types of SH-4 require PC to be in P2 to operate on the I-cache. | 93 | * All types of SH-4 require PC to be uncached to operate on the I-cache. |
231 | * Some types of SH-4 require PC to be in P2 to operate on the D-cache. | 94 | * Some types of SH-4 require PC to be uncached to operate on the D-cache. |
232 | */ | 95 | */ |
233 | if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) || | 96 | if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) || |
234 | (start < CACHE_OC_ADDRESS_ARRAY)) | 97 | (start < CACHE_OC_ADDRESS_ARRAY)) |
235 | exec_offset = 0x20000000; | 98 | exec_offset = cached_to_uncached; |
236 | 99 | ||
237 | local_irq_save(flags); | 100 | local_irq_save(flags); |
238 | __flush_cache_4096(start | SH_CACHE_ASSOC, | 101 | __flush_cache_one(start, phys, exec_offset); |
239 | P1SEGADDR(phys), exec_offset); | ||
240 | local_irq_restore(flags); | 102 | local_irq_restore(flags); |
241 | } | 103 | } |
242 | 104 | ||
@@ -244,24 +106,25 @@ static inline void flush_cache_4096(unsigned long start, | |||
244 | * Write back & invalidate the D-cache of the page. | 106 | * Write back & invalidate the D-cache of the page. |
245 | * (To avoid "alias" issues) | 107 | * (To avoid "alias" issues) |
246 | */ | 108 | */ |
247 | void flush_dcache_page(struct page *page) | 109 | static void sh4_flush_dcache_page(void *arg) |
248 | { | 110 | { |
249 | if (test_bit(PG_mapped, &page->flags)) { | 111 | struct page *page = arg; |
250 | unsigned long phys = PHYSADDR(page_address(page)); | 112 | unsigned long addr = (unsigned long)page_address(page); |
251 | unsigned long addr = CACHE_OC_ADDRESS_ARRAY; | 113 | #ifndef CONFIG_SMP |
252 | int i, n; | 114 | struct address_space *mapping = page_mapping(page); |
253 | 115 | ||
254 | /* Loop all the D-cache */ | 116 | if (mapping && !mapping_mapped(mapping)) |
255 | n = boot_cpu_data.dcache.n_aliases; | 117 | clear_bit(PG_dcache_clean, &page->flags); |
256 | for (i = 0; i < n; i++, addr += 4096) | 118 | else |
257 | flush_cache_4096(addr, phys); | 119 | #endif |
258 | } | 120 | flush_cache_one(CACHE_OC_ADDRESS_ARRAY | |
121 | (addr & shm_align_mask), page_to_phys(page)); | ||
259 | 122 | ||
260 | wmb(); | 123 | wmb(); |
261 | } | 124 | } |
262 | 125 | ||
263 | /* TODO: Selective icache invalidation through IC address array.. */ | 126 | /* TODO: Selective icache invalidation through IC address array.. */ |
264 | static void __uses_jump_to_uncached flush_icache_all(void) | 127 | static void flush_icache_all(void) |
265 | { | 128 | { |
266 | unsigned long flags, ccr; | 129 | unsigned long flags, ccr; |
267 | 130 | ||
@@ -269,9 +132,9 @@ static void __uses_jump_to_uncached flush_icache_all(void) | |||
269 | jump_to_uncached(); | 132 | jump_to_uncached(); |
270 | 133 | ||
271 | /* Flush I-cache */ | 134 | /* Flush I-cache */ |
272 | ccr = ctrl_inl(CCR); | 135 | ccr = __raw_readl(CCR); |
273 | ccr |= CCR_CACHE_ICI; | 136 | ccr |= CCR_CACHE_ICI; |
274 | ctrl_outl(ccr, CCR); | 137 | __raw_writel(ccr, CCR); |
275 | 138 | ||
276 | /* | 139 | /* |
277 | * back_to_cached() will take care of the barrier for us, don't add | 140 | * back_to_cached() will take care of the barrier for us, don't add |
@@ -282,133 +145,53 @@ static void __uses_jump_to_uncached flush_icache_all(void) | |||
282 | local_irq_restore(flags); | 145 | local_irq_restore(flags); |
283 | } | 146 | } |
284 | 147 | ||
285 | void flush_dcache_all(void) | 148 | static void flush_dcache_all(void) |
286 | { | 149 | { |
287 | (*__flush_dcache_segment_fn)(0UL, boot_cpu_data.dcache.way_size); | 150 | unsigned long addr, end_addr, entry_offset; |
288 | wmb(); | 151 | |
152 | end_addr = CACHE_OC_ADDRESS_ARRAY + | ||
153 | (current_cpu_data.dcache.sets << | ||
154 | current_cpu_data.dcache.entry_shift) * | ||
155 | current_cpu_data.dcache.ways; | ||
156 | |||
157 | entry_offset = 1 << current_cpu_data.dcache.entry_shift; | ||
158 | |||
159 | for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; ) { | ||
160 | __raw_writel(0, addr); addr += entry_offset; | ||
161 | __raw_writel(0, addr); addr += entry_offset; | ||
162 | __raw_writel(0, addr); addr += entry_offset; | ||
163 | __raw_writel(0, addr); addr += entry_offset; | ||
164 | __raw_writel(0, addr); addr += entry_offset; | ||
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 | } | ||
289 | } | 169 | } |
290 | 170 | ||
291 | void flush_cache_all(void) | 171 | static void sh4_flush_cache_all(void *unused) |
292 | { | 172 | { |
293 | flush_dcache_all(); | 173 | flush_dcache_all(); |
294 | flush_icache_all(); | 174 | flush_icache_all(); |
295 | } | 175 | } |
296 | 176 | ||
297 | static void __flush_cache_mm(struct mm_struct *mm, unsigned long start, | ||
298 | unsigned long end) | ||
299 | { | ||
300 | unsigned long d = 0, p = start & PAGE_MASK; | ||
301 | unsigned long alias_mask = boot_cpu_data.dcache.alias_mask; | ||
302 | unsigned long n_aliases = boot_cpu_data.dcache.n_aliases; | ||
303 | unsigned long select_bit; | ||
304 | unsigned long all_aliases_mask; | ||
305 | unsigned long addr_offset; | ||
306 | pgd_t *dir; | ||
307 | pmd_t *pmd; | ||
308 | pud_t *pud; | ||
309 | pte_t *pte; | ||
310 | int i; | ||
311 | |||
312 | dir = pgd_offset(mm, p); | ||
313 | pud = pud_offset(dir, p); | ||
314 | pmd = pmd_offset(pud, p); | ||
315 | end = PAGE_ALIGN(end); | ||
316 | |||
317 | all_aliases_mask = (1 << n_aliases) - 1; | ||
318 | |||
319 | do { | ||
320 | if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) { | ||
321 | p &= PMD_MASK; | ||
322 | p += PMD_SIZE; | ||
323 | pmd++; | ||
324 | |||
325 | continue; | ||
326 | } | ||
327 | |||
328 | pte = pte_offset_kernel(pmd, p); | ||
329 | |||
330 | do { | ||
331 | unsigned long phys; | ||
332 | pte_t entry = *pte; | ||
333 | |||
334 | if (!(pte_val(entry) & _PAGE_PRESENT)) { | ||
335 | pte++; | ||
336 | p += PAGE_SIZE; | ||
337 | continue; | ||
338 | } | ||
339 | |||
340 | phys = pte_val(entry) & PTE_PHYS_MASK; | ||
341 | |||
342 | if ((p ^ phys) & alias_mask) { | ||
343 | d |= 1 << ((p & alias_mask) >> PAGE_SHIFT); | ||
344 | d |= 1 << ((phys & alias_mask) >> PAGE_SHIFT); | ||
345 | |||
346 | if (d == all_aliases_mask) | ||
347 | goto loop_exit; | ||
348 | } | ||
349 | |||
350 | pte++; | ||
351 | p += PAGE_SIZE; | ||
352 | } while (p < end && ((unsigned long)pte & ~PAGE_MASK)); | ||
353 | pmd++; | ||
354 | } while (p < end); | ||
355 | |||
356 | loop_exit: | ||
357 | addr_offset = 0; | ||
358 | select_bit = 1; | ||
359 | |||
360 | for (i = 0; i < n_aliases; i++) { | ||
361 | if (d & select_bit) { | ||
362 | (*__flush_dcache_segment_fn)(addr_offset, PAGE_SIZE); | ||
363 | wmb(); | ||
364 | } | ||
365 | |||
366 | select_bit <<= 1; | ||
367 | addr_offset += PAGE_SIZE; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | /* | 177 | /* |
372 | * Note : (RPC) since the caches are physically tagged, the only point | 178 | * Note : (RPC) since the caches are physically tagged, the only point |
373 | * of flush_cache_mm for SH-4 is to get rid of aliases from the | 179 | * of flush_cache_mm for SH-4 is to get rid of aliases from the |
374 | * D-cache. The assumption elsewhere, e.g. flush_cache_range, is that | 180 | * D-cache. The assumption elsewhere, e.g. flush_cache_range, is that |
375 | * lines can stay resident so long as the virtual address they were | 181 | * lines can stay resident so long as the virtual address they were |
376 | * accessed with (hence cache set) is in accord with the physical | 182 | * accessed with (hence cache set) is in accord with the physical |
377 | * address (i.e. tag). It's no different here. So I reckon we don't | 183 | * address (i.e. tag). It's no different here. |
378 | * need to flush the I-cache, since aliases don't matter for that. We | ||
379 | * should try that. | ||
380 | * | 184 | * |
381 | * Caller takes mm->mmap_sem. | 185 | * Caller takes mm->mmap_sem. |
382 | */ | 186 | */ |
383 | void flush_cache_mm(struct mm_struct *mm) | 187 | static void sh4_flush_cache_mm(void *arg) |
384 | { | 188 | { |
385 | /* | 189 | struct mm_struct *mm = arg; |
386 | * If cache is only 4k-per-way, there are never any 'aliases'. Since | ||
387 | * the cache is physically tagged, the data can just be left in there. | ||
388 | */ | ||
389 | if (boot_cpu_data.dcache.n_aliases == 0) | ||
390 | return; | ||
391 | 190 | ||
392 | /* | 191 | if (cpu_context(smp_processor_id(), mm) == NO_CONTEXT) |
393 | * Don't bother groveling around the dcache for the VMA ranges | 192 | return; |
394 | * if there are too many PTEs to make it worthwhile. | ||
395 | */ | ||
396 | if (mm->nr_ptes >= MAX_DCACHE_PAGES) | ||
397 | flush_dcache_all(); | ||
398 | else { | ||
399 | struct vm_area_struct *vma; | ||
400 | |||
401 | /* | ||
402 | * In this case there are reasonably sized ranges to flush, | ||
403 | * iterate through the VMA list and take care of any aliases. | ||
404 | */ | ||
405 | for (vma = mm->mmap; vma; vma = vma->vm_next) | ||
406 | __flush_cache_mm(mm, vma->vm_start, vma->vm_end); | ||
407 | } | ||
408 | 193 | ||
409 | /* Only touch the icache if one of the VMAs has VM_EXEC set. */ | 194 | flush_dcache_all(); |
410 | if (mm->exec_vm) | ||
411 | flush_icache_all(); | ||
412 | } | 195 | } |
413 | 196 | ||
414 | /* | 197 | /* |
@@ -417,39 +200,66 @@ void flush_cache_mm(struct mm_struct *mm) | |||
417 | * ADDR: Virtual Address (U0 address) | 200 | * ADDR: Virtual Address (U0 address) |
418 | * PFN: Physical page number | 201 | * PFN: Physical page number |
419 | */ | 202 | */ |
420 | void flush_cache_page(struct vm_area_struct *vma, unsigned long address, | 203 | static void sh4_flush_cache_page(void *args) |
421 | unsigned long pfn) | ||
422 | { | 204 | { |
423 | unsigned long phys = pfn << PAGE_SHIFT; | 205 | struct flusher_data *data = args; |
424 | unsigned int alias_mask; | 206 | struct vm_area_struct *vma; |
425 | 207 | struct page *page; | |
426 | alias_mask = boot_cpu_data.dcache.alias_mask; | 208 | unsigned long address, pfn, phys; |
427 | 209 | int map_coherent = 0; | |
428 | /* We only need to flush D-cache when we have alias */ | 210 | pgd_t *pgd; |
429 | if ((address^phys) & alias_mask) { | 211 | pud_t *pud; |
430 | /* Loop 4K of the D-cache */ | 212 | pmd_t *pmd; |
431 | flush_cache_4096( | 213 | pte_t *pte; |
432 | CACHE_OC_ADDRESS_ARRAY | (address & alias_mask), | 214 | void *vaddr; |
433 | phys); | 215 | |
434 | /* Loop another 4K of the D-cache */ | 216 | vma = data->vma; |
435 | flush_cache_4096( | 217 | address = data->addr1 & PAGE_MASK; |
436 | CACHE_OC_ADDRESS_ARRAY | (phys & alias_mask), | 218 | pfn = data->addr2; |
437 | phys); | 219 | phys = pfn << PAGE_SHIFT; |
438 | } | 220 | page = pfn_to_page(pfn); |
221 | |||
222 | if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT) | ||
223 | return; | ||
439 | 224 | ||
440 | alias_mask = boot_cpu_data.icache.alias_mask; | 225 | pgd = pgd_offset(vma->vm_mm, address); |
441 | if (vma->vm_flags & VM_EXEC) { | 226 | pud = pud_offset(pgd, address); |
227 | pmd = pmd_offset(pud, address); | ||
228 | pte = pte_offset_kernel(pmd, address); | ||
229 | |||
230 | /* If the page isn't present, there is nothing to do here. */ | ||
231 | if (!(pte_val(*pte) & _PAGE_PRESENT)) | ||
232 | return; | ||
233 | |||
234 | if ((vma->vm_mm == current->active_mm)) | ||
235 | vaddr = NULL; | ||
236 | else { | ||
442 | /* | 237 | /* |
443 | * Evict entries from the portion of the cache from which code | 238 | * Use kmap_coherent or kmap_atomic to do flushes for |
444 | * may have been executed at this address (virtual). There's | 239 | * another ASID than the current one. |
445 | * no need to evict from the portion corresponding to the | ||
446 | * physical address as for the D-cache, because we know the | ||
447 | * kernel has never executed the code through its identity | ||
448 | * translation. | ||
449 | */ | 240 | */ |
450 | flush_cache_4096( | 241 | map_coherent = (current_cpu_data.dcache.n_aliases && |
451 | CACHE_IC_ADDRESS_ARRAY | (address & alias_mask), | 242 | test_bit(PG_dcache_clean, &page->flags) && |
452 | phys); | 243 | page_mapped(page)); |
244 | if (map_coherent) | ||
245 | vaddr = kmap_coherent(page, address); | ||
246 | else | ||
247 | vaddr = kmap_atomic(page, KM_USER0); | ||
248 | |||
249 | address = (unsigned long)vaddr; | ||
250 | } | ||
251 | |||
252 | flush_cache_one(CACHE_OC_ADDRESS_ARRAY | | ||
253 | (address & shm_align_mask), phys); | ||
254 | |||
255 | if (vma->vm_flags & VM_EXEC) | ||
256 | flush_icache_all(); | ||
257 | |||
258 | if (vaddr) { | ||
259 | if (map_coherent) | ||
260 | kunmap_coherent(vaddr); | ||
261 | else | ||
262 | kunmap_atomic(vaddr, KM_USER0); | ||
453 | } | 263 | } |
454 | } | 264 | } |
455 | 265 | ||
@@ -462,9 +272,19 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long address, | |||
462 | * Flushing the cache lines for U0 only isn't enough. | 272 | * Flushing the cache lines for U0 only isn't enough. |
463 | * We need to flush for P1 too, which may contain aliases. | 273 | * We need to flush for P1 too, which may contain aliases. |
464 | */ | 274 | */ |
465 | void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | 275 | static void sh4_flush_cache_range(void *args) |
466 | unsigned long end) | ||
467 | { | 276 | { |
277 | struct flusher_data *data = args; | ||
278 | struct vm_area_struct *vma; | ||
279 | unsigned long start, end; | ||
280 | |||
281 | vma = data->vma; | ||
282 | start = data->addr1; | ||
283 | end = data->addr2; | ||
284 | |||
285 | if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT) | ||
286 | return; | ||
287 | |||
468 | /* | 288 | /* |
469 | * If cache is only 4k-per-way, there are never any 'aliases'. Since | 289 | * If cache is only 4k-per-way, there are never any 'aliases'. Since |
470 | * the cache is physically tagged, the data can just be left in there. | 290 | * the cache is physically tagged, the data can just be left in there. |
@@ -472,42 +292,14 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | |||
472 | if (boot_cpu_data.dcache.n_aliases == 0) | 292 | if (boot_cpu_data.dcache.n_aliases == 0) |
473 | return; | 293 | return; |
474 | 294 | ||
475 | /* | 295 | flush_dcache_all(); |
476 | * Don't bother with the lookup and alias check if we have a | ||
477 | * wide range to cover, just blow away the dcache in its | ||
478 | * entirety instead. -- PFM. | ||
479 | */ | ||
480 | if (((end - start) >> PAGE_SHIFT) >= MAX_DCACHE_PAGES) | ||
481 | flush_dcache_all(); | ||
482 | else | ||
483 | __flush_cache_mm(vma->vm_mm, start, end); | ||
484 | 296 | ||
485 | if (vma->vm_flags & VM_EXEC) { | 297 | if (vma->vm_flags & VM_EXEC) |
486 | /* | ||
487 | * TODO: Is this required??? Need to look at how I-cache | ||
488 | * coherency is assured when new programs are loaded to see if | ||
489 | * this matters. | ||
490 | */ | ||
491 | flush_icache_all(); | 298 | flush_icache_all(); |
492 | } | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * flush_icache_user_range | ||
497 | * @vma: VMA of the process | ||
498 | * @page: page | ||
499 | * @addr: U0 address | ||
500 | * @len: length of the range (< page size) | ||
501 | */ | ||
502 | void flush_icache_user_range(struct vm_area_struct *vma, | ||
503 | struct page *page, unsigned long addr, int len) | ||
504 | { | ||
505 | flush_cache_page(vma, addr, page_to_pfn(page)); | ||
506 | mb(); | ||
507 | } | 299 | } |
508 | 300 | ||
509 | /** | 301 | /** |
510 | * __flush_cache_4096 | 302 | * __flush_cache_one |
511 | * | 303 | * |
512 | * @addr: address in memory mapped cache array | 304 | * @addr: address in memory mapped cache array |
513 | * @phys: P1 address to flush (has to match tags if addr has 'A' bit | 305 | * @phys: P1 address to flush (has to match tags if addr has 'A' bit |
@@ -520,7 +312,7 @@ void flush_icache_user_range(struct vm_area_struct *vma, | |||
520 | * operation (purge/write-back) is selected by the lower 2 bits of | 312 | * operation (purge/write-back) is selected by the lower 2 bits of |
521 | * 'phys'. | 313 | * 'phys'. |
522 | */ | 314 | */ |
523 | static void __flush_cache_4096(unsigned long addr, unsigned long phys, | 315 | static void __flush_cache_one(unsigned long addr, unsigned long phys, |
524 | unsigned long exec_offset) | 316 | unsigned long exec_offset) |
525 | { | 317 | { |
526 | int way_count; | 318 | int way_count; |
@@ -577,199 +369,25 @@ static void __flush_cache_4096(unsigned long addr, unsigned long phys, | |||
577 | } while (--way_count != 0); | 369 | } while (--way_count != 0); |
578 | } | 370 | } |
579 | 371 | ||
372 | extern void __weak sh4__flush_region_init(void); | ||
373 | |||
580 | /* | 374 | /* |
581 | * Break the 1, 2 and 4 way variants of this out into separate functions to | 375 | * SH-4 has virtually indexed and physically tagged cache. |
582 | * avoid nearly all the overhead of having the conditional stuff in the function | ||
583 | * bodies (+ the 1 and 2 way cases avoid saving any registers too). | ||
584 | */ | 376 | */ |
585 | static void __flush_dcache_segment_1way(unsigned long start, | 377 | void __init sh4_cache_init(void) |
586 | unsigned long extent_per_way) | ||
587 | { | ||
588 | unsigned long orig_sr, sr_with_bl; | ||
589 | unsigned long base_addr; | ||
590 | unsigned long way_incr, linesz, way_size; | ||
591 | struct cache_info *dcache; | ||
592 | register unsigned long a0, a0e; | ||
593 | |||
594 | asm volatile("stc sr, %0" : "=r" (orig_sr)); | ||
595 | sr_with_bl = orig_sr | (1<<28); | ||
596 | base_addr = ((unsigned long)&empty_zero_page[0]); | ||
597 | |||
598 | /* | ||
599 | * The previous code aligned base_addr to 16k, i.e. the way_size of all | ||
600 | * existing SH-4 D-caches. Whilst I don't see a need to have this | ||
601 | * aligned to any better than the cache line size (which it will be | ||
602 | * anyway by construction), let's align it to at least the way_size of | ||
603 | * any existing or conceivable SH-4 D-cache. -- RPC | ||
604 | */ | ||
605 | base_addr = ((base_addr >> 16) << 16); | ||
606 | base_addr |= start; | ||
607 | |||
608 | dcache = &boot_cpu_data.dcache; | ||
609 | linesz = dcache->linesz; | ||
610 | way_incr = dcache->way_incr; | ||
611 | way_size = dcache->way_size; | ||
612 | |||
613 | a0 = base_addr; | ||
614 | a0e = base_addr + extent_per_way; | ||
615 | do { | ||
616 | asm volatile("ldc %0, sr" : : "r" (sr_with_bl)); | ||
617 | asm volatile("movca.l r0, @%0\n\t" | ||
618 | "ocbi @%0" : : "r" (a0)); | ||
619 | a0 += linesz; | ||
620 | asm volatile("movca.l r0, @%0\n\t" | ||
621 | "ocbi @%0" : : "r" (a0)); | ||
622 | a0 += linesz; | ||
623 | asm volatile("movca.l r0, @%0\n\t" | ||
624 | "ocbi @%0" : : "r" (a0)); | ||
625 | a0 += linesz; | ||
626 | asm volatile("movca.l r0, @%0\n\t" | ||
627 | "ocbi @%0" : : "r" (a0)); | ||
628 | asm volatile("ldc %0, sr" : : "r" (orig_sr)); | ||
629 | a0 += linesz; | ||
630 | } while (a0 < a0e); | ||
631 | } | ||
632 | |||
633 | static void __flush_dcache_segment_2way(unsigned long start, | ||
634 | unsigned long extent_per_way) | ||
635 | { | 378 | { |
636 | unsigned long orig_sr, sr_with_bl; | 379 | printk("PVR=%08x CVR=%08x PRR=%08x\n", |
637 | unsigned long base_addr; | 380 | __raw_readl(CCN_PVR), |
638 | unsigned long way_incr, linesz, way_size; | 381 | __raw_readl(CCN_CVR), |
639 | struct cache_info *dcache; | 382 | __raw_readl(CCN_PRR)); |
640 | register unsigned long a0, a1, a0e; | 383 | |
641 | 384 | local_flush_icache_range = sh4_flush_icache_range; | |
642 | asm volatile("stc sr, %0" : "=r" (orig_sr)); | 385 | local_flush_dcache_page = sh4_flush_dcache_page; |
643 | sr_with_bl = orig_sr | (1<<28); | 386 | local_flush_cache_all = sh4_flush_cache_all; |
644 | base_addr = ((unsigned long)&empty_zero_page[0]); | 387 | local_flush_cache_mm = sh4_flush_cache_mm; |
645 | 388 | local_flush_cache_dup_mm = sh4_flush_cache_mm; | |
646 | /* See comment under 1-way above */ | 389 | local_flush_cache_page = sh4_flush_cache_page; |
647 | base_addr = ((base_addr >> 16) << 16); | 390 | local_flush_cache_range = sh4_flush_cache_range; |
648 | base_addr |= start; | 391 | |
649 | 392 | sh4__flush_region_init(); | |
650 | dcache = &boot_cpu_data.dcache; | ||
651 | linesz = dcache->linesz; | ||
652 | way_incr = dcache->way_incr; | ||
653 | way_size = dcache->way_size; | ||
654 | |||
655 | a0 = base_addr; | ||
656 | a1 = a0 + way_incr; | ||
657 | a0e = base_addr + extent_per_way; | ||
658 | do { | ||
659 | asm volatile("ldc %0, sr" : : "r" (sr_with_bl)); | ||
660 | asm volatile("movca.l r0, @%0\n\t" | ||
661 | "movca.l r0, @%1\n\t" | ||
662 | "ocbi @%0\n\t" | ||
663 | "ocbi @%1" : : | ||
664 | "r" (a0), "r" (a1)); | ||
665 | a0 += linesz; | ||
666 | a1 += linesz; | ||
667 | asm volatile("movca.l r0, @%0\n\t" | ||
668 | "movca.l r0, @%1\n\t" | ||
669 | "ocbi @%0\n\t" | ||
670 | "ocbi @%1" : : | ||
671 | "r" (a0), "r" (a1)); | ||
672 | a0 += linesz; | ||
673 | a1 += linesz; | ||
674 | asm volatile("movca.l r0, @%0\n\t" | ||
675 | "movca.l r0, @%1\n\t" | ||
676 | "ocbi @%0\n\t" | ||
677 | "ocbi @%1" : : | ||
678 | "r" (a0), "r" (a1)); | ||
679 | a0 += linesz; | ||
680 | a1 += linesz; | ||
681 | asm volatile("movca.l r0, @%0\n\t" | ||
682 | "movca.l r0, @%1\n\t" | ||
683 | "ocbi @%0\n\t" | ||
684 | "ocbi @%1" : : | ||
685 | "r" (a0), "r" (a1)); | ||
686 | asm volatile("ldc %0, sr" : : "r" (orig_sr)); | ||
687 | a0 += linesz; | ||
688 | a1 += linesz; | ||
689 | } while (a0 < a0e); | ||
690 | } | ||
691 | |||
692 | static void __flush_dcache_segment_4way(unsigned long start, | ||
693 | unsigned long extent_per_way) | ||
694 | { | ||
695 | unsigned long orig_sr, sr_with_bl; | ||
696 | unsigned long base_addr; | ||
697 | unsigned long way_incr, linesz, way_size; | ||
698 | struct cache_info *dcache; | ||
699 | register unsigned long a0, a1, a2, a3, a0e; | ||
700 | |||
701 | asm volatile("stc sr, %0" : "=r" (orig_sr)); | ||
702 | sr_with_bl = orig_sr | (1<<28); | ||
703 | base_addr = ((unsigned long)&empty_zero_page[0]); | ||
704 | |||
705 | /* See comment under 1-way above */ | ||
706 | base_addr = ((base_addr >> 16) << 16); | ||
707 | base_addr |= start; | ||
708 | |||
709 | dcache = &boot_cpu_data.dcache; | ||
710 | linesz = dcache->linesz; | ||
711 | way_incr = dcache->way_incr; | ||
712 | way_size = dcache->way_size; | ||
713 | |||
714 | a0 = base_addr; | ||
715 | a1 = a0 + way_incr; | ||
716 | a2 = a1 + way_incr; | ||
717 | a3 = a2 + way_incr; | ||
718 | a0e = base_addr + extent_per_way; | ||
719 | do { | ||
720 | asm volatile("ldc %0, sr" : : "r" (sr_with_bl)); | ||
721 | asm volatile("movca.l r0, @%0\n\t" | ||
722 | "movca.l r0, @%1\n\t" | ||
723 | "movca.l r0, @%2\n\t" | ||
724 | "movca.l r0, @%3\n\t" | ||
725 | "ocbi @%0\n\t" | ||
726 | "ocbi @%1\n\t" | ||
727 | "ocbi @%2\n\t" | ||
728 | "ocbi @%3\n\t" : : | ||
729 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
730 | a0 += linesz; | ||
731 | a1 += linesz; | ||
732 | a2 += linesz; | ||
733 | a3 += linesz; | ||
734 | asm volatile("movca.l r0, @%0\n\t" | ||
735 | "movca.l r0, @%1\n\t" | ||
736 | "movca.l r0, @%2\n\t" | ||
737 | "movca.l r0, @%3\n\t" | ||
738 | "ocbi @%0\n\t" | ||
739 | "ocbi @%1\n\t" | ||
740 | "ocbi @%2\n\t" | ||
741 | "ocbi @%3\n\t" : : | ||
742 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
743 | a0 += linesz; | ||
744 | a1 += linesz; | ||
745 | a2 += linesz; | ||
746 | a3 += linesz; | ||
747 | asm volatile("movca.l r0, @%0\n\t" | ||
748 | "movca.l r0, @%1\n\t" | ||
749 | "movca.l r0, @%2\n\t" | ||
750 | "movca.l r0, @%3\n\t" | ||
751 | "ocbi @%0\n\t" | ||
752 | "ocbi @%1\n\t" | ||
753 | "ocbi @%2\n\t" | ||
754 | "ocbi @%3\n\t" : : | ||
755 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
756 | a0 += linesz; | ||
757 | a1 += linesz; | ||
758 | a2 += linesz; | ||
759 | a3 += linesz; | ||
760 | asm volatile("movca.l r0, @%0\n\t" | ||
761 | "movca.l r0, @%1\n\t" | ||
762 | "movca.l r0, @%2\n\t" | ||
763 | "movca.l r0, @%3\n\t" | ||
764 | "ocbi @%0\n\t" | ||
765 | "ocbi @%1\n\t" | ||
766 | "ocbi @%2\n\t" | ||
767 | "ocbi @%3\n\t" : : | ||
768 | "r" (a0), "r" (a1), "r" (a2), "r" (a3)); | ||
769 | asm volatile("ldc %0, sr" : : "r" (orig_sr)); | ||
770 | a0 += linesz; | ||
771 | a1 += linesz; | ||
772 | a2 += linesz; | ||
773 | a3 += linesz; | ||
774 | } while (a0 < a0e); | ||
775 | } | 393 | } |