* arch/sh/mm/cache-sh4.c
*
* Copyright (C) 1999, 2000, 2002 Niibe Yutaka
- * Copyright (C) 2001 - 2006 Paul Mundt
+ * Copyright (C) 2001 - 2007 Paul Mundt
* Copyright (C) 2003 Richard Curnow
+ * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
*/
#include <linux/init.h>
#include <linux/mm.h>
-#include <asm/addrspace.h>
-#include <asm/pgtable.h>
-#include <asm/processor.h>
-#include <asm/cache.h>
-#include <asm/io.h>
-#include <asm/pgalloc.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
#include <asm/mmu_context.h>
#include <asm/cacheflush.h>
* entirety.
*/
#define MAX_DCACHE_PAGES 64 /* XXX: Tune for ways */
+#define MAX_ICACHE_PAGES 32
static void __flush_dcache_segment_1way(unsigned long start,
unsigned long extent);
static void compute_alias(struct cache_info *c)
{
c->alias_mask = ((c->sets - 1) << c->entry_shift) & ~(PAGE_SIZE - 1);
- c->n_aliases = (c->alias_mask >> PAGE_SHIFT) + 1;
+ c->n_aliases = c->alias_mask ? (c->alias_mask >> PAGE_SHIFT) + 1 : 0;
}
static void __init emit_cache_params(void)
ctrl_inl(CCN_CVR),
ctrl_inl(CCN_PRR));
printk("I-cache : n_ways=%d n_sets=%d way_incr=%d\n",
- cpu_data->icache.ways,
- cpu_data->icache.sets,
- cpu_data->icache.way_incr);
+ boot_cpu_data.icache.ways,
+ boot_cpu_data.icache.sets,
+ boot_cpu_data.icache.way_incr);
printk("I-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n",
- cpu_data->icache.entry_mask,
- cpu_data->icache.alias_mask,
- cpu_data->icache.n_aliases);
+ boot_cpu_data.icache.entry_mask,
+ boot_cpu_data.icache.alias_mask,
+ boot_cpu_data.icache.n_aliases);
printk("D-cache : n_ways=%d n_sets=%d way_incr=%d\n",
- cpu_data->dcache.ways,
- cpu_data->dcache.sets,
- cpu_data->dcache.way_incr);
+ boot_cpu_data.dcache.ways,
+ boot_cpu_data.dcache.sets,
+ boot_cpu_data.dcache.way_incr);
printk("D-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n",
- cpu_data->dcache.entry_mask,
- cpu_data->dcache.alias_mask,
- cpu_data->dcache.n_aliases);
+ boot_cpu_data.dcache.entry_mask,
+ boot_cpu_data.dcache.alias_mask,
+ boot_cpu_data.dcache.n_aliases);
+
+ /*
+ * Emit Secondary Cache parameters if the CPU has a probed L2.
+ */
+ if (boot_cpu_data.flags & CPU_HAS_L2_CACHE) {
+ printk("S-cache : n_ways=%d n_sets=%d way_incr=%d\n",
+ boot_cpu_data.scache.ways,
+ boot_cpu_data.scache.sets,
+ boot_cpu_data.scache.way_incr);
+ printk("S-cache : entry_mask=0x%08x alias_mask=0x%08x n_aliases=%d\n",
+ boot_cpu_data.scache.entry_mask,
+ boot_cpu_data.scache.alias_mask,
+ boot_cpu_data.scache.n_aliases);
+ }
if (!__flush_dcache_segment_fn)
panic("unknown number of cache ways\n");
/*
* SH-4 has virtually indexed and physically tagged cache.
*/
-
-/* Worst case assumed to be 64k cache, direct-mapped i.e. 4 synonym bits. */
-#define MAX_P3_SEMAPHORES 16
-
-struct semaphore p3map_sem[MAX_P3_SEMAPHORES];
-
void __init p3_cache_init(void)
{
- int i;
-
- compute_alias(&cpu_data->icache);
- compute_alias(&cpu_data->dcache);
+ compute_alias(&boot_cpu_data.icache);
+ compute_alias(&boot_cpu_data.dcache);
+ compute_alias(&boot_cpu_data.scache);
- switch (cpu_data->dcache.ways) {
+ switch (boot_cpu_data.dcache.ways) {
case 1:
__flush_dcache_segment_fn = __flush_dcache_segment_1way;
break;
}
emit_cache_params();
-
- if (remap_area_pages(P3SEG, 0, PAGE_SIZE * 4, _PAGE_CACHABLE))
- panic("%s failed.", __FUNCTION__);
-
- for (i = 0; i < cpu_data->dcache.n_aliases; i++)
- sema_init(&p3map_sem[i], 1);
}
/*
/*
* Write back the range of D-cache, and purge the I-cache.
*
- * Called from kernel/module.c:sys_init_module and routine for a.out format.
+ * Called from kernel/module.c:sys_init_module and routine for a.out format,
+ * signal handler code and kprobes code
*/
void flush_icache_range(unsigned long start, unsigned long end)
{
- flush_cache_all();
-}
-
-/*
- * Write back the D-cache and purge the I-cache for signal trampoline.
- * .. which happens to be the same behavior as flush_icache_range().
- * So, we simply flush out a line.
- */
-void flush_cache_sigtramp(unsigned long addr)
-{
- unsigned long v, index;
- unsigned long flags;
+ int icacheaddr;
+ unsigned long flags, v;
int i;
- v = addr & ~(L1_CACHE_BYTES-1);
- asm volatile("ocbwb %0"
- : /* no output */
- : "m" (__m(v)));
-
- index = CACHE_IC_ADDRESS_ARRAY | (v & cpu_data->icache.entry_mask);
-
- local_irq_save(flags);
- jump_to_P2();
-
- for (i = 0; i < cpu_data->icache.ways;
- i++, index += cpu_data->icache.way_incr)
- ctrl_outl(0, index); /* Clear out Valid-bit */
-
- back_to_P1();
- wmb();
- local_irq_restore(flags);
+ /* If there are too many pages then just blow the caches */
+ if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
+ flush_cache_all();
+ } else {
+ /* selectively flush d-cache then invalidate the i-cache */
+ /* this is inefficient, so only use for small ranges */
+ start &= ~(L1_CACHE_BYTES-1);
+ end += L1_CACHE_BYTES-1;
+ end &= ~(L1_CACHE_BYTES-1);
+
+ local_irq_save(flags);
+ jump_to_uncached();
+
+ for (v = start; v < end; v+=L1_CACHE_BYTES) {
+ asm volatile("ocbwb %0"
+ : /* no output */
+ : "m" (__m(v)));
+
+ icacheaddr = CACHE_IC_ADDRESS_ARRAY | (
+ v & cpu_data->icache.entry_mask);
+
+ for (i = 0; i < cpu_data->icache.ways;
+ i++, icacheaddr += cpu_data->icache.way_incr)
+ /* Clear i-cache line valid-bit */
+ ctrl_outl(0, icacheaddr);
+ }
+
+ back_to_cached();
+ local_irq_restore(flags);
+ }
}
static inline void flush_cache_4096(unsigned long start,
* All types of SH-4 require PC to be in P2 to operate on the I-cache.
* Some types of SH-4 require PC to be in P2 to operate on the D-cache.
*/
- if ((cpu_data->flags & CPU_HAS_P2_FLUSH_BUG) ||
+ if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) ||
(start < CACHE_OC_ADDRESS_ARRAY))
- exec_offset = 0x20000000;
+ exec_offset = 0x20000000;
local_irq_save(flags);
__flush_cache_4096(start | SH_CACHE_ASSOC,
int i, n;
/* Loop all the D-cache */
- n = cpu_data->dcache.n_aliases;
- for (i = 0; i < n; i++, addr += PAGE_SIZE)
+ n = boot_cpu_data.dcache.n_aliases;
+ for (i = 0; i < n; i++, addr += 4096)
flush_cache_4096(addr, phys);
}
}
/* TODO: Selective icache invalidation through IC address array.. */
-static inline void flush_icache_all(void)
+static void __uses_jump_to_uncached flush_icache_all(void)
{
unsigned long flags, ccr;
local_irq_save(flags);
- jump_to_P2();
+ jump_to_uncached();
/* Flush I-cache */
ccr = ctrl_inl(CCR);
ctrl_outl(ccr, CCR);
/*
- * back_to_P1() will take care of the barrier for us, don't add
+ * back_to_cached() will take care of the barrier for us, don't add
* another one!
*/
- back_to_P1();
+ back_to_cached();
local_irq_restore(flags);
}
void flush_dcache_all(void)
{
- (*__flush_dcache_segment_fn)(0UL, cpu_data->dcache.way_size);
+ (*__flush_dcache_segment_fn)(0UL, boot_cpu_data.dcache.way_size);
wmb();
}
unsigned long end)
{
unsigned long d = 0, p = start & PAGE_MASK;
- unsigned long alias_mask = cpu_data->dcache.alias_mask;
- unsigned long n_aliases = cpu_data->dcache.n_aliases;
+ unsigned long alias_mask = boot_cpu_data.dcache.alias_mask;
+ unsigned long n_aliases = boot_cpu_data.dcache.n_aliases;
unsigned long select_bit;
unsigned long all_aliases_mask;
unsigned long addr_offset;
* If cache is only 4k-per-way, there are never any 'aliases'. Since
* the cache is physically tagged, the data can just be left in there.
*/
- if (cpu_data->dcache.n_aliases == 0)
+ if (boot_cpu_data.dcache.n_aliases == 0)
return;
/*
unsigned long phys = pfn << PAGE_SHIFT;
unsigned int alias_mask;
- alias_mask = cpu_data->dcache.alias_mask;
+ alias_mask = boot_cpu_data.dcache.alias_mask;
/* We only need to flush D-cache when we have alias */
if ((address^phys) & alias_mask) {
phys);
}
- alias_mask = cpu_data->icache.alias_mask;
+ alias_mask = boot_cpu_data.icache.alias_mask;
if (vma->vm_flags & VM_EXEC) {
/*
* Evict entries from the portion of the cache from which code
* If cache is only 4k-per-way, there are never any 'aliases'. Since
* the cache is physically tagged, the data can just be left in there.
*/
- if (cpu_data->dcache.n_aliases == 0)
+ if (boot_cpu_data.dcache.n_aliases == 0)
return;
/*
unsigned long a, ea, p;
unsigned long temp_pc;
- dcache = &cpu_data->dcache;
+ dcache = &boot_cpu_data.dcache;
/* Write this way for better assembly. */
way_count = dcache->ways;
way_incr = dcache->way_incr;
base_addr = ((base_addr >> 16) << 16);
base_addr |= start;
- dcache = &cpu_data->dcache;
+ dcache = &boot_cpu_data.dcache;
linesz = dcache->linesz;
way_incr = dcache->way_incr;
way_size = dcache->way_size;
base_addr = ((base_addr >> 16) << 16);
base_addr |= start;
- dcache = &cpu_data->dcache;
+ dcache = &boot_cpu_data.dcache;
linesz = dcache->linesz;
way_incr = dcache->way_incr;
way_size = dcache->way_size;
base_addr = ((base_addr >> 16) << 16);
base_addr |= start;
- dcache = &cpu_data->dcache;
+ dcache = &boot_cpu_data.dcache;
linesz = dcache->linesz;
way_incr = dcache->way_incr;
way_size = dcache->way_size;