From: NIIBE Y. <gn...@ch...> - 2000-08-05 06:19:33
|
I've found SH-4 cache handling mistake. When we don't have valid TLB entries, flushing cache doesn't work well with current code. I've attached tentative patch below, which is better than nothing. I'll commit the changes by Stuart (I/O base for "unkown"), and chenges by me. Then, I'll check Jesper's patches. Index: arch/sh/mm/cache.c =================================================================== RCS file: /cvsroot/linuxsh/kernel/arch/sh/mm/cache.c,v retrieving revision 1.12 diff -u -r1.12 cache.c --- arch/sh/mm/cache.c 2000/07/19 11:22:47 1.12 +++ arch/sh/mm/cache.c 2000/08/05 04:28:18 @@ -209,10 +209,28 @@ * */ for (v = start; v < end; v+=L1_CACHE_BYTES) { +#if 0 addr = CACHE_IC_ADDRESS_ARRAY | (v&CACHE_IC_ENTRY_MASK) | 0x8 /* A-bit */; data = (v&0xfffffc00); /* Valid=0 */ ctrl_outl(data, addr); +#else + /* No, we can't use A-bit. If we don't have entry on ITLB, + * the cache line is not handled correctly... + */ + /* + * So... Flush possible two lines, anyway + */ + addr = CACHE_IC_ADDRESS_ARRAY | (v&CACHE_IC_ENTRY_MASK); + data = ctrl_inl(addr); + data = (data&0xfffffc00); + ctrl_outl(data, addr); + + addr = addr ^ 0x1000; + data = ctrl_inl(addr); + data = (data&0xfffffc00); + ctrl_outl(data, addr); +#endif } back_to_P1(); } @@ -225,8 +243,12 @@ */ void flush_icache_range(unsigned long start, unsigned long end) { + unsigned long flags; + + save_and_cli(flags); dcache_wback_range(start, end); icache_purge_range(start, end); + restore_flags(flags); } /* @@ -237,6 +259,7 @@ void flush_icache_page(struct vm_area_struct *vma, struct page *pg) { unsigned long phys, addr, data, i; + unsigned long flags; /* * Alas, we don't know where the virtual address is, @@ -246,6 +269,7 @@ /* Physical address of this page */ phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; + save_and_cli(flags); jump_to_P2(); /* Loop all the I-cache */ for (i=0; i<CACHE_IC_NUM_ENTRIES; i++) { @@ -257,6 +281,7 @@ } } back_to_P1(); + restore_flags(flags); } void flush_cache_all(void) @@ -326,6 +351,7 @@ jump_to_P2(); for (v = start; v < end; v+=L1_CACHE_BYTES) { +#if 0 addr = CACHE_OC_ADDRESS_ARRAY | (v&CACHE_OC_ENTRY_PHYS_MASK) | 0x8 /* A-bit */; data = (v&0xfffffc00); /* Update=0, Valid=0 */ @@ -335,6 +361,31 @@ ctrl_outl(data, addr | 0x1000); ctrl_outl(data, addr | 0x2000); ctrl_outl(data, addr | 0x3000); +#else + /* No, we can't use A-bit. If we don't have entry on UTLB, + * the cache line is not handled correctly... + */ + /* + * So... Flush possible two lines, anyway + */ + /* Try all possible cases */ + + addr = CACHE_OC_ADDRESS_ARRAY | (v&CACHE_OC_ENTRY_PHYS_MASK); + data = ctrl_inl(addr) & 0xfffffc00; + ctrl_outl(data, addr); + + addr ^= 0x1000; + data = ctrl_inl(addr) & 0xfffffc00; + ctrl_outl(data, addr); + + addr ^= 0x2000; + data = ctrl_inl(addr) & 0xfffffc00; + ctrl_outl(data, addr); + + addr ^= 0x1000; + data = ctrl_inl(addr) & 0xfffffc00; + ctrl_outl(data, addr); +#endif } back_to_P1(); } @@ -355,10 +406,12 @@ void flush_page_to_ram(struct page *pg) { unsigned long phys, addr, data, i; + unsigned long flags; /* Physical address of this page */ phys = (pg - mem_map)*PAGE_SIZE + __MEMORY_START; + save_and_cli(flags); jump_to_P2(); /* Loop all the D-cache */ for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) { @@ -370,5 +423,6 @@ } } back_to_P1(); + restore_flags(flags); } #endif -- |