diff options
Diffstat (limited to 'arch/metag/mm/cache.c')
-rw-r--r-- | arch/metag/mm/cache.c | 521 |
1 files changed, 0 insertions, 521 deletions
diff --git a/arch/metag/mm/cache.c b/arch/metag/mm/cache.c deleted file mode 100644 index a62285284ab8..000000000000 --- a/arch/metag/mm/cache.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * arch/metag/mm/cache.c - * - * Copyright (C) 2001, 2002, 2005, 2007, 2012 Imagination Technologies. - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 as published by the - * Free Software Foundation. - * - * Cache control code - */ - -#include <linux/export.h> -#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> - -#define DEFAULT_CACHE_WAYS_LOG2 2 - -/* - * Size of a set in the caches. Initialised for default 16K stride, adjusted - * according to values passed through TBI global heap segment via LDLK (on ATP) - * or config registers (on HTP/MTP) - */ -static int dcache_set_shift = METAG_TBI_CACHE_SIZE_BASE_LOG2 - - DEFAULT_CACHE_WAYS_LOG2; -static int icache_set_shift = METAG_TBI_CACHE_SIZE_BASE_LOG2 - - DEFAULT_CACHE_WAYS_LOG2; -/* - * The number of sets in the caches. Initialised for HTP/ATP, adjusted - * according to NOMMU setting in config registers - */ -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 - -static 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. - * - * Probe the L1 cache configuration to aid the L1 physical cache flushing - * functions. - */ -void __init metag_cache_probe(void) -{ -#ifndef CONFIG_METAG_META12 - int coreid = metag_in32(METAC_CORE_ID); - int config = metag_in32(METAC_CORE_CONFIG2); - int cfgcache = coreid & METAC_COREID_CFGCACHE_BITS; - - if (cfgcache == METAC_COREID_CFGCACHE_TYPE0 || - cfgcache == METAC_COREID_CFGCACHE_PRIVNOMMU) { - icache_sets_log2 = 1; - dcache_sets_log2 = 1; - } - - /* For normal size caches, the smallest size is 4Kb. - For small caches, the smallest size is 64b */ - icache_set_shift = (config & METAC_CORECFG2_ICSMALL_BIT) - ? 6 : 12; - icache_set_shift += (config & METAC_CORE_C2ICSZ_BITS) - >> METAC_CORE_C2ICSZ_S; - icache_set_shift -= icache_sets_log2; - - dcache_set_shift = (config & METAC_CORECFG2_DCSMALL_BIT) - ? 6 : 12; - 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; - int width, shift, addend; - PTBISEG seg; - - seg = __TBIFindSeg(NULL, TBID_SEG(TBID_THREAD_GLOBAL, - TBID_SEGSCOPE_GLOBAL, - TBID_SEGTYPE_HEAP)); - if (seg != NULL) { - val = seg->Data[1]; - - /* Work out width of I-cache size bit-field */ - u = ((unsigned long) METAG_TBI_ICACHE_SIZE_BITS) - >> METAG_TBI_ICACHE_SIZE_S; - width = 0; - while (u & 1) { - width++; - u >>= 1; - } - /* Extract sign-extended size addend value */ - shift = 32 - (METAG_TBI_ICACHE_SIZE_S + width); - addend = (long) ((val & METAG_TBI_ICACHE_SIZE_BITS) - << shift) - >> (shift + METAG_TBI_ICACHE_SIZE_S); - /* Now calculate I-cache set size */ - icache_set_shift = (METAG_TBI_CACHE_SIZE_BASE_LOG2 - - DEFAULT_CACHE_WAYS_LOG2) - + addend; - - /* Similarly for D-cache */ - u = ((unsigned long) METAG_TBI_DCACHE_SIZE_BITS) - >> METAG_TBI_DCACHE_SIZE_S; - width = 0; - while (u & 1) { - width++; - u >>= 1; - } - shift = 32 - (METAG_TBI_DCACHE_SIZE_S + width); - addend = (long) ((val & METAG_TBI_DCACHE_SIZE_BITS) - << shift) - >> (shift + METAG_TBI_DCACHE_SIZE_S); - dcache_set_shift = (METAG_TBI_CACHE_SIZE_BASE_LOG2 - - DEFAULT_CACHE_WAYS_LOG2) - + addend; - } -#endif -} - -static void metag_phys_data_cache_flush(const void *start) -{ - unsigned long flush0, flush1, flush2, flush3; - int loops, step; - int thread; - int part, offset; - int set_shift; - - /* Use a sequence of writes to flush the cache region requested */ - thread = (__core_reg_get(TXENABLE) & TXENABLE_THREAD_BITS) - >> TXENABLE_THREAD_S; - - /* Cache is broken into sets which lie in contiguous RAMs */ - set_shift = dcache_set_shift; - - /* Move to the base of the physical cache flush region */ - flush0 = LINSYSCFLUSH_DCACHE_LINE; - step = 64; - - /* Get partition data for this thread */ - part = metag_in32(SYSC_DCPART0 + - (SYSC_xCPARTn_STRIDE * thread)); - - if ((int)start < 0) - /* Access Global vs Local partition */ - part >>= SYSC_xCPARTG_AND_S - - SYSC_xCPARTL_AND_S; - - /* Extract offset and move SetOff */ - offset = (part & SYSC_xCPARTL_OR_BITS) - >> SYSC_xCPARTL_OR_S; - flush0 += (offset << (set_shift - 4)); - - /* Shrink size */ - part = (part & SYSC_xCPARTL_AND_BITS) - >> SYSC_xCPARTL_AND_S; - loops = ((part + 1) << (set_shift - 4)); - - /* Reduce loops by step of cache line size */ - loops /= step; - - flush1 = flush0 + (1 << set_shift); - flush2 = flush0 + (2 << set_shift); - flush3 = flush0 + (3 << set_shift); - - if (dcache_sets_log2 == 1) { - flush2 = flush1; - flush3 = flush1 + step; - flush1 = flush0 + step; - step <<= 1; - loops >>= 1; - } - - /* Clear loops ways in cache */ - while (loops-- != 0) { - /* Clear the ways. */ -#if 0 - /* - * GCC doesn't generate very good code for this so we - * provide inline assembly instead. - */ - metag_out8(0, flush0); - metag_out8(0, flush1); - metag_out8(0, flush2); - metag_out8(0, flush3); - - flush0 += step; - flush1 += step; - flush2 += step; - flush3 += step; -#else - asm volatile ( - "SETB\t[%0+%4++],%5\n" - "SETB\t[%1+%4++],%5\n" - "SETB\t[%2+%4++],%5\n" - "SETB\t[%3+%4++],%5\n" - : "+e" (flush0), - "+e" (flush1), - "+e" (flush2), - "+e" (flush3) - : "e" (step), "a" (0)); -#endif - } -} - -void metag_data_cache_flush_all(const void *start) -{ - if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_DC_ON_BIT) == 0) - /* No need to flush the data cache it's not actually enabled */ - return; - - metag_phys_data_cache_flush(start); -} - -void metag_data_cache_flush(const void *start, int bytes) -{ - unsigned long flush0; - int loops, step; - - if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_DC_ON_BIT) == 0) - /* No need to flush the data cache it's not actually enabled */ - return; - - if (bytes >= 4096) { - metag_phys_data_cache_flush(start); - return; - } - - /* Use linear cache flush mechanism on META IP */ - flush0 = (int)start; - loops = ((int)start & (DCACHE_LINE_BYTES - 1)) + bytes + - (DCACHE_LINE_BYTES - 1); - loops >>= DCACHE_LINE_S; - -#define PRIM_FLUSH(addr, offset) do { \ - int __addr = ((int) (addr)) + ((offset) * 64); \ - __builtin_dcache_flush((void *)(__addr)); \ - } while (0) - -#define LOOP_INC (4*64) - - do { - /* By default stop */ - step = 0; - - switch (loops) { - /* Drop Thru Cases! */ - default: - PRIM_FLUSH(flush0, 3); - loops -= 4; - step = 1; - case 3: - PRIM_FLUSH(flush0, 2); - case 2: - PRIM_FLUSH(flush0, 1); - case 1: - PRIM_FLUSH(flush0, 0); - flush0 += LOOP_INC; - case 0: - break; - } - } while (step); -} -EXPORT_SYMBOL(metag_data_cache_flush); - -static void metag_phys_code_cache_flush(const void *start, int bytes) -{ - unsigned long flush0, flush1, flush2, flush3, end_set; - int loops, step; - int thread; - int set_shift, set_size; - int part, offset; - - /* Use a sequence of writes to flush the cache region requested */ - thread = (__core_reg_get(TXENABLE) & TXENABLE_THREAD_BITS) - >> TXENABLE_THREAD_S; - set_shift = icache_set_shift; - - /* Move to the base of the physical cache flush region */ - flush0 = LINSYSCFLUSH_ICACHE_LINE; - step = 64; - - /* Get partition code for this thread */ - part = metag_in32(SYSC_ICPART0 + - (SYSC_xCPARTn_STRIDE * thread)); - - if ((int)start < 0) - /* Access Global vs Local partition */ - part >>= SYSC_xCPARTG_AND_S-SYSC_xCPARTL_AND_S; - - /* Extract offset and move SetOff */ - offset = (part & SYSC_xCPARTL_OR_BITS) - >> SYSC_xCPARTL_OR_S; - flush0 += (offset << (set_shift - 4)); - - /* Shrink size */ - part = (part & SYSC_xCPARTL_AND_BITS) - >> SYSC_xCPARTL_AND_S; - loops = ((part + 1) << (set_shift - 4)); - - /* Where does the Set end? */ - end_set = flush0 + loops; - set_size = loops; - -#ifdef CONFIG_METAG_META12 - if ((bytes < 4096) && (bytes < loops)) { - /* Unreachable on HTP/MTP */ - /* Only target the sets that could be relavent */ - flush0 += (loops - step) & ((int) start); - loops = (((int) start) & (step-1)) + bytes + step - 1; - } -#endif - - /* Reduce loops by step of cache line size */ - loops /= step; - - flush1 = flush0 + (1<<set_shift); - flush2 = flush0 + (2<<set_shift); - flush3 = flush0 + (3<<set_shift); - - if (icache_sets_log2 == 1) { - flush2 = flush1; - flush3 = flush1 + step; - flush1 = flush0 + step; -#if 0 - /* flush0 will stop one line early in this case - * (flush1 will do the final line). - * However we don't correct end_set here at the moment - * because it will never wrap on HTP/MTP - */ - end_set -= step; -#endif - step <<= 1; - loops >>= 1; - } - - /* Clear loops ways in cache */ - while (loops-- != 0) { -#if 0 - /* - * GCC doesn't generate very good code for this so we - * provide inline assembly instead. - */ - /* Clear the ways */ - metag_out8(0, flush0); - metag_out8(0, flush1); - metag_out8(0, flush2); - metag_out8(0, flush3); - - flush0 += step; - flush1 += step; - flush2 += step; - flush3 += step; -#else - asm volatile ( - "SETB\t[%0+%4++],%5\n" - "SETB\t[%1+%4++],%5\n" - "SETB\t[%2+%4++],%5\n" - "SETB\t[%3+%4++],%5\n" - : "+e" (flush0), - "+e" (flush1), - "+e" (flush2), - "+e" (flush3) - : "e" (step), "a" (0)); -#endif - - if (flush0 == end_set) { - /* Wrap within Set 0 */ - flush0 -= set_size; - flush1 -= set_size; - flush2 -= set_size; - flush3 -= set_size; - } - } -} - -void metag_code_cache_flush_all(const void *start) -{ - if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_IC_ON_BIT) == 0) - /* No need to flush the code cache it's not actually enabled */ - return; - - metag_phys_code_cache_flush(start, 4096); -} -EXPORT_SYMBOL(metag_code_cache_flush_all); - -void metag_code_cache_flush(const void *start, int bytes) -{ -#ifndef CONFIG_METAG_META12 - void *flush; - int loops, step; -#endif /* !CONFIG_METAG_META12 */ - - if ((metag_in32(SYSC_CACHE_MMU_CONFIG) & SYSC_CMMUCFG_IC_ON_BIT) == 0) - /* No need to flush the code cache it's not actually enabled */ - return; - -#ifdef CONFIG_METAG_META12 - /* CACHEWD isn't available on Meta1, so always do full cache flush */ - metag_phys_code_cache_flush(start, bytes); - -#else /* CONFIG_METAG_META12 */ - /* If large size do full physical cache flush */ - if (bytes >= 4096) { - metag_phys_code_cache_flush(start, bytes); - return; - } - - /* Use linear cache flush mechanism on META IP */ - flush = (void *)((int)start & ~(ICACHE_LINE_BYTES-1)); - loops = ((int)start & (ICACHE_LINE_BYTES-1)) + bytes + - (ICACHE_LINE_BYTES-1); - loops >>= ICACHE_LINE_S; - -#define PRIM_IFLUSH(addr, offset) \ - __builtin_meta2_cachewd(((addr) + ((offset) * 64)), CACHEW_ICACHE_BIT) - -#define LOOP_INC (4*64) - - do { - /* By default stop */ - step = 0; - - switch (loops) { - /* Drop Thru Cases! */ - default: - PRIM_IFLUSH(flush, 3); - loops -= 4; - step = 1; - case 3: - PRIM_IFLUSH(flush, 2); - case 2: - PRIM_IFLUSH(flush, 1); - case 1: - PRIM_IFLUSH(flush, 0); - flush += LOOP_INC; - case 0: - break; - } - } while (step); -#endif /* !CONFIG_METAG_META12 */ -} -EXPORT_SYMBOL(metag_code_cache_flush); |