patch-2.4.22 linux-2.4.22/arch/sh/mm/cache-sh4.c
Next file: linux-2.4.22/arch/sh/mm/clear_page.S
Previous file: linux-2.4.22/arch/sh/mm/Makefile
Back to the patch index
Back to the overall index
- Lines: 337
- Date:
2003-08-25 04:44:40.000000000 -0700
- Orig file:
linux-2.4.21/arch/sh/mm/cache-sh4.c
- Orig date:
2001-10-15 13:36:48.000000000 -0700
diff -urN linux-2.4.21/arch/sh/mm/cache-sh4.c linux-2.4.22/arch/sh/mm/cache-sh4.c
@@ -1,9 +1,8 @@
-/* $Id: cache-sh4.c,v 1.16 2001/09/10 11:06:35 dwmw2 Exp $
+/* $Id: cache-sh4.c,v 1.1.1.1.2.8 2003/07/09 09:59:30 trent Exp $
*
- * linux/arch/sh/mm/cache.c
- *
- * Copyright (C) 1999, 2000 Niibe Yutaka
+ * linux/arch/sh/mm/cache-sh4.c
*
+ * Copyright (C) 1999, 2000, 2002 Niibe Yutaka
*/
#include <linux/config.h>
@@ -42,6 +41,7 @@
#define CACHE_OC_ADDRESS_ARRAY 0xf4000000
#define CACHE_VALID 1
#define CACHE_UPDATED 2
+#define CACHE_ASSOC 8
#define CACHE_OC_WAY_SHIFT 13
#define CACHE_IC_WAY_SHIFT 13
@@ -56,8 +56,8 @@
static void __init
detect_cpu_and_cache_system(void)
{
-#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
- cpu_data->type = CPU_ST40STB1;
+#ifdef CONFIG_CPU_SUBTYPE_ST40
+ cpu_data->type = CPU_ST40;
#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751)
cpu_data->type = CPU_SH7750;
#else
@@ -109,7 +109,7 @@
unsigned long size, unsigned long flags);
if (remap_area_pages(P3SEG, 0, PAGE_SIZE*4, _PAGE_CACHABLE))
- panic("p3_cachie_init failed.");
+ panic("%s failed.", __FUNCTION__);
sema_init (&p3map_sem[0], 1);
sema_init (&p3map_sem[1], 1);
sema_init (&p3map_sem[2], 1);
@@ -177,6 +177,17 @@
}
}
+void __flush_icache_all(void)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ jump_to_P2();
+ ctrl_outl(CCR_CACHE_VAL|CCR_CACHE_ICI, CCR);
+ back_to_P1();
+ restore_flags(flags);
+}
+
/*
* Write back the range of D-cache, and purge the I-cache.
*
@@ -208,44 +219,26 @@
restore_flags(flags);
}
-/*
- * Writeback&Invalidate the D-cache of the page
- */
-static void __flush_dcache_page(unsigned long phys)
+static inline void flush_cache_4096(unsigned long start,
+ unsigned long phys)
{
- unsigned long addr, data;
- unsigned long flags;
-
- phys |= CACHE_VALID;
-
- save_and_cli(flags);
- jump_to_P2();
-
- /* Loop all the D-cache */
- for (addr = CACHE_OC_ADDRESS_ARRAY;
- addr < (CACHE_OC_ADDRESS_ARRAY
- +(CACHE_OC_NUM_ENTRIES<< CACHE_OC_ENTRY_SHIFT));
- addr += (1<<CACHE_OC_ENTRY_SHIFT)) {
- data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID);
- if (data == phys)
- ctrl_outl(0, addr);
- }
+ unsigned long flags;
+ extern void __flush_cache_4096(unsigned long addr, unsigned long phys, unsigned long exec_offset);
-#if 0 /* DEBUG DEBUG */
- /* Loop all the I-cache */
- for (addr = CACHE_IC_ADDRESS_ARRAY;
- addr < (CACHE_IC_ADDRESS_ARRAY
- +(CACHE_IC_NUM_ENTRIES<< CACHE_IC_ENTRY_SHIFT));
- addr += (1<<CACHE_IC_ENTRY_SHIFT)) {
- data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID);
- if (data == phys) {
- printk(KERN_INFO "__flush_cache_page: I-cache entry found\n");
- ctrl_outl(0, addr);
- }
- }
+#if defined(CONFIG_CPU_SUBTYPE_SH7751) || defined(CONFIG_CPU_SUBTYPE_ST40)
+ if (start >= CACHE_OC_ADDRESS_ARRAY) {
+ /*
+ * SH7751 and ST40 have no restriction to handle cache.
+ * (While SH7750 must do that at P2 area.)
+ */
+ __flush_cache_4096(start | CACHE_ASSOC, phys | 0x80000000, 0);
+ } else
#endif
- back_to_P1();
- restore_flags(flags);
+ {
+ save_and_cli(flags);
+ __flush_cache_4096(start | CACHE_ASSOC, phys | 0x80000000, 0x20000000);
+ restore_flags(flags);
+ }
}
/*
@@ -254,38 +247,77 @@
*/
void flush_dcache_page(struct page *page)
{
- if (test_bit(PG_mapped, &page->flags))
- __flush_dcache_page(PHYSADDR(page_address(page)));
+ if (test_bit(PG_mapped, &page->flags)) {
+ unsigned long phys = PHYSADDR(page_address(page));
+
+ /* Loop all the D-cache */
+ flush_cache_4096(CACHE_OC_ADDRESS_ARRAY, phys);
+ flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x1000, phys);
+ flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x2000, phys);
+ flush_cache_4096(CACHE_OC_ADDRESS_ARRAY | 0x3000, phys);
+ }
}
-void flush_cache_all(void)
+static inline void flush_icache_all(void)
{
- extern unsigned long empty_zero_page[1024];
unsigned long flags;
- unsigned long addr;
save_and_cli(flags);
-
- /* Prefetch the data to write back D-cache */
- for (addr = (unsigned long)empty_zero_page;
- addr < (unsigned long)empty_zero_page + 1024*16;
- addr += L1_CACHE_BYTES)
- asm volatile("pref @%0"::"r" (addr));
-
jump_to_P2();
- /* Flush D-cache/I-cache */
- ctrl_outl(CCR_CACHE_INIT, CCR);
+ /* Flush I-cache */
+ ctrl_outl(CCR_CACHE_VAL|CCR_CACHE_ICI, CCR);
back_to_P1();
restore_flags(flags);
}
+void flush_cache_all(void)
+{
+ extern void __flush_dcache_all(void);
+
+ __flush_dcache_all();
+ flush_icache_all();
+}
+
void flush_cache_mm(struct mm_struct *mm)
{
/* Is there any good way? */
/* XXX: possibly call flush_cache_range for each vm area */
+ /*
+ * FIXME: Really, the optimal solution here would be able to flush out
+ * individual lines created by the specified context, but this isn't
+ * feasible for a number of architectures (such as MIPS, and some
+ * SPARC) .. is this possible for SuperH?
+ *
+ * In the meantime, we'll just flush all of the caches.. this
+ * seems to be the simplest way to avoid at least a few wasted
+ * cache flushes. -Lethal
+ */
flush_cache_all();
}
+static void __flush_cache_page(struct vm_area_struct *vma,
+ unsigned long address,
+ unsigned long phys)
+{
+ /* We only need to flush D-cache when we have alias */
+ if ((address^phys) & CACHE_ALIAS) {
+ /* Loop 4K of the D-cache */
+ flush_cache_4096(
+ CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS),
+ phys);
+ /* Loop another 4K of the D-cache */
+ flush_cache_4096(
+ CACHE_OC_ADDRESS_ARRAY | (phys & CACHE_ALIAS),
+ phys);
+ }
+
+ if (vma->vm_flags & VM_EXEC)
+ /* Loop 4K (half) of the I-cache */
+ flush_cache_4096(
+ CACHE_IC_ADDRESS_ARRAY | (address & 0x1000),
+ phys);
+}
+
/*
* Write back and invalidate D-caches.
*
@@ -298,14 +330,53 @@
void flush_cache_range(struct mm_struct *mm, unsigned long start,
unsigned long end)
{
- /*
- * We could call flush_cache_page for the pages of these range,
- * but it's not efficient (scan the caches all the time...).
- *
- * We can't use A-bit magic, as there's the case we don't have
- * valid entry on TLB.
- */
- flush_cache_all();
+ extern void flush_cache_4096_all(unsigned long start);
+
+ unsigned long p = start & PAGE_MASK;
+ pgd_t *dir;
+ pmd_t *pmd;
+ pte_t *pte;
+ pte_t entry;
+ unsigned long phys;
+ unsigned long d = 0;
+
+ dir = pgd_offset(mm, p);
+ pmd = pmd_offset(dir, p);
+
+ do {
+ if (pmd_none(*pmd) || pmd_bad(*pmd)) {
+ p &= ~((1 << PMD_SHIFT) -1);
+ p += (1 << PMD_SHIFT);
+ pmd++;
+ continue;
+ }
+ pte = pte_offset(pmd, p);
+ do {
+ entry = *pte;
+ if ((pte_val(entry) & _PAGE_PRESENT)) {
+ phys = pte_val(entry)&PTE_PHYS_MASK;
+ if ((p^phys) & CACHE_ALIAS) {
+ d |= 1 << ((p & CACHE_ALIAS)>>12);
+ d |= 1 << ((phys & CACHE_ALIAS)>>12);
+ if (d == 0x0f)
+ goto loop_exit;
+ }
+ }
+ pte++;
+ p += PAGE_SIZE;
+ } while (p < end && (unsigned long)pte & PAGE_MASK);
+ pmd++;
+ } while (p < end);
+ loop_exit:
+ if (d & 1)
+ flush_cache_4096_all(0);
+ if (d & 2)
+ flush_cache_4096_all(0x1000);
+ if (d & 4)
+ flush_cache_4096_all(0x2000);
+ if (d & 8)
+ flush_cache_4096_all(0x3000);
+ flush_icache_all();
}
/*
@@ -319,8 +390,7 @@
pmd_t *pmd;
pte_t *pte;
pte_t entry;
- unsigned long phys, addr, data;
- unsigned long flags;
+ unsigned long phys;
dir = pgd_offset(vma->vm_mm, address);
pmd = pmd_offset(dir, address);
@@ -328,49 +398,11 @@
return;
pte = pte_offset(pmd, address);
entry = *pte;
- if (pte_none(entry) || !pte_present(entry))
+ if (!(pte_val(entry) & _PAGE_PRESENT))
return;
phys = pte_val(entry)&PTE_PHYS_MASK;
-
- phys |= CACHE_VALID;
- save_and_cli(flags);
- jump_to_P2();
-
- /* We only need to flush D-cache when we have alias */
- if ((address^phys) & CACHE_ALIAS) {
- /* Loop 4K of the D-cache */
- for (addr = CACHE_OC_ADDRESS_ARRAY | (address & CACHE_ALIAS);
- addr < (CACHE_OC_ADDRESS_ARRAY + (address & CACHE_ALIAS)
- +(CACHE_OC_NUM_ENTRIES/4<<CACHE_OC_ENTRY_SHIFT));
- addr += (1<<CACHE_OC_ENTRY_SHIFT)) {
- data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID);
- if (data == phys)
- ctrl_outl(0, addr);
- }
- /* Loop another 4K of the D-cache */
- for (addr = CACHE_OC_ADDRESS_ARRAY | (phys & CACHE_ALIAS);
- addr < (CACHE_OC_ADDRESS_ARRAY + (phys & CACHE_ALIAS)
- +(CACHE_OC_NUM_ENTRIES/4<<CACHE_OC_ENTRY_SHIFT));
- addr += (1<<CACHE_OC_ENTRY_SHIFT)) {
- data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID);
- if (data == phys)
- ctrl_outl(0, addr);
- }
- }
-
- if (vma->vm_flags & VM_EXEC)
- /* Loop 4K of the I-cache */
- for (addr = CACHE_IC_ADDRESS_ARRAY|(address&0x1000);
- addr < ((CACHE_IC_ADDRESS_ARRAY|(address&0x1000))
- +(CACHE_IC_NUM_ENTRIES/2<<CACHE_IC_ENTRY_SHIFT));
- addr += (1<<CACHE_IC_ENTRY_SHIFT)) {
- data = ctrl_inl(addr)&(0x1ffff000|CACHE_VALID);
- if (data == phys)
- ctrl_outl(0, addr);
- }
- back_to_P1();
- restore_flags(flags);
+ __flush_cache_page(vma, address, phys);
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)