patch-2.1.73 linux/arch/mips/mm/r4xx0.c
Next file: linux/arch/mips/mm/r6000.c
Previous file: linux/arch/mips/mm/r2300.c
Back to the patch index
Back to the overall index
- Lines: 535
- Date:
Wed Dec 10 10:31:10 1997
- Orig file:
v2.1.72/linux/arch/mips/mm/r4xx0.c
- Orig date:
Mon Aug 18 18:19:43 1997
diff -u --recursive --new-file v2.1.72/linux/arch/mips/mm/r4xx0.c linux/arch/mips/mm/r4xx0.c
@@ -3,7 +3,13 @@
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*
- * $Id: r4xx0.c,v 1.5 1997/08/08 18:13:07 miguel Exp $
+ * $Id: r4xx0.c,v 1.11 1997/12/02 06:33:53 ralf Exp $
+ *
+ * To do:
+ *
+ * - this code is a overbloated pig
+ * - many of the bug workarounds are not efficient at all, but at
+ * least they are functional ...
*/
#include <linux/config.h>
@@ -39,7 +45,17 @@
#undef DEBUG_CACHE
/*
- * Zero an entire page.
+ * On processors with QED R4600 style two set assosicative cache
+ * this is the bit which selects the way in the cache for the
+ * indexed cachops.
+ */
+#define icache_waybit (icache_size >> 1)
+#define dcache_waybit (dcache_size >> 1)
+
+/*
+ * Zero an entire page. We have three flavours of the routine available.
+ * One for CPU with 16byte, with 32byte cachelines plus a special version
+ * with nops which handles the buggy R4600 v1.x.
*/
static void r4k_clear_page_d16(unsigned long page)
@@ -108,7 +124,7 @@
* IDT R4600 V1.7 errata:
*
* 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D,
- * Hit_Invalidate_D and Create_Dirty_Exclusive_D should only be
+ * Hit_Invalidate_D and Create_Dirty_Excl_D should only be
* executed if there is no other dcache activity. If the dcache is
* accessed for another instruction immeidately preceding when these
* cache instructions are executing, it is possible that the dcache
@@ -167,6 +183,44 @@
}
/*
+ * And this one is for the R4600 V2.0
+ */
+static void r4k_clear_page_r4600_v2(unsigned long page)
+{
+ unsigned int flags;
+
+ save_and_cli(flags);
+ *(volatile unsigned int *)KSEG1;
+ __asm__ __volatile__(
+ ".set\tnoreorder\n\t"
+ ".set\tnoat\n\t"
+ ".set\tmips3\n\t"
+ "daddiu\t$1,%0,%2\n"
+ "1:\tcache\t%3,(%0)\n\t"
+ "sd\t$0,(%0)\n\t"
+ "sd\t$0,8(%0)\n\t"
+ "sd\t$0,16(%0)\n\t"
+ "sd\t$0,24(%0)\n\t"
+ "daddiu\t%0,64\n\t"
+ "cache\t%3,-32(%0)\n\t"
+ "sd\t$0,-32(%0)\n\t"
+ "sd\t$0,-24(%0)\n\t"
+ "sd\t$0,-16(%0)\n\t"
+ "bne\t$1,%0,1b\n\t"
+ "sd\t$0,-8(%0)\n\t"
+ ".set\tmips0\n\t"
+ ".set\tat\n\t"
+ ".set\treorder"
+ :"=r" (page)
+ :"0" (page),
+ "I" (PAGE_SIZE),
+ "i" (Create_Dirty_Excl_D)
+ :"$1","memory");
+ restore_flags(flags);
+}
+
+
+/*
* This is still inefficient. We only can do better if we know the
* virtual address where the copy will be accessed.
*/
@@ -355,6 +409,74 @@
"i" (Create_Dirty_Excl_D));
}
+static void r4k_copy_page_r4600_v2(unsigned long to, unsigned long from)
+{
+ unsigned long dummy1, dummy2;
+ unsigned long reg1, reg2, reg3, reg4;
+ unsigned int flags;
+
+ __save_and_cli(flags);
+ __asm__ __volatile__(
+ ".set\tnoreorder\n\t"
+ ".set\tnoat\n\t"
+ ".set\tmips3\n\t"
+ "daddiu\t$1,%0,%8\n"
+ "1:\tnop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "\tcache\t%9,(%0)\n\t"
+ "lw\t%2,(%1)\n\t"
+ "lw\t%3,4(%1)\n\t"
+ "lw\t%4,8(%1)\n\t"
+ "lw\t%5,12(%1)\n\t"
+ "sw\t%2,(%0)\n\t"
+ "sw\t%3,4(%0)\n\t"
+ "sw\t%4,8(%0)\n\t"
+ "sw\t%5,12(%0)\n\t"
+ "lw\t%2,16(%1)\n\t"
+ "lw\t%3,20(%1)\n\t"
+ "lw\t%4,24(%1)\n\t"
+ "lw\t%5,28(%1)\n\t"
+ "sw\t%2,16(%0)\n\t"
+ "sw\t%3,20(%0)\n\t"
+ "sw\t%4,24(%0)\n\t"
+ "sw\t%5,28(%0)\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cache\t%9,32(%0)\n\t"
+ "daddiu\t%0,64\n\t"
+ "daddiu\t%1,64\n\t"
+ "lw\t%2,-32(%1)\n\t"
+ "lw\t%3,-28(%1)\n\t"
+ "lw\t%4,-24(%1)\n\t"
+ "lw\t%5,-20(%1)\n\t"
+ "sw\t%2,-32(%0)\n\t"
+ "sw\t%3,-28(%0)\n\t"
+ "sw\t%4,-24(%0)\n\t"
+ "sw\t%5,-20(%0)\n\t"
+ "lw\t%2,-16(%1)\n\t"
+ "lw\t%3,-12(%1)\n\t"
+ "lw\t%4,-8(%1)\n\t"
+ "lw\t%5,-4(%1)\n\t"
+ "sw\t%2,-16(%0)\n\t"
+ "sw\t%3,-12(%0)\n\t"
+ "sw\t%4,-8(%0)\n\t"
+ "bne\t$1,%0,1b\n\t"
+ "sw\t%5,-4(%0)\n\t"
+ ".set\tmips0\n\t"
+ ".set\tat\n\t"
+ ".set\treorder"
+ :"=r" (dummy1), "=r" (dummy2),
+ "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4)
+ :"0" (to), "1" (from),
+ "I" (PAGE_SIZE),
+ "i" (Create_Dirty_Excl_D));
+ restore_flags(flags);
+}
+
/*
* If you think for one second that this stuff coming up is a lot
* of bulky code eating too many kernel cache lines. Think _again_.
@@ -1427,7 +1549,8 @@
pte_t *ptep;
int text;
- /* If ownes no valid ASID yet, cannot possibly have gotten
+ /*
+ * If ownes no valid ASID yet, cannot possibly have gotten
* this page into the cache.
*/
if(mm->context == 0)
@@ -1465,11 +1588,8 @@
*/
page = (KSEG0 + (page & (dcache_size - 1)));
blast_dcache16_page_indexed(page);
- blast_dcache16_page_indexed(page ^ 0x2000);
- if(text) {
+ if(text)
blast_icache16_page_indexed(page);
- blast_icache16_page_indexed(page ^ 0x2000);
- }
}
out:
restore_flags(flags);
@@ -1583,10 +1703,10 @@
*/
page = (KSEG0 + (page & (dcache_size - 1)));
blast_dcache32_page_indexed(page);
- blast_dcache32_page_indexed(page ^ 0x2000);
+ blast_dcache32_page_indexed(page ^ dcache_waybit);
if(text) {
blast_icache32_page_indexed(page);
- blast_icache32_page_indexed(page ^ 0x2000);
+ blast_icache32_page_indexed(page ^ icache_waybit);
}
}
out:
@@ -1761,7 +1881,9 @@
}
/*
- * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Invalidate_D,
+ * Writeback and invalidate the primary cache dcache before DMA.
+ *
+ * R4600 v2.0 bug: "The CACHE instructions Hit_Writeback_Inv_D,
* Hit_Writeback_D, Hit_Invalidate_D and Create_Dirty_Exclusive_D will only
* operate correctly if the internal data cache refill buffer is empty. These
* CACHE instructions should be separated from any potential data cache miss
@@ -1769,6 +1891,104 @@
* (Revision 2.0 device errata from IDT available on http://www.idt.com/
* in .pdf format.)
*/
+static void
+r4k_flush_cache_pre_dma_out_pc(unsigned long addr, unsigned long size)
+{
+ unsigned long end, a;
+ unsigned int cmode, flags;
+
+ cmode = read_32bit_cp0_register(CP0_CONFIG) & CONFIG_CM_CMASK;
+ if (cmode == CONFIG_CM_CACHABLE_WA ||
+ cmode == CONFIG_CM_CACHABLE_NO_WA) {
+ /* primary dcache is writethrough, therefore memory
+ is already consistent with the caches. */
+ return;
+ }
+
+ if (size >= dcache_size) {
+ flush_cache_all();
+ return;
+ }
+
+ /* Workaround for R4600 bug. See comment above. */
+ save_and_cli(flags);
+ *(volatile unsigned long *)KSEG1;
+
+ a = addr & ~(dc_lsize - 1);
+ end = (addr + size) & ~(dc_lsize - 1);
+ while (1) {
+ flush_dcache_line(a); /* Hit_Writeback_Inv_D */
+ if (a == end) break;
+ a += dc_lsize;
+ }
+ restore_flags(flags);
+}
+
+static void
+r4k_flush_cache_pre_dma_out_sc(unsigned long addr, unsigned long size)
+{
+ unsigned long end;
+ unsigned long a;
+
+ if (size >= scache_size) {
+ flush_cache_all();
+ return;
+ }
+
+ a = addr & ~(sc_lsize - 1);
+ end = (addr + size) & ~(sc_lsize - 1);
+ while (1) {
+ flush_scache_line(addr); /* Hit_Writeback_Inv_SD */
+ if (addr == end) break;
+ addr += sc_lsize;
+ }
+ r4k_flush_cache_pre_dma_out_pc(addr, size);
+}
+
+static void
+r4k_flush_cache_post_dma_in_pc(unsigned long addr, unsigned long size)
+{
+ unsigned long end;
+ unsigned long a;
+
+ if (size >= dcache_size) {
+ flush_cache_all();
+ return;
+ }
+
+ /* Workaround for R4600 bug. See comment above. */
+ *(volatile unsigned long *)KSEG1;
+
+ a = addr & ~(dc_lsize - 1);
+ end = (addr + size) & ~(dc_lsize - 1);
+ while (1) {
+ invalidate_dcache_line(a); /* Hit_Invalidate_D */
+ if (a == end) break;
+ a += dc_lsize;
+ }
+}
+
+static void
+r4k_flush_cache_post_dma_in_sc(unsigned long addr, unsigned long size)
+{
+ unsigned long end;
+ unsigned long a;
+
+ if (size >= scache_size) {
+ flush_cache_all();
+ return;
+ }
+
+ a = addr & ~(sc_lsize - 1);
+ end = (addr + size) & ~(sc_lsize - 1);
+ while (1) {
+ invalidate_scache_line(addr); /* Hit_Invalidate_SD */
+ if (addr == end) break;
+ addr += sc_lsize;
+ }
+ r4k_flush_cache_pre_dma_out_pc(addr, size);
+}
+
static void r4k_flush_page_to_ram_d32i32_r4600(unsigned long page)
{
page &= PAGE_MASK;
@@ -1776,23 +1996,14 @@
unsigned long flags;
#ifdef DEBUG_CACHE
- /* #if 1 */
printk("r4600_cram[%08lx]", page);
#endif
- /*
- * Workaround for R4600 bug. Explanation see above.
- */
- *(volatile unsigned long *)KSEG1;
-
save_and_cli(flags);
blast_dcache32_page(page);
- blast_dcache32_page(page ^ 0x2000);
#ifdef CONFIG_SGI
{
unsigned long tmp1, tmp2;
- /*
- * SGI goo. Have to check this closer ...
- */
+
__asm__ __volatile__("
.set noreorder
.set mips3
@@ -1822,20 +2033,48 @@
}
}
+/*
+ * While we're protected against bad userland addresses we don't care
+ * very much about what happens in that case. Usually a segmentation
+ * fault will dump the process later on anyway ...
+ */
static void r4k_flush_cache_sigtramp(unsigned long addr)
{
- addr &= ~(dc_lsize - 1);
- __asm__ __volatile__("nop;nop;nop;nop");
- flush_dcache_line(addr);
- flush_dcache_line(addr + dc_lsize);
- flush_icache_line(addr);
- flush_icache_line(addr + dc_lsize);
+ unsigned long daddr, iaddr;
+
+ daddr = addr & ~(dc_lsize - 1);
+ __asm__ __volatile__("nop;nop;nop;nop"); /* R4600 V1.7 */
+ protected_writeback_dcache_line(daddr);
+ protected_writeback_dcache_line(daddr + dc_lsize);
+ iaddr = addr & ~(ic_lsize - 1);
+ protected_flush_icache_line(iaddr);
+ protected_flush_icache_line(iaddr + ic_lsize);
+}
+
+static void r4600v20k_flush_cache_sigtramp(unsigned long addr)
+{
+ unsigned long daddr, iaddr;
+ unsigned int flags;
+
+ daddr = addr & ~(dc_lsize - 1);
+ save_and_cli(flags);
+
+ /* Clear internal cache refill buffer */
+ *(volatile unsigned int *)KSEG1;
+
+ protected_writeback_dcache_line(daddr);
+ protected_writeback_dcache_line(daddr + dc_lsize);
+ iaddr = addr & ~(ic_lsize - 1);
+ protected_flush_icache_line(iaddr);
+ protected_flush_icache_line(iaddr + ic_lsize);
+ restore_flags(flags);
}
#undef DEBUG_TLB
#undef DEBUG_TLBUPDATE
#define NTLB_ENTRIES 48 /* Fixed on all R4XX0 variants... */
+
#define NTLB_ENTRIES_HALF 24 /* Fixed on all R4XX0 variants... */
static inline void r4k_flush_tlb_all(void)
@@ -1943,7 +2182,7 @@
int oldpid, newpid, idx;
#ifdef DEBUG_TLB
- printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page);
+ printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page);
#endif
newpid = (vma->vm_mm->context & 0xff);
page &= (PAGE_MASK << 1);
@@ -2407,6 +2646,8 @@
static void setup_noscache_funcs(void)
{
+ unsigned int prid;
+
switch(dc_lsize) {
case 16:
clear_page = r4k_clear_page_d16;
@@ -2418,9 +2659,13 @@
flush_page_to_ram = r4k_flush_page_to_ram_d16i16;
break;
case 32:
- if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2010) {
+ prid = read_32bit_cp0_register(CP0_PRID) & 0xfff0;
+ if (prid == 0x2010) { /* R4600 V1.7 */
clear_page = r4k_clear_page_r4600_v1;
copy_page = r4k_copy_page_r4600_v1;
+ } else if (prid == 0x2020) { /* R4600 V2.0 */
+ clear_page = r4k_clear_page_r4600_v2;
+ copy_page = r4k_copy_page_r4600_v2;
} else {
clear_page = r4k_clear_page_d32;
copy_page = r4k_copy_page_d32;
@@ -2432,6 +2677,8 @@
flush_page_to_ram = r4k_flush_page_to_ram_d32i32;
break;
}
+ flush_cache_pre_dma_out = r4k_flush_cache_pre_dma_out_pc;
+ flush_cache_post_dma_in = r4k_flush_cache_post_dma_in_pc;
}
static void setup_scache_funcs(void)
@@ -2524,6 +2771,10 @@
};
break;
}
+
+ /* XXX Do these for Indy style caches also. No need for now ... */
+ flush_cache_pre_dma_out = r4k_flush_cache_pre_dma_out_sc;
+ flush_cache_post_dma_in = r4k_flush_cache_post_dma_in_sc;
}
typedef int (*probe_func_t)(unsigned long);
@@ -2534,7 +2785,11 @@
unsigned long cfg = read_32bit_cp0_register(CP0_CONFIG);
int sc_present = 0;
- printk("CPU REVISION IS: %08x\n", read_32bit_cp0_register(CP0_PRID));
+ printk("CPU revision is: %08x\n", read_32bit_cp0_register(CP0_PRID));
+
+ set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_NONCOHERENT);
+//set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_WA);
+//set_cp0_config(CONFIG_CM_CMASK, CONFIG_CM_CACHABLE_NO_WA);
probe_icache(cfg);
probe_dcache(cfg);
@@ -2546,7 +2801,6 @@
case CPU_R4400PC:
case CPU_R4400SC:
case CPU_R4400MC:
-try_again:
probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache));
sc_present = probe_scache_kseg1(cfg);
break;
@@ -2554,39 +2808,44 @@
case CPU_R4600:
case CPU_R4640:
case CPU_R4700:
- case CPU_R5000:
+ case CPU_R5000: /* XXX: We don't handle the true R5000 SCACHE */
+ case CPU_NEVADA:
probe_scache_kseg1 = (probe_func_t)
(KSEG1ADDR(&probe_scache_eeprom));
sc_present = probe_scache_eeprom(cfg);
- /* Try using tags if eeprom give us bogus data. */
- if(sc_present == -1)
- goto try_again;
+ /* Try using tags if eeprom gives us bogus data. */
+ if(sc_present == -1) {
+ probe_scache_kseg1 =
+ (probe_func_t) (KSEG1ADDR(&probe_scache));
+ sc_present = probe_scache_kseg1(cfg);
+ }
break;
};
- if(!sc_present) {
- /* Lacks secondary cache. */
- setup_noscache_funcs();
+ if(sc_present == 1
+ && (mips_cputype == CPU_R4000SC
+ || mips_cputype == CPU_R4000MC
+ || mips_cputype == CPU_R4400SC
+ || mips_cputype == CPU_R4400MC)) {
+ /* Has a true secondary cache. */
+ setup_scache_funcs();
} else {
- /* Has a secondary cache. */
- if(mips_cputype != CPU_R4600 &&
- mips_cputype != CPU_R4640 &&
- mips_cputype != CPU_R4700 &&
- mips_cputype != CPU_R5000) {
- setup_scache_funcs();
- } else {
- setup_noscache_funcs();
- if((mips_cputype != CPU_R5000)) {
- flush_cache_page =
- r4k_flush_cache_page_d32i32_r4600;
- flush_page_to_ram =
- r4k_flush_page_to_ram_d32i32_r4600;
- }
+ /* Lacks true secondary cache. */
+ setup_noscache_funcs();
+ if((mips_cputype != CPU_R5000)) { /* XXX */
+ flush_cache_page =
+ r4k_flush_cache_page_d32i32_r4600;
+ flush_page_to_ram =
+ r4k_flush_page_to_ram_d32i32_r4600;
}
}
+ /* XXX Handle true second level cache w/ split I/D */
flush_cache_sigtramp = r4k_flush_cache_sigtramp;
+ if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2020) {
+ flush_cache_sigtramp = r4600v20k_flush_cache_sigtramp;
+ }
flush_tlb_all = r4k_flush_tlb_all;
flush_tlb_mm = r4k_flush_tlb_mm;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov