656 lines · 16.6 KB · C
RawDownload
/* * Cache control for MicroBlaze cache memories * * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu> * Copyright (C) 2007-2009 PetaLogix * Copyright (C) 2007-2009 John Williams <john.williams@petalogix.com> * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this * archive for more details. */ #include <asm/cacheflush.h>#include <linux/cache.h>#include <asm/cpuinfo.h>#include <asm/pvr.h> static inline void __enable_icache_msr(void){	__asm__ __volatile__ ("	 msrset	r0, %0;"	\				"nop;"			\			: : "i" (MSR_ICE) : "memory");} static inline void __disable_icache_msr(void){	__asm__ __volatile__ ("	 msrclr	r0, %0;"	\				"nop;"			\			: : "i" (MSR_ICE) : "memory");} static inline void __enable_dcache_msr(void){	__asm__ __volatile__ ("	 msrset	r0, %0;"	\				"nop;"			\			: : "i" (MSR_DCE) : "memory");} static inline void __disable_dcache_msr(void){	__asm__ __volatile__ ("	 msrclr	r0, %0;"	\				"nop; "			\			: : "i" (MSR_DCE) : "memory");} static inline void __enable_icache_nomsr(void){	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\				"nop;"			\				"ori	r12, r12, %0;"	\				"mts	rmsr, r12;"	\				"nop;"			\			: : "i" (MSR_ICE) : "memory", "r12");} static inline void __disable_icache_nomsr(void){	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\				"nop;"			\				"andi	r12, r12, ~%0;"	\				"mts	rmsr, r12;"	\				"nop;"			\			: : "i" (MSR_ICE) : "memory", "r12");} static inline void __enable_dcache_nomsr(void){	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\				"nop;"			\				"ori	r12, r12, %0;"	\				"mts	rmsr, r12;"	\				"nop;"			\			: : "i" (MSR_DCE) : "memory", "r12");} static inline void __disable_dcache_nomsr(void){	__asm__ __volatile__ ("	 mfs	r12, rmsr;"	\				"nop;"			\				"andi	r12, r12, ~%0;"	\				"mts	rmsr, r12;"	\				"nop;"			\			: : "i" (MSR_DCE) : "memory", "r12");}  /* 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);				\	if (start <  UINT_MAX - cache_size)				\		end = min(start + cache_size, end);			\	start &= align;							\} while (0) /* * Helper macro to loop over the specified cache_size/line_length and * execute 'op' on that cacheline */#define CACHE_ALL_LOOP(cache_size, line_length, op)			\do {									\	unsigned int len = cache_size - line_length;			\	int step = -line_length;					\	WARN_ON(step >= 0);						\									\	__asm__ __volatile__ (" 1:      " #op " %0, r0;"		\					"bgtid   %0, 1b;"		\					"addk    %0, %0, %1;"		\					: : "r" (len), "r" (step)	\					: "memory");			\} while (0) /* 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 subtract * 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 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;"		\					: : "r" (start), "r" (count),	\					"r" (step) : "memory");		\} while (0) /* It is used only first parameter for OP - for wic, wdc */#define CACHE_RANGE_LOOP_1(start, end, line_length, op)			\do {									\	unsigned int volatile temp = 0;						\	unsigned int align = ~(line_length - 1);					\	end = ((end & align) == end) ? end - line_length : end & align;	\	WARN_ON(end < start);					\									\	__asm__ __volatile__ (" 1:	" #op "	%1, r0;"		\					"cmpu	%0, %1, %2;"		\					"bgtid	%0, 1b;"		\					"addk	%1, %1, %3;"		\				: : "r" (temp), "r" (start), "r" (end),	\					"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); 	CACHE_LOOP_LIMITS(start, end,			cpuinfo.icache_line_length, cpuinfo.icache_size); 	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);} 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); 	CACHE_LOOP_LIMITS(start, end,			cpuinfo.icache_line_length, cpuinfo.icache_size); 	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);} 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);} 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);} 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 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__);#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,			cpuinfo.dcache_line_length, cpuinfo.dcache_size); 	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);} 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); 	CACHE_LOOP_LIMITS(start, end,			cpuinfo.dcache_line_length, cpuinfo.dcache_size); 	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);} 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);

Lines 1–500 of 656.