blob: d9d63831cc2f9d6318257fa358ac41d41553c789 [file] [log] [blame]
Michal Simek8beb8502009-03-27 14:25:16 +01001/*
2 * Cache control for MicroBlaze cache memories
3 *
4 * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
5 * Copyright (C) 2007-2009 PetaLogix
Michal Simek2ee2ff82009-12-10 11:43:57 +01006 * Copyright (C) 2007-2009 John Williams <john.williams@petalogix.com>
Michal Simek8beb8502009-03-27 14:25:16 +01007 *
8 * This file is subject to the terms and conditions of the GNU General
9 * Public License. See the file COPYING in the main directory of this
10 * archive for more details.
11 */
12
13#include <asm/cacheflush.h>
14#include <linux/cache.h>
15#include <asm/cpuinfo.h>
Michal Simek2ee2ff82009-12-10 11:43:57 +010016#include <asm/pvr.h>
Michal Simek8beb8502009-03-27 14:25:16 +010017
Michal Simek2ee2ff82009-12-10 11:43:57 +010018static inline void __invalidate_flush_icache(unsigned int addr)
Michal Simek8beb8502009-03-27 14:25:16 +010019{
Michal Simek2ee2ff82009-12-10 11:43:57 +010020 __asm__ __volatile__ ("wic %0, r0;" \
21 : : "r" (addr));
Michal Simek8beb8502009-03-27 14:25:16 +010022}
23
Michal Simek2ee2ff82009-12-10 11:43:57 +010024static inline void __flush_dcache(unsigned int addr)
Michal Simek8beb8502009-03-27 14:25:16 +010025{
Michal Simek2ee2ff82009-12-10 11:43:57 +010026 __asm__ __volatile__ ("wdc.flush %0, r0;" \
27 : : "r" (addr));
Michal Simek8beb8502009-03-27 14:25:16 +010028}
29
Michal Simek2ee2ff82009-12-10 11:43:57 +010030static inline void __invalidate_dcache(unsigned int baseaddr,
31 unsigned int offset)
Michal Simek8beb8502009-03-27 14:25:16 +010032{
Michal Simek2ee2ff82009-12-10 11:43:57 +010033 __asm__ __volatile__ ("wdc.clear %0, %1;" \
34 : : "r" (baseaddr), "r" (offset));
Michal Simek8beb8502009-03-27 14:25:16 +010035}
36
Michal Simek2ee2ff82009-12-10 11:43:57 +010037static inline void __enable_icache_msr(void)
Michal Simek8beb8502009-03-27 14:25:16 +010038{
Michal Simek2ee2ff82009-12-10 11:43:57 +010039 __asm__ __volatile__ (" msrset r0, %0; \
40 nop; " \
41 : : "i" (MSR_ICE) : "memory");
Michal Simek8beb8502009-03-27 14:25:16 +010042}
43
Michal Simek2ee2ff82009-12-10 11:43:57 +010044static inline void __disable_icache_msr(void)
Michal Simek8beb8502009-03-27 14:25:16 +010045{
Michal Simek2ee2ff82009-12-10 11:43:57 +010046 __asm__ __volatile__ (" msrclr r0, %0; \
47 nop; " \
48 : : "i" (MSR_ICE) : "memory");
49}
50
51static inline void __enable_dcache_msr(void)
52{
53 __asm__ __volatile__ (" msrset r0, %0; \
54 nop; " \
55 : \
56 : "i" (MSR_DCE) \
Michal Simek8beb8502009-03-27 14:25:16 +010057 : "memory");
Michal Simek2ee2ff82009-12-10 11:43:57 +010058}
59
60static inline void __disable_dcache_msr(void)
61{
62 __asm__ __volatile__ (" msrclr r0, %0; \
63 nop; " \
64 : \
65 : "i" (MSR_DCE) \
66 : "memory");
67}
68
69static inline void __enable_icache_nomsr(void)
70{
71 __asm__ __volatile__ (" mfs r12, rmsr; \
72 nop; \
73 ori r12, r12, %0; \
74 mts rmsr, r12; \
75 nop; " \
76 : \
77 : "i" (MSR_ICE) \
Michal Simek8beb8502009-03-27 14:25:16 +010078 : "memory", "r12");
Michal Simek2ee2ff82009-12-10 11:43:57 +010079}
80
81static inline void __disable_icache_nomsr(void)
82{
83 __asm__ __volatile__ (" mfs r12, rmsr; \
84 nop; \
85 andi r12, r12, ~%0; \
86 mts rmsr, r12; \
87 nop; " \
88 : \
89 : "i" (MSR_ICE) \
90 : "memory", "r12");
91}
92
93static inline void __enable_dcache_nomsr(void)
94{
95 __asm__ __volatile__ (" mfs r12, rmsr; \
96 nop; \
97 ori r12, r12, %0; \
98 mts rmsr, r12; \
99 nop; " \
100 : \
101 : "i" (MSR_DCE) \
102 : "memory", "r12");
103}
104
105static inline void __disable_dcache_nomsr(void)
106{
107 __asm__ __volatile__ (" mfs r12, rmsr; \
108 nop; \
109 andi r12, r12, ~%0; \
110 mts rmsr, r12; \
111 nop; " \
112 : \
113 : "i" (MSR_DCE) \
114 : "memory", "r12");
115}
116
117
118/* Helper macro for computing the limits of cache range loops */
119#define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \
120do { \
121 int align = ~(cache_line_length - 1); \
122 end = min(start + cache_size, end); \
123 start &= align; \
124 end = ((end & align) + cache_line_length); \
125} while (0);
126
127/*
128 * Helper macro to loop over the specified cache_size/line_length and
129 * execute 'op' on that cacheline
130 */
131#define CACHE_ALL_LOOP(cache_size, line_length, op) \
132do { \
133 unsigned int len = cache_size; \
134 int step = -line_length; \
135 BUG_ON(step >= 0); \
136 \
137 __asm__ __volatile__ (" 1: " #op " %0, r0; \
138 bgtid %0, 1b; \
139 addk %0, %0, %1; \
140 " : : "r" (len), "r" (step) \
141 : "memory"); \
142} while (0);
143
144
145#define CACHE_ALL_LOOP2(cache_size, line_length, op) \
146do { \
147 unsigned int len = cache_size; \
148 int step = -line_length; \
149 BUG_ON(step >= 0); \
150 \
151 __asm__ __volatile__ (" 1: " #op " r0, %0; \
152 bgtid %0, 1b; \
153 addk %0, %0, %1; \
154 " : : "r" (len), "r" (step) \
155 : "memory"); \
156} while (0);
157
158/* for wdc.flush/clear */
159#define CACHE_RANGE_LOOP_2(start, end, line_length, op) \
160do { \
161 int step = -line_length; \
162 int count = end - start; \
163 BUG_ON(count <= 0); \
164 \
165 __asm__ __volatile__ (" 1: " #op " %0, %1; \
166 bgtid %1, 1b; \
167 addk %1, %1, %2; \
168 " : : "r" (start), "r" (count), \
169 "r" (step) : "memory"); \
170} while (0);
171
172/* It is used only first parameter for OP - for wic, wdc */
173#define CACHE_RANGE_LOOP_1(start, end, line_length, op) \
174do { \
175 int step = -line_length; \
176 int count = end - start; \
177 BUG_ON(count <= 0); \
178 \
179 __asm__ __volatile__ (" 1: addk %0, %0, %1; \
180 " #op " %0, r0; \
181 bgtid %1, 1b; \
182 addk %1, %1, %2; \
183 " : : "r" (start), "r" (count), \
184 "r" (step) : "memory"); \
185} while (0);
186
187static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
188{
189 unsigned long flags;
190
191 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
192 (unsigned int)start, (unsigned int) end);
193
194 CACHE_LOOP_LIMITS(start, end,
195 cpuinfo.icache_line_length, cpuinfo.icache_size);
196
197 local_irq_save(flags);
198 __disable_icache_msr();
199
200 CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
201
202 __enable_icache_msr();
203 local_irq_restore(flags);
204}
205
206static void __flush_icache_range_nomsr_irq(unsigned long start,
207 unsigned long end)
208{
209 unsigned long flags;
210
211 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
212 (unsigned int)start, (unsigned int) end);
213
214 CACHE_LOOP_LIMITS(start, end,
215 cpuinfo.icache_line_length, cpuinfo.icache_size);
216
217 local_irq_save(flags);
218 __disable_icache_nomsr();
219
220 CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
221
222 __enable_icache_nomsr();
223 local_irq_restore(flags);
224}
225
226static void __flush_icache_range_noirq(unsigned long start,
227 unsigned long end)
228{
229 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
230 (unsigned int)start, (unsigned int) end);
231
232 CACHE_LOOP_LIMITS(start, end,
233 cpuinfo.icache_line_length, cpuinfo.icache_size);
234 CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
235}
236
237static void __flush_icache_all_msr_irq(void)
238{
239 unsigned long flags;
240
241 pr_debug("%s\n", __func__);
242
243 local_irq_save(flags);
244 __disable_icache_msr();
245
246 CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
247
248 __enable_icache_msr();
249 local_irq_restore(flags);
250}
251
252static void __flush_icache_all_nomsr_irq(void)
253{
254 unsigned long flags;
255
256 pr_debug("%s\n", __func__);
257
258 local_irq_save(flags);
259 __disable_icache_nomsr();
260
261 CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
262
263 __enable_icache_nomsr();
264 local_irq_restore(flags);
265}
266
267static void __flush_icache_all_noirq(void)
268{
269 pr_debug("%s\n", __func__);
270 CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
271}
272
273static void __invalidate_dcache_all_msr_irq(void)
274{
275 unsigned long flags;
276
277 pr_debug("%s\n", __func__);
278
279 local_irq_save(flags);
280 __disable_dcache_msr();
281
282 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
283
284 __enable_dcache_msr();
285 local_irq_restore(flags);
286}
287
288static void __invalidate_dcache_all_nomsr_irq(void)
289{
290 unsigned long flags;
291
292 pr_debug("%s\n", __func__);
293
294 local_irq_save(flags);
295 __disable_dcache_nomsr();
296
297 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
298
299 __enable_dcache_nomsr();
300 local_irq_restore(flags);
301}
302
303static void __invalidate_dcache_all_noirq_wt(void)
304{
305 pr_debug("%s\n", __func__);
306 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc)
307}
308
309/* FIXME this is weird - should be only wdc but not work
310 * MS: I am getting bus errors and other weird things */
311static void __invalidate_dcache_all_wb(void)
312{
313 pr_debug("%s\n", __func__);
314 CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
315 wdc.clear)
316
317#if 0
318 unsigned int i;
319
320 pr_debug("%s\n", __func__);
321
322 /* Just loop through cache size and invalidate it */
323 for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length)
324 __invalidate_dcache(0, i);
Michal Simek8beb8502009-03-27 14:25:16 +0100325#endif
Michal Simek8beb8502009-03-27 14:25:16 +0100326}
327
Michal Simek2ee2ff82009-12-10 11:43:57 +0100328static void __invalidate_dcache_range_wb(unsigned long start,
329 unsigned long end)
Michal Simek8beb8502009-03-27 14:25:16 +0100330{
Michal Simek2ee2ff82009-12-10 11:43:57 +0100331 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
332 (unsigned int)start, (unsigned int) end);
333
334 CACHE_LOOP_LIMITS(start, end,
335 cpuinfo.dcache_line_length, cpuinfo.dcache_size);
336 CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
Michal Simek8beb8502009-03-27 14:25:16 +0100337}
338
Michal Simek2ee2ff82009-12-10 11:43:57 +0100339static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
340 unsigned long end)
Michal Simek8beb8502009-03-27 14:25:16 +0100341{
Michal Simek2ee2ff82009-12-10 11:43:57 +0100342 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
343 (unsigned int)start, (unsigned int) end);
344 CACHE_LOOP_LIMITS(start, end,
345 cpuinfo.dcache_line_length, cpuinfo.dcache_size);
Michal Simek8beb8502009-03-27 14:25:16 +0100346
Michal Simek2ee2ff82009-12-10 11:43:57 +0100347 CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
348}
Michal Simek8beb8502009-03-27 14:25:16 +0100349
Michal Simek2ee2ff82009-12-10 11:43:57 +0100350static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
351 unsigned long end)
352{
353 unsigned long flags;
Michal Simek8beb8502009-03-27 14:25:16 +0100354
Michal Simek2ee2ff82009-12-10 11:43:57 +0100355 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
356 (unsigned int)start, (unsigned int) end);
357 CACHE_LOOP_LIMITS(start, end,
358 cpuinfo.dcache_line_length, cpuinfo.dcache_size);
359
360 local_irq_save(flags);
361 __disable_dcache_msr();
362
363 CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
364
365 __enable_dcache_msr();
366 local_irq_restore(flags);
367}
368
369static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
370 unsigned long end)
371{
372 unsigned long flags;
373
374 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
375 (unsigned int)start, (unsigned int) end);
376
377 CACHE_LOOP_LIMITS(start, end,
378 cpuinfo.dcache_line_length, cpuinfo.dcache_size);
379
380 local_irq_save(flags);
381 __disable_dcache_nomsr();
382
383 CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
384
385 __enable_dcache_nomsr();
386 local_irq_restore(flags);
387}
388
389static void __flush_dcache_all_wb(void)
390{
391 pr_debug("%s\n", __func__);
392 CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
393 wdc.flush);
394}
395
396static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
397{
398 pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
399 (unsigned int)start, (unsigned int) end);
400
401 CACHE_LOOP_LIMITS(start, end,
402 cpuinfo.dcache_line_length, cpuinfo.dcache_size);
403 CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
404}
405
406/* struct for wb caches and for wt caches */
407struct scache *mbc;
408
409/* new wb cache model */
410const struct scache wb_msr = {
411 .ie = __enable_icache_msr,
412 .id = __disable_icache_msr,
413 .ifl = __flush_icache_all_noirq,
414 .iflr = __flush_icache_range_noirq,
415 .iin = __flush_icache_all_noirq,
416 .iinr = __flush_icache_range_noirq,
417 .de = __enable_dcache_msr,
418 .dd = __disable_dcache_msr,
419 .dfl = __flush_dcache_all_wb,
420 .dflr = __flush_dcache_range_wb,
421 .din = __invalidate_dcache_all_wb,
422 .dinr = __invalidate_dcache_range_wb,
423};
424
425/* There is only difference in ie, id, de, dd functions */
426const struct scache wb_nomsr = {
427 .ie = __enable_icache_nomsr,
428 .id = __disable_icache_nomsr,
429 .ifl = __flush_icache_all_noirq,
430 .iflr = __flush_icache_range_noirq,
431 .iin = __flush_icache_all_noirq,
432 .iinr = __flush_icache_range_noirq,
433 .de = __enable_dcache_nomsr,
434 .dd = __disable_dcache_nomsr,
435 .dfl = __flush_dcache_all_wb,
436 .dflr = __flush_dcache_range_wb,
437 .din = __invalidate_dcache_all_wb,
438 .dinr = __invalidate_dcache_range_wb,
439};
440
441/* Old wt cache model with disabling irq and turn off cache */
442const struct scache wt_msr = {
443 .ie = __enable_icache_msr,
444 .id = __disable_icache_msr,
445 .ifl = __flush_icache_all_msr_irq,
446 .iflr = __flush_icache_range_msr_irq,
447 .iin = __flush_icache_all_msr_irq,
448 .iinr = __flush_icache_range_msr_irq,
449 .de = __enable_dcache_msr,
450 .dd = __disable_dcache_msr,
451 .dfl = __invalidate_dcache_all_msr_irq,
452 .dflr = __invalidate_dcache_range_msr_irq_wt,
453 .din = __invalidate_dcache_all_msr_irq,
454 .dinr = __invalidate_dcache_range_msr_irq_wt,
455};
456
457const struct scache wt_nomsr = {
458 .ie = __enable_icache_nomsr,
459 .id = __disable_icache_nomsr,
460 .ifl = __flush_icache_all_nomsr_irq,
461 .iflr = __flush_icache_range_nomsr_irq,
462 .iin = __flush_icache_all_nomsr_irq,
463 .iinr = __flush_icache_range_nomsr_irq,
464 .de = __enable_dcache_nomsr,
465 .dd = __disable_dcache_nomsr,
466 .dfl = __invalidate_dcache_all_nomsr_irq,
467 .dflr = __invalidate_dcache_range_nomsr_irq,
468 .din = __invalidate_dcache_all_nomsr_irq,
469 .dinr = __invalidate_dcache_range_nomsr_irq,
470};
471
472/* New wt cache model for newer Microblaze versions */
473const struct scache wt_msr_noirq = {
474 .ie = __enable_icache_msr,
475 .id = __disable_icache_msr,
476 .ifl = __flush_icache_all_noirq,
477 .iflr = __flush_icache_range_noirq,
478 .iin = __flush_icache_all_noirq,
479 .iinr = __flush_icache_range_noirq,
480 .de = __enable_dcache_msr,
481 .dd = __disable_dcache_msr,
482 .dfl = __invalidate_dcache_all_noirq_wt,
483 .dflr = __invalidate_dcache_range_nomsr_wt,
484 .din = __invalidate_dcache_all_noirq_wt,
485 .dinr = __invalidate_dcache_range_nomsr_wt,
486};
487
488const struct scache wt_nomsr_noirq = {
489 .ie = __enable_icache_nomsr,
490 .id = __disable_icache_nomsr,
491 .ifl = __flush_icache_all_noirq,
492 .iflr = __flush_icache_range_noirq,
493 .iin = __flush_icache_all_noirq,
494 .iinr = __flush_icache_range_noirq,
495 .de = __enable_dcache_nomsr,
496 .dd = __disable_dcache_nomsr,
497 .dfl = __invalidate_dcache_all_noirq_wt,
498 .dflr = __invalidate_dcache_range_nomsr_wt,
499 .din = __invalidate_dcache_all_noirq_wt,
500 .dinr = __invalidate_dcache_range_nomsr_wt,
501};
502
503/* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */
504#define CPUVER_7_20_A 0x0c
505#define CPUVER_7_20_D 0x0f
506
507#define INFO(s) printk(KERN_INFO "cache: " s " \n");
508
509void microblaze_cache_init(void)
510{
511 if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) {
512 if (cpuinfo.dcache_wb) {
513 INFO("wb_msr");
514 mbc = (struct scache *)&wb_msr;
515 if (cpuinfo.ver_code < CPUVER_7_20_D) {
516 /* MS: problem with signal handling - hw bug */
517 INFO("WB won't work properly");
518 }
519 } else {
520 if (cpuinfo.ver_code >= CPUVER_7_20_A) {
521 INFO("wt_msr_noirq");
522 mbc = (struct scache *)&wt_msr_noirq;
523 } else {
524 INFO("wt_msr");
525 mbc = (struct scache *)&wt_msr;
526 }
527 }
528 } else {
529 if (cpuinfo.dcache_wb) {
530 INFO("wb_nomsr");
531 mbc = (struct scache *)&wb_nomsr;
532 if (cpuinfo.ver_code < CPUVER_7_20_D) {
533 /* MS: problem with signal handling - hw bug */
534 INFO("WB won't work properly");
535 }
536 } else {
537 if (cpuinfo.ver_code >= CPUVER_7_20_A) {
538 INFO("wt_nomsr_noirq");
539 mbc = (struct scache *)&wt_nomsr_noirq;
540 } else {
541 INFO("wt_nomsr");
542 mbc = (struct scache *)&wt_nomsr;
543 }
544 }
Michal Simek8beb8502009-03-27 14:25:16 +0100545 }
546}