diff options
Diffstat (limited to 'arch/mips/mm/c-tx39.c')
-rw-r--r-- | arch/mips/mm/c-tx39.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/arch/mips/mm/c-tx39.c b/arch/mips/mm/c-tx39.c new file mode 100644 index 000000000..03dfbb40e --- /dev/null +++ b/arch/mips/mm/c-tx39.c | |||
@@ -0,0 +1,414 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * r2300.c: R2000 and R3000 specific mmu/cache code. | ||
4 | * | ||
5 | * Copyright (C) 1996 David S. Miller (davem@davemloft.net) | ||
6 | * | ||
7 | * with a lot of changes to make this thing work for R3000s | ||
8 | * Tx39XX R4k style caches added. HK | ||
9 | * Copyright (C) 1998, 1999, 2000 Harald Koerfgen | ||
10 | * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/smp.h> | ||
16 | #include <linux/mm.h> | ||
17 | |||
18 | #include <asm/cacheops.h> | ||
19 | #include <asm/page.h> | ||
20 | #include <asm/mmu_context.h> | ||
21 | #include <asm/isadep.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/bootinfo.h> | ||
24 | #include <asm/cpu.h> | ||
25 | |||
26 | /* For R3000 cores with R4000 style caches */ | ||
27 | static unsigned long icache_size, dcache_size; /* Size in bytes */ | ||
28 | |||
29 | #include <asm/r4kcache.h> | ||
30 | |||
31 | /* This sequence is required to ensure icache is disabled immediately */ | ||
32 | #define TX39_STOP_STREAMING() \ | ||
33 | __asm__ __volatile__( \ | ||
34 | ".set push\n\t" \ | ||
35 | ".set noreorder\n\t" \ | ||
36 | "b 1f\n\t" \ | ||
37 | "nop\n\t" \ | ||
38 | "1:\n\t" \ | ||
39 | ".set pop" \ | ||
40 | ) | ||
41 | |||
42 | /* TX39H-style cache flush routines. */ | ||
43 | static void tx39h_flush_icache_all(void) | ||
44 | { | ||
45 | unsigned long flags, config; | ||
46 | |||
47 | /* disable icache (set ICE#) */ | ||
48 | local_irq_save(flags); | ||
49 | config = read_c0_conf(); | ||
50 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
51 | TX39_STOP_STREAMING(); | ||
52 | blast_icache16(); | ||
53 | write_c0_conf(config); | ||
54 | local_irq_restore(flags); | ||
55 | } | ||
56 | |||
57 | static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size) | ||
58 | { | ||
59 | /* Catch bad driver code */ | ||
60 | BUG_ON(size == 0); | ||
61 | |||
62 | iob(); | ||
63 | blast_inv_dcache_range(addr, addr + size); | ||
64 | } | ||
65 | |||
66 | |||
67 | /* TX39H2,TX39H3 */ | ||
68 | static inline void tx39_blast_dcache_page(unsigned long addr) | ||
69 | { | ||
70 | if (current_cpu_type() != CPU_TX3912) | ||
71 | blast_dcache16_page(addr); | ||
72 | } | ||
73 | |||
74 | static inline void tx39_blast_dcache_page_indexed(unsigned long addr) | ||
75 | { | ||
76 | blast_dcache16_page_indexed(addr); | ||
77 | } | ||
78 | |||
79 | static inline void tx39_blast_dcache(void) | ||
80 | { | ||
81 | blast_dcache16(); | ||
82 | } | ||
83 | |||
84 | static inline void tx39_blast_icache_page(unsigned long addr) | ||
85 | { | ||
86 | unsigned long flags, config; | ||
87 | /* disable icache (set ICE#) */ | ||
88 | local_irq_save(flags); | ||
89 | config = read_c0_conf(); | ||
90 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
91 | TX39_STOP_STREAMING(); | ||
92 | blast_icache16_page(addr); | ||
93 | write_c0_conf(config); | ||
94 | local_irq_restore(flags); | ||
95 | } | ||
96 | |||
97 | static inline void tx39_blast_icache_page_indexed(unsigned long addr) | ||
98 | { | ||
99 | unsigned long flags, config; | ||
100 | /* disable icache (set ICE#) */ | ||
101 | local_irq_save(flags); | ||
102 | config = read_c0_conf(); | ||
103 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
104 | TX39_STOP_STREAMING(); | ||
105 | blast_icache16_page_indexed(addr); | ||
106 | write_c0_conf(config); | ||
107 | local_irq_restore(flags); | ||
108 | } | ||
109 | |||
110 | static inline void tx39_blast_icache(void) | ||
111 | { | ||
112 | unsigned long flags, config; | ||
113 | /* disable icache (set ICE#) */ | ||
114 | local_irq_save(flags); | ||
115 | config = read_c0_conf(); | ||
116 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
117 | TX39_STOP_STREAMING(); | ||
118 | blast_icache16(); | ||
119 | write_c0_conf(config); | ||
120 | local_irq_restore(flags); | ||
121 | } | ||
122 | |||
123 | static void tx39__flush_cache_vmap(void) | ||
124 | { | ||
125 | tx39_blast_dcache(); | ||
126 | } | ||
127 | |||
128 | static void tx39__flush_cache_vunmap(void) | ||
129 | { | ||
130 | tx39_blast_dcache(); | ||
131 | } | ||
132 | |||
133 | static inline void tx39_flush_cache_all(void) | ||
134 | { | ||
135 | if (!cpu_has_dc_aliases) | ||
136 | return; | ||
137 | |||
138 | tx39_blast_dcache(); | ||
139 | } | ||
140 | |||
141 | static inline void tx39___flush_cache_all(void) | ||
142 | { | ||
143 | tx39_blast_dcache(); | ||
144 | tx39_blast_icache(); | ||
145 | } | ||
146 | |||
147 | static void tx39_flush_cache_mm(struct mm_struct *mm) | ||
148 | { | ||
149 | if (!cpu_has_dc_aliases) | ||
150 | return; | ||
151 | |||
152 | if (cpu_context(smp_processor_id(), mm) != 0) | ||
153 | tx39_blast_dcache(); | ||
154 | } | ||
155 | |||
156 | static void tx39_flush_cache_range(struct vm_area_struct *vma, | ||
157 | unsigned long start, unsigned long end) | ||
158 | { | ||
159 | if (!cpu_has_dc_aliases) | ||
160 | return; | ||
161 | if (!(cpu_context(smp_processor_id(), vma->vm_mm))) | ||
162 | return; | ||
163 | |||
164 | tx39_blast_dcache(); | ||
165 | } | ||
166 | |||
167 | static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn) | ||
168 | { | ||
169 | int exec = vma->vm_flags & VM_EXEC; | ||
170 | struct mm_struct *mm = vma->vm_mm; | ||
171 | pmd_t *pmdp; | ||
172 | pte_t *ptep; | ||
173 | |||
174 | /* | ||
175 | * If ownes no valid ASID yet, cannot possibly have gotten | ||
176 | * this page into the cache. | ||
177 | */ | ||
178 | if (cpu_context(smp_processor_id(), mm) == 0) | ||
179 | return; | ||
180 | |||
181 | page &= PAGE_MASK; | ||
182 | pmdp = pmd_off(mm, page); | ||
183 | ptep = pte_offset_kernel(pmdp, page); | ||
184 | |||
185 | /* | ||
186 | * If the page isn't marked valid, the page cannot possibly be | ||
187 | * in the cache. | ||
188 | */ | ||
189 | if (!(pte_val(*ptep) & _PAGE_PRESENT)) | ||
190 | return; | ||
191 | |||
192 | /* | ||
193 | * Doing flushes for another ASID than the current one is | ||
194 | * too difficult since stupid R4k caches do a TLB translation | ||
195 | * for every cache flush operation. So we do indexed flushes | ||
196 | * in that case, which doesn't overly flush the cache too much. | ||
197 | */ | ||
198 | if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) { | ||
199 | if (cpu_has_dc_aliases || exec) | ||
200 | tx39_blast_dcache_page(page); | ||
201 | if (exec) | ||
202 | tx39_blast_icache_page(page); | ||
203 | |||
204 | return; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Do indexed flush, too much work to get the (possible) TLB refills | ||
209 | * to work correctly. | ||
210 | */ | ||
211 | if (cpu_has_dc_aliases || exec) | ||
212 | tx39_blast_dcache_page_indexed(page); | ||
213 | if (exec) | ||
214 | tx39_blast_icache_page_indexed(page); | ||
215 | } | ||
216 | |||
217 | static void local_tx39_flush_data_cache_page(void * addr) | ||
218 | { | ||
219 | tx39_blast_dcache_page((unsigned long)addr); | ||
220 | } | ||
221 | |||
222 | static void tx39_flush_data_cache_page(unsigned long addr) | ||
223 | { | ||
224 | tx39_blast_dcache_page(addr); | ||
225 | } | ||
226 | |||
227 | static void tx39_flush_icache_range(unsigned long start, unsigned long end) | ||
228 | { | ||
229 | if (end - start > dcache_size) | ||
230 | tx39_blast_dcache(); | ||
231 | else | ||
232 | protected_blast_dcache_range(start, end); | ||
233 | |||
234 | if (end - start > icache_size) | ||
235 | tx39_blast_icache(); | ||
236 | else { | ||
237 | unsigned long flags, config; | ||
238 | /* disable icache (set ICE#) */ | ||
239 | local_irq_save(flags); | ||
240 | config = read_c0_conf(); | ||
241 | write_c0_conf(config & ~TX39_CONF_ICE); | ||
242 | TX39_STOP_STREAMING(); | ||
243 | protected_blast_icache_range(start, end); | ||
244 | write_c0_conf(config); | ||
245 | local_irq_restore(flags); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void tx39_flush_kernel_vmap_range(unsigned long vaddr, int size) | ||
250 | { | ||
251 | BUG(); | ||
252 | } | ||
253 | |||
254 | static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size) | ||
255 | { | ||
256 | unsigned long end; | ||
257 | |||
258 | if (((size | addr) & (PAGE_SIZE - 1)) == 0) { | ||
259 | end = addr + size; | ||
260 | do { | ||
261 | tx39_blast_dcache_page(addr); | ||
262 | addr += PAGE_SIZE; | ||
263 | } while(addr != end); | ||
264 | } else if (size > dcache_size) { | ||
265 | tx39_blast_dcache(); | ||
266 | } else { | ||
267 | blast_dcache_range(addr, addr + size); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | static void tx39_dma_cache_inv(unsigned long addr, unsigned long size) | ||
272 | { | ||
273 | unsigned long end; | ||
274 | |||
275 | if (((size | addr) & (PAGE_SIZE - 1)) == 0) { | ||
276 | end = addr + size; | ||
277 | do { | ||
278 | tx39_blast_dcache_page(addr); | ||
279 | addr += PAGE_SIZE; | ||
280 | } while(addr != end); | ||
281 | } else if (size > dcache_size) { | ||
282 | tx39_blast_dcache(); | ||
283 | } else { | ||
284 | blast_inv_dcache_range(addr, addr + size); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | static __init void tx39_probe_cache(void) | ||
289 | { | ||
290 | unsigned long config; | ||
291 | |||
292 | config = read_c0_conf(); | ||
293 | |||
294 | icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >> | ||
295 | TX39_CONF_ICS_SHIFT)); | ||
296 | dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >> | ||
297 | TX39_CONF_DCS_SHIFT)); | ||
298 | |||
299 | current_cpu_data.icache.linesz = 16; | ||
300 | switch (current_cpu_type()) { | ||
301 | case CPU_TX3912: | ||
302 | current_cpu_data.icache.ways = 1; | ||
303 | current_cpu_data.dcache.ways = 1; | ||
304 | current_cpu_data.dcache.linesz = 4; | ||
305 | break; | ||
306 | |||
307 | case CPU_TX3927: | ||
308 | current_cpu_data.icache.ways = 2; | ||
309 | current_cpu_data.dcache.ways = 2; | ||
310 | current_cpu_data.dcache.linesz = 16; | ||
311 | break; | ||
312 | |||
313 | case CPU_TX3922: | ||
314 | default: | ||
315 | current_cpu_data.icache.ways = 1; | ||
316 | current_cpu_data.dcache.ways = 1; | ||
317 | current_cpu_data.dcache.linesz = 16; | ||
318 | break; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | void tx39_cache_init(void) | ||
323 | { | ||
324 | extern void build_clear_page(void); | ||
325 | extern void build_copy_page(void); | ||
326 | unsigned long config; | ||
327 | |||
328 | config = read_c0_conf(); | ||
329 | config &= ~TX39_CONF_WBON; | ||
330 | write_c0_conf(config); | ||
331 | |||
332 | tx39_probe_cache(); | ||
333 | |||
334 | switch (current_cpu_type()) { | ||
335 | case CPU_TX3912: | ||
336 | /* TX39/H core (writethru direct-map cache) */ | ||
337 | __flush_cache_vmap = tx39__flush_cache_vmap; | ||
338 | __flush_cache_vunmap = tx39__flush_cache_vunmap; | ||
339 | flush_cache_all = tx39h_flush_icache_all; | ||
340 | __flush_cache_all = tx39h_flush_icache_all; | ||
341 | flush_cache_mm = (void *) tx39h_flush_icache_all; | ||
342 | flush_cache_range = (void *) tx39h_flush_icache_all; | ||
343 | flush_cache_page = (void *) tx39h_flush_icache_all; | ||
344 | flush_icache_range = (void *) tx39h_flush_icache_all; | ||
345 | local_flush_icache_range = (void *) tx39h_flush_icache_all; | ||
346 | |||
347 | local_flush_data_cache_page = (void *) tx39h_flush_icache_all; | ||
348 | flush_data_cache_page = (void *) tx39h_flush_icache_all; | ||
349 | |||
350 | _dma_cache_wback_inv = tx39h_dma_cache_wback_inv; | ||
351 | |||
352 | shm_align_mask = PAGE_SIZE - 1; | ||
353 | |||
354 | break; | ||
355 | |||
356 | case CPU_TX3922: | ||
357 | case CPU_TX3927: | ||
358 | default: | ||
359 | /* TX39/H2,H3 core (writeback 2way-set-associative cache) */ | ||
360 | /* board-dependent init code may set WBON */ | ||
361 | |||
362 | __flush_cache_vmap = tx39__flush_cache_vmap; | ||
363 | __flush_cache_vunmap = tx39__flush_cache_vunmap; | ||
364 | |||
365 | flush_cache_all = tx39_flush_cache_all; | ||
366 | __flush_cache_all = tx39___flush_cache_all; | ||
367 | flush_cache_mm = tx39_flush_cache_mm; | ||
368 | flush_cache_range = tx39_flush_cache_range; | ||
369 | flush_cache_page = tx39_flush_cache_page; | ||
370 | flush_icache_range = tx39_flush_icache_range; | ||
371 | local_flush_icache_range = tx39_flush_icache_range; | ||
372 | |||
373 | __flush_kernel_vmap_range = tx39_flush_kernel_vmap_range; | ||
374 | |||
375 | local_flush_data_cache_page = local_tx39_flush_data_cache_page; | ||
376 | flush_data_cache_page = tx39_flush_data_cache_page; | ||
377 | |||
378 | _dma_cache_wback_inv = tx39_dma_cache_wback_inv; | ||
379 | _dma_cache_wback = tx39_dma_cache_wback_inv; | ||
380 | _dma_cache_inv = tx39_dma_cache_inv; | ||
381 | |||
382 | shm_align_mask = max_t(unsigned long, | ||
383 | (dcache_size / current_cpu_data.dcache.ways) - 1, | ||
384 | PAGE_SIZE - 1); | ||
385 | |||
386 | break; | ||
387 | } | ||
388 | |||
389 | __flush_icache_user_range = flush_icache_range; | ||
390 | __local_flush_icache_user_range = local_flush_icache_range; | ||
391 | |||
392 | current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways; | ||
393 | current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways; | ||
394 | |||
395 | current_cpu_data.icache.sets = | ||
396 | current_cpu_data.icache.waysize / current_cpu_data.icache.linesz; | ||
397 | current_cpu_data.dcache.sets = | ||
398 | current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz; | ||
399 | |||
400 | if (current_cpu_data.dcache.waysize > PAGE_SIZE) | ||
401 | current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES; | ||
402 | |||
403 | current_cpu_data.icache.waybit = 0; | ||
404 | current_cpu_data.dcache.waybit = 0; | ||
405 | |||
406 | pr_info("Primary instruction cache %ldkB, linesize %d bytes\n", | ||
407 | icache_size >> 10, current_cpu_data.icache.linesz); | ||
408 | pr_info("Primary data cache %ldkB, linesize %d bytes\n", | ||
409 | dcache_size >> 10, current_cpu_data.dcache.linesz); | ||
410 | |||
411 | build_clear_page(); | ||
412 | build_copy_page(); | ||
413 | tx39h_flush_icache_all(); | ||
414 | } | ||