summaryrefslogtreecommitdiff
path: root/arch/metag/mm/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/metag/mm/cache.c')
-rw-r--r--arch/metag/mm/cache.c521
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);