File Index
Files and directories
Sign in
FILE

cache.c

File preview with syntax highlighting.

linux-6.19.14/arch/microblaze/kernel/cpu/cache.c C
  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
 18static inline void __enable_icache_msr(void)
 19{
 20	__asm__ __volatile__ ("	 msrset	r0, %0;"	\
 21				"nop;"			\
 22			: : "i" (MSR_ICE) : "memory");
 23}
 24
 25static inline void __disable_icache_msr(void)
 26{
 27	__asm__ __volatile__ ("	 msrclr	r0, %0;"	\
 28				"nop;"			\
 29			: : "i" (MSR_ICE) : "memory");
 30}
 31
 32static inline void __enable_dcache_msr(void)
 33{
 34	__asm__ __volatile__ ("	 msrset	r0, %0;"	\
 35				"nop;"			\
 36			: : "i" (MSR_DCE) : "memory");
 37}
 38
 39static inline void __disable_dcache_msr(void)
 40{
 41	__asm__ __volatile__ ("	 msrclr	r0, %0;"	\
 42				"nop; "			\
 43			: : "i" (MSR_DCE) : "memory");
 44}
 45
 46static inline void __enable_icache_nomsr(void)
 47{
 48	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\
 49				"nop;"			\
 50				"ori	r12, r12, %0;"	\
 51				"mts	rmsr, r12;"	\
 52				"nop;"			\
 53			: : "i" (MSR_ICE) : "memory", "r12");
 54}
 55
 56static inline void __disable_icache_nomsr(void)
 57{
 58	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\
 59				"nop;"			\
 60				"andi	r12, r12, ~%0;"	\
 61				"mts	rmsr, r12;"	\
 62				"nop;"			\
 63			: : "i" (MSR_ICE) : "memory", "r12");
 64}
 65
 66static inline void __enable_dcache_nomsr(void)
 67{
 68	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\
 69				"nop;"			\
 70				"ori	r12, r12, %0;"	\
 71				"mts	rmsr, r12;"	\
 72				"nop;"			\
 73			: : "i" (MSR_DCE) : "memory", "r12");
 74}
 75
 76static inline void __disable_dcache_nomsr(void)
 77{
 78	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\
 79				"nop;"			\
 80				"andi	r12, r12, ~%0;"	\
 81				"mts	rmsr, r12;"	\
 82				"nop;"			\
 83			: : "i" (MSR_DCE) : "memory", "r12");
 84}
 85
 86
 87/* Helper macro for computing the limits of cache range loops
 88 *
 89 * End address can be unaligned which is OK for C implementation.
 90 * ASM implementation align it in ASM macros
 91 */
 92#define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size)	\
 93do {									\
 94	int align = ~(cache_line_length - 1);				\
 95	if (start <  UINT_MAX - cache_size)				\
 96		end = min(start + cache_size, end);			\
 97	start &= align;							\
 98} while (0)
 99
100/*
101 * Helper macro to loop over the specified cache_size/line_length and
102 * execute 'op' on that cacheline
103 */
104#define CACHE_ALL_LOOP(cache_size, line_length, op)			\
105do {									\
106	unsigned int len = cache_size - line_length;			\
107	int step = -line_length;					\
108	WARN_ON(step >= 0);						\
109									\
110	__asm__ __volatile__ (" 1:      " #op " %0, r0;"		\
111					"bgtid   %0, 1b;"		\
112					"addk    %0, %0, %1;"		\
113					: : "r" (len), "r" (step)	\
114					: "memory");			\
115} while (0)
116
117/* Used for wdc.flush/clear which can use rB for offset which is not possible
118 * to use for simple wdc or wic.
119 *
120 * start address is cache aligned
121 * end address is not aligned, if end is aligned then I have to subtract
122 * cacheline length because I can't flush/invalidate the next cacheline.
123 * If is not, I align it because I will flush/invalidate whole line.
124 */
125#define CACHE_RANGE_LOOP_2(start, end, line_length, op)			\
126do {									\
127	int step = -line_length;					\
128	int align = ~(line_length - 1);					\
129	int count;							\
130	end = ((end & align) == end) ? end - line_length : end & align;	\
131	count = end - start;						\
132	WARN_ON(count < 0);						\
133									\
134	__asm__ __volatile__ (" 1:	" #op "	%0, %1;"		\
135					"bgtid	%1, 1b;"		\
136					"addk	%1, %1, %2;"		\
137					: : "r" (start), "r" (count),	\
138					"r" (step) : "memory");		\
139} while (0)
140
141/* It is used only first parameter for OP - for wic, wdc */
142#define CACHE_RANGE_LOOP_1(start, end, line_length, op)			\
143do {									\
144	unsigned int volatile temp = 0;						\
145	unsigned int align = ~(line_length - 1);					\
146	end = ((end & align) == end) ? end - line_length : end & align;	\
147	WARN_ON(end < start);					\
148									\
149	__asm__ __volatile__ (" 1:	" #op "	%1, r0;"		\
150					"cmpu	%0, %1, %2;"		\
151					"bgtid	%0, 1b;"		\
152					"addk	%1, %1, %3;"		\
153				: : "r" (temp), "r" (start), "r" (end),	\
154					"r" (line_length) : "memory");	\
155} while (0)
156
157#define ASM_LOOP
158
159static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
160{
161	unsigned long flags;
162#ifndef ASM_LOOP
163	int i;
164#endif
165	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
166				(unsigned int)start, (unsigned int) end);
167
168	CACHE_LOOP_LIMITS(start, end,
169			cpuinfo.icache_line_length, cpuinfo.icache_size);
170
171	local_irq_save(flags);
172	__disable_icache_msr();
173
174#ifdef ASM_LOOP
175	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
176#else
177	for (i = start; i < end; i += cpuinfo.icache_line_length)
178		__asm__ __volatile__ ("wic	%0, r0;"	\
179				: : "r" (i));
180#endif
181	__enable_icache_msr();
182	local_irq_restore(flags);
183}
184
185static void __flush_icache_range_nomsr_irq(unsigned long start,
186				unsigned long end)
187{
188	unsigned long flags;
189#ifndef ASM_LOOP
190	int i;
191#endif
192	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
193				(unsigned int)start, (unsigned int) end);
194
195	CACHE_LOOP_LIMITS(start, end,
196			cpuinfo.icache_line_length, cpuinfo.icache_size);
197
198	local_irq_save(flags);
199	__disable_icache_nomsr();
200
201#ifdef ASM_LOOP
202	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
203#else
204	for (i = start; i < end; i += cpuinfo.icache_line_length)
205		__asm__ __volatile__ ("wic	%0, r0;"	\
206				: : "r" (i));
207#endif
208
209	__enable_icache_nomsr();
210	local_irq_restore(flags);
211}
212
213static void __flush_icache_range_noirq(unsigned long start,
214				unsigned long end)
215{
216#ifndef ASM_LOOP
217	int i;
218#endif
219	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
220				(unsigned int)start, (unsigned int) end);
221
222	CACHE_LOOP_LIMITS(start, end,
223			cpuinfo.icache_line_length, cpuinfo.icache_size);
224#ifdef ASM_LOOP
225	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
226#else
227	for (i = start; i < end; i += cpuinfo.icache_line_length)
228		__asm__ __volatile__ ("wic	%0, r0;"	\
229				: : "r" (i));
230#endif
231}
232
233static void __flush_icache_all_msr_irq(void)
234{
235	unsigned long flags;
236#ifndef ASM_LOOP
237	int i;
238#endif
239	pr_debug("%s\n", __func__);
240
241	local_irq_save(flags);
242	__disable_icache_msr();
243#ifdef ASM_LOOP
244	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
245#else
246	for (i = 0; i < cpuinfo.icache_size;
247		 i += cpuinfo.icache_line_length)
248			__asm__ __volatile__ ("wic	%0, r0;" \
249					: : "r" (i));
250#endif
251	__enable_icache_msr();
252	local_irq_restore(flags);
253}
254
255static void __flush_icache_all_nomsr_irq(void)
256{
257	unsigned long flags;
258#ifndef ASM_LOOP
259	int i;
260#endif
261	pr_debug("%s\n", __func__);
262
263	local_irq_save(flags);
264	__disable_icache_nomsr();
265#ifdef ASM_LOOP
266	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
267#else
268	for (i = 0; i < cpuinfo.icache_size;
269		 i += cpuinfo.icache_line_length)
270			__asm__ __volatile__ ("wic	%0, r0;" \
271					: : "r" (i));
272#endif
273	__enable_icache_nomsr();
274	local_irq_restore(flags);
275}
276
277static void __flush_icache_all_noirq(void)
278{
279#ifndef ASM_LOOP
280	int i;
281#endif
282	pr_debug("%s\n", __func__);
283#ifdef ASM_LOOP
284	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
285#else
286	for (i = 0; i < cpuinfo.icache_size;
287		 i += cpuinfo.icache_line_length)
288			__asm__ __volatile__ ("wic	%0, r0;" \
289					: : "r" (i));
290#endif
291}
292
293static void __invalidate_dcache_all_msr_irq(void)
294{
295	unsigned long flags;
296#ifndef ASM_LOOP
297	int i;
298#endif
299	pr_debug("%s\n", __func__);
300
301	local_irq_save(flags);
302	__disable_dcache_msr();
303#ifdef ASM_LOOP
304	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
305#else
306	for (i = 0; i < cpuinfo.dcache_size;
307		 i += cpuinfo.dcache_line_length)
308			__asm__ __volatile__ ("wdc	%0, r0;" \
309					: : "r" (i));
310#endif
311	__enable_dcache_msr();
312	local_irq_restore(flags);
313}
314
315static void __invalidate_dcache_all_nomsr_irq(void)
316{
317	unsigned long flags;
318#ifndef ASM_LOOP
319	int i;
320#endif
321	pr_debug("%s\n", __func__);
322
323	local_irq_save(flags);
324	__disable_dcache_nomsr();
325#ifdef ASM_LOOP
326	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
327#else
328	for (i = 0; i < cpuinfo.dcache_size;
329		 i += cpuinfo.dcache_line_length)
330			__asm__ __volatile__ ("wdc	%0, r0;" \
331					: : "r" (i));
332#endif
333	__enable_dcache_nomsr();
334	local_irq_restore(flags);
335}
336
337static void __invalidate_dcache_all_noirq_wt(void)
338{
339#ifndef ASM_LOOP
340	int i;
341#endif
342	pr_debug("%s\n", __func__);
343#ifdef ASM_LOOP
344	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
345#else
346	for (i = 0; i < cpuinfo.dcache_size;
347		 i += cpuinfo.dcache_line_length)
348			__asm__ __volatile__ ("wdc	%0, r0;" \
349					: : "r" (i));
350#endif
351}
352
353/*
354 * FIXME It is blindly invalidation as is expected
355 * but can't be called on noMMU in microblaze_cache_init below
356 *
357 * MS: noMMU kernel won't boot if simple wdc is used
358 * The reason should be that there are discared data which kernel needs
359 */
360static void __invalidate_dcache_all_wb(void)
361{
362#ifndef ASM_LOOP
363	int i;
364#endif
365	pr_debug("%s\n", __func__);
366#ifdef ASM_LOOP
367	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
368					wdc);
369#else
370	for (i = 0; i < cpuinfo.dcache_size;
371		 i += cpuinfo.dcache_line_length)
372			__asm__ __volatile__ ("wdc	%0, r0;" \
373					: : "r" (i));
374#endif
375}
376
377static void __invalidate_dcache_range_wb(unsigned long start,
378						unsigned long end)
379{
380#ifndef ASM_LOOP
381	int i;
382#endif
383	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
384				(unsigned int)start, (unsigned int) end);
385
386	CACHE_LOOP_LIMITS(start, end,
387			cpuinfo.dcache_line_length, cpuinfo.dcache_size);
388#ifdef ASM_LOOP
389	CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
390#else
391	for (i = start; i < end; i += cpuinfo.dcache_line_length)
392		__asm__ __volatile__ ("wdc.clear	%0, r0;"	\
393				: : "r" (i));
394#endif
395}
396
397static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
398							unsigned long end)
399{
400#ifndef ASM_LOOP
401	int i;
402#endif
403	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
404				(unsigned int)start, (unsigned int) end);
405	CACHE_LOOP_LIMITS(start, end,
406			cpuinfo.dcache_line_length, cpuinfo.dcache_size);
407
408#ifdef ASM_LOOP
409	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
410#else
411	for (i = start; i < end; i += cpuinfo.dcache_line_length)
412		__asm__ __volatile__ ("wdc	%0, r0;"	\
413				: : "r" (i));
414#endif
415}
416
417static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
418							unsigned long end)
419{
420	unsigned long flags;
421#ifndef ASM_LOOP
422	int i;
423#endif
424	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
425				(unsigned int)start, (unsigned int) end);
426	CACHE_LOOP_LIMITS(start, end,
427			cpuinfo.dcache_line_length, cpuinfo.dcache_size);
428
429	local_irq_save(flags);
430	__disable_dcache_msr();
431
432#ifdef ASM_LOOP
433	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
434#else
435	for (i = start; i < end; i += cpuinfo.dcache_line_length)
436		__asm__ __volatile__ ("wdc	%0, r0;"	\
437				: : "r" (i));
438#endif
439
440	__enable_dcache_msr();
441	local_irq_restore(flags);
442}
443
444static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
445							unsigned long end)
446{
447	unsigned long flags;
448#ifndef ASM_LOOP
449	int i;
450#endif
451	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
452				(unsigned int)start, (unsigned int) end);
453
454	CACHE_LOOP_LIMITS(start, end,
455			cpuinfo.dcache_line_length, cpuinfo.dcache_size);
456
457	local_irq_save(flags);
458	__disable_dcache_nomsr();
459
460#ifdef ASM_LOOP
461	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
462#else
463	for (i = start; i < end; i += cpuinfo.dcache_line_length)
464		__asm__ __volatile__ ("wdc	%0, r0;"	\
465				: : "r" (i));
466#endif
467
468	__enable_dcache_nomsr();
469	local_irq_restore(flags);
470}
471
472static void __flush_dcache_all_wb(void)
473{
474#ifndef ASM_LOOP
475	int i;
476#endif
477	pr_debug("%s\n", __func__);
478#ifdef ASM_LOOP
479	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
480				wdc.flush);
481#else
482	for (i = 0; i < cpuinfo.dcache_size;
483		 i += cpuinfo.dcache_line_length)
484			__asm__ __volatile__ ("wdc.flush	%0, r0;" \
485					: : "r" (i));
486#endif
487}
488
489static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
490{
491#ifndef ASM_LOOP
492	int i;
493#endif
494	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
495				(unsigned int)start, (unsigned int) end);
496
497	CACHE_LOOP_LIMITS(start, end,
498			cpuinfo.dcache_line_length, cpuinfo.dcache_size);
499#ifdef ASM_LOOP
500	CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
501#else
502	for (i = start; i < end; i += cpuinfo.dcache_line_length)
503		__asm__ __volatile__ ("wdc.flush	%0, r0;"	\
504				: : "r" (i));
505#endif
506}
507
508/* struct for wb caches and for wt caches */
509struct scache *mbc;
510
511/* new wb cache model */
512static const struct scache wb_msr = {
513	.ie = __enable_icache_msr,
514	.id = __disable_icache_msr,
515	.ifl = __flush_icache_all_noirq,
516	.iflr = __flush_icache_range_noirq,
517	.iin = __flush_icache_all_noirq,
518	.iinr = __flush_icache_range_noirq,
519	.de = __enable_dcache_msr,
520	.dd = __disable_dcache_msr,
521	.dfl = __flush_dcache_all_wb,
522	.dflr = __flush_dcache_range_wb,
523	.din = __invalidate_dcache_all_wb,
524	.dinr = __invalidate_dcache_range_wb,
525};
526
527/* There is only difference in ie, id, de, dd functions */
528static const struct scache wb_nomsr = {
529	.ie = __enable_icache_nomsr,
530	.id = __disable_icache_nomsr,
531	.ifl = __flush_icache_all_noirq,
532	.iflr = __flush_icache_range_noirq,
533	.iin = __flush_icache_all_noirq,
534	.iinr = __flush_icache_range_noirq,
535	.de = __enable_dcache_nomsr,
536	.dd = __disable_dcache_nomsr,
537	.dfl = __flush_dcache_all_wb,
538	.dflr = __flush_dcache_range_wb,
539	.din = __invalidate_dcache_all_wb,
540	.dinr = __invalidate_dcache_range_wb,
541};
542
543/* Old wt cache model with disabling irq and turn off cache */
544static const struct scache wt_msr = {
545	.ie = __enable_icache_msr,
546	.id = __disable_icache_msr,
547	.ifl = __flush_icache_all_msr_irq,
548	.iflr = __flush_icache_range_msr_irq,
549	.iin = __flush_icache_all_msr_irq,
550	.iinr = __flush_icache_range_msr_irq,
551	.de = __enable_dcache_msr,
552	.dd = __disable_dcache_msr,
553	.dfl = __invalidate_dcache_all_msr_irq,
554	.dflr = __invalidate_dcache_range_msr_irq_wt,
555	.din = __invalidate_dcache_all_msr_irq,
556	.dinr = __invalidate_dcache_range_msr_irq_wt,
557};
558
559static const struct scache wt_nomsr = {
560	.ie = __enable_icache_nomsr,
561	.id = __disable_icache_nomsr,
562	.ifl = __flush_icache_all_nomsr_irq,
563	.iflr = __flush_icache_range_nomsr_irq,
564	.iin = __flush_icache_all_nomsr_irq,
565	.iinr = __flush_icache_range_nomsr_irq,
566	.de = __enable_dcache_nomsr,
567	.dd = __disable_dcache_nomsr,
568	.dfl = __invalidate_dcache_all_nomsr_irq,
569	.dflr = __invalidate_dcache_range_nomsr_irq,
570	.din = __invalidate_dcache_all_nomsr_irq,
571	.dinr = __invalidate_dcache_range_nomsr_irq,
572};
573
574/* New wt cache model for newer Microblaze versions */
575static const struct scache wt_msr_noirq = {
576	.ie = __enable_icache_msr,
577	.id = __disable_icache_msr,
578	.ifl = __flush_icache_all_noirq,
579	.iflr = __flush_icache_range_noirq,
580	.iin = __flush_icache_all_noirq,
581	.iinr = __flush_icache_range_noirq,
582	.de = __enable_dcache_msr,
583	.dd = __disable_dcache_msr,
584	.dfl = __invalidate_dcache_all_noirq_wt,
585	.dflr = __invalidate_dcache_range_nomsr_wt,
586	.din = __invalidate_dcache_all_noirq_wt,
587	.dinr = __invalidate_dcache_range_nomsr_wt,
588};
589
590static const struct scache wt_nomsr_noirq = {
591	.ie = __enable_icache_nomsr,
592	.id = __disable_icache_nomsr,
593	.ifl = __flush_icache_all_noirq,
594	.iflr = __flush_icache_range_noirq,
595	.iin = __flush_icache_all_noirq,
596	.iinr = __flush_icache_range_noirq,
597	.de = __enable_dcache_nomsr,
598	.dd = __disable_dcache_nomsr,
599	.dfl = __invalidate_dcache_all_noirq_wt,
600	.dflr = __invalidate_dcache_range_nomsr_wt,
601	.din = __invalidate_dcache_all_noirq_wt,
602	.dinr = __invalidate_dcache_range_nomsr_wt,
603};
604
605/* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */
606#define CPUVER_7_20_A	0x0c
607#define CPUVER_7_20_D	0x0f
608
609void microblaze_cache_init(void)
610{
611	if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) {
612		if (cpuinfo.dcache_wb) {
613			pr_info("wb_msr\n");
614			mbc = (struct scache *)&wb_msr;
615			if (cpuinfo.ver_code <= CPUVER_7_20_D) {
616				/* MS: problem with signal handling - hw bug */
617				pr_info("WB won't work properly\n");
618			}
619		} else {
620			if (cpuinfo.ver_code >= CPUVER_7_20_A) {
621				pr_info("wt_msr_noirq\n");
622				mbc = (struct scache *)&wt_msr_noirq;
623			} else {
624				pr_info("wt_msr\n");
625				mbc = (struct scache *)&wt_msr;
626			}
627		}
628	} else {
629		if (cpuinfo.dcache_wb) {
630			pr_info("wb_nomsr\n");
631			mbc = (struct scache *)&wb_nomsr;
632			if (cpuinfo.ver_code <= CPUVER_7_20_D) {
633				/* MS: problem with signal handling - hw bug */
634				pr_info("WB won't work properly\n");
635			}
636		} else {
637			if (cpuinfo.ver_code >= CPUVER_7_20_A) {
638				pr_info("wt_nomsr_noirq\n");
639				mbc = (struct scache *)&wt_nomsr_noirq;
640			} else {
641				pr_info("wt_nomsr\n");
642				mbc = (struct scache *)&wt_nomsr;
643			}
644		}
645	}
646	/*
647	 * FIXME Invalidation is done in U-BOOT
648	 * WT cache: Data is already written to main memory
649	 * WB cache: Discard data on noMMU which caused that kernel doesn't boot
650	 */
651	/* invalidate_dcache(); */
652	enable_dcache();
653
654	invalidate_icache();
655	enable_icache();
656}
~/ Read-only file preview.