diff options
author | Will Deacon <will.deacon@arm.com> | 2010-09-13 17:18:30 +0200 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-10-04 21:57:09 +0200 |
commit | 8925ec4c530094b878e7e28a1fd78e7122afd973 (patch) | |
tree | 2b7200f64892299c185d3a07d1f793ea9217b5d4 /arch/arm/kernel/setup.c | |
parent | ARM: 6382/1: Remove superfluous flush_kernel_dcache_page() (diff) | |
download | linux-8925ec4c530094b878e7e28a1fd78e7122afd973.tar.xz linux-8925ec4c530094b878e7e28a1fd78e7122afd973.zip |
ARM: 6385/1: setup: detect aliasing I-cache when D-cache is non-aliasing
Currently, the Kernel assumes that if a CPU has a non-aliasing D-cache
then the I-cache is also non-aliasing. This may not be true on ARM cores
from v6 onwards, which may have aliasing I-caches but non-aliasing
D-caches.
This patch adds a cpu_has_aliasing_icache function, which is called from
cacheid_init and adds CACHEID_VIPT_I_ALIASING to the cacheid when
appropriate. A utility macro, icache_is_vipt_aliasing(), is also
provided.
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/setup.c')
-rw-r--r-- | arch/arm/kernel/setup.c | 39 |
1 files changed, 36 insertions, 3 deletions
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index d5231ae7355a..9fc483393bae 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -238,6 +238,34 @@ int cpu_architecture(void) return cpu_arch; } +static int cpu_has_aliasing_icache(unsigned int arch) +{ + int aliasing_icache; + unsigned int id_reg, num_sets, line_size; + + /* arch specifies the register format */ + switch (arch) { + case CPU_ARCH_ARMv7: + asm("mcr p15, 2, %1, c0, c0, 0 @ set CSSELR\n" + "isb\n" + "mrc p15, 1, %0, c0, c0, 0 @ read CCSIDR" + : "=r" (id_reg) + : "r" (1)); + line_size = 4 << ((id_reg & 0x7) + 2); + num_sets = ((id_reg >> 13) & 0x7fff) + 1; + aliasing_icache = (line_size * num_sets) > PAGE_SIZE; + break; + case CPU_ARCH_ARMv6: + aliasing_icache = read_cpuid_cachetype() & (1 << 11); + break; + default: + /* I-cache aliases will be handled by D-cache aliasing code */ + aliasing_icache = 0; + } + + return aliasing_icache; +} + static void __init cacheid_init(void) { unsigned int cachetype = read_cpuid_cachetype(); @@ -249,10 +277,15 @@ static void __init cacheid_init(void) cacheid = CACHEID_VIPT_NONALIASING; if ((cachetype & (3 << 14)) == 1 << 14) cacheid |= CACHEID_ASID_TAGGED; - } else if (cachetype & (1 << 23)) + else if (cpu_has_aliasing_icache(CPU_ARCH_ARMv7)) + cacheid |= CACHEID_VIPT_I_ALIASING; + } else if (cachetype & (1 << 23)) { cacheid = CACHEID_VIPT_ALIASING; - else + } else { cacheid = CACHEID_VIPT_NONALIASING; + if (cpu_has_aliasing_icache(CPU_ARCH_ARMv6)) + cacheid |= CACHEID_VIPT_I_ALIASING; + } } else { cacheid = CACHEID_VIVT; } @@ -263,7 +296,7 @@ static void __init cacheid_init(void) cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown", cache_is_vivt() ? "VIVT" : icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" : - cache_is_vipt_aliasing() ? "VIPT aliasing" : + icache_is_vipt_aliasing() ? "VIPT aliasing" : cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown"); } |