Subject: Re: [2.5 patch] R5K SC support

From: Brian Murphy <[email-masked]>
Date: Tue, 06 Aug 2002 21:38:20 +0200

Ralf Baechle wrote:

>>+static void r5k_dma_cache_wback_inv_sc(unsigned long addr, unsigned long size)
>>+static void r5k_dma_cache_inv_sc(unsigned long addr, unsigned long size)
>>
>
>You can hook the second level cache support into the bcache hook.  That's
>working because unlike the R4000SC the R5000's second level cache does not
>have the additional constraint of the primary caches always being a subset
>of the second level caches.
>
>Arch/mips/sgi-ip22/ip22-sc.c is an example how this can be done.
>
>
>
This is all very fine in theory but the R5000 cache routines are 
automatically
selected by the R4000 secondary setup routines which pick out the incorrect
flush_range function and wback_inv and inv functions. These do not call
board cache routines at all.

It must be that the secondary cache for the ip22 architecture with an R5000
is not flagged as having a secondary cache in a way that the R4000 
secondary
cache routines can detect.

On the other hand with the application of this (attached) patch the 
cache is
detected correctly and appropriate routines are selected.

/Brian
Index: arch/mips/mm/c-r4k.c
===================================================================
RCS file: /cvs/linux-mips/arch/mips/mm/c-r4k.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- arch/mips/mm/c-r4k.c	18 Jul 2002 17:35:18 -0000	1.1
+++ arch/mips/mm/c-r4k.c	18 Jul 2002 17:41:34 -0000	1.2
@@ -36,6 +36,7 @@
 
 /* Secondary cache (if present) parameters. */
 static unsigned int scache_size, sc_lsize;	/* Again, in bytes */
+static int sc_present = 0;
 
 #include <asm/cacheops.h>
 #include <asm/r4kcache.h>
@@ -1195,6 +1196,126 @@
 #endif
 }
 
+static void r5k_flush_cache_range(struct mm_struct *mm,
+				  unsigned long start,
+				  unsigned long end)
+{
+	struct vm_area_struct *vma;
+	unsigned long flags;
+
+	if (mm->context == 0)
+		return;
+
+	start &= PAGE_MASK;
+#ifdef DEBUG_CACHE
+	printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end);
+#endif
+	vma = find_vma(mm, start);
+	if (vma) {
+		if (mm->context != current->active_mm->context) {
+			_flush_cache_all();
+		} else {
+			pgd_t *pgd;
+			pmd_t *pmd;
+			pte_t *pte;
+			int text;
+
+			__save_and_cli(flags);
+			text = vma->vm_flags & VM_EXEC;
+			while (start < end) {
+				pgd = pgd_offset(mm, start);
+				pmd = pmd_offset(pgd, start);
+				pte = pte_offset(pmd, start);
+
+				if (pte_val(*pte) & _PAGE_VALID) {
+					blast_dcache32_page(start);
+					if (text)
+						blast_icache32_page(start);
+				}
+				start += PAGE_SIZE;
+			}
+			__restore_flags(flags);
+		}
+	}
+}
+
+static void
+r5k_dma_cache_inv_sc(unsigned long addr, unsigned long size)
+{
+	unsigned long end, a;
+
+	if (size >= scache_size) {
+		blast_r5000_scache();
+		return;
+	}
+
+	/* We assume the address is in KSEG0. On the R5000 we
+	 * cannot invalidate less than a page at a time, and
+	 * there is no Hit_xxx cache operations.
+	 */
+	a = addr & ~(PAGE_SIZE - 1);
+	end = (addr + size - 1) & ~(PAGE_SIZE - 1);
+	while (a <= end) {
+		blast_r5000_scache_page_indexed(a);	/* Page_Invalidate */
+		a += PAGE_SIZE;
+	}
+}
+
+static void
+r5k_dma_cache_wback_inv(unsigned long addr, unsigned long size)
+{
+	unsigned long end, a;
+
+	if (size >= dcache_size) {
+		blast_dcache32();
+	} else {
+		a = addr & ~(dc_lsize - 1);
+		end = (addr + size - 1) & ~(dc_lsize - 1);
+		while (a <= end) {
+			flush_dcache_line(a); /* Hit_Writeback_Inv_D */
+			a += dc_lsize;
+		}
+	}
+	if (sc_present)
+		r5k_dma_cache_inv_sc(addr, size);
+}
+
+static void
+r5k_dma_cache_inv(unsigned long addr, unsigned long size)
+{
+	unsigned long end, a;
+
+	if (size >= dcache_size) {
+		blast_dcache32();
+	} else {
+		a = addr & ~(dc_lsize - 1);
+		end = (addr + size - 1) & ~(dc_lsize - 1);
+		while (a <= end) {
+			invalidate_dcache_line(a); /* Hit_Invalidate_D */
+			a += dc_lsize;
+		}
+	}
+	if (sc_present)
+		r5k_dma_cache_inv_sc(addr, size);
+}
+
+static void
+r5k_dma_cache_wback(unsigned long addr, unsigned long size)
+{
+	unsigned long end, a;
+
+	if (size >= dcache_size) {
+		blast_dcache32();
+	} else {
+		a = addr & ~(dc_lsize - 1);
+		end = (addr + size - 1) & ~(dc_lsize - 1);
+		while (a <= end) {
+			flush_dcache_line_wb(a); /* Hit_Writeback_D */
+			a += dc_lsize;
+		}
+	}
+}
+
 /* Detect and size the various r4k caches. */
 static void __init probe_icache(unsigned long config)
 {
@@ -1374,7 +1495,7 @@
 	_dma_cache_inv = r4k_dma_cache_inv_pc;
 }
 
-static void __init setup_scache_funcs(void)
+static void __init setup_scache_funcs_r4k(void)
 {
 	switch(sc_lsize) {
 	case 16:
@@ -1457,19 +1578,36 @@
 	_dma_cache_inv = r4k_dma_cache_inv_sc;
 }
 
+static void __init setup_scache_funcs_r5k(void) {
+	_clear_page = r4k_clear_page_d32;
+	_copy_page = r4k_copy_page_d32;
+	_flush_cache_all = r4k_flush_cache_all_d32i32;
+	_flush_cache_range = r5k_flush_cache_range;
+	_flush_cache_mm = r4k_flush_cache_mm_d32i32;
+	_flush_page_to_ram = r4k_flush_page_to_ram_d32;
+
+	___flush_cache_all = _flush_cache_all;
+	_flush_icache_page = r4k_flush_icache_page_p;
+	_dma_cache_wback_inv = r5k_dma_cache_wback_inv;
+	_dma_cache_wback = r5k_dma_cache_wback;
+	_dma_cache_inv = r5k_dma_cache_inv;
+}
+
 typedef int (*probe_func_t)(unsigned long);
 
 static inline void __init setup_scache(unsigned int config)
 {
 	probe_func_t probe_scache_kseg1;
-	int sc_present = 0;
 
 	/* Maybe the cpu knows about a l2 cache? */
 	probe_scache_kseg1 = (probe_func_t) (KSEG1ADDR(&probe_scache));
 	sc_present = probe_scache_kseg1(config);
 
 	if (sc_present) {
-		setup_scache_funcs();
+		if (mips_cpu.cputype == CPU_R5000)
+			setup_scache_funcs_r5k();
+		else
+			setup_scache_funcs_r4k();
 		return;
 	}
 
Index: include/asm-mips/r4kcache.h
===================================================================
RCS file: /cvs/linux-mips/include/asm-mips/r4kcache.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- include/asm-mips/r4kcache.h	18 Jul 2002 17:34:01 -0000	1.1
+++ include/asm-mips/r4kcache.h	18 Jul 2002 17:41:35 -0000	1.2
@@ -76,6 +76,19 @@
 		  "i" (Hit_Writeback_Inv_D));
 }
 
+extern inline void flush_dcache_line_wb(unsigned long addr)
+{
+	__asm__ __volatile__(
+		".set noreorder\n\t"
+		".set mips3\n\t"
+		"cache %1, (%0)\n\t"
+		".set mips0\n\t"
+		".set reorder"
+		:
+		: "r" (addr),
+		  "i" (Hit_Writeback_D));
+}
+
 static inline void invalidate_dcache_line(unsigned long addr)
 {
 	__asm__ __volatile__(
@@ -606,6 +619,40 @@
 static inline void blast_scache128_page_indexed(unsigned long page)
 {
 	cache128_unroll32(page,Index_Writeback_Inv_SD);
+}
+
+
+#define cache_unroll(base,op)	        	\
+	__asm__ __volatile__("	         	\
+		.set noreorder;		        \
+		.set mips3;		        \
+                cache %1, (%0);	                \
+		.set mips0;			\
+		.set reorder"			\
+		:				\
+		: "r" (base),			\
+		  "i" (op));
+
+extern inline void blast_r5000_scache(void)
+{
+	unsigned long start = KSEG0;
+	unsigned long end = KSEG0 + scache_size;
+
+	while(start < end) {
+		cache_unroll(start,Page_Invalidate);
+		start += 128*sc_lsize;
+	}
+}
+
+extern inline void blast_r5000_scache_page_indexed(unsigned long page)
+{
+	unsigned long start = page;
+	unsigned long end = page + PAGE_SIZE;
+
+	while(start < end) {
+		cache_unroll(start,Page_Invalidate);
+		start += 128*sc_lsize;
+	}
 }
 
 #endif /* !(_MIPS_R4KCACHE_H) */

---

From: "Maciej W. Rozycki" <[email-masked]>
Date: Wed, 7 Aug 2002 14:11:47 +0200 (MET DST)

> detected correctly and appropriate routines are selected.

 Hmm, I bet you wanted to put the code to arch/mips/mm/c-r5k.c, didn't
you?

-- 
+  Maciej W. Rozycki, Technical University of Gdansk, Poland   +
+--------------------------------------------------------------+
+        e-mail: [email-masked], PGP key available        +