diff options
-rw-r--r-- | arch/metag/mm/cache.c | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/arch/metag/mm/cache.c b/arch/metag/mm/cache.c index 89da74a80ea..4dd96e457fa 100644 --- a/arch/metag/mm/cache.c +++ b/arch/metag/mm/cache.c @@ -14,6 +14,7 @@ #include <linux/io.h> #include <asm/cacheflush.h> #include <asm/core_reg.h> +#include <asm/global_lock.h> #include <asm/metag_isa.h> #include <asm/metag_mem.h> #include <asm/metag_regs.h> @@ -36,6 +37,82 @@ static int icache_set_shift = METAG_TBI_CACHE_SIZE_BASE_LOG2 static unsigned char dcache_sets_log2 = DEFAULT_CACHE_WAYS_LOG2; static unsigned char icache_sets_log2 = DEFAULT_CACHE_WAYS_LOG2; +#ifndef CONFIG_METAG_META12 +/** + * metag_lnkget_probe() - Probe whether lnkget/lnkset go around the cache + */ +static volatile u32 lnkget_testdata[16] __initdata __aligned(64); + +#define LNKGET_CONSTANT 0xdeadbeef + +void __init metag_lnkget_probe(void) +{ + int temp; + long flags; + + /* + * It's conceivable the user has configured a globally coherent cache + * shared with non-Linux hardware threads, so use LOCK2 to prevent them + * from executing and causing cache eviction during the test. + */ + __global_lock2(flags); + + /* read a value to bring it into the cache */ + (void)lnkget_testdata[0]; + lnkget_testdata[0] = 0; + + /* lnkget/lnkset it to modify it */ + asm volatile( + "1: LNKGETD %0, [%1]\n" + " LNKSETD [%1], %2\n" + " DEFR %0, TXSTAT\n" + " ANDT %0, %0, #HI(0x3f000000)\n" + " CMPT %0, #HI(0x02000000)\n" + " BNZ 1b\n" + : "=&d" (temp) + : "da" (&lnkget_testdata[0]), "bd" (LNKGET_CONSTANT) + : "cc"); + + /* re-read it to see if the cached value changed */ + temp = lnkget_testdata[0]; + + __global_unlock2(flags); + + /* flush the cache line to fix any incoherency */ + __builtin_dcache_flush((void *)&lnkget_testdata[0]); + +#if defined(CONFIG_METAG_LNKGET_AROUND_CACHE) + /* if the cache is right, LNKGET_AROUND_CACHE is unnecessary */ + if (temp == LNKGET_CONSTANT) + pr_info("LNKGET/SET go through cache but CONFIG_METAG_LNKGET_AROUND_CACHE=y\n"); +#elif defined(CONFIG_METAG_ATOMICITY_LNKGET) + /* + * if the cache is wrong, LNKGET_AROUND_CACHE is really necessary + * because the kernel is configured to use LNKGET/SET for atomicity + */ + WARN(temp != LNKGET_CONSTANT, + "LNKGET/SET go around cache but CONFIG_METAG_LNKGET_AROUND_CACHE=n\n" + "Expect kernel failure as it's used for atomicity primitives\n"); +#elif defined(CONFIG_SMP) + /* + * if the cache is wrong, LNKGET_AROUND_CACHE should be used or the + * gateway page won't flush and userland could break. + */ + WARN(temp != LNKGET_CONSTANT, + "LNKGET/SET go around cache but CONFIG_METAG_LNKGET_AROUND_CACHE=n\n" + "Expect userland failure as it's used for user gateway page\n"); +#else + /* + * if the cache is wrong, LNKGET_AROUND_CACHE is set wrong, but it + * doesn't actually matter as it doesn't have any effect on !SMP && + * !ATOMICITY_LNKGET. + */ + if (temp != LNKGET_CONSTANT) + pr_warn("LNKGET/SET go around cache but CONFIG_METAG_LNKGET_AROUND_CACHE=n\n"); +#endif +} +#endif /* !CONFIG_METAG_META12 */ + /** * metag_cache_probe() - Probe L1 cache configuration. * @@ -68,6 +145,8 @@ void __init metag_cache_probe(void) dcache_set_shift += (config & METAC_CORECFG2_DCSZ_BITS) >> METAC_CORECFG2_DCSZ_S; dcache_set_shift -= dcache_sets_log2; + + metag_lnkget_probe(); #else /* Extract cache sizes from global heap segment */ unsigned long val, u; |