diff options
Diffstat (limited to 'arch/microblaze/kernel/cpu/cache.c')
| -rw-r--r-- | arch/microblaze/kernel/cpu/cache.c | 266 | 
1 files changed, 199 insertions, 67 deletions
diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c index 2a56bccce4e..109876e8d64 100644 --- a/arch/microblaze/kernel/cpu/cache.c +++ b/arch/microblaze/kernel/cpu/cache.c @@ -15,25 +15,6 @@  #include <asm/cpuinfo.h>  #include <asm/pvr.h> -static inline void __invalidate_flush_icache(unsigned int addr) -{ -	__asm__ __volatile__ ("wic	%0, r0;"	\ -					: : "r" (addr)); -} - -static inline void __flush_dcache(unsigned int addr) -{ -	__asm__ __volatile__ ("wdc.flush	%0, r0;"	\ -					: : "r" (addr)); -} - -static inline void __invalidate_dcache(unsigned int baseaddr, -						unsigned int offset) -{ -	__asm__ __volatile__ ("wdc.clear	%0, %1;"	\ -					: : "r" (baseaddr), "r" (offset)); -} -  static inline void __enable_icache_msr(void)  {  	__asm__ __volatile__ ("	msrset	r0, %0;		\ @@ -115,13 +96,16 @@ static inline void __disable_dcache_nomsr(void)  } -/* Helper macro for computing the limits of cache range loops */ +/* Helper macro for computing the limits of cache range loops + * + * End address can be unaligned which is OK for C implementation. + * ASM implementation align it in ASM macros + */  #define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size)	\  do {									\  	int align = ~(cache_line_length - 1);				\  	end = min(start + cache_size, end);				\  	start &= align;							\ -	end = ((end & align) + cache_line_length);			\  } while (0);  /* @@ -130,9 +114,9 @@ do {									\   */  #define CACHE_ALL_LOOP(cache_size, line_length, op)			\  do {									\ -	unsigned int len = cache_size;					\ +	unsigned int len = cache_size - line_length;			\  	int step = -line_length;					\ -	BUG_ON(step >= 0);						\ +	WARN_ON(step >= 0);						\  									\  	__asm__ __volatile__ (" 1:      " #op " %0, r0;			\  					bgtid   %0, 1b;			\ @@ -141,30 +125,26 @@ do {									\  					: "memory");			\  } while (0); - -#define CACHE_ALL_LOOP2(cache_size, line_length, op)			\ -do {									\ -	unsigned int len = cache_size;					\ -	int step = -line_length;					\ -	BUG_ON(step >= 0);						\ -									\ -	__asm__ __volatile__ (" 1:      " #op " r0, %0;			\ -					bgtid   %0, 1b;			\ -					addk    %0, %0, %1;		\ -					" : : "r" (len), "r" (step)	\ -					: "memory");			\ -} while (0); - -/* for wdc.flush/clear */ +/* Used for wdc.flush/clear which can use rB for offset which is not possible + * to use for simple wdc or wic. + * + * start address is cache aligned + * end address is not aligned, if end is aligned then I have to substract + * cacheline length because I can't flush/invalidate the next cacheline. + * If is not, I align it because I will flush/invalidate whole line. + */  #define CACHE_RANGE_LOOP_2(start, end, line_length, op)			\  do {									\  	int step = -line_length;					\ -	int count = end - start;					\ -	BUG_ON(count <= 0);						\ +	int align = ~(line_length - 1);					\ +	int count;							\ +	end = ((end & align) == end) ? end - line_length : end & align;	\ +	count = end - start;						\ +	WARN_ON(count < 0);						\  									\ -	__asm__ __volatile__ (" 1:	" #op " %0, %1;			\ -					bgtid   %1, 1b;			\ -					addk    %1, %1, %2;		\ +	__asm__ __volatile__ (" 1:	" #op "	%0, %1;			\ +					bgtid	%1, 1b;			\ +					addk	%1, %1, %2;		\  					" : : "r" (start), "r" (count),	\  					"r" (step) : "memory");		\  } while (0); @@ -173,9 +153,11 @@ do {									\  #define CACHE_RANGE_LOOP_1(start, end, line_length, op)			\  do {									\  	int volatile temp;						\ -	BUG_ON(end - start <= 0);					\ +	int align = ~(line_length - 1);					\ +	end = ((end & align) == end) ? end - line_length : end & align;	\ +	WARN_ON(end - start < 0);					\  									\ -	__asm__ __volatile__ (" 1:	" #op " %1, r0;			\ +	__asm__ __volatile__ (" 1:	" #op "	%1, r0;			\  					cmpu	%0, %1, %2;		\  					bgtid	%0, 1b;			\  					addk	%1, %1, %3;		\ @@ -183,10 +165,14 @@ do {									\  					"r" (line_length) : "memory");	\  } while (0); +#define ASM_LOOP +  static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end); @@ -196,8 +182,13 @@ static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)  	local_irq_save(flags);  	__disable_icache_msr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); - +#else +	for (i = start; i < end; i += cpuinfo.icache_line_length) +		__asm__ __volatile__ ("wic	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_icache_msr();  	local_irq_restore(flags);  } @@ -206,7 +197,9 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,  				unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end); @@ -216,7 +209,13 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,  	local_irq_save(flags);  	__disable_icache_nomsr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); +#else +	for (i = start; i < end; i += cpuinfo.icache_line_length) +		__asm__ __volatile__ ("wic	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_icache_nomsr();  	local_irq_restore(flags); @@ -225,25 +224,41 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,  static void __flush_icache_range_noirq(unsigned long start,  				unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.icache_line_length, cpuinfo.icache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic); +#else +	for (i = start; i < end; i += cpuinfo.icache_line_length) +		__asm__ __volatile__ ("wic	%0, r0;"	\ +				: : "r" (i)); +#endif  }  static void __flush_icache_all_msr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_icache_msr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); - +#else +	for (i = 0; i < cpuinfo.icache_size; +		 i += cpuinfo.icache_line_length) +			__asm__ __volatile__ ("wic	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_icache_msr();  	local_irq_restore(flags);  } @@ -251,35 +266,59 @@ static void __flush_icache_all_msr_irq(void)  static void __flush_icache_all_nomsr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_icache_nomsr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); - +#else +	for (i = 0; i < cpuinfo.icache_size; +		 i += cpuinfo.icache_line_length) +			__asm__ __volatile__ ("wic	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_icache_nomsr();  	local_irq_restore(flags);  }  static void __flush_icache_all_noirq(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic); +#else +	for (i = 0; i < cpuinfo.icache_size; +		 i += cpuinfo.icache_line_length) +			__asm__ __volatile__ ("wic	%0, r0;" \ +					: : "r" (i)); +#endif  }  static void __invalidate_dcache_all_msr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_dcache_msr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); - +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_dcache_msr();  	local_irq_restore(flags);  } @@ -287,60 +326,111 @@ static void __invalidate_dcache_all_msr_irq(void)  static void __invalidate_dcache_all_nomsr_irq(void)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__);  	local_irq_save(flags);  	__disable_dcache_nomsr(); - +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); - +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  	__enable_dcache_nomsr();  	local_irq_restore(flags);  }  static void __invalidate_dcache_all_noirq_wt(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc) +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  } -/* FIXME this is weird - should be only wdc but not work - * MS: I am getting bus errors and other weird things */ +/* FIXME It is blindly invalidation as is expected + * but can't be called on noMMU in microblaze_cache_init below + * + * MS: noMMU kernel won't boot if simple wdc is used + * The reason should be that there are discared data which kernel needs + */  static void __invalidate_dcache_all_wb(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); -	CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length, -					wdc.clear) +#ifdef ASM_LOOP +	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, +					wdc) +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc	%0, r0;" \ +					: : "r" (i)); +#endif  }  static void __invalidate_dcache_range_wb(unsigned long start,  						unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.dcache_line_length, cpuinfo.dcache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc.clear	%0, r0;"	\ +				: : "r" (i)); +#endif  }  static void __invalidate_dcache_range_nomsr_wt(unsigned long start,  							unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.dcache_line_length, cpuinfo.dcache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc	%0, r0;"	\ +				: : "r" (i)); +#endif  }  static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,  							unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end, @@ -349,7 +439,13 @@ static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,  	local_irq_save(flags);  	__disable_dcache_msr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_dcache_msr();  	local_irq_restore(flags); @@ -359,7 +455,9 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,  							unsigned long end)  {  	unsigned long flags; - +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end); @@ -369,7 +467,13 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,  	local_irq_save(flags);  	__disable_dcache_nomsr(); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc	%0, r0;"	\ +				: : "r" (i)); +#endif  	__enable_dcache_nomsr();  	local_irq_restore(flags); @@ -377,19 +481,38 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,  static void __flush_dcache_all_wb(void)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s\n", __func__); +#ifdef ASM_LOOP  	CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,  				wdc.flush); +#else +	for (i = 0; i < cpuinfo.dcache_size; +		 i += cpuinfo.dcache_line_length) +			__asm__ __volatile__ ("wdc.flush	%0, r0;" \ +					: : "r" (i)); +#endif  }  static void __flush_dcache_range_wb(unsigned long start, unsigned long end)  { +#ifndef ASM_LOOP +	int i; +#endif  	pr_debug("%s: start 0x%x, end 0x%x\n", __func__,  				(unsigned int)start, (unsigned int) end);  	CACHE_LOOP_LIMITS(start, end,  			cpuinfo.dcache_line_length, cpuinfo.dcache_size); +#ifdef ASM_LOOP  	CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush); +#else +	for (i = start; i < end; i += cpuinfo.dcache_line_length) +		__asm__ __volatile__ ("wdc.flush	%0, r0;"	\ +				: : "r" (i)); +#endif  }  /* struct for wb caches and for wt caches */ @@ -493,7 +616,7 @@ const struct scache wt_nomsr_noirq = {  #define CPUVER_7_20_A	0x0c  #define CPUVER_7_20_D	0x0f -#define INFO(s)	printk(KERN_INFO "cache: " s " \n"); +#define INFO(s)	printk(KERN_INFO "cache: " s "\n");  void microblaze_cache_init(void)  { @@ -532,4 +655,13 @@ void microblaze_cache_init(void)  			}  		}  	} +/* FIXME Invalidation is done in U-BOOT + * WT cache: Data is already written to main memory + * WB cache: Discard data on noMMU which caused that kernel doesn't boot + */ +	/* invalidate_dcache(); */ +	enable_dcache(); + +	invalidate_icache(); +	enable_icache();  }  |