Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
f26b2a56 | 2 | * arch/sh/mm/cache.c |
1da177e4 LT |
3 | * |
4 | * Copyright (C) 1999, 2000, 2002 Niibe Yutaka | |
dfff0fa6 | 5 | * Copyright (C) 2002 - 2009 Paul Mundt |
1da177e4 LT |
6 | * |
7 | * Released under the terms of the GNU GPL v2.0. | |
8 | */ | |
1da177e4 | 9 | #include <linux/mm.h> |
acca4f4d | 10 | #include <linux/init.h> |
52e27782 | 11 | #include <linux/mutex.h> |
e06c4e57 | 12 | #include <linux/fs.h> |
f26b2a56 | 13 | #include <linux/smp.h> |
7747b9a4 PM |
14 | #include <linux/highmem.h> |
15 | #include <linux/module.h> | |
1da177e4 LT |
16 | #include <asm/mmu_context.h> |
17 | #include <asm/cacheflush.h> | |
18 | ||
f26b2a56 PM |
19 | void (*local_flush_cache_all)(void *args) = cache_noop; |
20 | void (*local_flush_cache_mm)(void *args) = cache_noop; | |
21 | void (*local_flush_cache_dup_mm)(void *args) = cache_noop; | |
22 | void (*local_flush_cache_page)(void *args) = cache_noop; | |
23 | void (*local_flush_cache_range)(void *args) = cache_noop; | |
24 | void (*local_flush_dcache_page)(void *args) = cache_noop; | |
25 | void (*local_flush_icache_range)(void *args) = cache_noop; | |
26 | void (*local_flush_icache_page)(void *args) = cache_noop; | |
27 | void (*local_flush_cache_sigtramp)(void *args) = cache_noop; | |
28 | ||
37443ef3 PM |
29 | void (*__flush_wback_region)(void *start, int size); |
30 | void (*__flush_purge_region)(void *start, int size); | |
31 | void (*__flush_invalidate_region)(void *start, int size); | |
32 | ||
37443ef3 PM |
33 | static inline void noop__flush_region(void *start, int size) |
34 | { | |
35 | } | |
36 | ||
6f379578 PM |
37 | static inline void cacheop_on_each_cpu(void (*func) (void *info), void *info, |
38 | int wait) | |
39 | { | |
40 | preempt_disable(); | |
41 | smp_call_function(func, info, wait); | |
42 | func(info); | |
43 | preempt_enable(); | |
44 | } | |
45 | ||
ba1789ef PM |
46 | void copy_to_user_page(struct vm_area_struct *vma, struct page *page, |
47 | unsigned long vaddr, void *dst, const void *src, | |
48 | unsigned long len) | |
1da177e4 | 49 | { |
0dfae7d5 PM |
50 | if (boot_cpu_data.dcache.n_aliases && page_mapped(page) && |
51 | !test_bit(PG_dcache_dirty, &page->flags)) { | |
2277ab4a PM |
52 | void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); |
53 | memcpy(vto, src, len); | |
0906a3ad | 54 | kunmap_coherent(vto); |
2277ab4a PM |
55 | } else { |
56 | memcpy(dst, src, len); | |
0dfae7d5 PM |
57 | if (boot_cpu_data.dcache.n_aliases) |
58 | set_bit(PG_dcache_dirty, &page->flags); | |
2277ab4a | 59 | } |
ba1789ef PM |
60 | |
61 | if (vma->vm_flags & VM_EXEC) | |
62 | flush_cache_page(vma, vaddr, page_to_pfn(page)); | |
63 | } | |
64 | ||
65 | void copy_from_user_page(struct vm_area_struct *vma, struct page *page, | |
66 | unsigned long vaddr, void *dst, const void *src, | |
67 | unsigned long len) | |
68 | { | |
0dfae7d5 PM |
69 | if (boot_cpu_data.dcache.n_aliases && page_mapped(page) && |
70 | !test_bit(PG_dcache_dirty, &page->flags)) { | |
2277ab4a PM |
71 | void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK); |
72 | memcpy(dst, vfrom, len); | |
0906a3ad | 73 | kunmap_coherent(vfrom); |
2277ab4a PM |
74 | } else { |
75 | memcpy(dst, src, len); | |
0dfae7d5 PM |
76 | if (boot_cpu_data.dcache.n_aliases) |
77 | set_bit(PG_dcache_dirty, &page->flags); | |
2277ab4a | 78 | } |
1da177e4 | 79 | } |
39e688a9 | 80 | |
7747b9a4 PM |
81 | void copy_user_highpage(struct page *to, struct page *from, |
82 | unsigned long vaddr, struct vm_area_struct *vma) | |
83 | { | |
84 | void *vfrom, *vto; | |
85 | ||
7747b9a4 | 86 | vto = kmap_atomic(to, KM_USER1); |
7747b9a4 | 87 | |
0dfae7d5 PM |
88 | if (boot_cpu_data.dcache.n_aliases && page_mapped(from) && |
89 | !test_bit(PG_dcache_dirty, &from->flags)) { | |
2277ab4a PM |
90 | vfrom = kmap_coherent(from, vaddr); |
91 | copy_page(vto, vfrom); | |
0906a3ad | 92 | kunmap_coherent(vfrom); |
2277ab4a PM |
93 | } else { |
94 | vfrom = kmap_atomic(from, KM_USER0); | |
95 | copy_page(vto, vfrom); | |
96 | kunmap_atomic(vfrom, KM_USER0); | |
97 | } | |
98 | ||
99 | if (pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK)) | |
6e4154d4 | 100 | __flush_purge_region(vto, PAGE_SIZE); |
7747b9a4 PM |
101 | |
102 | kunmap_atomic(vto, KM_USER1); | |
103 | /* Make sure this page is cleared on other CPU's too before using it */ | |
104 | smp_wmb(); | |
105 | } | |
106 | EXPORT_SYMBOL(copy_user_highpage); | |
dfff0fa6 PM |
107 | |
108 | void clear_user_highpage(struct page *page, unsigned long vaddr) | |
109 | { | |
110 | void *kaddr = kmap_atomic(page, KM_USER0); | |
111 | ||
112 | clear_page(kaddr); | |
113 | ||
114 | if (pages_do_alias((unsigned long)kaddr, vaddr & PAGE_MASK)) | |
6e4154d4 | 115 | __flush_purge_region(kaddr, PAGE_SIZE); |
dfff0fa6 PM |
116 | |
117 | kunmap_atomic(kaddr, KM_USER0); | |
118 | } | |
119 | EXPORT_SYMBOL(clear_user_highpage); | |
9cef7492 PM |
120 | |
121 | void __update_cache(struct vm_area_struct *vma, | |
122 | unsigned long address, pte_t pte) | |
123 | { | |
124 | struct page *page; | |
125 | unsigned long pfn = pte_pfn(pte); | |
126 | ||
127 | if (!boot_cpu_data.dcache.n_aliases) | |
128 | return; | |
129 | ||
130 | page = pfn_to_page(pfn); | |
131 | if (pfn_valid(pfn) && page_mapping(page)) { | |
132 | int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); | |
133 | if (dirty) { | |
134 | unsigned long addr = (unsigned long)page_address(page); | |
135 | ||
136 | if (pages_do_alias(addr, address & PAGE_MASK)) | |
6e4154d4 | 137 | __flush_purge_region((void *)addr, PAGE_SIZE); |
9cef7492 PM |
138 | } |
139 | } | |
140 | } | |
c0fe478d PM |
141 | |
142 | void __flush_anon_page(struct page *page, unsigned long vmaddr) | |
143 | { | |
144 | unsigned long addr = (unsigned long) page_address(page); | |
145 | ||
146 | if (pages_do_alias(addr, vmaddr)) { | |
147 | if (boot_cpu_data.dcache.n_aliases && page_mapped(page) && | |
148 | !test_bit(PG_dcache_dirty, &page->flags)) { | |
149 | void *kaddr; | |
150 | ||
151 | kaddr = kmap_coherent(page, vmaddr); | |
6e4154d4 PM |
152 | /* XXX.. For now kunmap_coherent() does a purge */ |
153 | /* __flush_purge_region((void *)kaddr, PAGE_SIZE); */ | |
0906a3ad | 154 | kunmap_coherent(kaddr); |
c0fe478d | 155 | } else |
6e4154d4 | 156 | __flush_purge_region((void *)addr, PAGE_SIZE); |
c0fe478d PM |
157 | } |
158 | } | |
ecba1060 | 159 | |
f26b2a56 PM |
160 | void flush_cache_all(void) |
161 | { | |
6f379578 | 162 | cacheop_on_each_cpu(local_flush_cache_all, NULL, 1); |
f26b2a56 PM |
163 | } |
164 | ||
165 | void flush_cache_mm(struct mm_struct *mm) | |
166 | { | |
6f379578 | 167 | cacheop_on_each_cpu(local_flush_cache_mm, mm, 1); |
f26b2a56 PM |
168 | } |
169 | ||
170 | void flush_cache_dup_mm(struct mm_struct *mm) | |
171 | { | |
6f379578 | 172 | cacheop_on_each_cpu(local_flush_cache_dup_mm, mm, 1); |
f26b2a56 PM |
173 | } |
174 | ||
175 | void flush_cache_page(struct vm_area_struct *vma, unsigned long addr, | |
176 | unsigned long pfn) | |
177 | { | |
178 | struct flusher_data data; | |
179 | ||
180 | data.vma = vma; | |
181 | data.addr1 = addr; | |
182 | data.addr2 = pfn; | |
183 | ||
6f379578 | 184 | cacheop_on_each_cpu(local_flush_cache_page, (void *)&data, 1); |
f26b2a56 PM |
185 | } |
186 | ||
187 | void flush_cache_range(struct vm_area_struct *vma, unsigned long start, | |
188 | unsigned long end) | |
189 | { | |
190 | struct flusher_data data; | |
191 | ||
192 | data.vma = vma; | |
193 | data.addr1 = start; | |
194 | data.addr2 = end; | |
195 | ||
6f379578 | 196 | cacheop_on_each_cpu(local_flush_cache_range, (void *)&data, 1); |
f26b2a56 PM |
197 | } |
198 | ||
199 | void flush_dcache_page(struct page *page) | |
200 | { | |
6f379578 | 201 | cacheop_on_each_cpu(local_flush_dcache_page, page, 1); |
f26b2a56 PM |
202 | } |
203 | ||
204 | void flush_icache_range(unsigned long start, unsigned long end) | |
205 | { | |
206 | struct flusher_data data; | |
207 | ||
208 | data.vma = NULL; | |
209 | data.addr1 = start; | |
210 | data.addr2 = end; | |
211 | ||
6f379578 | 212 | cacheop_on_each_cpu(local_flush_icache_range, (void *)&data, 1); |
f26b2a56 PM |
213 | } |
214 | ||
215 | void flush_icache_page(struct vm_area_struct *vma, struct page *page) | |
216 | { | |
217 | /* Nothing uses the VMA, so just pass the struct page along */ | |
6f379578 | 218 | cacheop_on_each_cpu(local_flush_icache_page, page, 1); |
f26b2a56 PM |
219 | } |
220 | ||
221 | void flush_cache_sigtramp(unsigned long address) | |
222 | { | |
6f379578 | 223 | cacheop_on_each_cpu(local_flush_cache_sigtramp, (void *)address, 1); |
f26b2a56 PM |
224 | } |
225 | ||
27d59ec1 PM |
226 | static void compute_alias(struct cache_info *c) |
227 | { | |
228 | c->alias_mask = ((c->sets - 1) << c->entry_shift) & ~(PAGE_SIZE - 1); | |
229 | c->n_aliases = c->alias_mask ? (c->alias_mask >> PAGE_SHIFT) + 1 : 0; | |
230 | } | |
231 | ||
232 | static void __init emit_cache_params(void) | |
233 | { | |
234 | printk(KERN_NOTICE "I-cache : n_ways=%d n_sets=%d way_incr=%d\n", | |
235 | boot_cpu_data.icache.ways, | |
236 | boot_cpu_data.icache.sets, | |
237 | boot_cpu_data.icache.way_incr); | |
238 | printk(KERN_NOTICE "I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | |
239 | boot_cpu_data.icache.entry_mask, | |
240 | boot_cpu_data.icache.alias_mask, | |
241 | boot_cpu_data.icache.n_aliases); | |
242 | printk(KERN_NOTICE "D-cache : n_ways=%d n_sets=%d way_incr=%d\n", | |
243 | boot_cpu_data.dcache.ways, | |
244 | boot_cpu_data.dcache.sets, | |
245 | boot_cpu_data.dcache.way_incr); | |
246 | printk(KERN_NOTICE "D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | |
247 | boot_cpu_data.dcache.entry_mask, | |
248 | boot_cpu_data.dcache.alias_mask, | |
249 | boot_cpu_data.dcache.n_aliases); | |
250 | ||
251 | /* | |
252 | * Emit Secondary Cache parameters if the CPU has a probed L2. | |
253 | */ | |
254 | if (boot_cpu_data.flags & CPU_HAS_L2_CACHE) { | |
255 | printk(KERN_NOTICE "S-cache : n_ways=%d n_sets=%d way_incr=%d\n", | |
256 | boot_cpu_data.scache.ways, | |
257 | boot_cpu_data.scache.sets, | |
258 | boot_cpu_data.scache.way_incr); | |
259 | printk(KERN_NOTICE "S-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n", | |
260 | boot_cpu_data.scache.entry_mask, | |
261 | boot_cpu_data.scache.alias_mask, | |
262 | boot_cpu_data.scache.n_aliases); | |
263 | } | |
264 | } | |
265 | ||
ecba1060 PM |
266 | void __init cpu_cache_init(void) |
267 | { | |
27d59ec1 PM |
268 | compute_alias(&boot_cpu_data.icache); |
269 | compute_alias(&boot_cpu_data.dcache); | |
270 | compute_alias(&boot_cpu_data.scache); | |
271 | ||
37443ef3 PM |
272 | __flush_wback_region = noop__flush_region; |
273 | __flush_purge_region = noop__flush_region; | |
274 | __flush_invalidate_region = noop__flush_region; | |
275 | ||
109b44a8 PM |
276 | if (boot_cpu_data.family == CPU_FAMILY_SH2) { |
277 | extern void __weak sh2_cache_init(void); | |
278 | ||
279 | sh2_cache_init(); | |
280 | } | |
281 | ||
a58e1a2a PM |
282 | if (boot_cpu_data.family == CPU_FAMILY_SH2A) { |
283 | extern void __weak sh2a_cache_init(void); | |
284 | ||
285 | sh2a_cache_init(); | |
286 | } | |
287 | ||
79f1c9da PM |
288 | if (boot_cpu_data.family == CPU_FAMILY_SH3) { |
289 | extern void __weak sh3_cache_init(void); | |
290 | ||
291 | sh3_cache_init(); | |
0d051d90 PM |
292 | |
293 | if ((boot_cpu_data.type == CPU_SH7705) && | |
294 | (boot_cpu_data.dcache.sets == 512)) { | |
295 | extern void __weak sh7705_cache_init(void); | |
296 | ||
297 | sh7705_cache_init(); | |
298 | } | |
79f1c9da PM |
299 | } |
300 | ||
ecba1060 PM |
301 | if ((boot_cpu_data.family == CPU_FAMILY_SH4) || |
302 | (boot_cpu_data.family == CPU_FAMILY_SH4A) || | |
303 | (boot_cpu_data.family == CPU_FAMILY_SH4AL_DSP)) { | |
304 | extern void __weak sh4_cache_init(void); | |
305 | ||
306 | sh4_cache_init(); | |
307 | } | |
27d59ec1 | 308 | |
2b431518 PM |
309 | if (boot_cpu_data.family == CPU_FAMILY_SH5) { |
310 | extern void __weak sh5_cache_init(void); | |
311 | ||
312 | sh5_cache_init(); | |
313 | } | |
314 | ||
27d59ec1 | 315 | emit_cache_params(); |
ecba1060 | 316 | } |