
Hi Aneesh,
Le 22/12/2010 12:54, Aneesh V a écrit :
Add a framework for layered cache maintenance
- separate out SOC specific outer cache maintenance from maintenance of caches known to CPU
Add generic ARMv7 cache maintenance operations that affect all caches known to ARMv7 CPUs. For instance in Cortex-A8 these opertions will affect both L1 and L2 caches. In Cortex-A9 these will affect only L1 cache
D-cache operations supported:
- Invalidate entire D-cache
- Invalidate D-cache range
- Flush(clean& invalidate) entire D-cache
- Flush D-cache range
I-cache operations supported:
- Invalidate entire I-cache
Add maintenance functions for TLB, branch predictor array etc.
Enable -march=armv7-a so that armv7 assembly instructions can be used
Signed-off-by: Aneesh Vaneesh@ti.com
arch/arm/cpu/armv7/Makefile | 2 +- arch/arm/cpu/armv7/cache_v7.c | 359 +++++++++++++++++++++++++++++++++++++++++ arch/arm/cpu/armv7/config.mk | 2 +- arch/arm/include/asm/armv7.h | 63 +++++++ include/common.h | 5 +- 5 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 arch/arm/cpu/armv7/cache_v7.c create mode 100644 arch/arm/include/asm/armv7.h
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 8c0e915..299792a 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(CPU).o
START := start.o -COBJS := cpu.o +COBJS := cpu.o cache_v7.o COBJS += syslib.o
SRCS := $(START:.o=.S) $(COBJS:.o=.c) diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c new file mode 100644 index 0000000..0521d66 --- /dev/null +++ b/arch/arm/cpu/armv7/cache_v7.c @@ -0,0 +1,359 @@ +/*
- (C) Copyright 2010
- Texas Instruments Incorporated - http://www.ti.com/
- Aneesh Vaneesh@ti.com
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include<linux/types.h> +#include<common.h> +#include<asm/armv7.h>
+#define ARMV7_DCACHE_INVAL_ALL 1 +#define ARMV7_DCACHE_CLEAN_INVAL_ALL 2 +#define ARMV7_DCACHE_INVAL_RANGE 3 +#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4
+struct v7_outer_cache_ops v7_outer_cache;
+#ifndef CONFIG_SYS_NO_DCACHE +/*
- Write the level and type you want to Cache Size Selection Register(CSSELR)
- to get size details from Current Cache Size ID Register(CCSIDR)
- */
+static void set_csselr(u32 level, u32 type) +{ u32 csselr = level<< 1 | type;
- /* Write to Cache Size Selection Register(CSSELR) */
- asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
+}
+static u32 get_ccsidr(void) +{
- u32 ccsidr;
- /* Read current CP15 Cache Size ID Register */
- asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
- return ccsidr;
+}
+static u32 get_clidr(void) +{
- u32 clidr;
- /* Read current CP15 Cache Level ID Register */
- asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr));
- return clidr;
+}
+/* round up the input number to a power of 2 and get the log2 */ +static inline u32 log_2_round_up(u32 num) +{
- /* count leading zeros */
- asm volatile ("CLZ %0, %0" : "+r" (num));
- /* position of most significant 1 */
- num = 31 - num;
- return num;
+}
+static void v7_inval_dcache_level_setway(u32 level, u32 num_sets,
u32 num_ways, u32 way_shift,
u32 log2_line_len)
+{
- int way, set, setway;
- /*
* For optimal assembly code:
* a. count down
* b. have bigger loop inside
*/
Out of curiosity, can you elaborate on why the compiler would optimize better in these cases?
- for (way = num_ways - 1; way>= 0 ; way--)
for (set = num_sets - 1; set>= 0; set--) {
Please fix whitespacing around operators. The best way to ''catch'em all'' is to run Linux' checkpatch.pl (I do this with option --no-tree) on all patches that you submit to u-boot and, fix all warning and errors and if some are left that you think should not be fixed, mention them and explain why they're wrongly emitted.
setway = (level<< 1) | (set<< log2_line_len) |
(way<< way_shift);
/* Invalidate data/unified cache line by set/way */
asm volatile (" mcr p15, 0, %0, c7, c6, 2"
: : "r" (setway));
}
- /* Make sure the operation is complete */
- asm volatile ("DMB");
+}
+static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets,
u32 num_ways, u32 way_shift,
u32 log2_line_len)
+{
- int way, set, setway;
- /*
* For optimal assembly code:
* a. count down
* b. have bigger loop inside
*/
- for (way = num_ways - 1; way>= 0 ; way--)
for (set = num_sets - 1; set>= 0; set--) {
setway = (level<< 1) | (set<< log2_line_len) |
(way<< way_shift);
/*
* Clean& Invalidate data/unified
* cache line by set/way
*/
asm volatile (" mcr p15, 0, %0, c7, c14, 2"
: : "r" (setway));
}
- /* Make sure the operation is complete */
- asm volatile ("DMB");
+}
+static void v7_maint_dcache_level_setway(u32 level, u32 operation) +{
- u32 ccsidr;
- u32 num_sets, num_ways, log2_line_len, log2_num_ways;
- u32 way_shift;
- set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED);
- ccsidr = get_ccsidr();
- log2_line_len = mask_n_get(ccsidr, 0, 2) + 2;
- /* Converting from words to bytes */
- log2_line_len += 2;
- num_ways = mask_n_get(ccsidr, 3, 12) + 1;
- num_sets = mask_n_get(ccsidr, 13, 27) + 1;
- /*
* According to ARMv7 ARM number of sets and number of ways need
* not be a power of 2
*/
- log2_num_ways = log_2_round_up(num_ways);
- way_shift = (32 - log2_num_ways);
- if (operation == ARMV7_DCACHE_INVAL_ALL)
v7_inval_dcache_level_setway(level, num_sets, num_ways,
way_shift, log2_line_len);
- else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL)
v7_clean_inval_dcache_level_setway(level, num_sets, num_ways,
way_shift, log2_line_len);
+}
+static void v7_maint_dcache_all(u32 operation) +{
- u32 level, cache_type, level_start_bit = 0;
- u32 clidr = get_clidr();
- for (level = 0; level< 7; level++) {
cache_type = mask_n_get(clidr, level_start_bit,
level_start_bit + 2);
if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) ||
(cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) ||
(cache_type == ARMV7_CLIDR_CTYPE_UNIFIED))
v7_maint_dcache_level_setway(level, operation);
level_start_bit += 3;
- }
+}
+static void v7_dcache_clean_inval_range(u32 start,
u32 stop, u32 line_len)
+{
- u32 mva;
- /* Align start to cache line boundary */
- start&= ~(line_len - 1);
- for (mva = start; mva< stop; mva = mva + line_len)
/* DCCIMVAC - Clean& Invalidate data cache by MVA to PoC */
asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva));
+}
+static void v7_dcache_inval_range(u32 start, u32 stop, u32 line_len) +{
- u32 mva;
- /*
* If start address is not aligned to cache-line flush the first
* line to prevent affecting somebody else's buffer
*/
- if (start& (line_len - 1)) {
v7_dcache_clean_inval_range(start, start + 1, line_len);
/* move to next cache line */
start = (start + line_len - 1)& ~(line_len - 1);
- }
- /*
* If stop address is not aligned to cache-line flush the last
* line to prevent affecting somebody else's buffer
*/
- if (stop& (line_len - 1)) {
v7_dcache_clean_inval_range(stop, stop + 1, line_len);
/* align to the beginning of this cache line */
stop&= ~(line_len - 1);
- }
- for (mva = start; mva< stop; mva = mva + line_len)
/* DCIMVAC - Invalidate data cache by MVA to PoC */
asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva));
+}
+static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op) +{
- u32 line_len, ccsidr;
- ccsidr = get_ccsidr();
- line_len = mask_n_get(ccsidr, 0, 2) + 2;
- /* Converting from words to bytes */
- line_len += 2;
- /* converting from log2(linelen) to linelen */
- line_len = 1<< line_len;
- switch (range_op) {
- case ARMV7_DCACHE_CLEAN_INVAL_RANGE:
v7_dcache_clean_inval_range(start, stop, line_len);
break;
- case ARMV7_DCACHE_INVAL_RANGE:
v7_dcache_inval_range(start, stop, line_len);
break;
- }
- /* Make sure the operation is complete */
- asm volatile ("DMB");
+}
+/* Invalidate TLB */ +static void v7_inval_tlb(void) +{
- /* Invalidate entire unified TLB */
- asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0));
- /* Invalidate entire data TLB */
- asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0));
- /* Invalidate entire instruction TLB */
- asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0));
- /* Full system DSB - make sure that the invalidation is complete */
- asm volatile ("DSB");
- /* Full system ISB - make sure the instruction stream sees it */
- asm volatile ("ISB");
+}
+void invalidate_dcache_all(void) +{
- v7_maint_dcache_all(ARMV7_DCACHE_INVAL_ALL);
- if (v7_outer_cache.inval_all)
v7_outer_cache.inval_all();
Why use pointers here rather than weak functions?
+}
+/*
- Performs a clean& invalidation of the entire data cache
- at all levels
- */
+void flush_dcache_all(void) +{
- v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL);
- if (v7_outer_cache.flush_all)
v7_outer_cache.flush_all();
+}
+/*
- Invalidates range in all levels of D-cache/unified cache used:
- Affects the range [start, stop - 1]
- */
+void invalidate_dcache_range(unsigned long start, unsigned long stop) +{
- v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE);
- if (v7_outer_cache.inval_range)
/* physical address is same as virtual address */
v7_outer_cache.inval_range(start, stop);
+}
+/*
- Flush range(clean& invalidate) from all levels of D-cache/unified
- cache used:
- Affects the range [start, stop - 1]
- */
+void flush_dcache_range(unsigned long start, unsigned long stop) +{
- v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE);
- if (v7_outer_cache.flush_range)
/* physical address is same as virtual address */
v7_outer_cache.flush_range(start, stop);
+} +static void __v7_setup_outer_cache_ops(void) +{
- puts("v7_setup_outer_cache_ops: dummy implementation! "
"real implementation not available!!\n");
+}
+void v7_setup_outer_cache_ops(void)
- __attribute__((weak, alias("__v7_setup_outer_cache_ops")));
+void arm_init_before_mmu(void) +{
- v7_setup_outer_cache_ops();
- if (v7_outer_cache.enable)
v7_outer_cache.enable();
- invalidate_dcache_all();
- v7_inval_tlb();
+} +#else +void invalidate_dcache_all(void) +{ +}
+void flush_dcache_all(void) +{ +}
+void invalidate_dcache_range(unsigned long start, unsigned long stop) +{ +}
+void flush_dcache_range(unsigned long start, unsigned long stop) +{ +}
+void arm_init_before_mmu(void) +{ +} +#endif
+#ifndef CONFIG_SYS_NO_ICACHE +/* Invalidate entire I-cache and branch predictor array */ +void invalidate_icache_all(void) +{
- /*
* Invalidate all instruction caches to PoU.
* Also flushes branch target cache.
*/
- asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0));
- /* Invalidate entire branch predictor array */
- asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0));
- /* Full system DSB - make sure that the invalidation is complete */
- asm volatile ("DSB");
- /* Full system ISB - make sure the instruction stream sees it */
- asm volatile ("ISB");
+} +#else +void invalidate_icache_all(void) +{ +} +#endif
+/*
- Flush range from all levels of d-cache/unified-cache used:
- Affects the range [start, start + size - 1]
- */
+void flush_cache(unsigned long start, unsigned long size) +{
- flush_dcache_range(start, start + size);
+}
This function is the only one which is defined to something non-empty when CONFIG_SYS_NO_DCACHE is defined. Why is it not in the big #ifndef for dcache above ?
diff --git a/arch/arm/cpu/armv7/config.mk b/arch/arm/cpu/armv7/config.mk index 49ac9c7..7f9b171 100644 --- a/arch/arm/cpu/armv7/config.mk +++ b/arch/arm/cpu/armv7/config.mk @@ -23,7 +23,7 @@ PLATFORM_RELFLAGS += -fno-common -ffixed-r8 -msoft-float
# Make ARMv5 to allow more compilers to work, even though its v7a. -PLATFORM_CPPFLAGS += -march=armv5 +PLATFORM_CPPFLAGS += -march=armv7-a
Did you check that this does not break any board using armv7?
# ========================================================================= # # Supply options according to compiler version diff --git a/arch/arm/include/asm/armv7.h b/arch/arm/include/asm/armv7.h new file mode 100644 index 0000000..57409b6 --- /dev/null +++ b/arch/arm/include/asm/armv7.h @@ -0,0 +1,63 @@ +/*
- (C) Copyright 2010
- Texas Instruments Incorporated - http://www.ti.com/
- Aneesh Vaneesh@ti.com
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#ifndef ARMV7_H +#define ARMV7_H
+#include<linux/types.h>
+/*
- Values for InD field in CSSELR
- Selects the type of cache
- */
+#define ARMV7_CSSELR_IND_DATA_UNIFIED 0 +#define ARMV7_CSSELR_IND_INSTRUCTION 1
+/* Values for Ctype fields in CLIDR */ +#define ARMV7_CLIDR_CTYPE_NO_CACHE 0 +#define ARMV7_CLIDR_CTYPE_INSTRUCTION_ONLY 1 +#define ARMV7_CLIDR_CTYPE_DATA_ONLY 2 +#define ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA 3 +#define ARMV7_CLIDR_CTYPE_UNIFIED 4
+/* some utility macros */ +#define mask(start, end) \
- (((1<< ((end) - (start) + 1)) - 1)<< (start))
+#define mask_n_get(reg, start, end) \
- (((reg)& mask(start, end))>> (start))
Seeing as these functions are only used in the ARMv7 cache C file, they should be moved there.
+struct v7_outer_cache_ops {
- void (*enable)(void);
- void (*disable)(void);
- void (*flush_all)(void);
- void (*inval_all)(void);
- void (*flush_range)(u32 start, u32 end);
- void (*inval_range)(u32 start, u32 end);
+};
+extern struct v7_outer_cache_ops v7_outer_cache;
+void v7_setup_outer_cache_ops(void); +#endif diff --git a/include/common.h b/include/common.h index 189ad81..d750ff9 100644 --- a/include/common.h +++ b/include/common.h @@ -411,6 +411,7 @@ void icache_disable(void); int dcache_status (void); void dcache_enable (void); void dcache_disable(void); +void mmu_disable(void); void relocate_code (ulong, gd_t *, ulong) __attribute__ ((noreturn)); ulong get_endaddr (void); void trap_init (ulong); @@ -603,9 +604,11 @@ ulong video_setmem (ulong);
/* arch/$(ARCH)/lib/cache.c */ void flush_cache (unsigned long, unsigned long); +void flush_dcache_all(void); void flush_dcache_range(unsigned long start, unsigned long stop); void invalidate_dcache_range(unsigned long start, unsigned long stop);
+void invalidate_dcache_all(void); +void invalidate_icache_all(void);
/* arch/$(ARCH)/lib/ticks.S */ unsigned long long get_ticks(void);
Amicalement,