patch-1.3.93 linux/arch/sparc/mm/srmmu.c

Next file: linux/arch/sparc/mm/srmmu_mp.S
Previous file: linux/arch/sparc/mm/s4cuflt.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.92/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
@@ -1,11 +1,14 @@
-/* $Id: srmmu.c,v 1.34 1996/03/01 07:16:23 davem Exp $
+/* $Id: srmmu.c,v 1.59 1996/04/21 10:32:21 davem Exp $
  * srmmu.c:  SRMMU specific routines for memory management.
  *
  * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
  * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
+ * Copyright (C) 1996 Eddie C. Dost    (ecd@pool.informatik.rwth-aachen.de)
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
+#include <linux/mm.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -13,100 +16,120 @@
 #include <asm/kdebug.h>
 #include <asm/vaddrs.h>
 #include <asm/traps.h>
-#include <asm/mp.h>
+#include <asm/smp.h>
 #include <asm/mbus.h>
 #include <asm/cache.h>
 #include <asm/oplib.h>
 #include <asm/sbus.h>
 #include <asm/iommu.h>
+#include <asm/asi.h>
+#include <asm/msi.h>
 
 /* Now the cpu specific definitions. */
 #include <asm/viking.h>
+#include <asm/mxcc.h>
 #include <asm/ross.h>
 #include <asm/tsunami.h>
 #include <asm/swift.h>
 
 enum mbus_module srmmu_modtype;
 unsigned int hwbug_bitmask;
-
 int hyper_cache_size;
+int hyper_line_size;
 
-ctxd_t *srmmu_context_table;
+#ifdef __SMP__
+extern void smp_capture(void);
+extern void smp_release(void);
+#else
+#define smp_capture()
+#define smp_release()
+#endif /* !(__SMP__) */
+
+static void (*ctxd_set)(ctxd_t *ctxp, pgd_t *pgdp);
+static void (*pmd_set)(pmd_t *pmdp, pte_t *ptep);
+
+static void (*flush_page_for_dma)(unsigned long page);
+static void (*flush_cache_page_to_uncache)(unsigned long page);
+static void (*flush_tlb_page_for_cbit)(unsigned long page);
+#ifdef __SMP__
+static void (*local_flush_page_for_dma)(unsigned long page);
+static void (*local_flush_cache_page_to_uncache)(unsigned long page);
+static void (*local_flush_tlb_page_for_cbit)(unsigned long page);
+#endif
+
+static struct srmmu_stats {
+	int invall;
+	int invpg;
+	int invrnge;
+	int invmm;
+} module_stats;
 
-/* In general all page table modifications should use the V8 atomic
- * swap instruction.  This insures the mmu and the cpu are in sync
- * with respect to ref/mod bits in the page tables.
- */
-static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
-{
-	__asm__ __volatile__("swap [%1], %0\n\t" :
-			     "=&r" (value), "=&r" (addr) :
-			     "0" (value), "1" (addr));
-	return value;
-}
+static char *srmmu_name;
 
-/* Functions really use this, not srmmu_swap directly. */
-#define srmmu_set_entry(ptr, newentry) \
-        srmmu_swap((unsigned long *) (ptr), (newentry))
+ctxd_t *srmmu_ctx_table_phys;
+ctxd_t *srmmu_context_table;
 
-/* We still don't use these at all, perhaps we don't need them
- * at all.
+static struct srmmu_trans {
+	unsigned long vbase;
+	unsigned long pbase;
+	int size;
+} srmmu_map[SPARC_PHYS_BANKS];
+
+static int can_cache_ptables = 0;
+static int viking_mxcc_present = 0;
+
+/* Physical memory can be _very_ non-contiguous on the sun4m, especially
+ * the SS10/20 class machines and with the latest openprom revisions.
+ * So we have to crunch the free page pool.
  */
-unsigned long (*srmmu_read_physical)(unsigned long paddr);
-void (*srmmu_write_physical)(unsigned long paddr, unsigned long word);
-
-static unsigned long gensrmmu_read_physical(unsigned long paddr)
+static inline unsigned long srmmu_v2p(unsigned long vaddr)
 {
-	unsigned long word;
+	int i;
 
-	__asm__ __volatile__("lda [%1] %2, %0\n\t" :
-			     "=r" (word) :
-			     "r" (paddr), "i" (ASI_M_BYPASS) :
-			     "memory");
-	return word;
+	for(i=0; srmmu_map[i].size != 0; i++) {
+		if(srmmu_map[i].vbase <= vaddr &&
+		   (srmmu_map[i].vbase + srmmu_map[i].size > vaddr))
+			return (vaddr - srmmu_map[i].vbase) + srmmu_map[i].pbase;
+	}
+	return 0xffffffffUL;
 }
 
-static unsigned long msparc_read_physical(unsigned long paddr)
+static inline unsigned long srmmu_p2v(unsigned long paddr)
 {
-	unsigned long word, flags;
+	int i;
 
-	save_flags(flags); cli();
-	__asm__ __volatile__("lda [%%g0] %3, %%g1\n\t"
-			     "or  %%g1, %4, %%g2\n\t"
-			     "sta %%g2, [%%g0] %3\n\t"
-			     "lda [%1] %2, %0\n\t"
-			     "sta %%g1, [%%g0] %3\n\t" :
-			     "=r" (word) :
-			     "r" (paddr), "i" (ASI_M_BYPASS),
-			     "i" (ASI_M_MMUREGS), "r" (VIKING_ACENABLE) :
-			     "g1", "g2", "memory");
-	restore_flags(flags);
-	return word;
+	for(i=0; srmmu_map[i].size != 0; i++) {
+		if(srmmu_map[i].pbase <= paddr &&
+		   (srmmu_map[i].pbase + srmmu_map[i].size > paddr))
+			return (paddr - srmmu_map[i].pbase) + srmmu_map[i].vbase;
+	}
+	return 0xffffffffUL;
 }
 
-static void gensrmmu_write_physical(unsigned long paddr, unsigned long word)
+/* In general all page table modifications should use the V8 atomic
+ * swap instruction.  This insures the mmu and the cpu are in sync
+ * with respect to ref/mod bits in the page tables.
+ */
+static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
 {
-	__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
-			     "r" (word), "r" (paddr), "i" (ASI_M_BYPASS) :
-			     "memory");
+#if CONFIG_AP1000
+  /* the AP1000 has its memory on bus 8, not 0 like suns do */
+  if (!(value&0xf0000000))
+    value |= 0x80000000;
+  if (value == 0x80000000) value = 0;
+#endif
+	__asm__ __volatile__("swap [%2], %0\n\t" :
+			     "=&r" (value) :
+			     "0" (value), "r" (addr));
+	return value;
 }
 
-static void msparc_write_physical(unsigned long paddr, unsigned long word)
-{
-	unsigned long flags;
+/* Functions really use this, not srmmu_swap directly. */
+#define srmmu_set_entry(ptr, newentry) \
+        srmmu_swap((unsigned long *) (ptr), (newentry))
 
-	save_flags(flags); cli();
-	__asm__ __volatile__("lda [%%g0] %3, %%g1\n\t"
-			     "or  %%g1, %4, %%g2\n\t"
-			     "sta %%g2, [%%g0] %3\n\t"
-			     "sta %0, [%1] %2\n\t"
-			     "sta %%g1, [%%g0] %3\n\t" : :
-			     "r" (word), "r" (paddr), "i" (ASI_M_BYPASS),
-			     "i" (ASI_M_MMUREGS), "r" (VIKING_ACENABLE) :
-			     "g1", "g2", "memory");
-	restore_flags(flags);
-}
 
+/* The very generic SRMMU page table operations. */
 static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); }
 static unsigned int srmmu_pgdir_align(unsigned int addr) { return SRMMU_PGDIR_ALIGN(addr); }
 
@@ -116,27 +139,19 @@
 }
 
 static unsigned long srmmu_pgd_page(pgd_t pgd)
-{ return PAGE_OFFSET + ((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
+{ return srmmu_p2v((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); }
 
 static unsigned long srmmu_pmd_page(pmd_t pmd)
-{ return PAGE_OFFSET + ((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
+{ return srmmu_p2v((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); }
 
 static unsigned long srmmu_pte_page(pte_t pte)
-{ return PAGE_OFFSET + ((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
+{ return srmmu_p2v((pte_val(pte) & SRMMU_PTE_PMASK) << 4); }
 
 static int srmmu_pte_none(pte_t pte)          { return !pte_val(pte); }
 static int srmmu_pte_present(pte_t pte)
 { return ((pte_val(pte) & SRMMU_ET_MASK) == SRMMU_ET_PTE); }
 
-static int srmmu_pte_inuse(pte_t *ptep)
-{ return mem_map[MAP_NR(ptep)].reserved || mem_map[MAP_NR(ptep)].count != 1; }
-
-static void srmmu_pte_clear(pte_t *ptep)      { pte_val(*ptep) = 0; }
-static void srmmu_pte_reuse(pte_t *ptep)
-{
-	if(!mem_map[MAP_NR(ptep)].reserved)
-		mem_map[MAP_NR(ptep)].count++;
-}
+static void srmmu_pte_clear(pte_t *ptep)      { set_pte(ptep, __pte(0)); }
 
 static int srmmu_pmd_none(pmd_t pmd)          { return !pmd_val(pmd); }
 static int srmmu_pmd_bad(pmd_t pmd)
@@ -145,15 +160,7 @@
 static int srmmu_pmd_present(pmd_t pmd)
 { return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
 
-static int srmmu_pmd_inuse(pmd_t *pmdp)
-{ return mem_map[MAP_NR(pmdp)].reserved || mem_map[MAP_NR(pmdp)].count != 1; }
-
-static void srmmu_pmd_clear(pmd_t *pmdp)      { pmd_val(*pmdp) = 0; }
-static void srmmu_pmd_reuse(pmd_t * pmdp)
-{
-	if (!mem_map[MAP_NR(pmdp)].reserved)
-		mem_map[MAP_NR(pmdp)].count++;
-}
+static void srmmu_pmd_clear(pmd_t *pmdp)      { set_pte((pte_t *)pmdp, __pte(0)); }
 
 static int srmmu_pgd_none(pgd_t pgd)          { return !pgd_val(pgd); }
 static int srmmu_pgd_bad(pgd_t pgd)
@@ -162,13 +169,7 @@
 static int srmmu_pgd_present(pgd_t pgd)
 { return ((pgd_val(pgd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); }
 
-static int srmmu_pgd_inuse(pgd_t *pgdp)       { return mem_map[MAP_NR(pgdp)].reserved; }
-static void srmmu_pgd_clear(pgd_t * pgdp)     { pgd_val(*pgdp) = 0; }
-static void srmmu_pgd_reuse(pgd_t *pgdp)
-{
-	if (!mem_map[MAP_NR(pgdp)].reserved)
-		mem_map[MAP_NR(pgdp)].count++;
-}
+static void srmmu_pgd_clear(pgd_t * pgdp)     { set_pte((pte_t *)pgdp, __pte(0)); }
 
 static int srmmu_pte_write(pte_t pte)         { return pte_val(pte) & SRMMU_WRITE; }
 static int srmmu_pte_dirty(pte_t pte)         { return pte_val(pte) & SRMMU_DIRTY; }
@@ -186,19 +187,29 @@
  * and a page entry and page directory to the page they refer to.
  */
 static pte_t srmmu_mk_pte(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = ((page - PAGE_OFFSET) >> 4) | pgprot_val(pgprot); return pte; }
+{ pte_t pte; pte_val(pte) = ((srmmu_v2p(page)) >> 4) | pgprot_val(pgprot); return pte; }
 
-static pte_t srmmu_mk_pte_io(unsigned long page, pgprot_t pgprot)
-{ pte_t pte; pte_val(pte) = ((page) >> 4) | pgprot_val(pgprot); return pte; }
+static pte_t srmmu_mk_pte_io(unsigned long page, pgprot_t pgprot, int space)
+{
+	pte_t pte;
+	pte_val(pte) = ((page) >> 4) | (space << 28) | pgprot_val(pgprot);
+	return pte;
+}
 
 static void srmmu_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
-{ srmmu_set_entry(ctxp, (SRMMU_ET_PTD | ((((unsigned long) pgdp) - PAGE_OFFSET) >> 4))); }
+{ 
+	srmmu_set_entry(ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
+}
 
 static void srmmu_pgd_set(pgd_t * pgdp, pmd_t * pmdp)
-{ srmmu_set_entry(pgdp, (SRMMU_ET_PTD | ((((unsigned long) pmdp) - PAGE_OFFSET) >> 4))); }
+{
+	srmmu_set_entry(pgdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pmdp) >> 4)));
+}
 
 static void srmmu_pmd_set(pmd_t * pmdp, pte_t * ptep)
-{ srmmu_set_entry(pmdp, (SRMMU_ET_PTD | ((((unsigned long) ptep) - PAGE_OFFSET) >> 4))); }
+{
+	srmmu_set_entry(pmdp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) ptep) >> 4)));
+}
 
 static pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot)
 { pte_val(pte) = (pte_val(pte) & ~0xff) | pgprot_val(newprot); return pte; }
@@ -212,22 +223,70 @@
 /* Find an entry in the second-level page table.. */
 static pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address)
 {
-	return (pmd_t *) pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+	return (pmd_t *) srmmu_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
 }
 
 /* Find an entry in the third-level page table.. */ 
 static pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address)
 {
-	return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+	return (pte_t *) srmmu_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
 }
 
 /* This must update the context table entry for this process. */
 static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
 	if(tsk->mm->context != NO_CONTEXT)
-		srmmu_ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+}
+
+static inline void srmmu_uncache_page(unsigned long addr)
+{
+	pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, addr);
+	pmd_t *pmdp = srmmu_pmd_offset(pgdp, addr);
+	pte_t *ptep = srmmu_pte_offset(pmdp, addr);
+
+	flush_cache_page_to_uncache(addr);
+	set_pte(ptep, __pte((pte_val(*ptep) & ~SRMMU_CACHE)));
+	flush_tlb_page_for_cbit(addr);
+}
+
+static inline void srmmu_recache_page(unsigned long addr)
+{
+	pgd_t *pgdp = srmmu_pgd_offset(init_task.mm, addr);
+	pmd_t *pmdp = srmmu_pmd_offset(pgdp, addr);
+	pte_t *ptep = srmmu_pte_offset(pmdp, addr);
+
+	set_pte(ptep, __pte((pte_val(*ptep) | SRMMU_CACHE)));
+	flush_tlb_page_for_cbit(addr);
+}
+
+static inline unsigned long srmmu_getpage(void)
+{
+	unsigned long page = get_free_page(GFP_KERNEL);
+
+	if (can_cache_ptables)
+		return page;
+
+	if(page)
+		srmmu_uncache_page(page);
+	return page;
 }
 
+static inline void srmmu_putpage(unsigned long page)
+{
+	if (!can_cache_ptables)
+		srmmu_recache_page(page);
+	free_page(page);
+}
+
+/* The easy versions. */
+#define NEW_PGD() (pgd_t *) srmmu_getpage()
+#define NEW_PMD() (pmd_t *) srmmu_getpage()
+#define NEW_PTE() (pte_t *) srmmu_getpage()
+#define FREE_PGD(chunk) srmmu_putpage((unsigned long)(chunk))
+#define FREE_PMD(chunk) srmmu_putpage((unsigned long)(chunk))
+#define FREE_PTE(chunk) srmmu_putpage((unsigned long)(chunk))
+
 /*
  * Allocate and free page tables. The xxx_kernel() versions are
  * used to allocate a kernel page table - this turns on ASN bits
@@ -235,29 +294,27 @@
  */
 static void srmmu_pte_free_kernel(pte_t *pte)
 {
-	mem_map[MAP_NR(pte)].reserved = 0;
-	free_page((unsigned long) pte);
+	FREE_PTE(pte);
 }
 
 static pte_t *srmmu_pte_alloc_kernel(pmd_t *pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
 	if(srmmu_pmd_none(*pmd)) {
-		pte_t *page = (pte_t *) get_free_page(GFP_KERNEL);
+		pte_t *page = NEW_PTE();
 		if(srmmu_pmd_none(*pmd)) {
 			if(page) {
-				srmmu_pmd_set(pmd, page);
-				mem_map[MAP_NR(page)].reserved = 1;
+				pmd_set(pmd, page);
 				return page + address;
 			}
-			srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+			pmd_set(pmd, BAD_PAGETABLE);
 			return NULL;
 		}
-		free_page((unsigned long) page);
+		FREE_PTE(page);
 	}
 	if(srmmu_pmd_bad(*pmd)) {
 		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+		pmd_set(pmd, BAD_PAGETABLE);
 		return NULL;
 	}
 	return (pte_t *) srmmu_pmd_page(*pmd) + address;
@@ -265,29 +322,27 @@
 
 static void srmmu_pmd_free_kernel(pmd_t *pmd)
 {
-	mem_map[MAP_NR(pmd)].reserved = 0;
-	free_page((unsigned long) pmd);
+	FREE_PMD(pmd);
 }
 
 static pmd_t *srmmu_pmd_alloc_kernel(pgd_t *pgd, unsigned long address)
 {
 	address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
 	if(srmmu_pgd_none(*pgd)) {
-		pmd_t *page = (pmd_t *) get_free_page(GFP_KERNEL);
+		pmd_t *page = NEW_PMD();
 		if(srmmu_pgd_none(*pgd)) {
 			if(page) {
-				srmmu_pgd_set(pgd, page);
-				mem_map[MAP_NR(page)].reserved = 1;
+				pgd_set(pgd, page);
 				return page + address;
 			}
-			srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+			pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
 			return NULL;
 		}
-		free_page((unsigned long) page);
+		FREE_PMD(page);
 	}
 	if(srmmu_pgd_bad(*pgd)) {
 		printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
-		srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+		pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
 		return NULL;
 	}
 	return (pmd_t *) pgd_page(*pgd) + address;
@@ -295,56 +350,56 @@
 
 static void srmmu_pte_free(pte_t *pte)
 {
-	free_page((unsigned long) pte);
+	FREE_PTE(pte);
 }
 
 static pte_t *srmmu_pte_alloc(pmd_t * pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1);
 	if(srmmu_pmd_none(*pmd)) {
-		pte_t *page = (pte_t *) get_free_page(GFP_KERNEL);
+		pte_t *page = NEW_PTE();
 		if(srmmu_pmd_none(*pmd)) {
 			if(page) {
-				srmmu_pmd_set(pmd, page);
+				pmd_set(pmd, page);
 				return page + address;
 			}
-			srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+			pmd_set(pmd, BAD_PAGETABLE);
 			return NULL;
 		}
-		free_page((unsigned long) page);
+		FREE_PTE(page);
 	}
 	if(srmmu_pmd_bad(*pmd)) {
 		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
-		srmmu_pmd_set(pmd, (pte_t *) BAD_PAGETABLE);
+		pmd_set(pmd, BAD_PAGETABLE);
 		return NULL;
 	}
-	return (pte_t *) pmd_page(*pmd) + address;
+	return ((pte_t *) srmmu_pmd_page(*pmd)) + address;
 }
 
 /* Real three-level page tables on SRMMU. */
 static void srmmu_pmd_free(pmd_t * pmd)
 {
-	free_page((unsigned long) pmd);
+	FREE_PMD(pmd);
 }
 
 static pmd_t *srmmu_pmd_alloc(pgd_t * pgd, unsigned long address)
 {
 	address = (address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1);
 	if(srmmu_pgd_none(*pgd)) {
-		pmd_t *page = (pmd_t *) get_free_page(GFP_KERNEL);
+		pmd_t *page = NEW_PMD();
 		if(srmmu_pgd_none(*pgd)) {
 			if(page) {
-				srmmu_pgd_set(pgd, page);
+				pgd_set(pgd, page);
 				return page + address;
 			}
-			srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+			pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
 			return NULL;
 		}
-		free_page((unsigned long) page);
+		FREE_PMD(page);
 	}
 	if(srmmu_pgd_bad(*pgd)) {
 		printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
-		srmmu_pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
+		pgd_set(pgd, (pmd_t *) BAD_PAGETABLE);
 		return NULL;
 	}
 	return (pmd_t *) srmmu_pgd_page(*pgd) + address;
@@ -352,395 +407,876 @@
 
 static void srmmu_pgd_free(pgd_t *pgd)
 {
-	free_page((unsigned long) pgd);
+	FREE_PGD(pgd);
 }
 
 static pgd_t *srmmu_pgd_alloc(void)
 {
-	return (pgd_t *) get_free_page(GFP_KERNEL);
+	return NEW_PGD();
 }
 
-/* Tsunami invalidates.  It's page level tlb invalidation is not very
- * useful at all, you must be in the context that page exists in to
- * get a match.  It might be worthwhile to try someday though...
+static void srmmu_set_pte(pte_t *ptep, pte_t pteval)
+{
+	srmmu_set_entry(ptep, pte_val(pteval));
+}
+
+static void srmmu_quick_kernel_fault(unsigned long address)
+{
+	printk("Penguin faults at address %08lx\n", address);
+	panic("Srmmu bolixed...");
+}
+
+static inline void alloc_context(struct mm_struct *mm)
+{
+	struct ctx_list *ctxp;
+
+	ctxp = ctx_free.next;
+	if(ctxp != &ctx_free) {
+		remove_from_ctx_list(ctxp);
+		add_to_used_ctxlist(ctxp);
+		mm->context = ctxp->ctx_number;
+		ctxp->ctx_mm = mm;
+		return;
+	}
+	ctxp = ctx_used.next;
+	if(ctxp->ctx_mm == current->mm)
+		ctxp = ctxp->next;
+	if(ctxp == &ctx_used)
+		panic("out of mmu contexts");
+	flush_cache_mm(ctxp->ctx_mm);
+	flush_tlb_mm(ctxp->ctx_mm);
+	remove_from_ctx_list(ctxp);
+	add_to_used_ctxlist(ctxp);
+	ctxp->ctx_mm->context = NO_CONTEXT;
+	ctxp->ctx_mm = mm;
+	mm->context = ctxp->ctx_number;
+}
+
+static void srmmu_switch_to_context(struct task_struct *tsk)
+{
+	/* Kernel threads can execute in any context and so can tasks
+	 * sleeping in the middle of exiting. If this task has already
+	 * been allocated a piece of the mmu realestate, just jump to
+	 * it.
+	 */
+	if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
+	   (tsk->flags & PF_EXITING))
+		return;
+	if(tsk->mm->context == NO_CONTEXT) {
+		alloc_context(tsk->mm);
+		ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+	}
+	srmmu_set_context(tsk->mm->context);
+}
+
+/* Low level IO area allocation on the SRMMU. */
+void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+{
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+	unsigned long tmp;
+
+	physaddr &= PAGE_MASK;
+	pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
+	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
+	ptep = srmmu_pte_offset(pmdp, virt_addr);
+	tmp = (physaddr >> 4) | SRMMU_ET_PTE;
+
+	/* I need to test whether this is consistant over all
+	 * sun4m's.  The bus_type represents the upper 4 bits of
+	 * 36-bit physical address on the I/O space lines...
+	 */
+	tmp |= (bus_type << 28);
+	if(rdonly)
+		tmp |= SRMMU_PRIV_RDONLY;
+	else
+		tmp |= SRMMU_PRIV;
+	flush_page_to_ram(virt_addr);
+	srmmu_set_entry(ptep, tmp);
+	flush_tlb_all();
+}
+
+static char *srmmu_lockarea(char *vaddr, unsigned long len)
+{
+	return vaddr;
+}
+
+static void srmmu_unlockarea(char *vaddr, unsigned long len)
+{
+}
+
+/* On the SRMMU we do not have the problems with limited tlb entries
+ * for mapping kernel pages, so we just take things from the free page
+ * pool.  As a side effect we are putting a little too much pressure
+ * on the gfp() subsystem.  This setup also makes the logic of the
+ * iommu mapping code a lot easier as we can transparently handle
+ * mappings on the kernel stack without any special code as we did
+ * need on the sun4c.
  */
-/* static */ inline void tsunami_invalidate_all(void)
+struct task_struct *srmmu_alloc_task_struct(void)
 {
-	tsunami_invalidate_icache();
-	tsunami_invalidate_dcache();
-	srmmu_flush_whole_tlb();
+	unsigned long page;
+
+	page = get_free_page(GFP_KERNEL);
+	if(!page)
+		return (struct task_struct *) 0;
+	return (struct task_struct *) page;
 }
-static void tsunami_invalidate_mm(struct mm_struct *mm)
+
+unsigned long srmmu_alloc_kernel_stack(struct task_struct *tsk)
 {
-	tsunami_invalidate_all();
+	unsigned long pages;
+
+	pages = __get_free_pages(GFP_KERNEL, 2, 0);
+	if(!pages)
+		return 0;
+	memset((void *) pages, 0, (PAGE_SIZE << 2));
+	return pages;
 }
 
-static void tsunami_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void srmmu_free_task_struct(struct task_struct *tsk)
 {
-	tsunami_invalidate_all();
+	free_page((unsigned long) tsk);
 }
 
-/* XXX do page level tlb flushes at some point XXX */
-static void tsunami_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+static void srmmu_free_kernel_stack(unsigned long stack)
 {
-	tsunami_invalidate_all();
+	free_pages(stack, 2);
 }
 
-/* Swift invalidates.  It has the recommended SRMMU specification flushing
- * facilities, so we can do things in a more fine grained fashion than we
- * could on the tsunami.  Let's watch out for HARDWARE BUGS...
+/* Tsunami flushes.  It's page level tlb invalidation is not very
+ * useful at all, you must be in the context that page exists in to
+ * get a match.
  */
-static inline void swift_invalidate_all(void)
+static void tsunami_flush_cache_all(void)
 {
-	unsigned long addr = 0;
+	flush_user_windows();
+	tsunami_flush_icache();
+	tsunami_flush_dcache();
+}
 
-	/* Invalidate all cache tags */
-	for(addr = 0; addr < (PAGE_SIZE << 2); addr += 16) {
-		swift_inv_insn_tag(addr); /* whiz- */
-		swift_inv_data_tag(addr); /* bang */
+static void tsunami_flush_cache_mm(struct mm_struct *mm)
+{
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		tsunami_flush_icache();
+		tsunami_flush_dcache();
+#ifndef __SMP__
 	}
+#endif
+}
+
+static void tsunami_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		tsunami_flush_icache();
+		tsunami_flush_dcache();
+#ifndef __SMP__
+	}
+#endif
+}
+
+static void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+	struct mm_struct *mm = vma->vm_mm;
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		tsunami_flush_icache();
+		tsunami_flush_dcache();
+#ifndef __SMP__
+	}
+#endif
+}
+
+static void tsunami_flush_cache_page_to_uncache(unsigned long page)
+{
+	tsunami_flush_dcache();
+}
+
+/* Tsunami does not have a Copy-back style virtual cache. */
+static void tsunami_flush_page_to_ram(unsigned long page)
+{
+}
+
+/* However, Tsunami is not IO coherent. */
+static void tsunami_flush_page_for_dma(unsigned long page)
+{
+	tsunami_flush_dcache();
+}
+
+/* TLB flushes seem to upset the tsunami sometimes, I can't figure out
+ * what the hell is going on.  All I see is a tlb flush (page or whole,
+ * there is no consistant pattern) and then total local variable corruption
+ * in the procedure who called us after return.  Usually triggerable
+ * by "cool" programs like crashme and bonnie.  I played around a bit
+ * and adding a bunch of forced nops seems to make the problems all
+ * go away. (missed instruction fetches possibly? ugh...)
+ */
+#define TSUNAMI_SUCKS do { nop(); nop(); nop(); nop(); nop(); \
+	                   nop(); nop(); nop(); nop(); nop(); } while(0)
+
+static void tsunami_flush_tlb_all(void)
+{
+	module_stats.invall++;
 	srmmu_flush_whole_tlb();
+	TSUNAMI_SUCKS;
 }
 
-static void swift_invalidate_mm(struct mm_struct *mm)
+static void tsunami_flush_tlb_mm(struct mm_struct *mm)
 {
-	unsigned long flags;
-	int cc, ncc = mm->context;
+	module_stats.invmm++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		srmmu_flush_whole_tlb();
+		TSUNAMI_SUCKS;
+#ifndef __SMP__
+        }
+#endif
+}
 
-	if(ncc == NO_CONTEXT)
-		return;
+static void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	module_stats.invrnge++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		srmmu_flush_whole_tlb();
+		TSUNAMI_SUCKS;
+#ifndef __SMP__
+	}
+#endif
+}
 
-	/* have context will travel... */
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+static void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	int octx;
+	struct mm_struct *mm = vma->vm_mm;
 
-	swift_flush_context(); /* POOF! */
-	srmmu_flush_tlb_ctx(); /* POW! */
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_page(page);
+		TSUNAMI_SUCKS;
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+	module_stats.invpg++;
 }
 
-static void swift_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void tsunami_flush_tlb_page_for_cbit(unsigned long page)
 {
-	unsigned long flags, addr;
-	int cc, ncc = mm->context;
+	srmmu_flush_tlb_page(page);
+}
 
-	if(ncc == NO_CONTEXT)
-		return;
+/* Swift flushes.  It has the recommended SRMMU specification flushing
+ * facilities, so we can do things in a more fine grained fashion than we
+ * could on the tsunami.  Let's watch out for HARDWARE BUGS...
+ */
+
+static void swift_flush_cache_all(void)
+{
+	flush_user_windows();
+	swift_idflash_clear();
+}
+
+static void swift_flush_cache_mm(struct mm_struct *mm)
+{
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		swift_idflash_clear();
+#ifndef __SMP__
+	}
+#endif
+}
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+static void swift_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		swift_idflash_clear();
+#ifndef __SMP__
+	}
+#endif
+}
 
-	/* XXX Inefficient, we don't do the best we can... XXX */
-	addr = start & SRMMU_PGDIR_MASK;
-	while(addr < end) {
-		swift_flush_region(addr);
-		srmmu_flush_tlb_region(addr);
-		addr += SRMMU_PGDIR_SIZE;
+static void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+	struct mm_struct *mm = vma->vm_mm;
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		if(vma->vm_flags & VM_EXEC)
+			swift_flush_icache();
+		swift_flush_dcache();
+#ifndef __SMP__
 	}
+#endif
+}
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+/* Not copy-back on swift. */
+static void swift_flush_page_to_ram(unsigned long page)
+{
 }
 
-static void swift_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+/* But not IO coherent either. */
+static void swift_flush_page_for_dma(unsigned long page)
 {
-	unsigned long flags;
-	int cc, ncc = vmp->vm_mm->context;
+	swift_flush_dcache();
+}
 
-	if(ncc == NO_CONTEXT)
-		return;
+static void swift_flush_cache_page_to_uncache(unsigned long page)
+{
+	swift_flush_dcache();
+}
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+static void swift_flush_tlb_all(void)
+{
+	module_stats.invall++;
+	srmmu_flush_whole_tlb();
+}
 
-	swift_flush_page(page);
-	srmmu_flush_tlb_page(page);
+static void swift_flush_tlb_mm(struct mm_struct *mm)
+{
+	module_stats.invmm++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT)
+#endif
+		srmmu_flush_whole_tlb();
+}
+
+static void swift_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	module_stats.invrnge++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT)
+#endif
+		srmmu_flush_whole_tlb();
+}
+
+static void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+	struct mm_struct *mm = vma->vm_mm;
+	if(mm->context != NO_CONTEXT)
+#endif
+		srmmu_flush_whole_tlb();
+	module_stats.invpg++;
+}
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+static void swift_flush_tlb_page_for_cbit(unsigned long page)
+{
+	srmmu_flush_whole_tlb();
 }
 
 /* The following are all MBUS based SRMMU modules, and therefore could
- * be found in a multiprocessor configuration.
+ * be found in a multiprocessor configuration.  On the whole, these
+ * chips seems to be much more touchy about DVMA and page tables
+ * with respect to cache coherency.
  */
 
-/* Viking invalidates.  For Sun's mainline MBUS processor it is pretty much
+/* Viking flushes.  For Sun's mainline MBUS processor it is pretty much
  * a crappy mmu.  The on-chip I&D caches only have full flushes, no fine
  * grained cache invalidations.  It only has these "flash clear" things
  * just like the MicroSparcI.  Added to this many revs of the chip are
- * teaming with hardware buggery.
- *
- * XXX need to handle SMP broadcast invalidations! XXX
+ * teaming with hardware buggery.  Someday maybe we'll do direct
+ * diagnostic tag accesses for page level flushes as those should
+ * be painless and will increase performance due to the frequency of
+ * page level flushes. This is a must to _really_ flush the caches,
+ * crazy hardware ;-)
  */
-static inline void viking_invalidate_all(void)
+
+static void viking_flush_cache_all(void)
 {
 	viking_flush_icache();
-	viking_flush_dcache();
-	srmmu_flush_whole_tlb();
 }
-static void viking_invalidate_mm(struct mm_struct *mm)
+
+static void viking_flush_cache_mm(struct mm_struct *mm)
 {
-	unsigned long flags;
-	int cc, ncc = mm->context;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		viking_flush_icache();
+#ifndef __SMP__
+	}
+#endif
+}
 
-	if(ncc == NO_CONTEXT)
-		return;
+static void viking_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		viking_flush_icache();
+#ifndef __SMP__
+	}
+#endif
+}
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+static void viking_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
+{
+#ifndef __SMP__
+	struct mm_struct *mm = vma->vm_mm;
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		if(vma->vm_flags & VM_EXEC)
+			viking_flush_icache();
+#ifndef __SMP__
+	}
+#endif
+}
 
-	viking_flush_icache();
-	viking_flush_dcache();
-	srmmu_flush_tlb_ctx();
+/* Non-mxcc vikings are copy-back but are pure-physical so no flushing. */
+static void viking_flush_page_to_ram(unsigned long page)
+{
+}
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+/* Viking is IO cache coherent. */
+static void viking_flush_page_for_dma(unsigned long page)
+{
 }
 
-static void viking_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+static void viking_mxcc_flush_page(unsigned long page)
 {
-	unsigned long flags, addr;
-	int cc, ncc = mm->context;
+	unsigned long ppage = srmmu_hwprobe(page);
+	unsigned long paddr0, paddr1;
 
-	if(ncc == NO_CONTEXT)
+	if (!ppage)
 		return;
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
-
-	/* XXX Inefficient, we don't do the best we can... XXX */
-	viking_flush_icache();
-	viking_flush_dcache();
-	addr = start & SRMMU_PGDIR_MASK;
-	while(addr < end) {
-		srmmu_flush_tlb_region(addr);
-		addr += SRMMU_PGDIR_SIZE;
-	}
+	paddr0 = (ppage >> 28) | 0x10;		/* Set cacheable bit. */
+	paddr1 = (ppage << 4) & PAGE_MASK;
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+	/* Read the page's data through the stream registers,
+	 * and write it back to memory. This will issue
+	 * coherent write invalidates to all other caches, thus
+         * should also be sufficient in an MP system.
+	 */
+	__asm__ __volatile__ ("or %%g0, %0, %%g2\n\t"
+			      "or %%g0, %1, %%g3\n"
+			      "1:\n\t"
+			      "stda %%g2, [%2] %5\n\t"
+			      "stda %%g2, [%3] %5\n\t"
+			      "add %%g3, %4, %%g3\n\t"
+			      "btst 0xfff, %%g3\n\t"
+			      "bne 1b\n\t"
+			      "nop\n\t" : :
+			      "r" (paddr0), "r" (paddr1),
+			      "r" (MXCC_SRCSTREAM),
+			      "r" (MXCC_DESSTREAM),
+			      "r" (MXCC_STREAM_SIZE),
+			      "i" (ASI_M_MXCC) : "g2", "g3");
+
+	/* This was handcoded after a look at the gcc output from
+	 *
+	 *	do {
+	 *		mxcc_set_stream_src(paddr);
+	 *		mxcc_set_stream_dst(paddr);
+	 *		paddr[1] += MXCC_STREAM_SIZE;
+	 *	} while (paddr[1] & ~PAGE_MASK);
+	 */
 }
-static void viking_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+
+static void viking_no_mxcc_flush_page(unsigned long page)
 {
-	unsigned long flags;
-	int cc, ncc = vmp->vm_mm->context;
+	unsigned long ppage = srmmu_hwprobe(page) >> 8;
+	int set, block;
+	unsigned long ptag[2];
+	unsigned long vaddr;
+	int i;
 
-	if(ncc == NO_CONTEXT)
+	if (!ppage)
 		return;
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+	for (set = 0; set < 128; set++) {
+		for (block = 0; block < 4; block++) {
 
-	viking_flush_icache();
-	viking_flush_dcache();
-	srmmu_flush_tlb_page(page);
+			viking_get_dcache_ptag(set, block, ptag);
+
+			if (ptag[1] != ppage)
+				continue;
+			if (!(ptag[0] & VIKING_PTAG_VALID))
+				continue;
+			if (!(ptag[0] & VIKING_PTAG_DIRTY))
+				continue;
+
+			/* There was a great cache from TI
+			 * with comfort as much as vi,
+			 * 4 pages to flush,
+			 * 4 pages, no rush,
+			 * since anything else makes him die.
+			 */
+			vaddr = (KERNBASE + PAGE_SIZE) | (set << 5);
+			for (i = 0; i < 8; i++) {
+				__asm__ __volatile__ ("ld [%0], %%g2\n\t" : :
+						      "r" (vaddr) : "g2");
+				vaddr += PAGE_SIZE;
+			}
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+			/* Continue with next set. */
+			break;
+		}
+	}
 }
 
-/* Cypress invalidates. */
-static inline void cypress_invalidate_all(void)
+static void viking_flush_tlb_all(void)
 {
+	module_stats.invall++;
 	srmmu_flush_whole_tlb();
 }
-static void cypress_invalidate_mm(struct mm_struct *mm)
+
+static void viking_flush_tlb_mm(struct mm_struct *mm)
 {
-	unsigned long flags;
-	int cc, ncc = mm->context;
+	int octx;
+	module_stats.invmm++;
 
-	if(ncc == NO_CONTEXT)
-		return;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_ctx();
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+}
 
-	/* have context will travel... */
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+static void viking_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+	int octx;
+	module_stats.invrnge++;
 
-	cypress_flush_context(); /* POOF! */
-	srmmu_flush_whole_tlb(); /* POW! */
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		start &= SRMMU_PMD_MASK;
+		while(start < end) {
+			srmmu_flush_tlb_segment(start);
+			start += SRMMU_PMD_SIZE;
+		}
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+}
+
+static void viking_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	int octx;
+	struct mm_struct *mm = vma->vm_mm;
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+	module_stats.invpg++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_page(page);
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
 }
-static void cypress_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+
+static void viking_flush_tlb_page_for_cbit(unsigned long page)
 {
-	unsigned long flags, addr;
-	int cc, ncc = mm->context;
+	srmmu_flush_tlb_page(page);
+}
 
-	if(ncc == NO_CONTEXT)
-		return;
+/* Cypress flushes. */
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
-
-	/* XXX Inefficient, we don't do the best we can... XXX */
-	addr = start & SRMMU_PGDIR_MASK;
-	while(addr < end) {
-		cypress_flush_region(addr);
-		addr += SRMMU_PGDIR_SIZE;
-	}
+static void cypress_flush_tlb_all(void)
+{
+	module_stats.invall++;
 	srmmu_flush_whole_tlb();
+}
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+static void cypress_flush_tlb_mm(struct mm_struct *mm)
+{
+	int octx;
+
+	module_stats.invmm++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_ctx();
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
 }
 
-static void cypress_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+static void cypress_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	unsigned long flags;
-	int cc, ncc = vmp->vm_mm->context;
+	int octx;
 
-	if(ncc == NO_CONTEXT)
-		return;
+	module_stats.invrnge++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		start &= SRMMU_PMD_MASK;
+		while(start < end) {
+			srmmu_flush_tlb_segment(start);
+			start += SRMMU_PMD_SIZE;
+		}
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+}
 
-	save_flags(flags); cli();
-	cc = srmmu_get_context();
-	if(cc != ncc)
-		srmmu_set_context(ncc);
+static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	int octx;
+	struct mm_struct *mm = vma->vm_mm;
 
-	swift_flush_page(page);
-	srmmu_flush_whole_tlb();
+	module_stats.invpg++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_page(page);
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
+}
 
-	if(cc != ncc)
-		srmmu_set_context(cc);
-	restore_flags(flags);
+/* Hypersparc flushes.  Very nice chip... */
+static void hypersparc_flush_cache_all(void)
+{
+	flush_user_windows();
+	hyper_flush_unconditional_combined();
+	hyper_flush_whole_icache();
 }
 
-/* Hypersparc invalidates. */
-static inline void hypersparc_invalidate_all(void)
+static void hypersparc_flush_cache_mm(struct mm_struct *mm)
 {
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		hyper_flush_unconditional_combined();
+		hyper_flush_whole_icache();
+#ifndef __SMP__
+	}
+#endif
+}
 
-	hyper_flush_whole_icache();
-	srmmu_flush_whole_tlb();
+static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		flush_user_windows();
+		hyper_flush_unconditional_combined();
+		hyper_flush_whole_icache();
+#ifndef __SMP__
+	}
+#endif
 }
 
-static void hypersparc_invalidate_mm(struct mm_struct *mm)
+/* HyperSparc requires a valid mapping where we are about to flush
+ * in order to check for a physical tag match during the flush.
+ */
+static void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
 {
+	struct mm_struct *mm = vma->vm_mm;
+	volatile unsigned long clear;
+	int octx;
 
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+		octx = srmmu_get_context();
+		flush_user_windows();
+		srmmu_set_context(mm->context);
+		hyper_flush_whole_icache();
+		if(!srmmu_hwprobe(page))
+			goto no_mapping;
+		hyper_flush_cache_page(page);
+	no_mapping:
+		clear = srmmu_get_fstatus();
+		srmmu_set_context(octx);
+#ifndef __SMP__
+	}
+#endif
 }
 
-static void hypersparc_invalidate_range(struct mm_struct *mm, unsigned long start, unsigned long end)
+/* HyperSparc is copy-back. */
+static void hypersparc_flush_page_to_ram(unsigned long page)
 {
+	volatile unsigned long clear;
 
+	if(srmmu_hwprobe(page))
+		hyper_flush_cache_page(page);
+	clear = srmmu_get_fstatus();
 }
 
-static void hypersparc_invalidate_page(struct vm_area_struct *vmp, unsigned long page)
+/* HyperSparc is IO cache coherent. */
+static void hypersparc_flush_page_for_dma(unsigned long page)
 {
+	volatile unsigned long clear;
 
+	if(srmmu_hwprobe(page))
+		hyper_flush_cache_page(page);
+	clear = srmmu_get_fstatus();
 }
 
-static void srmmu_set_pte(pte_t *ptep, pte_t pteval)
+static void hypersparc_flush_cache_page_to_uncache(unsigned long page)
 {
-	srmmu_set_entry(ptep, pte_val(pteval));
+	volatile unsigned long clear;
+
+	if(srmmu_hwprobe(page))
+		hyper_flush_cache_page(page);
+	clear = srmmu_get_fstatus();
 }
 
-static void srmmu_quick_kernel_fault(unsigned long address)
+static void hypersparc_flush_tlb_all(void)
 {
-	printk("SRMMU: quick_kernel_fault called for %08lx\n", address);
-	panic("Srmmu bolixed...");
+	module_stats.invall++;
+	srmmu_flush_whole_tlb();
 }
 
-static inline void alloc_context(struct mm_struct *mm)
+static void hypersparc_flush_tlb_mm(struct mm_struct *mm)
 {
-	struct ctx_list *ctxp;
+	int octx;
 
-	ctxp = ctx_free.next;
-	if(ctxp != &ctx_free) {
-		remove_from_ctx_list(ctxp);
-		add_to_used_ctxlist(ctxp);
-		mm->context = ctxp->ctx_number;
-		ctxp->ctx_mm = mm;
-		return;
+	module_stats.invmm++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_ctx();
+		srmmu_set_context(octx);
+
+#ifndef __SMP__
 	}
-	ctxp = ctx_used.next;
-	if(ctxp->ctx_mm == current->mm)
-		ctxp = ctxp->next;
-	if(ctxp == &ctx_used)
-		panic("out of mmu contexts");
-	remove_from_ctx_list(ctxp);
-	add_to_used_ctxlist(ctxp);
-	ctxp->ctx_mm->context = NO_CONTEXT;
-	ctxp->ctx_mm = mm;
-	mm->context = ctxp->ctx_number;
+#endif
 }
 
-static void srmmu_switch_to_context(struct task_struct *tsk)
+static void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end)
 {
-	/* Kernel threads can execute in any context and so can tasks
-	 * sleeping in the middle of exiting. If this task has already
-	 * been allocated a piece of the mmu realestate, just jump to
-	 * it.
-	 */
-	if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
-	   (tsk->flags & PF_EXITING))
-		return;
-	if(tsk->mm->context == NO_CONTEXT) {
-		alloc_context(tsk->mm);
-		srmmu_ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+	int octx;
+
+	module_stats.invrnge++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
+
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		start &= SRMMU_PMD_MASK;
+		while(start < end) {
+			srmmu_flush_tlb_segment(start);
+			start += SRMMU_PMD_SIZE;
+		}
+		srmmu_set_context(octx);
+
+#ifndef __SMP__
 	}
-	srmmu_set_context(tsk->mm->context);
+#endif
 }
 
-/* Low level IO area allocation on the SRMMU. */
-void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
+static void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 {
-	pgd_t *pgdp;
-	pmd_t *pmdp;
-	pte_t *ptep;
-	unsigned long tmp;
+	struct mm_struct *mm = vma->vm_mm;
+	int octx;
 
-	physaddr &= PAGE_MASK;
-	pgdp = srmmu_pgd_offset(init_task.mm, virt_addr);
-	pmdp = srmmu_pmd_offset(pgdp, virt_addr);
-	ptep = srmmu_pte_offset(pmdp, virt_addr);
-	tmp = (physaddr >> 4) | SRMMU_ET_PTE;
+	module_stats.invpg++;
+#ifndef __SMP__
+	if(mm->context != NO_CONTEXT) {
+#endif
 
-	/* I need to test whether this is consistent over all
-	 * sun4m's.  The bus_type represents the upper 4 bits of
-	 * 36-bit physical address on the I/O space lines...
-	 */
-	tmp |= (bus_type << 28);
-	if(rdonly)
-		tmp |= SRMMU_PRIV_RDONLY;
-	else
-		tmp |= SRMMU_PRIV;
-	srmmu_set_entry(ptep, tmp);
-	invalidate_all();
+		octx = srmmu_get_context();
+		srmmu_set_context(mm->context);
+		srmmu_flush_tlb_page(page);
+		srmmu_set_context(octx);
+
+#ifndef __SMP__
+	}
+#endif
 }
 
-static char *srmmu_lockarea(char *vaddr, unsigned long len)
+static void hypersparc_flush_tlb_page_for_cbit(unsigned long page)
 {
-	return vaddr;
+	srmmu_flush_tlb_page(page);
 }
 
-static void srmmu_unlockarea(char *vaddr, unsigned long len)
+static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
+{
+	hyper_flush_whole_icache();
+	srmmu_set_entry(ctxp, (SRMMU_ET_PTD | (srmmu_v2p((unsigned long) pgdp) >> 4)));
+}
+
+static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) 
 {
+	if(tsk->mm->context != NO_CONTEXT) {
+		hyper_flush_whole_icache();
+		ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp);
+	}
+}
+
+static void hypersparc_set_pte(pte_t *ptep, pte_t pteval)
+{
+	/* xor is your friend */
+	__asm__ __volatile__("rd	%%psr, %%g1\n\t"
+			     "wr	%%g1, %4, %%psr\n\t"
+			     "nop; nop; nop;\n\t"
+			     "swap	[%0], %1\n\t"
+			     "wr	%%g1, 0x0, %%psr\n\t"
+			     "nop; nop; nop;\n\t" :
+			     "=r" (ptep), "=r" (pteval) :
+			     "0" (ptep), "1" (pteval), "i" (PSR_ET) :
+			     "g1");
+}
+
+static void hypersparc_switch_to_context(struct task_struct *tsk)
+{
+	/* Kernel threads can execute in any context and so can tasks
+	 * sleeping in the middle of exiting. If this task has already
+	 * been allocated a piece of the mmu realestate, just jump to
+	 * it.
+	 */
+	hyper_flush_whole_icache();
+	if((tsk->tss.flags & SPARC_FLAG_KTHREAD) ||
+	   (tsk->flags & PF_EXITING))
+		return;
+	if(tsk->mm->context == NO_CONTEXT) {
+		alloc_context(tsk->mm);
+		ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd);
+	}
+	srmmu_set_context(tsk->mm->context);
 }
 
 /* IOMMU things go here. */
@@ -748,6 +1284,9 @@
 #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
 static unsigned long first_dvma_page, last_dvma_page;
 
+#define IOPERM        (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
+#define MKIOPTE(phys) (((((phys)>>4) & IOPTE_PAGE) | IOPERM) & ~IOPTE_WAZ)
+
 static inline void srmmu_map_dvma_pages_for_iommu(struct iommu_struct *iommu)
 {
 	unsigned long first = first_dvma_page;
@@ -757,8 +1296,7 @@
 	iopte = iommu->page_table;
 	iopte += ((DVMA_VADDR - iommu->start) >> PAGE_SHIFT);
 	while(first <= last) {
-		iopte_val(*iopte++) = ((((first - PAGE_OFFSET) >> 4) & IOPTE_PAGE) |
-				       (IOPTE_WRITE | IOPTE_VALID)) & ~(IOPTE_WAZ);
+		iopte_val(*iopte++) = MKIOPTE(srmmu_v2p(first));
 		first += PAGE_SIZE;
 	}
 }
@@ -803,7 +1341,7 @@
 	tmp |= (IOMMU_RNGE_64MB | IOMMU_CTRL_ENAB);
 	iommu->regs->control = tmp;
 	iommu_invalidate(iommu->regs);
-	iommu->start = 0xfc000000;
+	iommu->plow = iommu->start = 0xfc000000;
 	iommu->end = 0xffffffff;
 
 	/* Allocate IOMMU page table */
@@ -813,16 +1351,17 @@
 	/* Stupid alignment constraints give me a headache. */
 	memory_start = PAGE_ALIGN(memory_start);
 	memory_start = (((memory_start) + (ptsize - 1)) & ~(ptsize - 1));
-	iommu->page_table = (iopte_t *) memory_start;
+	iommu->lowest = iommu->page_table = (iopte_t *) memory_start;
 	memory_start += ptsize;
 
 	/* Initialize new table. */
+	flush_cache_all();
+	srmmu_uncache_iommu_page_table((unsigned long) iommu->page_table, ptsize);
+	flush_tlb_all();
 	memset(iommu->page_table, 0, ptsize);
 	srmmu_map_dvma_pages_for_iommu(iommu);
-	iommu->regs->base = (((unsigned long) iommu->page_table) - PAGE_OFFSET) >> 4;
-	srmmu_uncache_iommu_page_table((unsigned long) iommu->page_table, ptsize);
+	iommu->regs->base = srmmu_v2p((unsigned long) iommu->page_table) >> 4;
 	iommu_invalidate(iommu->regs);
-	invalidate_all();
 
 	sbus->iommu = iommu;
 	printk("IOMMU: impl %d vers %d page table at %p of size %d bytes\n",
@@ -830,41 +1369,68 @@
 	return memory_start;
 }
 
-
-static char *srmmu_get_scsi_buffer(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+static char *srmmu_get_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
 {
 	struct iommu_struct *iommu = sbus->iommu;
 	unsigned long page = (unsigned long) vaddr;
 	unsigned long start, end, offset;
 	iopte_t *iopte;
 
-	if(len > PAGE_SIZE)
-		panic("Can only handle page sized iommu mappings.");
 	offset = page & ~PAGE_MASK;
 	page &= PAGE_MASK;
 
-	start = iommu->start;
+	start = iommu->plow;
 	end = KADB_DEBUGGER_BEGVM; /* Don't step on kadb/prom. */
-	iopte = iommu->page_table;
+	iopte = iommu->lowest;
 	while(start < end) {
 		if(!(iopte_val(*iopte) & IOPTE_VALID))
 			break;
 		iopte++;
 		start += PAGE_SIZE;
 	}
-	if(start == KADB_DEBUGGER_BEGVM)
-		panic("Could not find free iommu entry in get_scsi_buffer.");
 
+	flush_page_for_dma(page);
 	vaddr = (char *) (start | offset);
-	iopte_val(*iopte) = ((((page - PAGE_OFFSET) >> 4) & IOPTE_PAGE) |
-		(IOPTE_WRITE | IOPTE_VALID)) & ~(IOPTE_WAZ);
-	iommu_invalidate(iommu->regs);
-	invalidate_all();
+	iopte_val(*iopte) = MKIOPTE(srmmu_v2p(page));
+	iommu_invalidate_page(iommu->regs, start);
+	iommu->lowest = iopte + 1;
+	iommu->plow = start + PAGE_SIZE;
 
 	return vaddr;
 }
 
-static void srmmu_release_scsi_buffer(char *vaddr, unsigned long len, struct linux_sbus *sbus)
+static void srmmu_get_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
+{
+	struct iommu_struct *iommu = sbus->iommu;
+        unsigned long page, start, end, offset;
+	iopte_t *iopte = iommu->lowest;
+
+	start = iommu->plow;
+	end = KADB_DEBUGGER_BEGVM;
+	while(sz >= 0) {
+		page = ((unsigned long) sg[sz].addr) & PAGE_MASK;
+		offset = ((unsigned long) sg[sz].addr) & ~PAGE_MASK;
+		while(start < end) {
+			if(!(iopte_val(*iopte) & IOPTE_VALID))
+				break;
+			iopte++;
+			start += PAGE_SIZE;
+		}
+		if(start == KADB_DEBUGGER_BEGVM)
+			panic("Wheee, iomapping overflow.");
+		flush_page_for_dma(page);
+		sg[sz].alt_addr = (char *) (start | offset);
+		iopte_val(*iopte) = MKIOPTE(srmmu_v2p(page));
+		iommu_invalidate_page(iommu->regs, start);
+		iopte++;
+		start += PAGE_SIZE;
+		sz--;
+	}
+	iommu->lowest = iopte;
+	iommu->plow = start;
+}
+
+static void srmmu_release_scsi_one(char *vaddr, unsigned long len, struct linux_sbus *sbus)
 {
 	struct iommu_struct *iommu = sbus->iommu;
 	unsigned long page = (unsigned long) vaddr;
@@ -875,61 +1441,89 @@
 	page &= PAGE_MASK;
 	iopte = iommu->page_table + ((page - iommu->start) >> PAGE_SHIFT);
 	iopte_val(*iopte) = 0;
-	iommu_invalidate(iommu->regs);
-	invalidate_all();
+	iommu_invalidate_page(iommu->regs, page);
+	if(iopte < iommu->lowest) {
+		iommu->lowest = iopte;
+		iommu->plow = page;
+	}
 }
 
-/* On the SRMMU we do not have the problems with limited tlb entries
- * for mapping kernel pages, so we just take things from the free page
- * pool.  As a side effect we are putting a little too much pressure
- * on the gfp() subsystem and we don't catch stack overflow like we
- * did on the sun4c with virtual kstack mappings.  This setup also
- * makes the logic of the iommu mapping code a lot easier as we can
- * transparently handle mappings on the kernel stack without any
- * special code as we did need on the sun4c.
- */
-struct task_struct *srmmu_alloc_task_struct(void)
+static void srmmu_release_scsi_sgl(struct mmu_sglist *sg, int sz, struct linux_sbus *sbus)
 {
+	struct iommu_struct *iommu = sbus->iommu;
 	unsigned long page;
+	iopte_t *iopte;
 
-	page = get_free_page(GFP_KERNEL);
-	if(!page)
-		return (struct task_struct *) 0;
-	return (struct task_struct *) page;
+	while(sz >= 0) {
+		page = ((unsigned long)sg[sz].alt_addr) & PAGE_MASK;
+		iopte = iommu->page_table + ((page - iommu->start) >> PAGE_SHIFT);
+		iopte_val(*iopte) = 0;
+		iommu_invalidate_page(iommu->regs, page);
+		if(iopte < iommu->lowest) {
+			iommu->lowest = iopte;
+			iommu->plow = page;
+		}
+		sg[sz].alt_addr = 0;
+		sz--;
+	}
 }
 
-unsigned long srmmu_alloc_kernel_stack(struct task_struct *tsk)
+static unsigned long mempool;
+
+/* NOTE: All of this startup code assumes the low 16mb (approx.) of
+ *       kernel mappings are done with one single contiguous chunk of
+ *       ram.  On small ram machines (classics mainly) we only get
+ *       around 8mb mapped for us.
+ */
+
+static unsigned long kbpage;
+
+/* Some dirty hacks to abstract away the painful boot up init. */
+static inline unsigned long srmmu_early_paddr(unsigned long vaddr)
 {
-	unsigned long pages;
+	return ((vaddr - PAGE_OFFSET) + kbpage);
+}
 
-	pages = __get_free_pages(GFP_KERNEL, 1, 0);
-	if(!pages)
-		return 0;
-	memset((void *) pages, 0, (PAGE_SIZE << 1));
-	return pages;
+static inline void srmmu_early_pgd_set(pgd_t *pgdp, pmd_t *pmdp)
+{
+	srmmu_set_entry(pgdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) pmdp) >> 4)));
 }
 
-static void srmmu_free_task_struct(struct task_struct *tsk)
+static inline void srmmu_early_pmd_set(pmd_t *pmdp, pte_t *ptep)
 {
-	free_page((unsigned long) tsk);
+	srmmu_set_entry(pmdp, (SRMMU_ET_PTD | (srmmu_early_paddr((unsigned long) ptep) >> 4)));
 }
 
-static void srmmu_free_kernel_stack(unsigned long stack)
+static inline unsigned long srmmu_early_pgd_page(pgd_t pgd)
 {
-	free_pages(stack, 1);
+	return (((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4) - kbpage) + PAGE_OFFSET;
 }
 
-static unsigned long mempool;
+static inline unsigned long srmmu_early_pmd_page(pmd_t pmd)
+{
+	return (((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4) - kbpage) + PAGE_OFFSET;
+}
+
+static inline pmd_t *srmmu_early_pmd_offset(pgd_t *dir, unsigned long address)
+{
+	return (pmd_t *) srmmu_early_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1));
+}
+
+static inline pte_t *srmmu_early_pte_offset(pmd_t *dir, unsigned long address)
+{
+	return (pte_t *) srmmu_early_pmd_page(*dir) + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1));
+}
 
 /* Allocate a block of RAM which is aligned to its size.
  * This procedure can be used until the call to mem_init().
  */
-static void *srmmu_init_alloc(unsigned long *kbrk, unsigned size)
+static void *srmmu_init_alloc(unsigned long *kbrk, unsigned long size)
 {
-	register unsigned mask = size - 1;
-	register unsigned long ret;
+	unsigned long mask = size - 1;
+	unsigned long ret;
 
-	if(size==0) return 0x0;
+	if(!size)
+		return 0x0;
 	if(size & mask) {
 		prom_printf("panic: srmmu_init_alloc botch\n");
 		prom_halt();
@@ -950,12 +1544,12 @@
 		pgdp = srmmu_pgd_offset(init_task.mm, start);
 		if(srmmu_pgd_none(*pgdp)) {
 			pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
-			srmmu_pgd_set(pgdp, pmdp);
+			srmmu_early_pgd_set(pgdp, pmdp);
 		}
-		pmdp = srmmu_pmd_offset(pgdp, start);
+		pmdp = srmmu_early_pmd_offset(pgdp, start);
 		if(srmmu_pmd_none(*pmdp)) {
 			ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
-			srmmu_pmd_set(pmdp, ptep);
+			srmmu_early_pmd_set(pmdp, ptep);
 		}
 		start = (start + SRMMU_PMD_SIZE) & SRMMU_PMD_MASK;
 	}
@@ -965,34 +1559,60 @@
  * looking at the prom's page table directly which is what most
  * other OS's do.  Yuck... this is much better.
  */
-static inline void srmmu_inherit_prom_mappings(void)
+void srmmu_inherit_prom_mappings(unsigned long start,unsigned long end)
 {
 	pgd_t *pgdp;
 	pmd_t *pmdp;
 	pte_t *ptep;
-	unsigned long start, end;
+	int what = 0; /* 0 = normal-pte, 1 = pmd-level pte, 2 = pgd-level pte */
 	unsigned long prompte;
 
-	start = KADB_DEBUGGER_BEGVM;
-	end = LINUX_OPPROM_ENDVM;
-	while(start < end) {
-		/* Something going wrong here on some ss5's... */
-		prompte = srmmu_hwprobe(start);
-
-		if((prompte & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
-			pgdp = srmmu_pgd_offset(init_task.mm, start);
-			if(srmmu_pgd_none(*pgdp)) {
-				pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
-				srmmu_pgd_set(pgdp, pmdp);
-			}
-			pmdp = srmmu_pmd_offset(pgdp, start);
-			if(srmmu_pmd_none(*pmdp)) {
-				ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
-				srmmu_pmd_set(pmdp, ptep);
-			}
-			ptep = srmmu_pte_offset(pmdp, start);
-			pte_val(*ptep) = prompte;
+	while(start <= end) {
+		if (start == 0)
+			break; /* probably wrap around */
+		if(start == 0xfef00000)
+			start = KADB_DEBUGGER_BEGVM;
+		if(!(prompte = srmmu_hwprobe(start))) {
+			start += PAGE_SIZE;
+			continue;
+		}
+    
+		/* A red snapper, see what it really is. */
+		what = 0;
+    
+		if(!(start & ~(SRMMU_PMD_MASK))) {
+			if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PMD_SIZE) == prompte)
+				what = 1;
+		}
+    
+		if(!(start & ~(SRMMU_PGDIR_MASK))) {
+			if(srmmu_hwprobe((start-PAGE_SIZE) + SRMMU_PGDIR_SIZE) ==
+			   prompte)
+				what = 2;
+		}
+    
+		pgdp = srmmu_pgd_offset(init_task.mm, start);
+		if(what == 2) {
+			pgd_val(*pgdp) = prompte;
+			start += SRMMU_PGDIR_SIZE;
+			continue;
+		}
+		if(srmmu_pgd_none(*pgdp)) {
+			pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
+			srmmu_early_pgd_set(pgdp, pmdp);
+		}
+		pmdp = srmmu_early_pmd_offset(pgdp, start);
+		if(what == 1) {
+			pmd_val(*pmdp) = prompte;
+			start += SRMMU_PMD_SIZE;
+			continue;
 		}
+		if(srmmu_pmd_none(*pmdp)) {
+			ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
+			srmmu_early_pmd_set(pmdp, ptep);
+		}
+		ptep = srmmu_early_pte_offset(pmdp, start);
+		pte_val(*ptep) = prompte;
 		start += PAGE_SIZE;
 	}
 }
@@ -1006,53 +1626,204 @@
 	pte_t *ptep;
 
 	start = DVMA_VADDR;
-	dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
+	if (viking_mxcc_present)
+		dvma_prot = __pgprot(SRMMU_CACHE | SRMMU_ET_PTE | SRMMU_PRIV);
+	else
+		dvma_prot = __pgprot(SRMMU_ET_PTE | SRMMU_PRIV);
 	while(first <= last) {
 		pgdp = srmmu_pgd_offset(init_task.mm, start);
 		pmdp = srmmu_pmd_offset(pgdp, start);
 		ptep = srmmu_pte_offset(pmdp, start);
 
-		/* Map with cacheable bit clear. */
 		srmmu_set_entry(ptep, pte_val(srmmu_mk_pte(first, dvma_prot)));
 
 		first += PAGE_SIZE;
 		start += PAGE_SIZE;
 	}
+
+	/* Uncache DVMA pages. */
+	if (!viking_mxcc_present) {
+		first = first_dvma_page;
+		last = last_dvma_page;
+		while(first <= last) {
+			pgdp = srmmu_pgd_offset(init_task.mm, first);
+			pmdp = srmmu_pmd_offset(pgdp, first);
+			ptep = srmmu_pte_offset(pmdp, first);
+			pte_val(*ptep) &= ~SRMMU_CACHE;
+			first += PAGE_SIZE;
+		}
+	}
 }
 
 static void srmmu_map_kernel(unsigned long start, unsigned long end)
 {
+	unsigned long last_page;
+	int srmmu_bank, phys_bank, i;
 	pgd_t *pgdp;
 	pmd_t *pmdp;
 	pte_t *ptep;
 
-	end = (PAGE_ALIGN(end) + PAGE_SIZE);
-	while(start < end) {
+	end = PAGE_ALIGN(end);
+
+	if(start == (KERNBASE + PAGE_SIZE)) {
+		unsigned long pte;
+		unsigned long tmp;
+
+		pgdp = srmmu_pgd_offset(init_task.mm, KERNBASE);
+		pmdp = srmmu_early_pmd_offset(pgdp, KERNBASE);
+		ptep = srmmu_early_pte_offset(pmdp, KERNBASE);
+
+		/* Put a real mapping in for the KERNBASE page. */
+		tmp = kbpage;
+		pte = (tmp) >> 4;
+		pte |= (SRMMU_CACHE | SRMMU_PRIV | SRMMU_VALID);
+		pte_val(*ptep) = pte;
+	}
+
+	/* Copy over mappings prom already gave us. */
+	last_page = (srmmu_hwprobe(start) & SRMMU_PTE_PMASK) << 4;
+	while((srmmu_hwprobe(start) & SRMMU_ET_MASK) == SRMMU_ET_PTE) {
+		unsigned long tmp;
+
 		pgdp = srmmu_pgd_offset(init_task.mm, start);
-		if(srmmu_pgd_none(*pgdp)) {
-			pmdp = srmmu_init_alloc(&mempool, SRMMU_PMD_TABLE_SIZE);
-			srmmu_pgd_set(pgdp, pmdp);
-		}
-		pmdp = srmmu_pmd_offset(pgdp, start);
-		if(srmmu_pmd_none(*pmdp)) {
-			ptep = srmmu_init_alloc(&mempool, SRMMU_PTE_TABLE_SIZE);
-			srmmu_pmd_set(pmdp, ptep);
-		}
-		ptep = srmmu_pte_offset(pmdp, start);
-		*ptep = srmmu_mk_pte(start, SRMMU_PAGE_KERNEL);
+		pmdp = srmmu_early_pmd_offset(pgdp, start);
+		ptep = srmmu_early_pte_offset(pmdp, start);
+		tmp = srmmu_hwprobe(start);
+		tmp &= ~(0xff);
+		tmp |= (SRMMU_CACHE | SRMMU_PRIV | SRMMU_VALID);
+		pte_val(*ptep) = tmp;
+		start += PAGE_SIZE;
+		tmp = (srmmu_hwprobe(start) & SRMMU_PTE_PMASK) << 4;
+
+		/* Never a cross bank boundry, thank you. */
+		if(tmp != last_page + PAGE_SIZE)
+			break;
+		last_page = tmp;
+	}
+
+	/* Ok, that was assumed to be one full bank, begin
+	 * construction of srmmu_map[].
+	 */
+	for(phys_bank = 0; sp_banks[phys_bank].num_bytes != 0; phys_bank++) {
+		if(kbpage >= sp_banks[phys_bank].base_addr &&
+		   (kbpage <
+		    (sp_banks[phys_bank].base_addr + sp_banks[phys_bank].num_bytes)))
+			break; /* found it */
+	}
+	srmmu_bank = 0;
+	srmmu_map[srmmu_bank].vbase = KERNBASE;
+	srmmu_map[srmmu_bank].pbase = sp_banks[phys_bank].base_addr;
+	srmmu_map[srmmu_bank].size = sp_banks[phys_bank].num_bytes;
+	if(kbpage != sp_banks[phys_bank].base_addr) {
+		prom_printf("Detected PenguinPages, getting out of here.\n");
+		prom_halt();
+#if 0
+		srmmu_map[srmmu_bank].pbase = kbpage;
+		srmmu_map[srmmu_bank].size -=
+			(kbpage - sp_banks[phys_bank].base_addr);
+#endif
+	}
+	/* Prom didn't map all of this first bank, fill
+	 * in the rest by hand.
+	 */
+	while(start < (srmmu_map[srmmu_bank].vbase + srmmu_map[srmmu_bank].size)) {
+		unsigned long pteval;
+
+		pgdp = srmmu_pgd_offset(init_task.mm, start);
+		pmdp = srmmu_early_pmd_offset(pgdp, start);
+		ptep = srmmu_early_pte_offset(pmdp, start);
+
+		pteval = (start - KERNBASE + srmmu_map[srmmu_bank].pbase) >> 4;
+		pteval |= (SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV);
+		pte_val(*ptep) = pteval;
 		start += PAGE_SIZE;
 	}
+
+	/* Mark this sp_bank invalid... */
+	sp_banks[phys_bank].base_addr |= 1;
+	srmmu_bank++;
+
+	/* Now, deal with what is left. */
+	while(start < end) {
+		unsigned long baddr;
+		int btg;
+
+		/* Find a usable cluster of physical ram. */
+		for(i=0; sp_banks[i].num_bytes != 0; i++)
+			if(!(sp_banks[i].base_addr & 1))
+				break;
+		if(sp_banks[i].num_bytes == 0)
+			break;
+
+		/* Add it to srmmu_map */
+		srmmu_map[srmmu_bank].vbase = start;
+		srmmu_map[srmmu_bank].pbase = sp_banks[i].base_addr;
+		srmmu_map[srmmu_bank].size = sp_banks[i].num_bytes;
+		srmmu_bank++;
+
+		btg = sp_banks[i].num_bytes;
+		baddr = sp_banks[i].base_addr;
+		while(btg) {
+			pgdp = srmmu_pgd_offset(init_task.mm, start);
+			pmdp = srmmu_early_pmd_offset(pgdp, start);
+			ptep = srmmu_early_pte_offset(pmdp, start);
+			pte_val(*ptep) = (SRMMU_VALID | SRMMU_CACHE | SRMMU_PRIV);
+			pte_val(*ptep) |= (baddr >> 4);
+
+			baddr += PAGE_SIZE;
+			start += PAGE_SIZE;
+			btg -= PAGE_SIZE;
+		}
+		sp_banks[i].base_addr |= 1;
+	}
+	if(start < end) {
+		prom_printf("weird, didn't use all of physical memory... ");
+		prom_halt();
+	}
+	for(phys_bank = 0; sp_banks[phys_bank].num_bytes != 0; phys_bank++)
+		sp_banks[phys_bank].base_addr &= ~1;
+#if 0
+	for(i = 0; srmmu_map[i].size != 0; i++) {
+		prom_printf("srmmu_map[%d]: vbase=%08lx pbase=%08lx size=%d\n",
+			    i, srmmu_map[i].vbase,
+			    srmmu_map[i].pbase, srmmu_map[i].size);
+	}
+	prom_getchar();
+	for(i = 0; sp_banks[i].num_bytes != 0; i++) {
+		prom_printf("sp_banks[%d]: base_addr=%08lx num_bytes=%d\n",
+			    i,
+			    sp_banks[i].base_addr,
+			    sp_banks[i].num_bytes);
+	}
+	prom_getchar();
+	prom_halt();
+#endif
 }
 
 /* Paging initialization on the Sparc Reference MMU. */
 extern unsigned long free_area_init(unsigned long, unsigned long);
 extern unsigned long sparc_context_init(unsigned long, int);
 
+extern int physmem_mapped_contig;
+extern int linux_num_cpus;
+
+void (*poke_srmmu)(void);
+
 unsigned long srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
 {
+	unsigned long ptables_start, first_mapped_page;
 	int i, cpunode;
 	char node_str[128];
+	pgd_t *pgdp;
+	pmd_t *pmdp;
+	pte_t *ptep;
 
+	physmem_mapped_contig = 0; /* for init.c:taint_real_pages() */
+
+#if CONFIG_AP1000
+        printk("Forcing num_contexts to 1024\n");
+        num_contexts = 1024;
+#else
 	/* Find the number of contexts on the srmmu. */
 	cpunode = prom_getchild(prom_root_node);
 	num_contexts = 0;
@@ -1063,64 +1834,104 @@
 			break;
 		}
 	}
+#endif
 	if(!num_contexts) {
 		prom_printf("Something wrong, cant find cpu node in paging_init.\n");
 		prom_halt();
 	}
 		
-	prom_printf("Number of MMU contexts %d\n", num_contexts);
-	mempool = start_mem;
+	ptables_start = mempool = PAGE_ALIGN(start_mem);
 	memset(swapper_pg_dir, 0, PAGE_SIZE);
-	srmmu_map_kernel(KERNBASE, end_mem);
+	first_mapped_page = KERNBASE;
+	kbpage = srmmu_hwprobe(KERNBASE);
+	if((kbpage & SRMMU_ET_MASK) != SRMMU_ET_PTE) {
+		kbpage = srmmu_hwprobe(KERNBASE + PAGE_SIZE);
+		kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
+		kbpage -= PAGE_SIZE;
+		first_mapped_page += PAGE_SIZE;
+	} else
+		kbpage = (kbpage & SRMMU_PTE_PMASK) << 4;
+
+	srmmu_allocate_ptable_skeleton(KERNBASE, end_mem);
+#if CONFIG_SUN_IO
 	srmmu_allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_END);
 	srmmu_allocate_ptable_skeleton(DVMA_VADDR, DVMA_END);
+#endif
+
+	/* Steal DVMA pages now, I still don't like how we waste all this. */
 	mempool = PAGE_ALIGN(mempool);
 	first_dvma_page = mempool;
 	last_dvma_page = (mempool + (DVMA_LEN) - PAGE_SIZE);
 	mempool = last_dvma_page + PAGE_SIZE;
-	srmmu_map_dvma_pages_for_cpu(first_dvma_page, last_dvma_page);
 
-	srmmu_inherit_prom_mappings();
+#if CONFIG_AP1000
+        ap_inherit_mappings();
+#else
+        srmmu_inherit_prom_mappings(0xfe400000,(LINUX_OPPROM_ENDVM-PAGE_SIZE));
+#endif
+	srmmu_map_kernel(first_mapped_page, end_mem);
+#if CONFIG_SUN_IO
+	srmmu_map_dvma_pages_for_cpu(first_dvma_page, last_dvma_page);
+#endif
 	srmmu_context_table = srmmu_init_alloc(&mempool, num_contexts*sizeof(ctxd_t));
+	srmmu_ctx_table_phys = (ctxd_t *) srmmu_v2p((unsigned long) srmmu_context_table);
 	for(i = 0; i < num_contexts; i++)
-		srmmu_ctxd_set(&srmmu_context_table[i], swapper_pg_dir);
-
-	prom_printf("Taking over MMU from PROM.\n");
-	srmmu_flush_whole_tlb();
-	srmmu_set_ctable_ptr(((unsigned)srmmu_context_table) - PAGE_OFFSET);
-	srmmu_flush_whole_tlb();
+		ctxd_set(&srmmu_context_table[i], swapper_pg_dir);
 
 	start_mem = PAGE_ALIGN(mempool);
+
+	/* Some SRMMU's are _very_ stupid indeed. */
+	if(!can_cache_ptables) {
+		for( ; ptables_start < start_mem; ptables_start += PAGE_SIZE) {
+			pgdp = srmmu_pgd_offset(init_task.mm, ptables_start);
+			pmdp = srmmu_early_pmd_offset(pgdp, ptables_start);
+			ptep = srmmu_early_pte_offset(pmdp, ptables_start);
+			pte_val(*ptep) &= ~SRMMU_CACHE;
+		}
+
+		pgdp = srmmu_pgd_offset(init_task.mm, (unsigned long)swapper_pg_dir);
+		pmdp = srmmu_early_pmd_offset(pgdp, (unsigned long)swapper_pg_dir);
+		ptep = srmmu_early_pte_offset(pmdp, (unsigned long)swapper_pg_dir);
+		pte_val(*ptep) &= ~SRMMU_CACHE;
+	}
+
+	flush_cache_all();
+	srmmu_set_ctable_ptr((unsigned long) srmmu_ctx_table_phys);
+	flush_tlb_all();
+	poke_srmmu();
+
 	start_mem = sparc_context_init(start_mem, num_contexts);
 	start_mem = free_area_init(start_mem, end_mem);
 
-	prom_printf("survived...\n");
 	return PAGE_ALIGN(start_mem);
 }
 
-/* Test the WP bit on the Sparc Reference MMU. */
-void srmmu_test_wp(void)
-{
-	pgd_t *pgdp;
-	
-	wp_works_ok = -1;
-	/* We mapped page zero as a read-only page in paging_init()
-	 * So fire up the test, then invalidate the pgd for page zero.
-	 * It is no longer needed.
-	 */
-
-	/* Let it rip... */
-	__asm__ __volatile__("st %%g0, [0x0]\n\t": : :"memory");
-	if (wp_works_ok < 0)
-		wp_works_ok = 0;
-
-	pgdp = srmmu_pgd_offset(init_task.mm, 0x0);
-	pgd_val(*pgdp) = 0x0;
-}
+static char srmmuinfo[512];
 
 static char *srmmu_mmu_info(void)
 {
-	return "";
+	sprintf(srmmuinfo, "MMU type\t: %s\n"
+		"invall\t\t: %d\n"
+		"invmm\t\t: %d\n"
+		"invrnge\t\t: %d\n"
+		"invpg\t\t: %d\n"
+		"contexts\t: %d\n"
+		"big_chunks\t: %d\n"
+		"little_chunks\t: %d\n",
+		srmmu_name,
+		module_stats.invall,
+		module_stats.invmm,
+		module_stats.invrnge,
+		module_stats.invpg,
+		num_contexts,
+#if 0
+		num_big_chunks,
+		num_little_chunks
+#else
+		0, 0
+#endif
+		);
+	return srmmuinfo;
 }
 
 static void srmmu_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte)
@@ -1133,7 +1944,39 @@
 	struct mm_struct *mm = current->mm;
 
 	if(mm->context != NO_CONTEXT) {
-		srmmu_ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
+		flush_cache_mm(mm);
+		ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
+		flush_tlb_mm(mm);
+		ctx_old = ctx_list_pool + mm->context;
+		remove_from_ctx_list(ctx_old);
+		add_to_free_ctxlist(ctx_old);
+		mm->context = NO_CONTEXT;
+	}
+}
+
+static void srmmu_flush_hook(void)
+{
+	if(current->tss.flags & SPARC_FLAG_KTHREAD) {
+		alloc_context(current->mm);
+		ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
+		srmmu_set_context(current->mm->context);
+	}
+}
+
+static void hypersparc_exit_hook(void)
+{
+	struct ctx_list *ctx_old;
+	struct mm_struct *mm = current->mm;
+
+	if(mm->context != NO_CONTEXT) {
+		/* HyperSparc is copy-back, any data for this
+		 * process in a modified cache line is stale
+		 * and must be written back to main memory now
+		 * else we eat shit later big time.
+		 */
+		flush_cache_mm(mm);
+		ctxd_set(&srmmu_context_table[mm->context], swapper_pg_dir);
+		flush_tlb_mm(mm);
 		ctx_old = ctx_list_pool + mm->context;
 		remove_from_ctx_list(ctx_old);
 		add_to_free_ctxlist(ctx_old);
@@ -1141,12 +1984,12 @@
 	}
 }
 
-static void
-srmmu_flush_hook(void)
+static void hypersparc_flush_hook(void)
 {
 	if(current->tss.flags & SPARC_FLAG_KTHREAD) {
 		alloc_context(current->mm);
-		srmmu_ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
+		flush_cache_mm(current->mm);
+		ctxd_set(&srmmu_context_table[current->mm->context], current->mm->pgd);
 		srmmu_set_context(current->mm->context);
 	}
 }
@@ -1158,56 +2001,96 @@
 	prom_halt();
 }
 
-void init_hypersparc(void)
+void poke_hypersparc(void)
 {
+	volatile unsigned long clear;
 	unsigned long mreg = srmmu_get_mmureg();
 
-	prom_printf("HyperSparc MMU detected.\n");
-	if(mreg & HYPERSPARC_CSIZE)
-		hyper_cache_size = (256 * 1024);
-	else
-		hyper_cache_size = (128 * 1024);
+	hyper_flush_unconditional_combined();
 
-	srmmu_modtype = HyperSparc;
-	hwbug_bitmask |= HWBUG_VACFLUSH_BITROT;
+	mreg &= ~(HYPERSPARC_CWENABLE);
+	mreg |= (HYPERSPARC_CENABLE | HYPERSPARC_WBENABLE);
+	mreg |= (HYPERSPARC_CMODE);
+
+	srmmu_set_mmureg(mreg);
+	hyper_clear_all_tags();
 
+	put_ross_icr(HYPERSPARC_ICCR_FTD | HYPERSPARC_ICCR_ICE);
 	hyper_flush_whole_icache();
-	hyper_flush_all_combined();
+	clear = srmmu_get_faddr();
+	clear = srmmu_get_fstatus();
+}
 
-	/* Keep things sane for now, cache in write-through mode. */
-	mreg &= ~(HYPERSPARC_CWENABLE | HYPERSPARC_CMODE | HYPERSPARC_WBENABLE);
-	mreg |= HYPERSPARC_CENABLE;
-	srmmu_set_mmureg(mreg);
-	put_ross_icr(get_ross_icr() | 0x3);
-	invalidate_all = hypersparc_invalidate_all;
-	invalidate_mm = hypersparc_invalidate_mm;
-	invalidate_page = hypersparc_invalidate_page;
-	invalidate_range = hypersparc_invalidate_range;
+void init_hypersparc(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+
+	srmmu_name = "ROSS HyperSparc";
+	can_cache_ptables = 0;
+	if(mreg & HYPERSPARC_CSIZE) {
+		hyper_cache_size = (256 * 1024);
+		hyper_line_size = 64;
+	} else {
+		hyper_cache_size = (128 * 1024);
+		hyper_line_size = 32;
+	}
+
+	flush_cache_all = hypersparc_flush_cache_all;
+	flush_cache_mm = hypersparc_flush_cache_mm;
+	flush_cache_range = hypersparc_flush_cache_range;
+	flush_cache_page = hypersparc_flush_cache_page;
+
+	flush_tlb_all = hypersparc_flush_tlb_all;
+	flush_tlb_mm = hypersparc_flush_tlb_mm;
+	flush_tlb_range = hypersparc_flush_tlb_range;
+	flush_tlb_page = hypersparc_flush_tlb_page;
+
+	flush_page_to_ram = hypersparc_flush_page_to_ram;
+	flush_page_for_dma = hypersparc_flush_page_for_dma;
+	flush_cache_page_to_uncache = hypersparc_flush_cache_page_to_uncache;
+	flush_tlb_page_for_cbit = hypersparc_flush_tlb_page_for_cbit;
+
+	ctxd_set = hypersparc_ctxd_set;
+	switch_to_context = hypersparc_switch_to_context;
+	mmu_exit_hook = hypersparc_exit_hook;
+	mmu_flush_hook = hypersparc_flush_hook;
+	sparc_update_rootmmu_dir = hypersparc_update_rootmmu_dir;
+	set_pte = hypersparc_set_pte;
+	poke_srmmu = poke_hypersparc;
 }
 
-void init_cypress_common(void)
+void poke_cypress(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
 
 	mreg &= ~CYPRESS_CMODE;
 	mreg |= CYPRESS_CENABLE;
 	srmmu_set_mmureg(mreg);
-	invalidate_all = cypress_invalidate_all;
-	invalidate_mm = cypress_invalidate_mm;
-	invalidate_page = cypress_invalidate_page;
-	invalidate_range = cypress_invalidate_range;
+}
+
+void init_cypress_common(void)
+{
+	can_cache_ptables = 0;
+	flush_tlb_all = cypress_flush_tlb_all;
+	flush_tlb_mm = cypress_flush_tlb_mm;
+	flush_tlb_page = cypress_flush_tlb_page;
+	flush_tlb_range = cypress_flush_tlb_range;
+	poke_srmmu = poke_cypress;
+
+	/* XXX Need to write cache flushes for this one... XXX */
+
 }
 
 void init_cypress_604(void)
 {
-	prom_printf("Cypress 604(UP) MMU detected.\n");
+	srmmu_name = "ROSS Cypress-604(UP)";
 	srmmu_modtype = Cypress;
 	init_cypress_common();
 }
 
 void init_cypress_605(unsigned long mrev)
 {
-	prom_printf("Cypress 605(MP) MMU detected.\n");
+	srmmu_name = "ROSS Cypress-605(MP)";
 	if(mrev == 0xe) {
 		srmmu_modtype = Cypress_vE;
 		hwbug_bitmask |= HWBUG_COPYBACK_BROKEN;
@@ -1222,17 +2105,36 @@
 	init_cypress_common();
 }
 
-#define SWIFT_REVISION_ADDR  0x10003000
-void init_swift(void)
+void poke_swift(void)
 {
-	unsigned long swift_rev, addr;
 	unsigned long mreg = srmmu_get_mmureg();
 
-	prom_printf("Swift MMU detected.\n");
+	/* Clear any crap from the cache or else... */
+	swift_idflash_clear();
+	mreg |= (SWIFT_IE | SWIFT_DE); /* I & D caches on */
+
+	/* The Swift branch folding logic is completely broken.  At
+	 * trap time, if things are just right, if can mistakedly
+	 * think that a trap is coming from kernel mode when in fact
+	 * it is coming from user mode (it mis-executes the branch in
+	 * the trap code).  So you see things like crashme completely
+	 * hosing your machine which is completely unacceptable.  Turn
+	 * this shit off... nice job Fujitsu.
+	 */
+	mreg &= ~(SWIFT_BF);
+	srmmu_set_mmureg(mreg);
+}
+
+#define SWIFT_MASKID_ADDR  0x10003018
+void init_swift(void)
+{
+	unsigned long swift_rev;
+
 	__asm__ __volatile__("lda [%1] %2, %0\n\t"
 			     "srl %0, 0x18, %0\n\t" :
 			     "=r" (swift_rev) :
-			     "r" (SWIFT_REVISION_ADDR), "i" (0x20));
+			     "r" (SWIFT_MASKID_ADDR), "i" (ASI_M_BYPASS));
+	srmmu_name = "Fujitsu Swift";
 	switch(swift_rev) {
 	case 0x11:
 	case 0x20:
@@ -1270,103 +2172,172 @@
 		srmmu_modtype = Swift_ok;
 		break;
 	};
-	/* Clear any crap from the cache or else... */
-	for(addr = 0; addr < (PAGE_SIZE * 4); addr += 16) {
-		swift_inv_insn_tag(addr); /* whiz- */
-		swift_inv_data_tag(addr); /* bang */
-	}
-	mreg |= (SWIFT_IE | SWIFT_DE); /* I & D caches on */
-
-	/* The Swift branch folding logic is completely broken.  At
-	 * trap time, if things are just right, if can mistakenly
-	 * thing that a trap is coming from kernel mode when in fact
-	 * it is coming from user mode (it misexecutes the branch in
-	 * the trap code).  So you see things like crashme completely
-	 * hosing your machine which is completely unacceptable.  Turn
-	 * this crap off... nice job Fujitsu.
-	 */
-	mreg &= ~(SWIFT_BF);
-	srmmu_set_mmureg(mreg);
 
-	invalidate_all = swift_invalidate_all;
-	invalidate_mm = swift_invalidate_mm;
-	invalidate_page = swift_invalidate_page;
-	invalidate_range = swift_invalidate_range;
+	flush_cache_all = swift_flush_cache_all;
+	flush_cache_mm = swift_flush_cache_mm;
+	flush_cache_page = swift_flush_cache_page;
+	flush_cache_range = swift_flush_cache_range;
+
+	flush_tlb_all = swift_flush_tlb_all;
+	flush_tlb_mm = swift_flush_tlb_mm;
+	flush_tlb_page = swift_flush_tlb_page;
+	flush_tlb_range = swift_flush_tlb_range;
+
+	flush_page_to_ram = swift_flush_page_to_ram;
+	flush_page_for_dma = swift_flush_page_for_dma;
+	flush_cache_page_to_uncache = swift_flush_cache_page_to_uncache;
+	flush_tlb_page_for_cbit = swift_flush_tlb_page_for_cbit;
 
 	/* Are you now convinced that the Swift is one of the
 	 * biggest VLSI abortions of all time?  Bravo Fujitsu!
+	 * Fujitsu, the !#?!%$'d up processor people.  I bet if
+	 * you examined the microcode of the Swift you'd find
+	 * XXX's all over the place.
 	 */
+	poke_srmmu = poke_swift;
+}
+
+void poke_tsunami(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+
+	tsunami_flush_icache();
+	tsunami_flush_dcache();
+	mreg &= ~TSUNAMI_ITD;
+	mreg |= (TSUNAMI_IENAB | TSUNAMI_DENAB);
+	srmmu_set_mmureg(mreg);
 }
 
-void init_tsunami(unsigned long mreg)
+void init_tsunami(void)
 {
 	/* Tsunami's pretty sane, Sun and TI actually got it
 	 * somewhat right this time.  Fujitsu should have
 	 * taken some lessons from them.
 	 */
 
-	prom_printf("Tsunami MMU detected.\n");
+	srmmu_name = "TI Tsunami";
 	srmmu_modtype = Tsunami;
-	tsunami_invalidate_icache();
-	tsunami_invalidate_dcache();
-	mreg &= ~TSUNAMI_ITD;
-	mreg |= (TSUNAMI_IENAB | TSUNAMI_DENAB);
-	srmmu_set_mmureg(mreg);
-	invalidate_all = tsunami_invalidate_all;
-	invalidate_mm = tsunami_invalidate_mm;
-	invalidate_page = tsunami_invalidate_page;
-	invalidate_range = tsunami_invalidate_range;
+	can_cache_ptables = 1;
+
+	flush_cache_all = tsunami_flush_cache_all;
+	flush_cache_mm = tsunami_flush_cache_mm;
+	flush_cache_page = tsunami_flush_cache_page;
+	flush_cache_range = tsunami_flush_cache_range;
+
+	flush_tlb_all = tsunami_flush_tlb_all;
+	flush_tlb_mm = tsunami_flush_tlb_mm;
+	flush_tlb_page = tsunami_flush_tlb_page;
+	flush_tlb_range = tsunami_flush_tlb_range;
+
+	flush_page_to_ram = tsunami_flush_page_to_ram;
+	flush_page_for_dma = tsunami_flush_page_for_dma;
+	flush_cache_page_to_uncache = tsunami_flush_cache_page_to_uncache;
+	flush_tlb_page_for_cbit = tsunami_flush_tlb_page_for_cbit;
+
+	poke_srmmu = poke_tsunami;
 }
 
-void init_viking(unsigned long psr_vers, unsigned long mod_rev)
+void poke_viking(void)
 {
 	unsigned long mreg = srmmu_get_mmureg();
+	static int smp_catch = 0;
 
-	/* Ahhh, the viking.  SRMMU VLSI abortion number two... */
+	if(viking_mxcc_present) {
+		unsigned long mxcc_control;
 
-	prom_printf("Viking MMU detected.\n");
-	if(!psr_vers && ! mod_rev) {
-		srmmu_modtype = Viking_12;
-		hwbug_bitmask |= (HWBUG_MODIFIED_BITROT | HWBUG_PC_BADFAULT_ADDR);
-
-		/* On a fault, the chip gets entirely confused.  It will
-		 * do one of two things.  Either it will set the modified
-		 * bit for a read-only page (!!!) or it will improperly
-		 * report a fault when a dcti/loadstore sequence is the
-		 * last two instructions on a page.  Oh baby...
-		 */
+		__asm__ __volatile__("set -1, %%g2\n\t"
+				     "set -1, %%g3\n\t"
+				     "stda %%g2, [%1] %2\n\t"
+				     "lda [%3] %2, %0\n\t" :
+				     "=r" (mxcc_control) :
+				     "r" (MXCC_EREG), "i" (ASI_M_MXCC),
+				     "r" (MXCC_CREG) : "g2", "g3");
+		mxcc_control |= (MXCC_CTL_ECE | MXCC_CTL_PRE | MXCC_CTL_MCE);
+		mxcc_control &= ~(MXCC_CTL_PARE | MXCC_CTL_RRC);
+		mreg &= ~(VIKING_PCENABLE);
+		__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+				     "r" (mxcc_control), "r" (MXCC_CREG),
+				     "i" (ASI_M_MXCC));
+		srmmu_set_mmureg(mreg);
+		mreg |= VIKING_TCENABLE;
 	} else {
-		if(psr_vers) {
-			srmmu_modtype = Viking_2x;
-			hwbug_bitmask |= HWBUG_PC_BADFAULT_ADDR; /* see above */
-		} else {
-			if(mod_rev == 1) {
-				srmmu_modtype = Viking_30;
-				hwbug_bitmask |= HWBUG_PACINIT_BITROT;
-
-				/* At boot time the physical cache
-				 * has cherry bombs in it, so you
-				 * have to scrape it by hand before
-				 * enabling it.  Nice CAD tools guys.
-				 */
-			} else {
-				if(mod_rev < 8)
-					srmmu_modtype = Viking_35;
-				else
-					srmmu_modtype = Viking_new;
-			}
+		unsigned long bpreg;
+
+		mreg &= ~(VIKING_TCENABLE);
+		if(smp_catch++) {
+			/* Must disable mixed-cmd mode here for
+			 * other cpu's.
+			 */
+			bpreg = viking_get_bpreg();
+			bpreg &= ~(VIKING_ACTION_MIX);
+			viking_set_bpreg(bpreg);
+
+			/* Just in case PROM does something funny. */
+			msi_set_sync();
 		}
 	}
-	/* XXX Dave, play with the MXCC you pinhead XXX */
+
+	viking_unlock_icache();
 	viking_flush_icache();
+#if 0
+	viking_unlock_dcache();
 	viking_flush_dcache();
-	mreg |= (VIKING_DCENABLE | VIKING_ICENABLE | VIKING_SBENABLE |
-		 VIKING_TCENABLE | VIKING_DPENABLE);
+#endif
+	mreg |= VIKING_SPENABLE;
+	mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
+	mreg |= VIKING_SBENABLE;
+	mreg &= ~(VIKING_ACENABLE);
+#if CONFIG_AP1000
+        mreg &= ~(VIKING_SBENABLE);
+#endif
+#ifdef __SMP__
+	mreg &= ~(VIKING_SBENABLE);
+#endif
 	srmmu_set_mmureg(mreg);
-	invalidate_all = viking_invalidate_all;
-	invalidate_mm = viking_invalidate_mm;
-	invalidate_page = viking_invalidate_page;
-	invalidate_range = viking_invalidate_range;
+}
+
+void init_viking(void)
+{
+	unsigned long mreg = srmmu_get_mmureg();
+
+	/* Ahhh, the viking.  SRMMU VLSI abortion number two... */
+
+	if(mreg & VIKING_MMODE) {
+		unsigned long bpreg;
+
+		srmmu_name = "TI Viking";
+		viking_mxcc_present = 0;
+		can_cache_ptables = 0;
+
+		bpreg = viking_get_bpreg();
+		bpreg &= ~(VIKING_ACTION_MIX);
+		viking_set_bpreg(bpreg);
+
+		msi_set_sync();
+
+		flush_cache_page_to_uncache = viking_no_mxcc_flush_page;
+	} else {
+		srmmu_name = "TI Viking/MXCC";
+		viking_mxcc_present = 1;
+		can_cache_ptables = 1;
+		flush_cache_page_to_uncache = viking_mxcc_flush_page;
+	}
+
+	flush_cache_all = viking_flush_cache_all;
+	flush_cache_mm = viking_flush_cache_mm;
+	flush_cache_page = viking_flush_cache_page;
+	flush_cache_range = viking_flush_cache_range;
+
+	flush_tlb_all = viking_flush_tlb_all;
+	flush_tlb_mm = viking_flush_tlb_mm;
+	flush_tlb_page = viking_flush_tlb_page;
+	flush_tlb_range = viking_flush_tlb_range;
+
+	flush_page_to_ram = viking_flush_page_to_ram;
+	flush_page_for_dma = viking_flush_page_for_dma;
+	flush_tlb_page_for_cbit = viking_flush_tlb_page_for_cbit;
+
+	poke_srmmu = poke_viking;
 }
 
 /* Probe for the srmmu chip version. */
@@ -1418,13 +2389,13 @@
 	if(psr_typ == 4 &&
 	   ((psr_vers == 0) ||
 	    ((psr_vers == 1) && (mod_typ == 0) && (mod_rev == 0)))) {
-		init_viking(psr_vers, mod_rev);
+		init_viking();
 		return;
 	}
 
 	/* Finally the Tsunami. */
 	if(psr_typ == 4 && psr_vers == 1 && (mod_typ || mod_rev)) {
-		init_tsunami(mreg);
+		init_tsunami();
 		return;
 	}
 
@@ -1438,6 +2409,10 @@
 extern unsigned long spwin_srmmu_stackchk, srmmu_fwin_stackchk,
 	tsetup_srmmu_stackchk, srmmu_rett_stackchk;
 
+#ifdef __SMP__
+extern unsigned long rirq_mmu_patchme, srmmu_reti_stackchk;
+#endif
+
 extern unsigned long srmmu_fault;
 
 #define PATCH_BRANCH(insn, dest) do { \
@@ -1454,15 +2429,35 @@
 	PATCH_BRANCH(fwin_mmu_patchme, srmmu_fwin_stackchk);
 	PATCH_BRANCH(tsetup_mmu_patchme, tsetup_srmmu_stackchk);
 	PATCH_BRANCH(rtrap_mmu_patchme, srmmu_rett_stackchk);
+#ifdef __SMP__
+	PATCH_BRANCH(rirq_mmu_patchme, srmmu_reti_stackchk);
+#endif
 	PATCH_BRANCH(sparc_ttable[SP_TRAP_TFLT].inst_three, srmmu_fault);
 	PATCH_BRANCH(sparc_ttable[SP_TRAP_DFLT].inst_three, srmmu_fault);
+	PATCH_BRANCH(sparc_ttable[SP_TRAP_DACC].inst_three, srmmu_fault);
+}
+
+#ifdef __SMP__
+/* Local cross-calls. */
+static void smp_flush_page_for_dma(unsigned long page)
+{
+	xc1((smpfunc_t) local_flush_page_for_dma, page);
+}
+
+static void smp_flush_cache_page_to_uncache(unsigned long page)
+{
+	xc1((smpfunc_t) local_flush_cache_page_to_uncache, page);
+}
+
+static void smp_flush_tlb_page_for_cbit(unsigned long page)
+{
+	xc1((smpfunc_t) local_flush_tlb_page_for_cbit, page);
 }
+#endif
 
 /* Load up routines and constants for sun4m mmu */
 void ld_mmu_srmmu(void)
 {
-	prom_printf("Loading srmmu MMU routines\n");
-
 	/* First the constants */
 	pmd_shift = SRMMU_PMD_SHIFT;
 	pmd_size = SRMMU_PMD_SIZE;
@@ -1497,23 +2492,17 @@
 
 	pte_none = srmmu_pte_none;
 	pte_present = srmmu_pte_present;
-	pte_inuse = srmmu_pte_inuse;
 	pte_clear = srmmu_pte_clear;
-	pte_reuse = srmmu_pte_reuse;
 
 	pmd_none = srmmu_pmd_none;
 	pmd_bad = srmmu_pmd_bad;
 	pmd_present = srmmu_pmd_present;
-	pmd_inuse = srmmu_pmd_inuse;
 	pmd_clear = srmmu_pmd_clear;
-	pmd_reuse = srmmu_pmd_reuse;
 
 	pgd_none = srmmu_pgd_none;
 	pgd_bad = srmmu_pgd_bad;
 	pgd_present = srmmu_pgd_present;
-	pgd_inuse = srmmu_pgd_inuse;
 	pgd_clear = srmmu_pgd_clear;
-	pgd_reuse = srmmu_pgd_reuse;
 
 	mk_pte = srmmu_mk_pte;
 	pgd_set = srmmu_pgd_set;
@@ -1547,9 +2536,15 @@
 	mmu_flush_hook = srmmu_flush_hook;
 	mmu_lockarea = srmmu_lockarea;
 	mmu_unlockarea = srmmu_unlockarea;
-	mmu_get_scsi_buffer = srmmu_get_scsi_buffer;
-	mmu_release_scsi_buffer = srmmu_release_scsi_buffer;
+
+	mmu_get_scsi_one = srmmu_get_scsi_one;
+	mmu_get_scsi_sgl = srmmu_get_scsi_sgl;
+	mmu_release_scsi_one = srmmu_release_scsi_one;
+	mmu_release_scsi_sgl = srmmu_release_scsi_sgl;
+
 	mmu_info = srmmu_mmu_info;
+        mmu_v2p = srmmu_v2p;
+        mmu_p2v = srmmu_p2v;
 
 	/* Task struct and kernel stack allocating/freeing. */
 	alloc_kernel_stack = srmmu_alloc_kernel_stack;
@@ -1559,13 +2554,40 @@
 
 	quick_kernel_fault = srmmu_quick_kernel_fault;
 
+	/* SRMMU specific. */
+	ctxd_set = srmmu_ctxd_set;
+	pmd_set = srmmu_pmd_set;
+
 	get_srmmu_type();
-	if(!(srmmu_get_mmureg() & 0x800)) {
-		srmmu_read_physical = msparc_read_physical;
-		srmmu_write_physical = msparc_write_physical;
-	} else {
-		srmmu_read_physical = gensrmmu_read_physical;
-		srmmu_write_physical = gensrmmu_write_physical;
-	}
 	patch_window_trap_handlers();
+
+#ifdef __SMP__
+	/* El switcheroo... */
+
+	local_flush_cache_all = flush_cache_all;
+	local_flush_cache_mm = flush_cache_mm;
+	local_flush_cache_range = flush_cache_range;
+	local_flush_cache_page = flush_cache_page;
+	local_flush_tlb_all = flush_tlb_all;
+	local_flush_tlb_mm = flush_tlb_mm;
+	local_flush_tlb_range = flush_tlb_range;
+	local_flush_tlb_page = flush_tlb_page;
+	local_flush_page_to_ram = flush_page_to_ram;
+	local_flush_page_for_dma = flush_page_for_dma;
+	local_flush_cache_page_to_uncache = flush_cache_page_to_uncache;
+	local_flush_tlb_page_for_cbit = flush_tlb_page_for_cbit;
+
+	flush_cache_all = smp_flush_cache_all;
+	flush_cache_mm = smp_flush_cache_mm;
+	flush_cache_range = smp_flush_cache_range;
+	flush_cache_page = smp_flush_cache_page;
+	flush_tlb_all = smp_flush_tlb_all;
+	flush_tlb_mm = smp_flush_tlb_mm;
+	flush_tlb_range = smp_flush_tlb_range;
+	flush_tlb_page = smp_flush_tlb_page;
+	flush_page_to_ram = smp_flush_page_to_ram;
+	flush_page_for_dma = smp_flush_page_for_dma;
+	flush_cache_page_to_uncache = smp_flush_cache_page_to_uncache;
+	flush_tlb_page_for_cbit = smp_flush_tlb_page_for_cbit;
+#endif
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this