xref: /openbmc/linux/arch/microblaze/kernel/cpu/cache.c (revision a09d2831)
1 /*
2  * Cache control for MicroBlaze cache memories
3  *
4  * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu>
5  * Copyright (C) 2007-2009 PetaLogix
6  * Copyright (C) 2007-2009 John Williams <john.williams@petalogix.com>
7  *
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>
16 #include <asm/pvr.h>
17 
18 static inline void __invalidate_flush_icache(unsigned int addr)
19 {
20 	__asm__ __volatile__ ("wic	%0, r0;"	\
21 					: : "r" (addr));
22 }
23 
24 static inline void __flush_dcache(unsigned int addr)
25 {
26 	__asm__ __volatile__ ("wdc.flush	%0, r0;"	\
27 					: : "r" (addr));
28 }
29 
30 static inline void __invalidate_dcache(unsigned int baseaddr,
31 						unsigned int offset)
32 {
33 	__asm__ __volatile__ ("wdc.clear	%0, %1;"	\
34 					: : "r" (baseaddr), "r" (offset));
35 }
36 
37 static inline void __enable_icache_msr(void)
38 {
39 	__asm__ __volatile__ ("	msrset	r0, %0;		\
40 				nop; "			\
41 			: : "i" (MSR_ICE) : "memory");
42 }
43 
44 static inline void __disable_icache_msr(void)
45 {
46 	__asm__ __volatile__ ("	msrclr	r0, %0;		\
47 				nop; "			\
48 			: : "i" (MSR_ICE) : "memory");
49 }
50 
51 static inline void __enable_dcache_msr(void)
52 {
53 	__asm__ __volatile__ ("	msrset	r0, %0;		\
54 				nop; "			\
55 				:			\
56 				: "i" (MSR_DCE)		\
57 				: "memory");
58 }
59 
60 static inline void __disable_dcache_msr(void)
61 {
62 	__asm__ __volatile__ ("	msrclr	r0, %0;		\
63 				nop; "			\
64 				:			\
65 				: "i" (MSR_DCE)		\
66 				: "memory");
67 }
68 
69 static 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)		\
78 				: "memory", "r12");
79 }
80 
81 static 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 
93 static 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 
105 static 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)	\
120 do {									\
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)			\
132 do {									\
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)			\
146 do {									\
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)			\
160 do {									\
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)			\
174 do {									\
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 
187 static 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 
206 static 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 
226 static 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 
237 static 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 
252 static 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 
267 static 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 
273 static 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 
288 static 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 
303 static 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 */
311 static 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);
325 #endif
326 }
327 
328 static void __invalidate_dcache_range_wb(unsigned long start,
329 						unsigned long end)
330 {
331 	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);
337 }
338 
339 static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
340 							unsigned long end)
341 {
342 	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);
346 
347 	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
348 }
349 
350 static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
351 							unsigned long end)
352 {
353 	unsigned long flags;
354 
355 	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 
369 static 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 
389 static 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 
396 static 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 */
407 struct scache *mbc;
408 
409 /* new wb cache model */
410 const 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 */
426 const 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 */
442 const 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 
457 const 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 */
473 const 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 
488 const 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 
509 void 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 		}
545 	}
546 }
547