162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * srmmu.c:  SRMMU specific routines for memory management.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1995 David S. Miller  (davem@caip.rutgers.edu)
662306a36Sopenharmony_ci * Copyright (C) 1995,2002 Pete Zaitcev (zaitcev@yahoo.com)
762306a36Sopenharmony_ci * Copyright (C) 1996 Eddie C. Dost    (ecd@skynet.be)
862306a36Sopenharmony_ci * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
962306a36Sopenharmony_ci * Copyright (C) 1999,2000 Anton Blanchard (anton@samba.org)
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/seq_file.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/memblock.h>
1562306a36Sopenharmony_ci#include <linux/pagemap.h>
1662306a36Sopenharmony_ci#include <linux/vmalloc.h>
1762306a36Sopenharmony_ci#include <linux/kdebug.h>
1862306a36Sopenharmony_ci#include <linux/export.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/log2.h>
2262306a36Sopenharmony_ci#include <linux/gfp.h>
2362306a36Sopenharmony_ci#include <linux/fs.h>
2462306a36Sopenharmony_ci#include <linux/mm.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <asm/mmu_context.h>
2762306a36Sopenharmony_ci#include <asm/cacheflush.h>
2862306a36Sopenharmony_ci#include <asm/tlbflush.h>
2962306a36Sopenharmony_ci#include <asm/io-unit.h>
3062306a36Sopenharmony_ci#include <asm/pgalloc.h>
3162306a36Sopenharmony_ci#include <asm/pgtable.h>
3262306a36Sopenharmony_ci#include <asm/bitext.h>
3362306a36Sopenharmony_ci#include <asm/vaddrs.h>
3462306a36Sopenharmony_ci#include <asm/cache.h>
3562306a36Sopenharmony_ci#include <asm/traps.h>
3662306a36Sopenharmony_ci#include <asm/oplib.h>
3762306a36Sopenharmony_ci#include <asm/mbus.h>
3862306a36Sopenharmony_ci#include <asm/page.h>
3962306a36Sopenharmony_ci#include <asm/asi.h>
4062306a36Sopenharmony_ci#include <asm/smp.h>
4162306a36Sopenharmony_ci#include <asm/io.h>
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Now the cpu specific definitions. */
4462306a36Sopenharmony_ci#include <asm/turbosparc.h>
4562306a36Sopenharmony_ci#include <asm/tsunami.h>
4662306a36Sopenharmony_ci#include <asm/viking.h>
4762306a36Sopenharmony_ci#include <asm/swift.h>
4862306a36Sopenharmony_ci#include <asm/leon.h>
4962306a36Sopenharmony_ci#include <asm/mxcc.h>
5062306a36Sopenharmony_ci#include <asm/ross.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include "mm_32.h"
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cienum mbus_module srmmu_modtype;
5562306a36Sopenharmony_cistatic unsigned int hwbug_bitmask;
5662306a36Sopenharmony_ciint vac_cache_size;
5762306a36Sopenharmony_ciEXPORT_SYMBOL(vac_cache_size);
5862306a36Sopenharmony_ciint vac_line_size;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciextern struct resource sparc_iomap;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciextern unsigned long last_valid_pfn;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic pgd_t *srmmu_swapper_pg_dir;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciconst struct sparc32_cachetlb_ops *sparc32_cachetlb_ops;
6762306a36Sopenharmony_ciEXPORT_SYMBOL(sparc32_cachetlb_ops);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#ifdef CONFIG_SMP
7062306a36Sopenharmony_ciconst struct sparc32_cachetlb_ops *local_ops;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define FLUSH_BEGIN(mm)
7362306a36Sopenharmony_ci#define FLUSH_END
7462306a36Sopenharmony_ci#else
7562306a36Sopenharmony_ci#define FLUSH_BEGIN(mm) if ((mm)->context != NO_CONTEXT) {
7662306a36Sopenharmony_ci#define FLUSH_END	}
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciint flush_page_for_dma_global = 1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cichar *srmmu_name;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cictxd_t *srmmu_ctx_table_phys;
8462306a36Sopenharmony_cistatic ctxd_t *srmmu_context_table;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciint viking_mxcc_present;
8762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(srmmu_context_spinlock);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int is_hypersparc;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int srmmu_cache_pagetables;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* these will be initialized in srmmu_nocache_calcsize() */
9462306a36Sopenharmony_cistatic unsigned long srmmu_nocache_size;
9562306a36Sopenharmony_cistatic unsigned long srmmu_nocache_end;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* 1 bit <=> 256 bytes of nocache <=> 64 PTEs */
9862306a36Sopenharmony_ci#define SRMMU_NOCACHE_BITMAP_SHIFT (PAGE_SHIFT - 4)
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* The context table is a nocache user with the biggest alignment needs. */
10162306a36Sopenharmony_ci#define SRMMU_NOCACHE_ALIGN_MAX (sizeof(ctxd_t)*SRMMU_MAX_CONTEXTS)
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civoid *srmmu_nocache_pool;
10462306a36Sopenharmony_cistatic struct bit_map srmmu_nocache_map;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline int srmmu_pmd_none(pmd_t pmd)
10762306a36Sopenharmony_ci{ return !(pmd_val(pmd) & 0xFFFFFFF); }
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* XXX should we hyper_flush_whole_icache here - Anton */
11062306a36Sopenharmony_cistatic inline void srmmu_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	pte_t pte;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	pte = __pte((SRMMU_ET_PTD | (__nocache_pa(pgdp) >> 4)));
11562306a36Sopenharmony_ci	set_pte((pte_t *)ctxp, pte);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * Locations of MSI Registers.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ci#define MSI_MBUS_ARBEN	0xe0001008	/* MBus Arbiter Enable register */
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci * Useful bits in the MSI Registers.
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_ci#define MSI_ASYNC_MODE  0x80000000	/* Operate the MSI asynchronously */
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic void msi_set_sync(void)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	__asm__ __volatile__ ("lda [%0] %1, %%g3\n\t"
13162306a36Sopenharmony_ci			      "andn %%g3, %2, %%g3\n\t"
13262306a36Sopenharmony_ci			      "sta %%g3, [%0] %1\n\t" : :
13362306a36Sopenharmony_ci			      "r" (MSI_MBUS_ARBEN),
13462306a36Sopenharmony_ci			      "i" (ASI_M_CTL), "r" (MSI_ASYNC_MODE) : "g3");
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_civoid pmd_set(pmd_t *pmdp, pte_t *ptep)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	unsigned long ptp = __nocache_pa(ptep) >> 4;
14062306a36Sopenharmony_ci	set_pte((pte_t *)&pmd_val(*pmdp), __pte(SRMMU_ET_PTD | ptp));
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * size: bytes to allocate in the nocache area.
14562306a36Sopenharmony_ci * align: bytes, number to align at.
14662306a36Sopenharmony_ci * Returns the virtual address of the allocated area.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic void *__srmmu_get_nocache(int size, int align)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	int offset, minsz = 1 << SRMMU_NOCACHE_BITMAP_SHIFT;
15162306a36Sopenharmony_ci	unsigned long addr;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (size < minsz) {
15462306a36Sopenharmony_ci		printk(KERN_ERR "Size 0x%x too small for nocache request\n",
15562306a36Sopenharmony_ci		       size);
15662306a36Sopenharmony_ci		size = minsz;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci	if (size & (minsz - 1)) {
15962306a36Sopenharmony_ci		printk(KERN_ERR "Size 0x%x unaligned in nocache request\n",
16062306a36Sopenharmony_ci		       size);
16162306a36Sopenharmony_ci		size += minsz - 1;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	BUG_ON(align > SRMMU_NOCACHE_ALIGN_MAX);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	offset = bit_map_string_get(&srmmu_nocache_map,
16662306a36Sopenharmony_ci				    size >> SRMMU_NOCACHE_BITMAP_SHIFT,
16762306a36Sopenharmony_ci				    align >> SRMMU_NOCACHE_BITMAP_SHIFT);
16862306a36Sopenharmony_ci	if (offset == -1) {
16962306a36Sopenharmony_ci		printk(KERN_ERR "srmmu: out of nocache %d: %d/%d\n",
17062306a36Sopenharmony_ci		       size, (int) srmmu_nocache_size,
17162306a36Sopenharmony_ci		       srmmu_nocache_map.used << SRMMU_NOCACHE_BITMAP_SHIFT);
17262306a36Sopenharmony_ci		return NULL;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	addr = SRMMU_NOCACHE_VADDR + (offset << SRMMU_NOCACHE_BITMAP_SHIFT);
17662306a36Sopenharmony_ci	return (void *)addr;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_civoid *srmmu_get_nocache(int size, int align)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	void *tmp;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	tmp = __srmmu_get_nocache(size, align);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (tmp)
18662306a36Sopenharmony_ci		memset(tmp, 0, size);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return tmp;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_civoid srmmu_free_nocache(void *addr, int size)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	unsigned long vaddr;
19462306a36Sopenharmony_ci	int offset;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	vaddr = (unsigned long)addr;
19762306a36Sopenharmony_ci	if (vaddr < SRMMU_NOCACHE_VADDR) {
19862306a36Sopenharmony_ci		printk("Vaddr %lx is smaller than nocache base 0x%lx\n",
19962306a36Sopenharmony_ci		    vaddr, (unsigned long)SRMMU_NOCACHE_VADDR);
20062306a36Sopenharmony_ci		BUG();
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	if (vaddr + size > srmmu_nocache_end) {
20362306a36Sopenharmony_ci		printk("Vaddr %lx is bigger than nocache end 0x%lx\n",
20462306a36Sopenharmony_ci		    vaddr, srmmu_nocache_end);
20562306a36Sopenharmony_ci		BUG();
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	if (!is_power_of_2(size)) {
20862306a36Sopenharmony_ci		printk("Size 0x%x is not a power of 2\n", size);
20962306a36Sopenharmony_ci		BUG();
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci	if (size < SRMMU_NOCACHE_BITMAP_SHIFT) {
21262306a36Sopenharmony_ci		printk("Size 0x%x is too small\n", size);
21362306a36Sopenharmony_ci		BUG();
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	if (vaddr & (size - 1)) {
21662306a36Sopenharmony_ci		printk("Vaddr %lx is not aligned to size 0x%x\n", vaddr, size);
21762306a36Sopenharmony_ci		BUG();
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	offset = (vaddr - SRMMU_NOCACHE_VADDR) >> SRMMU_NOCACHE_BITMAP_SHIFT;
22162306a36Sopenharmony_ci	size = size >> SRMMU_NOCACHE_BITMAP_SHIFT;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	bit_map_clear(&srmmu_nocache_map, offset, size);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void srmmu_early_allocate_ptable_skeleton(unsigned long start,
22762306a36Sopenharmony_ci						 unsigned long end);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/* Return how much physical memory we have.  */
23062306a36Sopenharmony_cistatic unsigned long __init probe_memory(void)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	unsigned long total = 0;
23362306a36Sopenharmony_ci	int i;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; sp_banks[i].num_bytes; i++)
23662306a36Sopenharmony_ci		total += sp_banks[i].num_bytes;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return total;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci * Reserve nocache dynamically proportionally to the amount of
24362306a36Sopenharmony_ci * system RAM. -- Tomas Szepe <szepe@pinerecords.com>, June 2002
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic void __init srmmu_nocache_calcsize(void)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	unsigned long sysmemavail = probe_memory() / 1024;
24862306a36Sopenharmony_ci	int srmmu_nocache_npages;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	srmmu_nocache_npages =
25162306a36Sopenharmony_ci		sysmemavail / SRMMU_NOCACHE_ALCRATIO / 1024 * 256;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci /* P3 XXX The 4x overuse: corroborated by /proc/meminfo. */
25462306a36Sopenharmony_ci	// if (srmmu_nocache_npages < 256) srmmu_nocache_npages = 256;
25562306a36Sopenharmony_ci	if (srmmu_nocache_npages < SRMMU_MIN_NOCACHE_PAGES)
25662306a36Sopenharmony_ci		srmmu_nocache_npages = SRMMU_MIN_NOCACHE_PAGES;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* anything above 1280 blows up */
25962306a36Sopenharmony_ci	if (srmmu_nocache_npages > SRMMU_MAX_NOCACHE_PAGES)
26062306a36Sopenharmony_ci		srmmu_nocache_npages = SRMMU_MAX_NOCACHE_PAGES;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	srmmu_nocache_size = srmmu_nocache_npages * PAGE_SIZE;
26362306a36Sopenharmony_ci	srmmu_nocache_end = SRMMU_NOCACHE_VADDR + srmmu_nocache_size;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void __init srmmu_nocache_init(void)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	void *srmmu_nocache_bitmap;
26962306a36Sopenharmony_ci	unsigned int bitmap_bits;
27062306a36Sopenharmony_ci	pgd_t *pgd;
27162306a36Sopenharmony_ci	p4d_t *p4d;
27262306a36Sopenharmony_ci	pud_t *pud;
27362306a36Sopenharmony_ci	pmd_t *pmd;
27462306a36Sopenharmony_ci	pte_t *pte;
27562306a36Sopenharmony_ci	unsigned long paddr, vaddr;
27662306a36Sopenharmony_ci	unsigned long pteval;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	bitmap_bits = srmmu_nocache_size >> SRMMU_NOCACHE_BITMAP_SHIFT;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	srmmu_nocache_pool = memblock_alloc(srmmu_nocache_size,
28162306a36Sopenharmony_ci					    SRMMU_NOCACHE_ALIGN_MAX);
28262306a36Sopenharmony_ci	if (!srmmu_nocache_pool)
28362306a36Sopenharmony_ci		panic("%s: Failed to allocate %lu bytes align=0x%x\n",
28462306a36Sopenharmony_ci		      __func__, srmmu_nocache_size, SRMMU_NOCACHE_ALIGN_MAX);
28562306a36Sopenharmony_ci	memset(srmmu_nocache_pool, 0, srmmu_nocache_size);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	srmmu_nocache_bitmap =
28862306a36Sopenharmony_ci		memblock_alloc(BITS_TO_LONGS(bitmap_bits) * sizeof(long),
28962306a36Sopenharmony_ci			       SMP_CACHE_BYTES);
29062306a36Sopenharmony_ci	if (!srmmu_nocache_bitmap)
29162306a36Sopenharmony_ci		panic("%s: Failed to allocate %zu bytes\n", __func__,
29262306a36Sopenharmony_ci		      BITS_TO_LONGS(bitmap_bits) * sizeof(long));
29362306a36Sopenharmony_ci	bit_map_init(&srmmu_nocache_map, srmmu_nocache_bitmap, bitmap_bits);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	srmmu_swapper_pg_dir = __srmmu_get_nocache(SRMMU_PGD_TABLE_SIZE, SRMMU_PGD_TABLE_SIZE);
29662306a36Sopenharmony_ci	memset(__nocache_fix(srmmu_swapper_pg_dir), 0, SRMMU_PGD_TABLE_SIZE);
29762306a36Sopenharmony_ci	init_mm.pgd = srmmu_swapper_pg_dir;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	srmmu_early_allocate_ptable_skeleton(SRMMU_NOCACHE_VADDR, srmmu_nocache_end);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	paddr = __pa((unsigned long)srmmu_nocache_pool);
30262306a36Sopenharmony_ci	vaddr = SRMMU_NOCACHE_VADDR;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	while (vaddr < srmmu_nocache_end) {
30562306a36Sopenharmony_ci		pgd = pgd_offset_k(vaddr);
30662306a36Sopenharmony_ci		p4d = p4d_offset(pgd, vaddr);
30762306a36Sopenharmony_ci		pud = pud_offset(p4d, vaddr);
30862306a36Sopenharmony_ci		pmd = pmd_offset(__nocache_fix(pud), vaddr);
30962306a36Sopenharmony_ci		pte = pte_offset_kernel(__nocache_fix(pmd), vaddr);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		pteval = ((paddr >> 4) | SRMMU_ET_PTE | SRMMU_PRIV);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		if (srmmu_cache_pagetables)
31462306a36Sopenharmony_ci			pteval |= SRMMU_CACHE;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		set_pte(__nocache_fix(pte), __pte(pteval));
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		vaddr += PAGE_SIZE;
31962306a36Sopenharmony_ci		paddr += PAGE_SIZE;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	flush_cache_all();
32362306a36Sopenharmony_ci	flush_tlb_all();
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cipgd_t *get_pgd_fast(void)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	pgd_t *pgd = NULL;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	pgd = __srmmu_get_nocache(SRMMU_PGD_TABLE_SIZE, SRMMU_PGD_TABLE_SIZE);
33162306a36Sopenharmony_ci	if (pgd) {
33262306a36Sopenharmony_ci		pgd_t *init = pgd_offset_k(0);
33362306a36Sopenharmony_ci		memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
33462306a36Sopenharmony_ci		memcpy(pgd + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
33562306a36Sopenharmony_ci						(PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return pgd;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/*
34262306a36Sopenharmony_ci * Hardware needs alignment to 256 only, but we align to whole page size
34362306a36Sopenharmony_ci * to reduce fragmentation problems due to the buddy principle.
34462306a36Sopenharmony_ci * XXX Provide actual fragmentation statistics in /proc.
34562306a36Sopenharmony_ci *
34662306a36Sopenharmony_ci * Alignments up to the page size are the same for physical and virtual
34762306a36Sopenharmony_ci * addresses of the nocache area.
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_cipgtable_t pte_alloc_one(struct mm_struct *mm)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	pte_t *ptep;
35262306a36Sopenharmony_ci	struct page *page;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (!(ptep = pte_alloc_one_kernel(mm)))
35562306a36Sopenharmony_ci		return NULL;
35662306a36Sopenharmony_ci	page = pfn_to_page(__nocache_pa((unsigned long)ptep) >> PAGE_SHIFT);
35762306a36Sopenharmony_ci	spin_lock(&mm->page_table_lock);
35862306a36Sopenharmony_ci	if (page_ref_inc_return(page) == 2 &&
35962306a36Sopenharmony_ci			!pagetable_pte_ctor(page_ptdesc(page))) {
36062306a36Sopenharmony_ci		page_ref_dec(page);
36162306a36Sopenharmony_ci		ptep = NULL;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	spin_unlock(&mm->page_table_lock);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return ptep;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_civoid pte_free(struct mm_struct *mm, pgtable_t ptep)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct page *page;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	page = pfn_to_page(__nocache_pa((unsigned long)ptep) >> PAGE_SHIFT);
37362306a36Sopenharmony_ci	spin_lock(&mm->page_table_lock);
37462306a36Sopenharmony_ci	if (page_ref_dec_return(page) == 1)
37562306a36Sopenharmony_ci		pagetable_pte_dtor(page_ptdesc(page));
37662306a36Sopenharmony_ci	spin_unlock(&mm->page_table_lock);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	srmmu_free_nocache(ptep, SRMMU_PTE_TABLE_SIZE);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/* context handling - a dynamically sized pool is used */
38262306a36Sopenharmony_ci#define NO_CONTEXT	-1
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistruct ctx_list {
38562306a36Sopenharmony_ci	struct ctx_list *next;
38662306a36Sopenharmony_ci	struct ctx_list *prev;
38762306a36Sopenharmony_ci	unsigned int ctx_number;
38862306a36Sopenharmony_ci	struct mm_struct *ctx_mm;
38962306a36Sopenharmony_ci};
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic struct ctx_list *ctx_list_pool;
39262306a36Sopenharmony_cistatic struct ctx_list ctx_free;
39362306a36Sopenharmony_cistatic struct ctx_list ctx_used;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci/* At boot time we determine the number of contexts */
39662306a36Sopenharmony_cistatic int num_contexts;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic inline void remove_from_ctx_list(struct ctx_list *entry)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	entry->next->prev = entry->prev;
40162306a36Sopenharmony_ci	entry->prev->next = entry->next;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic inline void add_to_ctx_list(struct ctx_list *head, struct ctx_list *entry)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	entry->next = head;
40762306a36Sopenharmony_ci	(entry->prev = head->prev)->next = entry;
40862306a36Sopenharmony_ci	head->prev = entry;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci#define add_to_free_ctxlist(entry) add_to_ctx_list(&ctx_free, entry)
41162306a36Sopenharmony_ci#define add_to_used_ctxlist(entry) add_to_ctx_list(&ctx_used, entry)
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic inline void alloc_context(struct mm_struct *old_mm, struct mm_struct *mm)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct ctx_list *ctxp;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ctxp = ctx_free.next;
41962306a36Sopenharmony_ci	if (ctxp != &ctx_free) {
42062306a36Sopenharmony_ci		remove_from_ctx_list(ctxp);
42162306a36Sopenharmony_ci		add_to_used_ctxlist(ctxp);
42262306a36Sopenharmony_ci		mm->context = ctxp->ctx_number;
42362306a36Sopenharmony_ci		ctxp->ctx_mm = mm;
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	ctxp = ctx_used.next;
42762306a36Sopenharmony_ci	if (ctxp->ctx_mm == old_mm)
42862306a36Sopenharmony_ci		ctxp = ctxp->next;
42962306a36Sopenharmony_ci	if (ctxp == &ctx_used)
43062306a36Sopenharmony_ci		panic("out of mmu contexts");
43162306a36Sopenharmony_ci	flush_cache_mm(ctxp->ctx_mm);
43262306a36Sopenharmony_ci	flush_tlb_mm(ctxp->ctx_mm);
43362306a36Sopenharmony_ci	remove_from_ctx_list(ctxp);
43462306a36Sopenharmony_ci	add_to_used_ctxlist(ctxp);
43562306a36Sopenharmony_ci	ctxp->ctx_mm->context = NO_CONTEXT;
43662306a36Sopenharmony_ci	ctxp->ctx_mm = mm;
43762306a36Sopenharmony_ci	mm->context = ctxp->ctx_number;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic inline void free_context(int context)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct ctx_list *ctx_old;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ctx_old = ctx_list_pool + context;
44562306a36Sopenharmony_ci	remove_from_ctx_list(ctx_old);
44662306a36Sopenharmony_ci	add_to_free_ctxlist(ctx_old);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void __init sparc_context_init(int numctx)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	int ctx;
45262306a36Sopenharmony_ci	unsigned long size;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	size = numctx * sizeof(struct ctx_list);
45562306a36Sopenharmony_ci	ctx_list_pool = memblock_alloc(size, SMP_CACHE_BYTES);
45662306a36Sopenharmony_ci	if (!ctx_list_pool)
45762306a36Sopenharmony_ci		panic("%s: Failed to allocate %lu bytes\n", __func__, size);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	for (ctx = 0; ctx < numctx; ctx++) {
46062306a36Sopenharmony_ci		struct ctx_list *clist;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		clist = (ctx_list_pool + ctx);
46362306a36Sopenharmony_ci		clist->ctx_number = ctx;
46462306a36Sopenharmony_ci		clist->ctx_mm = NULL;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci	ctx_free.next = ctx_free.prev = &ctx_free;
46762306a36Sopenharmony_ci	ctx_used.next = ctx_used.prev = &ctx_used;
46862306a36Sopenharmony_ci	for (ctx = 0; ctx < numctx; ctx++)
46962306a36Sopenharmony_ci		add_to_free_ctxlist(ctx_list_pool + ctx);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_civoid switch_mm(struct mm_struct *old_mm, struct mm_struct *mm,
47362306a36Sopenharmony_ci	       struct task_struct *tsk)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	unsigned long flags;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (mm->context == NO_CONTEXT) {
47862306a36Sopenharmony_ci		spin_lock_irqsave(&srmmu_context_spinlock, flags);
47962306a36Sopenharmony_ci		alloc_context(old_mm, mm);
48062306a36Sopenharmony_ci		spin_unlock_irqrestore(&srmmu_context_spinlock, flags);
48162306a36Sopenharmony_ci		srmmu_ctxd_set(&srmmu_context_table[mm->context], mm->pgd);
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (sparc_cpu_model == sparc_leon)
48562306a36Sopenharmony_ci		leon_switch_mm();
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (is_hypersparc)
48862306a36Sopenharmony_ci		hyper_flush_whole_icache();
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	srmmu_set_context(mm->context);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/* Low level IO area allocation on the SRMMU. */
49462306a36Sopenharmony_cistatic inline void srmmu_mapioaddr(unsigned long physaddr,
49562306a36Sopenharmony_ci				   unsigned long virt_addr, int bus_type)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	pgd_t *pgdp;
49862306a36Sopenharmony_ci	p4d_t *p4dp;
49962306a36Sopenharmony_ci	pud_t *pudp;
50062306a36Sopenharmony_ci	pmd_t *pmdp;
50162306a36Sopenharmony_ci	pte_t *ptep;
50262306a36Sopenharmony_ci	unsigned long tmp;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	physaddr &= PAGE_MASK;
50562306a36Sopenharmony_ci	pgdp = pgd_offset_k(virt_addr);
50662306a36Sopenharmony_ci	p4dp = p4d_offset(pgdp, virt_addr);
50762306a36Sopenharmony_ci	pudp = pud_offset(p4dp, virt_addr);
50862306a36Sopenharmony_ci	pmdp = pmd_offset(pudp, virt_addr);
50962306a36Sopenharmony_ci	ptep = pte_offset_kernel(pmdp, virt_addr);
51062306a36Sopenharmony_ci	tmp = (physaddr >> 4) | SRMMU_ET_PTE;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* I need to test whether this is consistent over all
51362306a36Sopenharmony_ci	 * sun4m's.  The bus_type represents the upper 4 bits of
51462306a36Sopenharmony_ci	 * 36-bit physical address on the I/O space lines...
51562306a36Sopenharmony_ci	 */
51662306a36Sopenharmony_ci	tmp |= (bus_type << 28);
51762306a36Sopenharmony_ci	tmp |= SRMMU_PRIV;
51862306a36Sopenharmony_ci	__flush_page_to_ram(virt_addr);
51962306a36Sopenharmony_ci	set_pte(ptep, __pte(tmp));
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_civoid srmmu_mapiorange(unsigned int bus, unsigned long xpa,
52362306a36Sopenharmony_ci		      unsigned long xva, unsigned int len)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	while (len != 0) {
52662306a36Sopenharmony_ci		len -= PAGE_SIZE;
52762306a36Sopenharmony_ci		srmmu_mapioaddr(xpa, xva, bus);
52862306a36Sopenharmony_ci		xva += PAGE_SIZE;
52962306a36Sopenharmony_ci		xpa += PAGE_SIZE;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci	flush_tlb_all();
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic inline void srmmu_unmapioaddr(unsigned long virt_addr)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	pgd_t *pgdp;
53762306a36Sopenharmony_ci	p4d_t *p4dp;
53862306a36Sopenharmony_ci	pud_t *pudp;
53962306a36Sopenharmony_ci	pmd_t *pmdp;
54062306a36Sopenharmony_ci	pte_t *ptep;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	pgdp = pgd_offset_k(virt_addr);
54462306a36Sopenharmony_ci	p4dp = p4d_offset(pgdp, virt_addr);
54562306a36Sopenharmony_ci	pudp = pud_offset(p4dp, virt_addr);
54662306a36Sopenharmony_ci	pmdp = pmd_offset(pudp, virt_addr);
54762306a36Sopenharmony_ci	ptep = pte_offset_kernel(pmdp, virt_addr);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* No need to flush uncacheable page. */
55062306a36Sopenharmony_ci	__pte_clear(ptep);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_civoid srmmu_unmapiorange(unsigned long virt_addr, unsigned int len)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	while (len != 0) {
55662306a36Sopenharmony_ci		len -= PAGE_SIZE;
55762306a36Sopenharmony_ci		srmmu_unmapioaddr(virt_addr);
55862306a36Sopenharmony_ci		virt_addr += PAGE_SIZE;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	flush_tlb_all();
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* tsunami.S */
56462306a36Sopenharmony_ciextern void tsunami_flush_cache_all(void);
56562306a36Sopenharmony_ciextern void tsunami_flush_cache_mm(struct mm_struct *mm);
56662306a36Sopenharmony_ciextern void tsunami_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
56762306a36Sopenharmony_ciextern void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
56862306a36Sopenharmony_ciextern void tsunami_flush_page_to_ram(unsigned long page);
56962306a36Sopenharmony_ciextern void tsunami_flush_page_for_dma(unsigned long page);
57062306a36Sopenharmony_ciextern void tsunami_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
57162306a36Sopenharmony_ciextern void tsunami_flush_tlb_all(void);
57262306a36Sopenharmony_ciextern void tsunami_flush_tlb_mm(struct mm_struct *mm);
57362306a36Sopenharmony_ciextern void tsunami_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
57462306a36Sopenharmony_ciextern void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
57562306a36Sopenharmony_ciextern void tsunami_setup_blockops(void);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/* swift.S */
57862306a36Sopenharmony_ciextern void swift_flush_cache_all(void);
57962306a36Sopenharmony_ciextern void swift_flush_cache_mm(struct mm_struct *mm);
58062306a36Sopenharmony_ciextern void swift_flush_cache_range(struct vm_area_struct *vma,
58162306a36Sopenharmony_ci				    unsigned long start, unsigned long end);
58262306a36Sopenharmony_ciextern void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
58362306a36Sopenharmony_ciextern void swift_flush_page_to_ram(unsigned long page);
58462306a36Sopenharmony_ciextern void swift_flush_page_for_dma(unsigned long page);
58562306a36Sopenharmony_ciextern void swift_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
58662306a36Sopenharmony_ciextern void swift_flush_tlb_all(void);
58762306a36Sopenharmony_ciextern void swift_flush_tlb_mm(struct mm_struct *mm);
58862306a36Sopenharmony_ciextern void swift_flush_tlb_range(struct vm_area_struct *vma,
58962306a36Sopenharmony_ci				  unsigned long start, unsigned long end);
59062306a36Sopenharmony_ciextern void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci#if 0  /* P3: deadwood to debug precise flushes on Swift. */
59362306a36Sopenharmony_civoid swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	int cctx, ctx1;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	page &= PAGE_MASK;
59862306a36Sopenharmony_ci	if ((ctx1 = vma->vm_mm->context) != -1) {
59962306a36Sopenharmony_ci		cctx = srmmu_get_context();
60062306a36Sopenharmony_ci/* Is context # ever different from current context? P3 */
60162306a36Sopenharmony_ci		if (cctx != ctx1) {
60262306a36Sopenharmony_ci			printk("flush ctx %02x curr %02x\n", ctx1, cctx);
60362306a36Sopenharmony_ci			srmmu_set_context(ctx1);
60462306a36Sopenharmony_ci			swift_flush_page(page);
60562306a36Sopenharmony_ci			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
60662306a36Sopenharmony_ci					"r" (page), "i" (ASI_M_FLUSH_PROBE));
60762306a36Sopenharmony_ci			srmmu_set_context(cctx);
60862306a36Sopenharmony_ci		} else {
60962306a36Sopenharmony_ci			 /* Rm. prot. bits from virt. c. */
61062306a36Sopenharmony_ci			/* swift_flush_cache_all(); */
61162306a36Sopenharmony_ci			/* swift_flush_cache_page(vma, page); */
61262306a36Sopenharmony_ci			swift_flush_page(page);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci			__asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
61562306a36Sopenharmony_ci				"r" (page), "i" (ASI_M_FLUSH_PROBE));
61662306a36Sopenharmony_ci			/* same as above: srmmu_flush_tlb_page() */
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci#endif
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci/*
62362306a36Sopenharmony_ci * The following are all MBUS based SRMMU modules, and therefore could
62462306a36Sopenharmony_ci * be found in a multiprocessor configuration.  On the whole, these
62562306a36Sopenharmony_ci * chips seems to be much more touchy about DVMA and page tables
62662306a36Sopenharmony_ci * with respect to cache coherency.
62762306a36Sopenharmony_ci */
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/* viking.S */
63062306a36Sopenharmony_ciextern void viking_flush_cache_all(void);
63162306a36Sopenharmony_ciextern void viking_flush_cache_mm(struct mm_struct *mm);
63262306a36Sopenharmony_ciextern void viking_flush_cache_range(struct vm_area_struct *vma, unsigned long start,
63362306a36Sopenharmony_ci				     unsigned long end);
63462306a36Sopenharmony_ciextern void viking_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
63562306a36Sopenharmony_ciextern void viking_flush_page_to_ram(unsigned long page);
63662306a36Sopenharmony_ciextern void viking_flush_page_for_dma(unsigned long page);
63762306a36Sopenharmony_ciextern void viking_flush_sig_insns(struct mm_struct *mm, unsigned long addr);
63862306a36Sopenharmony_ciextern void viking_flush_page(unsigned long page);
63962306a36Sopenharmony_ciextern void viking_mxcc_flush_page(unsigned long page);
64062306a36Sopenharmony_ciextern void viking_flush_tlb_all(void);
64162306a36Sopenharmony_ciextern void viking_flush_tlb_mm(struct mm_struct *mm);
64262306a36Sopenharmony_ciextern void viking_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
64362306a36Sopenharmony_ci				   unsigned long end);
64462306a36Sopenharmony_ciextern void viking_flush_tlb_page(struct vm_area_struct *vma,
64562306a36Sopenharmony_ci				  unsigned long page);
64662306a36Sopenharmony_ciextern void sun4dsmp_flush_tlb_all(void);
64762306a36Sopenharmony_ciextern void sun4dsmp_flush_tlb_mm(struct mm_struct *mm);
64862306a36Sopenharmony_ciextern void sun4dsmp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
64962306a36Sopenharmony_ci				   unsigned long end);
65062306a36Sopenharmony_ciextern void sun4dsmp_flush_tlb_page(struct vm_area_struct *vma,
65162306a36Sopenharmony_ci				  unsigned long page);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci/* hypersparc.S */
65462306a36Sopenharmony_ciextern void hypersparc_flush_cache_all(void);
65562306a36Sopenharmony_ciextern void hypersparc_flush_cache_mm(struct mm_struct *mm);
65662306a36Sopenharmony_ciextern void hypersparc_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
65762306a36Sopenharmony_ciextern void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page);
65862306a36Sopenharmony_ciextern void hypersparc_flush_page_to_ram(unsigned long page);
65962306a36Sopenharmony_ciextern void hypersparc_flush_page_for_dma(unsigned long page);
66062306a36Sopenharmony_ciextern void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
66162306a36Sopenharmony_ciextern void hypersparc_flush_tlb_all(void);
66262306a36Sopenharmony_ciextern void hypersparc_flush_tlb_mm(struct mm_struct *mm);
66362306a36Sopenharmony_ciextern void hypersparc_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
66462306a36Sopenharmony_ciextern void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
66562306a36Sopenharmony_ciextern void hypersparc_setup_blockops(void);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/*
66862306a36Sopenharmony_ci * NOTE: All of this startup code assumes the low 16mb (approx.) of
66962306a36Sopenharmony_ci *       kernel mappings are done with one single contiguous chunk of
67062306a36Sopenharmony_ci *       ram.  On small ram machines (classics mainly) we only get
67162306a36Sopenharmony_ci *       around 8mb mapped for us.
67262306a36Sopenharmony_ci */
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void __init early_pgtable_allocfail(char *type)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	prom_printf("inherit_prom_mappings: Cannot alloc kernel %s.\n", type);
67762306a36Sopenharmony_ci	prom_halt();
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic void __init srmmu_early_allocate_ptable_skeleton(unsigned long start,
68162306a36Sopenharmony_ci							unsigned long end)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	pgd_t *pgdp;
68462306a36Sopenharmony_ci	p4d_t *p4dp;
68562306a36Sopenharmony_ci	pud_t *pudp;
68662306a36Sopenharmony_ci	pmd_t *pmdp;
68762306a36Sopenharmony_ci	pte_t *ptep;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	while (start < end) {
69062306a36Sopenharmony_ci		pgdp = pgd_offset_k(start);
69162306a36Sopenharmony_ci		p4dp = p4d_offset(pgdp, start);
69262306a36Sopenharmony_ci		pudp = pud_offset(p4dp, start);
69362306a36Sopenharmony_ci		if (pud_none(*__nocache_fix(pudp))) {
69462306a36Sopenharmony_ci			pmdp = __srmmu_get_nocache(
69562306a36Sopenharmony_ci			    SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE);
69662306a36Sopenharmony_ci			if (pmdp == NULL)
69762306a36Sopenharmony_ci				early_pgtable_allocfail("pmd");
69862306a36Sopenharmony_ci			memset(__nocache_fix(pmdp), 0, SRMMU_PMD_TABLE_SIZE);
69962306a36Sopenharmony_ci			pud_set(__nocache_fix(pudp), pmdp);
70062306a36Sopenharmony_ci		}
70162306a36Sopenharmony_ci		pmdp = pmd_offset(__nocache_fix(pudp), start);
70262306a36Sopenharmony_ci		if (srmmu_pmd_none(*__nocache_fix(pmdp))) {
70362306a36Sopenharmony_ci			ptep = __srmmu_get_nocache(PTE_SIZE, PTE_SIZE);
70462306a36Sopenharmony_ci			if (ptep == NULL)
70562306a36Sopenharmony_ci				early_pgtable_allocfail("pte");
70662306a36Sopenharmony_ci			memset(__nocache_fix(ptep), 0, PTE_SIZE);
70762306a36Sopenharmony_ci			pmd_set(__nocache_fix(pmdp), ptep);
70862306a36Sopenharmony_ci		}
70962306a36Sopenharmony_ci		if (start > (0xffffffffUL - PMD_SIZE))
71062306a36Sopenharmony_ci			break;
71162306a36Sopenharmony_ci		start = (start + PMD_SIZE) & PMD_MASK;
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void __init srmmu_allocate_ptable_skeleton(unsigned long start,
71662306a36Sopenharmony_ci						  unsigned long end)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	pgd_t *pgdp;
71962306a36Sopenharmony_ci	p4d_t *p4dp;
72062306a36Sopenharmony_ci	pud_t *pudp;
72162306a36Sopenharmony_ci	pmd_t *pmdp;
72262306a36Sopenharmony_ci	pte_t *ptep;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	while (start < end) {
72562306a36Sopenharmony_ci		pgdp = pgd_offset_k(start);
72662306a36Sopenharmony_ci		p4dp = p4d_offset(pgdp, start);
72762306a36Sopenharmony_ci		pudp = pud_offset(p4dp, start);
72862306a36Sopenharmony_ci		if (pud_none(*pudp)) {
72962306a36Sopenharmony_ci			pmdp = __srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE);
73062306a36Sopenharmony_ci			if (pmdp == NULL)
73162306a36Sopenharmony_ci				early_pgtable_allocfail("pmd");
73262306a36Sopenharmony_ci			memset(pmdp, 0, SRMMU_PMD_TABLE_SIZE);
73362306a36Sopenharmony_ci			pud_set((pud_t *)pgdp, pmdp);
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci		pmdp = pmd_offset(pudp, start);
73662306a36Sopenharmony_ci		if (srmmu_pmd_none(*pmdp)) {
73762306a36Sopenharmony_ci			ptep = __srmmu_get_nocache(PTE_SIZE,
73862306a36Sopenharmony_ci							     PTE_SIZE);
73962306a36Sopenharmony_ci			if (ptep == NULL)
74062306a36Sopenharmony_ci				early_pgtable_allocfail("pte");
74162306a36Sopenharmony_ci			memset(ptep, 0, PTE_SIZE);
74262306a36Sopenharmony_ci			pmd_set(pmdp, ptep);
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci		if (start > (0xffffffffUL - PMD_SIZE))
74562306a36Sopenharmony_ci			break;
74662306a36Sopenharmony_ci		start = (start + PMD_SIZE) & PMD_MASK;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci/* These flush types are not available on all chips... */
75162306a36Sopenharmony_cistatic inline unsigned long srmmu_probe(unsigned long vaddr)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	unsigned long retval;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	if (sparc_cpu_model != sparc_leon) {
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		vaddr &= PAGE_MASK;
75862306a36Sopenharmony_ci		__asm__ __volatile__("lda [%1] %2, %0\n\t" :
75962306a36Sopenharmony_ci				     "=r" (retval) :
76062306a36Sopenharmony_ci				     "r" (vaddr | 0x400), "i" (ASI_M_FLUSH_PROBE));
76162306a36Sopenharmony_ci	} else {
76262306a36Sopenharmony_ci		retval = leon_swprobe(vaddr, NULL);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	return retval;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci/*
76862306a36Sopenharmony_ci * This is much cleaner than poking around physical address space
76962306a36Sopenharmony_ci * looking at the prom's page table directly which is what most
77062306a36Sopenharmony_ci * other OS's do.  Yuck... this is much better.
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_cistatic void __init srmmu_inherit_prom_mappings(unsigned long start,
77362306a36Sopenharmony_ci					       unsigned long end)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	unsigned long probed;
77662306a36Sopenharmony_ci	unsigned long addr;
77762306a36Sopenharmony_ci	pgd_t *pgdp;
77862306a36Sopenharmony_ci	p4d_t *p4dp;
77962306a36Sopenharmony_ci	pud_t *pudp;
78062306a36Sopenharmony_ci	pmd_t *pmdp;
78162306a36Sopenharmony_ci	pte_t *ptep;
78262306a36Sopenharmony_ci	int what; /* 0 = normal-pte, 1 = pmd-level pte, 2 = pgd-level pte */
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	while (start <= end) {
78562306a36Sopenharmony_ci		if (start == 0)
78662306a36Sopenharmony_ci			break; /* probably wrap around */
78762306a36Sopenharmony_ci		if (start == 0xfef00000)
78862306a36Sopenharmony_ci			start = KADB_DEBUGGER_BEGVM;
78962306a36Sopenharmony_ci		probed = srmmu_probe(start);
79062306a36Sopenharmony_ci		if (!probed) {
79162306a36Sopenharmony_ci			/* continue probing until we find an entry */
79262306a36Sopenharmony_ci			start += PAGE_SIZE;
79362306a36Sopenharmony_ci			continue;
79462306a36Sopenharmony_ci		}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		/* A red snapper, see what it really is. */
79762306a36Sopenharmony_ci		what = 0;
79862306a36Sopenharmony_ci		addr = start - PAGE_SIZE;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		if (!(start & ~(PMD_MASK))) {
80162306a36Sopenharmony_ci			if (srmmu_probe(addr + PMD_SIZE) == probed)
80262306a36Sopenharmony_ci				what = 1;
80362306a36Sopenharmony_ci		}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci		if (!(start & ~(PGDIR_MASK))) {
80662306a36Sopenharmony_ci			if (srmmu_probe(addr + PGDIR_SIZE) == probed)
80762306a36Sopenharmony_ci				what = 2;
80862306a36Sopenharmony_ci		}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		pgdp = pgd_offset_k(start);
81162306a36Sopenharmony_ci		p4dp = p4d_offset(pgdp, start);
81262306a36Sopenharmony_ci		pudp = pud_offset(p4dp, start);
81362306a36Sopenharmony_ci		if (what == 2) {
81462306a36Sopenharmony_ci			*__nocache_fix(pgdp) = __pgd(probed);
81562306a36Sopenharmony_ci			start += PGDIR_SIZE;
81662306a36Sopenharmony_ci			continue;
81762306a36Sopenharmony_ci		}
81862306a36Sopenharmony_ci		if (pud_none(*__nocache_fix(pudp))) {
81962306a36Sopenharmony_ci			pmdp = __srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE,
82062306a36Sopenharmony_ci						   SRMMU_PMD_TABLE_SIZE);
82162306a36Sopenharmony_ci			if (pmdp == NULL)
82262306a36Sopenharmony_ci				early_pgtable_allocfail("pmd");
82362306a36Sopenharmony_ci			memset(__nocache_fix(pmdp), 0, SRMMU_PMD_TABLE_SIZE);
82462306a36Sopenharmony_ci			pud_set(__nocache_fix(pudp), pmdp);
82562306a36Sopenharmony_ci		}
82662306a36Sopenharmony_ci		pmdp = pmd_offset(__nocache_fix(pudp), start);
82762306a36Sopenharmony_ci		if (what == 1) {
82862306a36Sopenharmony_ci			*(pmd_t *)__nocache_fix(pmdp) = __pmd(probed);
82962306a36Sopenharmony_ci			start += PMD_SIZE;
83062306a36Sopenharmony_ci			continue;
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci		if (srmmu_pmd_none(*__nocache_fix(pmdp))) {
83362306a36Sopenharmony_ci			ptep = __srmmu_get_nocache(PTE_SIZE, PTE_SIZE);
83462306a36Sopenharmony_ci			if (ptep == NULL)
83562306a36Sopenharmony_ci				early_pgtable_allocfail("pte");
83662306a36Sopenharmony_ci			memset(__nocache_fix(ptep), 0, PTE_SIZE);
83762306a36Sopenharmony_ci			pmd_set(__nocache_fix(pmdp), ptep);
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci		ptep = pte_offset_kernel(__nocache_fix(pmdp), start);
84062306a36Sopenharmony_ci		*__nocache_fix(ptep) = __pte(probed);
84162306a36Sopenharmony_ci		start += PAGE_SIZE;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci#define KERNEL_PTE(page_shifted) ((page_shifted)|SRMMU_CACHE|SRMMU_PRIV|SRMMU_VALID)
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci/* Create a third-level SRMMU 16MB page mapping. */
84862306a36Sopenharmony_cistatic void __init do_large_mapping(unsigned long vaddr, unsigned long phys_base)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	pgd_t *pgdp = pgd_offset_k(vaddr);
85162306a36Sopenharmony_ci	unsigned long big_pte;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	big_pte = KERNEL_PTE(phys_base >> 4);
85462306a36Sopenharmony_ci	*__nocache_fix(pgdp) = __pgd(big_pte);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci/* Map sp_bank entry SP_ENTRY, starting at virtual address VBASE. */
85862306a36Sopenharmony_cistatic unsigned long __init map_spbank(unsigned long vbase, int sp_entry)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	unsigned long pstart = (sp_banks[sp_entry].base_addr & PGDIR_MASK);
86162306a36Sopenharmony_ci	unsigned long vstart = (vbase & PGDIR_MASK);
86262306a36Sopenharmony_ci	unsigned long vend = PGDIR_ALIGN(vbase + sp_banks[sp_entry].num_bytes);
86362306a36Sopenharmony_ci	/* Map "low" memory only */
86462306a36Sopenharmony_ci	const unsigned long min_vaddr = PAGE_OFFSET;
86562306a36Sopenharmony_ci	const unsigned long max_vaddr = PAGE_OFFSET + SRMMU_MAXMEM;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (vstart < min_vaddr || vstart >= max_vaddr)
86862306a36Sopenharmony_ci		return vstart;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	if (vend > max_vaddr || vend < min_vaddr)
87162306a36Sopenharmony_ci		vend = max_vaddr;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	while (vstart < vend) {
87462306a36Sopenharmony_ci		do_large_mapping(vstart, pstart);
87562306a36Sopenharmony_ci		vstart += PGDIR_SIZE; pstart += PGDIR_SIZE;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci	return vstart;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic void __init map_kernel(void)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	int i;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	if (phys_base > 0) {
88562306a36Sopenharmony_ci		do_large_mapping(PAGE_OFFSET, phys_base);
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	for (i = 0; sp_banks[i].num_bytes != 0; i++) {
88962306a36Sopenharmony_ci		map_spbank((unsigned long)__va(sp_banks[i].base_addr), i);
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_civoid (*poke_srmmu)(void) = NULL;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_civoid __init srmmu_paging_init(void)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	int i;
89862306a36Sopenharmony_ci	phandle cpunode;
89962306a36Sopenharmony_ci	char node_str[128];
90062306a36Sopenharmony_ci	pgd_t *pgd;
90162306a36Sopenharmony_ci	p4d_t *p4d;
90262306a36Sopenharmony_ci	pud_t *pud;
90362306a36Sopenharmony_ci	pmd_t *pmd;
90462306a36Sopenharmony_ci	pte_t *pte;
90562306a36Sopenharmony_ci	unsigned long pages_avail;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	init_mm.context = (unsigned long) NO_CONTEXT;
90862306a36Sopenharmony_ci	sparc_iomap.start = SUN4M_IOBASE_VADDR;	/* 16MB of IOSPACE on all sun4m's. */
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (sparc_cpu_model == sun4d)
91162306a36Sopenharmony_ci		num_contexts = 65536; /* We know it is Viking */
91262306a36Sopenharmony_ci	else {
91362306a36Sopenharmony_ci		/* Find the number of contexts on the srmmu. */
91462306a36Sopenharmony_ci		cpunode = prom_getchild(prom_root_node);
91562306a36Sopenharmony_ci		num_contexts = 0;
91662306a36Sopenharmony_ci		while (cpunode != 0) {
91762306a36Sopenharmony_ci			prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
91862306a36Sopenharmony_ci			if (!strcmp(node_str, "cpu")) {
91962306a36Sopenharmony_ci				num_contexts = prom_getintdefault(cpunode, "mmu-nctx", 0x8);
92062306a36Sopenharmony_ci				break;
92162306a36Sopenharmony_ci			}
92262306a36Sopenharmony_ci			cpunode = prom_getsibling(cpunode);
92362306a36Sopenharmony_ci		}
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (!num_contexts) {
92762306a36Sopenharmony_ci		prom_printf("Something wrong, can't find cpu node in paging_init.\n");
92862306a36Sopenharmony_ci		prom_halt();
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	pages_avail = 0;
93262306a36Sopenharmony_ci	last_valid_pfn = bootmem_init(&pages_avail);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	srmmu_nocache_calcsize();
93562306a36Sopenharmony_ci	srmmu_nocache_init();
93662306a36Sopenharmony_ci	srmmu_inherit_prom_mappings(0xfe400000, (LINUX_OPPROM_ENDVM - PAGE_SIZE));
93762306a36Sopenharmony_ci	map_kernel();
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* ctx table has to be physically aligned to its size */
94062306a36Sopenharmony_ci	srmmu_context_table = __srmmu_get_nocache(num_contexts * sizeof(ctxd_t), num_contexts * sizeof(ctxd_t));
94162306a36Sopenharmony_ci	srmmu_ctx_table_phys = (ctxd_t *)__nocache_pa(srmmu_context_table);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	for (i = 0; i < num_contexts; i++)
94462306a36Sopenharmony_ci		srmmu_ctxd_set(__nocache_fix(&srmmu_context_table[i]), srmmu_swapper_pg_dir);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	flush_cache_all();
94762306a36Sopenharmony_ci	srmmu_set_ctable_ptr((unsigned long)srmmu_ctx_table_phys);
94862306a36Sopenharmony_ci#ifdef CONFIG_SMP
94962306a36Sopenharmony_ci	/* Stop from hanging here... */
95062306a36Sopenharmony_ci	local_ops->tlb_all();
95162306a36Sopenharmony_ci#else
95262306a36Sopenharmony_ci	flush_tlb_all();
95362306a36Sopenharmony_ci#endif
95462306a36Sopenharmony_ci	poke_srmmu();
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	srmmu_allocate_ptable_skeleton(sparc_iomap.start, IOBASE_END);
95762306a36Sopenharmony_ci	srmmu_allocate_ptable_skeleton(DVMA_VADDR, DVMA_END);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	srmmu_allocate_ptable_skeleton(
96062306a36Sopenharmony_ci		__fix_to_virt(__end_of_fixed_addresses - 1), FIXADDR_TOP);
96162306a36Sopenharmony_ci	srmmu_allocate_ptable_skeleton(PKMAP_BASE, PKMAP_END);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	pgd = pgd_offset_k(PKMAP_BASE);
96462306a36Sopenharmony_ci	p4d = p4d_offset(pgd, PKMAP_BASE);
96562306a36Sopenharmony_ci	pud = pud_offset(p4d, PKMAP_BASE);
96662306a36Sopenharmony_ci	pmd = pmd_offset(pud, PKMAP_BASE);
96762306a36Sopenharmony_ci	pte = pte_offset_kernel(pmd, PKMAP_BASE);
96862306a36Sopenharmony_ci	pkmap_page_table = pte;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	flush_cache_all();
97162306a36Sopenharmony_ci	flush_tlb_all();
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	sparc_context_init(num_contexts);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	{
97662306a36Sopenharmony_ci		unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 };
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		max_zone_pfn[ZONE_DMA] = max_low_pfn;
97962306a36Sopenharmony_ci		max_zone_pfn[ZONE_NORMAL] = max_low_pfn;
98062306a36Sopenharmony_ci		max_zone_pfn[ZONE_HIGHMEM] = highend_pfn;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		free_area_init(max_zone_pfn);
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_civoid mmu_info(struct seq_file *m)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	seq_printf(m,
98962306a36Sopenharmony_ci		   "MMU type\t: %s\n"
99062306a36Sopenharmony_ci		   "contexts\t: %d\n"
99162306a36Sopenharmony_ci		   "nocache total\t: %ld\n"
99262306a36Sopenharmony_ci		   "nocache used\t: %d\n",
99362306a36Sopenharmony_ci		   srmmu_name,
99462306a36Sopenharmony_ci		   num_contexts,
99562306a36Sopenharmony_ci		   srmmu_nocache_size,
99662306a36Sopenharmony_ci		   srmmu_nocache_map.used << SRMMU_NOCACHE_BITMAP_SHIFT);
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ciint init_new_context(struct task_struct *tsk, struct mm_struct *mm)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	mm->context = NO_CONTEXT;
100262306a36Sopenharmony_ci	return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_civoid destroy_context(struct mm_struct *mm)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	unsigned long flags;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
101062306a36Sopenharmony_ci		flush_cache_mm(mm);
101162306a36Sopenharmony_ci		srmmu_ctxd_set(&srmmu_context_table[mm->context], srmmu_swapper_pg_dir);
101262306a36Sopenharmony_ci		flush_tlb_mm(mm);
101362306a36Sopenharmony_ci		spin_lock_irqsave(&srmmu_context_spinlock, flags);
101462306a36Sopenharmony_ci		free_context(mm->context);
101562306a36Sopenharmony_ci		spin_unlock_irqrestore(&srmmu_context_spinlock, flags);
101662306a36Sopenharmony_ci		mm->context = NO_CONTEXT;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci/* Init various srmmu chip types. */
102162306a36Sopenharmony_cistatic void __init srmmu_is_bad(void)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	prom_printf("Could not determine SRMMU chip type.\n");
102462306a36Sopenharmony_ci	prom_halt();
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic void __init init_vac_layout(void)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	phandle nd;
103062306a36Sopenharmony_ci	int cache_lines;
103162306a36Sopenharmony_ci	char node_str[128];
103262306a36Sopenharmony_ci#ifdef CONFIG_SMP
103362306a36Sopenharmony_ci	int cpu = 0;
103462306a36Sopenharmony_ci	unsigned long max_size = 0;
103562306a36Sopenharmony_ci	unsigned long min_line_size = 0x10000000;
103662306a36Sopenharmony_ci#endif
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	nd = prom_getchild(prom_root_node);
103962306a36Sopenharmony_ci	while ((nd = prom_getsibling(nd)) != 0) {
104062306a36Sopenharmony_ci		prom_getstring(nd, "device_type", node_str, sizeof(node_str));
104162306a36Sopenharmony_ci		if (!strcmp(node_str, "cpu")) {
104262306a36Sopenharmony_ci			vac_line_size = prom_getint(nd, "cache-line-size");
104362306a36Sopenharmony_ci			if (vac_line_size == -1) {
104462306a36Sopenharmony_ci				prom_printf("can't determine cache-line-size, halting.\n");
104562306a36Sopenharmony_ci				prom_halt();
104662306a36Sopenharmony_ci			}
104762306a36Sopenharmony_ci			cache_lines = prom_getint(nd, "cache-nlines");
104862306a36Sopenharmony_ci			if (cache_lines == -1) {
104962306a36Sopenharmony_ci				prom_printf("can't determine cache-nlines, halting.\n");
105062306a36Sopenharmony_ci				prom_halt();
105162306a36Sopenharmony_ci			}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci			vac_cache_size = cache_lines * vac_line_size;
105462306a36Sopenharmony_ci#ifdef CONFIG_SMP
105562306a36Sopenharmony_ci			if (vac_cache_size > max_size)
105662306a36Sopenharmony_ci				max_size = vac_cache_size;
105762306a36Sopenharmony_ci			if (vac_line_size < min_line_size)
105862306a36Sopenharmony_ci				min_line_size = vac_line_size;
105962306a36Sopenharmony_ci			//FIXME: cpus not contiguous!!
106062306a36Sopenharmony_ci			cpu++;
106162306a36Sopenharmony_ci			if (cpu >= nr_cpu_ids || !cpu_online(cpu))
106262306a36Sopenharmony_ci				break;
106362306a36Sopenharmony_ci#else
106462306a36Sopenharmony_ci			break;
106562306a36Sopenharmony_ci#endif
106662306a36Sopenharmony_ci		}
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci	if (nd == 0) {
106962306a36Sopenharmony_ci		prom_printf("No CPU nodes found, halting.\n");
107062306a36Sopenharmony_ci		prom_halt();
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci#ifdef CONFIG_SMP
107362306a36Sopenharmony_ci	vac_cache_size = max_size;
107462306a36Sopenharmony_ci	vac_line_size = min_line_size;
107562306a36Sopenharmony_ci#endif
107662306a36Sopenharmony_ci	printk("SRMMU: Using VAC size of %d bytes, line size %d bytes.\n",
107762306a36Sopenharmony_ci	       (int)vac_cache_size, (int)vac_line_size);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic void poke_hypersparc(void)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	volatile unsigned long clear;
108362306a36Sopenharmony_ci	unsigned long mreg = srmmu_get_mmureg();
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	hyper_flush_unconditional_combined();
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	mreg &= ~(HYPERSPARC_CWENABLE);
108862306a36Sopenharmony_ci	mreg |= (HYPERSPARC_CENABLE | HYPERSPARC_WBENABLE);
108962306a36Sopenharmony_ci	mreg |= (HYPERSPARC_CMODE);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	srmmu_set_mmureg(mreg);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci#if 0 /* XXX I think this is bad news... -DaveM */
109462306a36Sopenharmony_ci	hyper_clear_all_tags();
109562306a36Sopenharmony_ci#endif
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	put_ross_icr(HYPERSPARC_ICCR_FTD | HYPERSPARC_ICCR_ICE);
109862306a36Sopenharmony_ci	hyper_flush_whole_icache();
109962306a36Sopenharmony_ci	clear = srmmu_get_faddr();
110062306a36Sopenharmony_ci	clear = srmmu_get_fstatus();
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic const struct sparc32_cachetlb_ops hypersparc_ops = {
110462306a36Sopenharmony_ci	.cache_all	= hypersparc_flush_cache_all,
110562306a36Sopenharmony_ci	.cache_mm	= hypersparc_flush_cache_mm,
110662306a36Sopenharmony_ci	.cache_page	= hypersparc_flush_cache_page,
110762306a36Sopenharmony_ci	.cache_range	= hypersparc_flush_cache_range,
110862306a36Sopenharmony_ci	.tlb_all	= hypersparc_flush_tlb_all,
110962306a36Sopenharmony_ci	.tlb_mm		= hypersparc_flush_tlb_mm,
111062306a36Sopenharmony_ci	.tlb_page	= hypersparc_flush_tlb_page,
111162306a36Sopenharmony_ci	.tlb_range	= hypersparc_flush_tlb_range,
111262306a36Sopenharmony_ci	.page_to_ram	= hypersparc_flush_page_to_ram,
111362306a36Sopenharmony_ci	.sig_insns	= hypersparc_flush_sig_insns,
111462306a36Sopenharmony_ci	.page_for_dma	= hypersparc_flush_page_for_dma,
111562306a36Sopenharmony_ci};
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic void __init init_hypersparc(void)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	srmmu_name = "ROSS HyperSparc";
112062306a36Sopenharmony_ci	srmmu_modtype = HyperSparc;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	init_vac_layout();
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	is_hypersparc = 1;
112562306a36Sopenharmony_ci	sparc32_cachetlb_ops = &hypersparc_ops;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	poke_srmmu = poke_hypersparc;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	hypersparc_setup_blockops();
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void poke_swift(void)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	unsigned long mreg;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	/* Clear any crap from the cache or else... */
113762306a36Sopenharmony_ci	swift_flush_cache_all();
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* Enable I & D caches */
114062306a36Sopenharmony_ci	mreg = srmmu_get_mmureg();
114162306a36Sopenharmony_ci	mreg |= (SWIFT_IE | SWIFT_DE);
114262306a36Sopenharmony_ci	/*
114362306a36Sopenharmony_ci	 * The Swift branch folding logic is completely broken.  At
114462306a36Sopenharmony_ci	 * trap time, if things are just right, if can mistakenly
114562306a36Sopenharmony_ci	 * think that a trap is coming from kernel mode when in fact
114662306a36Sopenharmony_ci	 * it is coming from user mode (it mis-executes the branch in
114762306a36Sopenharmony_ci	 * the trap code).  So you see things like crashme completely
114862306a36Sopenharmony_ci	 * hosing your machine which is completely unacceptable.  Turn
114962306a36Sopenharmony_ci	 * this shit off... nice job Fujitsu.
115062306a36Sopenharmony_ci	 */
115162306a36Sopenharmony_ci	mreg &= ~(SWIFT_BF);
115262306a36Sopenharmony_ci	srmmu_set_mmureg(mreg);
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic const struct sparc32_cachetlb_ops swift_ops = {
115662306a36Sopenharmony_ci	.cache_all	= swift_flush_cache_all,
115762306a36Sopenharmony_ci	.cache_mm	= swift_flush_cache_mm,
115862306a36Sopenharmony_ci	.cache_page	= swift_flush_cache_page,
115962306a36Sopenharmony_ci	.cache_range	= swift_flush_cache_range,
116062306a36Sopenharmony_ci	.tlb_all	= swift_flush_tlb_all,
116162306a36Sopenharmony_ci	.tlb_mm		= swift_flush_tlb_mm,
116262306a36Sopenharmony_ci	.tlb_page	= swift_flush_tlb_page,
116362306a36Sopenharmony_ci	.tlb_range	= swift_flush_tlb_range,
116462306a36Sopenharmony_ci	.page_to_ram	= swift_flush_page_to_ram,
116562306a36Sopenharmony_ci	.sig_insns	= swift_flush_sig_insns,
116662306a36Sopenharmony_ci	.page_for_dma	= swift_flush_page_for_dma,
116762306a36Sopenharmony_ci};
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci#define SWIFT_MASKID_ADDR  0x10003018
117062306a36Sopenharmony_cistatic void __init init_swift(void)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	unsigned long swift_rev;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	__asm__ __volatile__("lda [%1] %2, %0\n\t"
117562306a36Sopenharmony_ci			     "srl %0, 0x18, %0\n\t" :
117662306a36Sopenharmony_ci			     "=r" (swift_rev) :
117762306a36Sopenharmony_ci			     "r" (SWIFT_MASKID_ADDR), "i" (ASI_M_BYPASS));
117862306a36Sopenharmony_ci	srmmu_name = "Fujitsu Swift";
117962306a36Sopenharmony_ci	switch (swift_rev) {
118062306a36Sopenharmony_ci	case 0x11:
118162306a36Sopenharmony_ci	case 0x20:
118262306a36Sopenharmony_ci	case 0x23:
118362306a36Sopenharmony_ci	case 0x30:
118462306a36Sopenharmony_ci		srmmu_modtype = Swift_lots_o_bugs;
118562306a36Sopenharmony_ci		hwbug_bitmask |= (HWBUG_KERN_ACCBROKEN | HWBUG_KERN_CBITBROKEN);
118662306a36Sopenharmony_ci		/*
118762306a36Sopenharmony_ci		 * Gee george, I wonder why Sun is so hush hush about
118862306a36Sopenharmony_ci		 * this hardware bug... really braindamage stuff going
118962306a36Sopenharmony_ci		 * on here.  However I think we can find a way to avoid
119062306a36Sopenharmony_ci		 * all of the workaround overhead under Linux.  Basically,
119162306a36Sopenharmony_ci		 * any page fault can cause kernel pages to become user
119262306a36Sopenharmony_ci		 * accessible (the mmu gets confused and clears some of
119362306a36Sopenharmony_ci		 * the ACC bits in kernel ptes).  Aha, sounds pretty
119462306a36Sopenharmony_ci		 * horrible eh?  But wait, after extensive testing it appears
119562306a36Sopenharmony_ci		 * that if you use pgd_t level large kernel pte's (like the
119662306a36Sopenharmony_ci		 * 4MB pages on the Pentium) the bug does not get tripped
119762306a36Sopenharmony_ci		 * at all.  This avoids almost all of the major overhead.
119862306a36Sopenharmony_ci		 * Welcome to a world where your vendor tells you to,
119962306a36Sopenharmony_ci		 * "apply this kernel patch" instead of "sorry for the
120062306a36Sopenharmony_ci		 * broken hardware, send it back and we'll give you
120162306a36Sopenharmony_ci		 * properly functioning parts"
120262306a36Sopenharmony_ci		 */
120362306a36Sopenharmony_ci		break;
120462306a36Sopenharmony_ci	case 0x25:
120562306a36Sopenharmony_ci	case 0x31:
120662306a36Sopenharmony_ci		srmmu_modtype = Swift_bad_c;
120762306a36Sopenharmony_ci		hwbug_bitmask |= HWBUG_KERN_CBITBROKEN;
120862306a36Sopenharmony_ci		/*
120962306a36Sopenharmony_ci		 * You see Sun allude to this hardware bug but never
121062306a36Sopenharmony_ci		 * admit things directly, they'll say things like,
121162306a36Sopenharmony_ci		 * "the Swift chip cache problems" or similar.
121262306a36Sopenharmony_ci		 */
121362306a36Sopenharmony_ci		break;
121462306a36Sopenharmony_ci	default:
121562306a36Sopenharmony_ci		srmmu_modtype = Swift_ok;
121662306a36Sopenharmony_ci		break;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	sparc32_cachetlb_ops = &swift_ops;
122062306a36Sopenharmony_ci	flush_page_for_dma_global = 0;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/*
122362306a36Sopenharmony_ci	 * Are you now convinced that the Swift is one of the
122462306a36Sopenharmony_ci	 * biggest VLSI abortions of all time?  Bravo Fujitsu!
122562306a36Sopenharmony_ci	 * Fujitsu, the !#?!%$'d up processor people.  I bet if
122662306a36Sopenharmony_ci	 * you examined the microcode of the Swift you'd find
122762306a36Sopenharmony_ci	 * XXX's all over the place.
122862306a36Sopenharmony_ci	 */
122962306a36Sopenharmony_ci	poke_srmmu = poke_swift;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic void turbosparc_flush_cache_all(void)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	flush_user_windows();
123562306a36Sopenharmony_ci	turbosparc_idflash_clear();
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_cistatic void turbosparc_flush_cache_mm(struct mm_struct *mm)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	FLUSH_BEGIN(mm)
124162306a36Sopenharmony_ci	flush_user_windows();
124262306a36Sopenharmony_ci	turbosparc_idflash_clear();
124362306a36Sopenharmony_ci	FLUSH_END
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic void turbosparc_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
124762306a36Sopenharmony_ci{
124862306a36Sopenharmony_ci	FLUSH_BEGIN(vma->vm_mm)
124962306a36Sopenharmony_ci	flush_user_windows();
125062306a36Sopenharmony_ci	turbosparc_idflash_clear();
125162306a36Sopenharmony_ci	FLUSH_END
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic void turbosparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	FLUSH_BEGIN(vma->vm_mm)
125762306a36Sopenharmony_ci	flush_user_windows();
125862306a36Sopenharmony_ci	if (vma->vm_flags & VM_EXEC)
125962306a36Sopenharmony_ci		turbosparc_flush_icache();
126062306a36Sopenharmony_ci	turbosparc_flush_dcache();
126162306a36Sopenharmony_ci	FLUSH_END
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci/* TurboSparc is copy-back, if we turn it on, but this does not work. */
126562306a36Sopenharmony_cistatic void turbosparc_flush_page_to_ram(unsigned long page)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci#ifdef TURBOSPARC_WRITEBACK
126862306a36Sopenharmony_ci	volatile unsigned long clear;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	if (srmmu_probe(page))
127162306a36Sopenharmony_ci		turbosparc_flush_page_cache(page);
127262306a36Sopenharmony_ci	clear = srmmu_get_fstatus();
127362306a36Sopenharmony_ci#endif
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic void turbosparc_flush_page_for_dma(unsigned long page)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	turbosparc_flush_dcache();
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic void turbosparc_flush_tlb_all(void)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	srmmu_flush_whole_tlb();
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic void turbosparc_flush_tlb_mm(struct mm_struct *mm)
129162306a36Sopenharmony_ci{
129262306a36Sopenharmony_ci	FLUSH_BEGIN(mm)
129362306a36Sopenharmony_ci	srmmu_flush_whole_tlb();
129462306a36Sopenharmony_ci	FLUSH_END
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_cistatic void turbosparc_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	FLUSH_BEGIN(vma->vm_mm)
130062306a36Sopenharmony_ci	srmmu_flush_whole_tlb();
130162306a36Sopenharmony_ci	FLUSH_END
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic void turbosparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	FLUSH_BEGIN(vma->vm_mm)
130762306a36Sopenharmony_ci	srmmu_flush_whole_tlb();
130862306a36Sopenharmony_ci	FLUSH_END
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_cistatic void poke_turbosparc(void)
131362306a36Sopenharmony_ci{
131462306a36Sopenharmony_ci	unsigned long mreg = srmmu_get_mmureg();
131562306a36Sopenharmony_ci	unsigned long ccreg;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	/* Clear any crap from the cache or else... */
131862306a36Sopenharmony_ci	turbosparc_flush_cache_all();
131962306a36Sopenharmony_ci	/* Temporarily disable I & D caches */
132062306a36Sopenharmony_ci	mreg &= ~(TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE);
132162306a36Sopenharmony_ci	mreg &= ~(TURBOSPARC_PCENABLE);		/* Don't check parity */
132262306a36Sopenharmony_ci	srmmu_set_mmureg(mreg);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	ccreg = turbosparc_get_ccreg();
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci#ifdef TURBOSPARC_WRITEBACK
132762306a36Sopenharmony_ci	ccreg |= (TURBOSPARC_SNENABLE);		/* Do DVMA snooping in Dcache */
132862306a36Sopenharmony_ci	ccreg &= ~(TURBOSPARC_uS2 | TURBOSPARC_WTENABLE);
132962306a36Sopenharmony_ci			/* Write-back D-cache, emulate VLSI
133062306a36Sopenharmony_ci			 * abortion number three, not number one */
133162306a36Sopenharmony_ci#else
133262306a36Sopenharmony_ci	/* For now let's play safe, optimize later */
133362306a36Sopenharmony_ci	ccreg |= (TURBOSPARC_SNENABLE | TURBOSPARC_WTENABLE);
133462306a36Sopenharmony_ci			/* Do DVMA snooping in Dcache, Write-thru D-cache */
133562306a36Sopenharmony_ci	ccreg &= ~(TURBOSPARC_uS2);
133662306a36Sopenharmony_ci			/* Emulate VLSI abortion number three, not number one */
133762306a36Sopenharmony_ci#endif
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	switch (ccreg & 7) {
134062306a36Sopenharmony_ci	case 0: /* No SE cache */
134162306a36Sopenharmony_ci	case 7: /* Test mode */
134262306a36Sopenharmony_ci		break;
134362306a36Sopenharmony_ci	default:
134462306a36Sopenharmony_ci		ccreg |= (TURBOSPARC_SCENABLE);
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci	turbosparc_set_ccreg(ccreg);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	mreg |= (TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* I & D caches on */
134962306a36Sopenharmony_ci	mreg |= (TURBOSPARC_ICSNOOP);		/* Icache snooping on */
135062306a36Sopenharmony_ci	srmmu_set_mmureg(mreg);
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic const struct sparc32_cachetlb_ops turbosparc_ops = {
135462306a36Sopenharmony_ci	.cache_all	= turbosparc_flush_cache_all,
135562306a36Sopenharmony_ci	.cache_mm	= turbosparc_flush_cache_mm,
135662306a36Sopenharmony_ci	.cache_page	= turbosparc_flush_cache_page,
135762306a36Sopenharmony_ci	.cache_range	= turbosparc_flush_cache_range,
135862306a36Sopenharmony_ci	.tlb_all	= turbosparc_flush_tlb_all,
135962306a36Sopenharmony_ci	.tlb_mm		= turbosparc_flush_tlb_mm,
136062306a36Sopenharmony_ci	.tlb_page	= turbosparc_flush_tlb_page,
136162306a36Sopenharmony_ci	.tlb_range	= turbosparc_flush_tlb_range,
136262306a36Sopenharmony_ci	.page_to_ram	= turbosparc_flush_page_to_ram,
136362306a36Sopenharmony_ci	.sig_insns	= turbosparc_flush_sig_insns,
136462306a36Sopenharmony_ci	.page_for_dma	= turbosparc_flush_page_for_dma,
136562306a36Sopenharmony_ci};
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic void __init init_turbosparc(void)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	srmmu_name = "Fujitsu TurboSparc";
137062306a36Sopenharmony_ci	srmmu_modtype = TurboSparc;
137162306a36Sopenharmony_ci	sparc32_cachetlb_ops = &turbosparc_ops;
137262306a36Sopenharmony_ci	poke_srmmu = poke_turbosparc;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_cistatic void poke_tsunami(void)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	unsigned long mreg = srmmu_get_mmureg();
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	tsunami_flush_icache();
138062306a36Sopenharmony_ci	tsunami_flush_dcache();
138162306a36Sopenharmony_ci	mreg &= ~TSUNAMI_ITD;
138262306a36Sopenharmony_ci	mreg |= (TSUNAMI_IENAB | TSUNAMI_DENAB);
138362306a36Sopenharmony_ci	srmmu_set_mmureg(mreg);
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic const struct sparc32_cachetlb_ops tsunami_ops = {
138762306a36Sopenharmony_ci	.cache_all	= tsunami_flush_cache_all,
138862306a36Sopenharmony_ci	.cache_mm	= tsunami_flush_cache_mm,
138962306a36Sopenharmony_ci	.cache_page	= tsunami_flush_cache_page,
139062306a36Sopenharmony_ci	.cache_range	= tsunami_flush_cache_range,
139162306a36Sopenharmony_ci	.tlb_all	= tsunami_flush_tlb_all,
139262306a36Sopenharmony_ci	.tlb_mm		= tsunami_flush_tlb_mm,
139362306a36Sopenharmony_ci	.tlb_page	= tsunami_flush_tlb_page,
139462306a36Sopenharmony_ci	.tlb_range	= tsunami_flush_tlb_range,
139562306a36Sopenharmony_ci	.page_to_ram	= tsunami_flush_page_to_ram,
139662306a36Sopenharmony_ci	.sig_insns	= tsunami_flush_sig_insns,
139762306a36Sopenharmony_ci	.page_for_dma	= tsunami_flush_page_for_dma,
139862306a36Sopenharmony_ci};
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_cistatic void __init init_tsunami(void)
140162306a36Sopenharmony_ci{
140262306a36Sopenharmony_ci	/*
140362306a36Sopenharmony_ci	 * Tsunami's pretty sane, Sun and TI actually got it
140462306a36Sopenharmony_ci	 * somewhat right this time.  Fujitsu should have
140562306a36Sopenharmony_ci	 * taken some lessons from them.
140662306a36Sopenharmony_ci	 */
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	srmmu_name = "TI Tsunami";
140962306a36Sopenharmony_ci	srmmu_modtype = Tsunami;
141062306a36Sopenharmony_ci	sparc32_cachetlb_ops = &tsunami_ops;
141162306a36Sopenharmony_ci	poke_srmmu = poke_tsunami;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	tsunami_setup_blockops();
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic void poke_viking(void)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	unsigned long mreg = srmmu_get_mmureg();
141962306a36Sopenharmony_ci	static int smp_catch;
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	if (viking_mxcc_present) {
142262306a36Sopenharmony_ci		unsigned long mxcc_control = mxcc_get_creg();
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		mxcc_control |= (MXCC_CTL_ECE | MXCC_CTL_PRE | MXCC_CTL_MCE);
142562306a36Sopenharmony_ci		mxcc_control &= ~(MXCC_CTL_RRC);
142662306a36Sopenharmony_ci		mxcc_set_creg(mxcc_control);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci		/*
142962306a36Sopenharmony_ci		 * We don't need memory parity checks.
143062306a36Sopenharmony_ci		 * XXX This is a mess, have to dig out later. ecd.
143162306a36Sopenharmony_ci		viking_mxcc_turn_off_parity(&mreg, &mxcc_control);
143262306a36Sopenharmony_ci		 */
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		/* We do cache ptables on MXCC. */
143562306a36Sopenharmony_ci		mreg |= VIKING_TCENABLE;
143662306a36Sopenharmony_ci	} else {
143762306a36Sopenharmony_ci		unsigned long bpreg;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		mreg &= ~(VIKING_TCENABLE);
144062306a36Sopenharmony_ci		if (smp_catch++) {
144162306a36Sopenharmony_ci			/* Must disable mixed-cmd mode here for other cpu's. */
144262306a36Sopenharmony_ci			bpreg = viking_get_bpreg();
144362306a36Sopenharmony_ci			bpreg &= ~(VIKING_ACTION_MIX);
144462306a36Sopenharmony_ci			viking_set_bpreg(bpreg);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci			/* Just in case PROM does something funny. */
144762306a36Sopenharmony_ci			msi_set_sync();
144862306a36Sopenharmony_ci		}
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	mreg |= VIKING_SPENABLE;
145262306a36Sopenharmony_ci	mreg |= (VIKING_ICENABLE | VIKING_DCENABLE);
145362306a36Sopenharmony_ci	mreg |= VIKING_SBENABLE;
145462306a36Sopenharmony_ci	mreg &= ~(VIKING_ACENABLE);
145562306a36Sopenharmony_ci	srmmu_set_mmureg(mreg);
145662306a36Sopenharmony_ci}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_cistatic struct sparc32_cachetlb_ops viking_ops __ro_after_init = {
145962306a36Sopenharmony_ci	.cache_all	= viking_flush_cache_all,
146062306a36Sopenharmony_ci	.cache_mm	= viking_flush_cache_mm,
146162306a36Sopenharmony_ci	.cache_page	= viking_flush_cache_page,
146262306a36Sopenharmony_ci	.cache_range	= viking_flush_cache_range,
146362306a36Sopenharmony_ci	.tlb_all	= viking_flush_tlb_all,
146462306a36Sopenharmony_ci	.tlb_mm		= viking_flush_tlb_mm,
146562306a36Sopenharmony_ci	.tlb_page	= viking_flush_tlb_page,
146662306a36Sopenharmony_ci	.tlb_range	= viking_flush_tlb_range,
146762306a36Sopenharmony_ci	.page_to_ram	= viking_flush_page_to_ram,
146862306a36Sopenharmony_ci	.sig_insns	= viking_flush_sig_insns,
146962306a36Sopenharmony_ci	.page_for_dma	= viking_flush_page_for_dma,
147062306a36Sopenharmony_ci};
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci#ifdef CONFIG_SMP
147362306a36Sopenharmony_ci/* On sun4d the cpu broadcasts local TLB flushes, so we can just
147462306a36Sopenharmony_ci * perform the local TLB flush and all the other cpus will see it.
147562306a36Sopenharmony_ci * But, unfortunately, there is a bug in the sun4d XBUS backplane
147662306a36Sopenharmony_ci * that requires that we add some synchronization to these flushes.
147762306a36Sopenharmony_ci *
147862306a36Sopenharmony_ci * The bug is that the fifo which keeps track of all the pending TLB
147962306a36Sopenharmony_ci * broadcasts in the system is an entry or two too small, so if we
148062306a36Sopenharmony_ci * have too many going at once we'll overflow that fifo and lose a TLB
148162306a36Sopenharmony_ci * flush resulting in corruption.
148262306a36Sopenharmony_ci *
148362306a36Sopenharmony_ci * Our workaround is to take a global spinlock around the TLB flushes,
148462306a36Sopenharmony_ci * which guarentees we won't ever have too many pending.  It's a big
148562306a36Sopenharmony_ci * hammer, but a semaphore like system to make sure we only have N TLB
148662306a36Sopenharmony_ci * flushes going at once will require SMP locking anyways so there's
148762306a36Sopenharmony_ci * no real value in trying any harder than this.
148862306a36Sopenharmony_ci */
148962306a36Sopenharmony_cistatic struct sparc32_cachetlb_ops viking_sun4d_smp_ops __ro_after_init = {
149062306a36Sopenharmony_ci	.cache_all	= viking_flush_cache_all,
149162306a36Sopenharmony_ci	.cache_mm	= viking_flush_cache_mm,
149262306a36Sopenharmony_ci	.cache_page	= viking_flush_cache_page,
149362306a36Sopenharmony_ci	.cache_range	= viking_flush_cache_range,
149462306a36Sopenharmony_ci	.tlb_all	= sun4dsmp_flush_tlb_all,
149562306a36Sopenharmony_ci	.tlb_mm		= sun4dsmp_flush_tlb_mm,
149662306a36Sopenharmony_ci	.tlb_page	= sun4dsmp_flush_tlb_page,
149762306a36Sopenharmony_ci	.tlb_range	= sun4dsmp_flush_tlb_range,
149862306a36Sopenharmony_ci	.page_to_ram	= viking_flush_page_to_ram,
149962306a36Sopenharmony_ci	.sig_insns	= viking_flush_sig_insns,
150062306a36Sopenharmony_ci	.page_for_dma	= viking_flush_page_for_dma,
150162306a36Sopenharmony_ci};
150262306a36Sopenharmony_ci#endif
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic void __init init_viking(void)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	unsigned long mreg = srmmu_get_mmureg();
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* Ahhh, the viking.  SRMMU VLSI abortion number two... */
150962306a36Sopenharmony_ci	if (mreg & VIKING_MMODE) {
151062306a36Sopenharmony_ci		srmmu_name = "TI Viking";
151162306a36Sopenharmony_ci		viking_mxcc_present = 0;
151262306a36Sopenharmony_ci		msi_set_sync();
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci		/*
151562306a36Sopenharmony_ci		 * We need this to make sure old viking takes no hits
151662306a36Sopenharmony_ci		 * on it's cache for dma snoops to workaround the
151762306a36Sopenharmony_ci		 * "load from non-cacheable memory" interrupt bug.
151862306a36Sopenharmony_ci		 * This is only necessary because of the new way in
151962306a36Sopenharmony_ci		 * which we use the IOMMU.
152062306a36Sopenharmony_ci		 */
152162306a36Sopenharmony_ci		viking_ops.page_for_dma = viking_flush_page;
152262306a36Sopenharmony_ci#ifdef CONFIG_SMP
152362306a36Sopenharmony_ci		viking_sun4d_smp_ops.page_for_dma = viking_flush_page;
152462306a36Sopenharmony_ci#endif
152562306a36Sopenharmony_ci		flush_page_for_dma_global = 0;
152662306a36Sopenharmony_ci	} else {
152762306a36Sopenharmony_ci		srmmu_name = "TI Viking/MXCC";
152862306a36Sopenharmony_ci		viking_mxcc_present = 1;
152962306a36Sopenharmony_ci		srmmu_cache_pagetables = 1;
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	sparc32_cachetlb_ops = (const struct sparc32_cachetlb_ops *)
153362306a36Sopenharmony_ci		&viking_ops;
153462306a36Sopenharmony_ci#ifdef CONFIG_SMP
153562306a36Sopenharmony_ci	if (sparc_cpu_model == sun4d)
153662306a36Sopenharmony_ci		sparc32_cachetlb_ops = (const struct sparc32_cachetlb_ops *)
153762306a36Sopenharmony_ci			&viking_sun4d_smp_ops;
153862306a36Sopenharmony_ci#endif
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	poke_srmmu = poke_viking;
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci/* Probe for the srmmu chip version. */
154462306a36Sopenharmony_cistatic void __init get_srmmu_type(void)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	unsigned long mreg, psr;
154762306a36Sopenharmony_ci	unsigned long mod_typ, mod_rev, psr_typ, psr_vers;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	srmmu_modtype = SRMMU_INVAL_MOD;
155062306a36Sopenharmony_ci	hwbug_bitmask = 0;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	mreg = srmmu_get_mmureg(); psr = get_psr();
155362306a36Sopenharmony_ci	mod_typ = (mreg & 0xf0000000) >> 28;
155462306a36Sopenharmony_ci	mod_rev = (mreg & 0x0f000000) >> 24;
155562306a36Sopenharmony_ci	psr_typ = (psr >> 28) & 0xf;
155662306a36Sopenharmony_ci	psr_vers = (psr >> 24) & 0xf;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	/* First, check for sparc-leon. */
155962306a36Sopenharmony_ci	if (sparc_cpu_model == sparc_leon) {
156062306a36Sopenharmony_ci		init_leon();
156162306a36Sopenharmony_ci		return;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	/* Second, check for HyperSparc or Cypress. */
156562306a36Sopenharmony_ci	if (mod_typ == 1) {
156662306a36Sopenharmony_ci		switch (mod_rev) {
156762306a36Sopenharmony_ci		case 7:
156862306a36Sopenharmony_ci			/* UP or MP Hypersparc */
156962306a36Sopenharmony_ci			init_hypersparc();
157062306a36Sopenharmony_ci			break;
157162306a36Sopenharmony_ci		case 0:
157262306a36Sopenharmony_ci		case 2:
157362306a36Sopenharmony_ci		case 10:
157462306a36Sopenharmony_ci		case 11:
157562306a36Sopenharmony_ci		case 12:
157662306a36Sopenharmony_ci		case 13:
157762306a36Sopenharmony_ci		case 14:
157862306a36Sopenharmony_ci		case 15:
157962306a36Sopenharmony_ci		default:
158062306a36Sopenharmony_ci			prom_printf("Sparc-Linux Cypress support does not longer exit.\n");
158162306a36Sopenharmony_ci			prom_halt();
158262306a36Sopenharmony_ci			break;
158362306a36Sopenharmony_ci		}
158462306a36Sopenharmony_ci		return;
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	/* Now Fujitsu TurboSparc. It might happen that it is
158862306a36Sopenharmony_ci	 * in Swift emulation mode, so we will check later...
158962306a36Sopenharmony_ci	 */
159062306a36Sopenharmony_ci	if (psr_typ == 0 && psr_vers == 5) {
159162306a36Sopenharmony_ci		init_turbosparc();
159262306a36Sopenharmony_ci		return;
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	/* Next check for Fujitsu Swift. */
159662306a36Sopenharmony_ci	if (psr_typ == 0 && psr_vers == 4) {
159762306a36Sopenharmony_ci		phandle cpunode;
159862306a36Sopenharmony_ci		char node_str[128];
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci		/* Look if it is not a TurboSparc emulating Swift... */
160162306a36Sopenharmony_ci		cpunode = prom_getchild(prom_root_node);
160262306a36Sopenharmony_ci		while ((cpunode = prom_getsibling(cpunode)) != 0) {
160362306a36Sopenharmony_ci			prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
160462306a36Sopenharmony_ci			if (!strcmp(node_str, "cpu")) {
160562306a36Sopenharmony_ci				if (!prom_getintdefault(cpunode, "psr-implementation", 1) &&
160662306a36Sopenharmony_ci				    prom_getintdefault(cpunode, "psr-version", 1) == 5) {
160762306a36Sopenharmony_ci					init_turbosparc();
160862306a36Sopenharmony_ci					return;
160962306a36Sopenharmony_ci				}
161062306a36Sopenharmony_ci				break;
161162306a36Sopenharmony_ci			}
161262306a36Sopenharmony_ci		}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci		init_swift();
161562306a36Sopenharmony_ci		return;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	/* Now the Viking family of srmmu. */
161962306a36Sopenharmony_ci	if (psr_typ == 4 &&
162062306a36Sopenharmony_ci	   ((psr_vers == 0) ||
162162306a36Sopenharmony_ci	    ((psr_vers == 1) && (mod_typ == 0) && (mod_rev == 0)))) {
162262306a36Sopenharmony_ci		init_viking();
162362306a36Sopenharmony_ci		return;
162462306a36Sopenharmony_ci	}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	/* Finally the Tsunami. */
162762306a36Sopenharmony_ci	if (psr_typ == 4 && psr_vers == 1 && (mod_typ || mod_rev)) {
162862306a36Sopenharmony_ci		init_tsunami();
162962306a36Sopenharmony_ci		return;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* Oh well */
163362306a36Sopenharmony_ci	srmmu_is_bad();
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci#ifdef CONFIG_SMP
163762306a36Sopenharmony_ci/* Local cross-calls. */
163862306a36Sopenharmony_cistatic void smp_flush_page_for_dma(unsigned long page)
163962306a36Sopenharmony_ci{
164062306a36Sopenharmony_ci	xc1(local_ops->page_for_dma, page);
164162306a36Sopenharmony_ci	local_ops->page_for_dma(page);
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic void smp_flush_cache_all(void)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	xc0(local_ops->cache_all);
164762306a36Sopenharmony_ci	local_ops->cache_all();
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic void smp_flush_tlb_all(void)
165162306a36Sopenharmony_ci{
165262306a36Sopenharmony_ci	xc0(local_ops->tlb_all);
165362306a36Sopenharmony_ci	local_ops->tlb_all();
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic void smp_flush_cache_mm(struct mm_struct *mm)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
165962306a36Sopenharmony_ci		cpumask_t cpu_mask;
166062306a36Sopenharmony_ci		cpumask_copy(&cpu_mask, mm_cpumask(mm));
166162306a36Sopenharmony_ci		cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
166262306a36Sopenharmony_ci		if (!cpumask_empty(&cpu_mask))
166362306a36Sopenharmony_ci			xc1(local_ops->cache_mm, (unsigned long)mm);
166462306a36Sopenharmony_ci		local_ops->cache_mm(mm);
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_cistatic void smp_flush_tlb_mm(struct mm_struct *mm)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
167162306a36Sopenharmony_ci		cpumask_t cpu_mask;
167262306a36Sopenharmony_ci		cpumask_copy(&cpu_mask, mm_cpumask(mm));
167362306a36Sopenharmony_ci		cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
167462306a36Sopenharmony_ci		if (!cpumask_empty(&cpu_mask)) {
167562306a36Sopenharmony_ci			xc1(local_ops->tlb_mm, (unsigned long)mm);
167662306a36Sopenharmony_ci			if (atomic_read(&mm->mm_users) == 1 && current->active_mm == mm)
167762306a36Sopenharmony_ci				cpumask_copy(mm_cpumask(mm),
167862306a36Sopenharmony_ci					     cpumask_of(smp_processor_id()));
167962306a36Sopenharmony_ci		}
168062306a36Sopenharmony_ci		local_ops->tlb_mm(mm);
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_cistatic void smp_flush_cache_range(struct vm_area_struct *vma,
168562306a36Sopenharmony_ci				  unsigned long start,
168662306a36Sopenharmony_ci				  unsigned long end)
168762306a36Sopenharmony_ci{
168862306a36Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
169162306a36Sopenharmony_ci		cpumask_t cpu_mask;
169262306a36Sopenharmony_ci		cpumask_copy(&cpu_mask, mm_cpumask(mm));
169362306a36Sopenharmony_ci		cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
169462306a36Sopenharmony_ci		if (!cpumask_empty(&cpu_mask))
169562306a36Sopenharmony_ci			xc3(local_ops->cache_range, (unsigned long)vma, start,
169662306a36Sopenharmony_ci			    end);
169762306a36Sopenharmony_ci		local_ops->cache_range(vma, start, end);
169862306a36Sopenharmony_ci	}
169962306a36Sopenharmony_ci}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_cistatic void smp_flush_tlb_range(struct vm_area_struct *vma,
170262306a36Sopenharmony_ci				unsigned long start,
170362306a36Sopenharmony_ci				unsigned long end)
170462306a36Sopenharmony_ci{
170562306a36Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
170862306a36Sopenharmony_ci		cpumask_t cpu_mask;
170962306a36Sopenharmony_ci		cpumask_copy(&cpu_mask, mm_cpumask(mm));
171062306a36Sopenharmony_ci		cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
171162306a36Sopenharmony_ci		if (!cpumask_empty(&cpu_mask))
171262306a36Sopenharmony_ci			xc3(local_ops->tlb_range, (unsigned long)vma, start,
171362306a36Sopenharmony_ci			    end);
171462306a36Sopenharmony_ci		local_ops->tlb_range(vma, start, end);
171562306a36Sopenharmony_ci	}
171662306a36Sopenharmony_ci}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_cistatic void smp_flush_cache_page(struct vm_area_struct *vma, unsigned long page)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
172362306a36Sopenharmony_ci		cpumask_t cpu_mask;
172462306a36Sopenharmony_ci		cpumask_copy(&cpu_mask, mm_cpumask(mm));
172562306a36Sopenharmony_ci		cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
172662306a36Sopenharmony_ci		if (!cpumask_empty(&cpu_mask))
172762306a36Sopenharmony_ci			xc2(local_ops->cache_page, (unsigned long)vma, page);
172862306a36Sopenharmony_ci		local_ops->cache_page(vma, page);
172962306a36Sopenharmony_ci	}
173062306a36Sopenharmony_ci}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	struct mm_struct *mm = vma->vm_mm;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	if (mm->context != NO_CONTEXT) {
173762306a36Sopenharmony_ci		cpumask_t cpu_mask;
173862306a36Sopenharmony_ci		cpumask_copy(&cpu_mask, mm_cpumask(mm));
173962306a36Sopenharmony_ci		cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
174062306a36Sopenharmony_ci		if (!cpumask_empty(&cpu_mask))
174162306a36Sopenharmony_ci			xc2(local_ops->tlb_page, (unsigned long)vma, page);
174262306a36Sopenharmony_ci		local_ops->tlb_page(vma, page);
174362306a36Sopenharmony_ci	}
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic void smp_flush_page_to_ram(unsigned long page)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	/* Current theory is that those who call this are the one's
174962306a36Sopenharmony_ci	 * who have just dirtied their cache with the pages contents
175062306a36Sopenharmony_ci	 * in kernel space, therefore we only run this on local cpu.
175162306a36Sopenharmony_ci	 *
175262306a36Sopenharmony_ci	 * XXX This experiment failed, research further... -DaveM
175362306a36Sopenharmony_ci	 */
175462306a36Sopenharmony_ci#if 1
175562306a36Sopenharmony_ci	xc1(local_ops->page_to_ram, page);
175662306a36Sopenharmony_ci#endif
175762306a36Sopenharmony_ci	local_ops->page_to_ram(page);
175862306a36Sopenharmony_ci}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cistatic void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	cpumask_t cpu_mask;
176362306a36Sopenharmony_ci	cpumask_copy(&cpu_mask, mm_cpumask(mm));
176462306a36Sopenharmony_ci	cpumask_clear_cpu(smp_processor_id(), &cpu_mask);
176562306a36Sopenharmony_ci	if (!cpumask_empty(&cpu_mask))
176662306a36Sopenharmony_ci		xc2(local_ops->sig_insns, (unsigned long)mm, insn_addr);
176762306a36Sopenharmony_ci	local_ops->sig_insns(mm, insn_addr);
176862306a36Sopenharmony_ci}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_cistatic struct sparc32_cachetlb_ops smp_cachetlb_ops __ro_after_init = {
177162306a36Sopenharmony_ci	.cache_all	= smp_flush_cache_all,
177262306a36Sopenharmony_ci	.cache_mm	= smp_flush_cache_mm,
177362306a36Sopenharmony_ci	.cache_page	= smp_flush_cache_page,
177462306a36Sopenharmony_ci	.cache_range	= smp_flush_cache_range,
177562306a36Sopenharmony_ci	.tlb_all	= smp_flush_tlb_all,
177662306a36Sopenharmony_ci	.tlb_mm		= smp_flush_tlb_mm,
177762306a36Sopenharmony_ci	.tlb_page	= smp_flush_tlb_page,
177862306a36Sopenharmony_ci	.tlb_range	= smp_flush_tlb_range,
177962306a36Sopenharmony_ci	.page_to_ram	= smp_flush_page_to_ram,
178062306a36Sopenharmony_ci	.sig_insns	= smp_flush_sig_insns,
178162306a36Sopenharmony_ci	.page_for_dma	= smp_flush_page_for_dma,
178262306a36Sopenharmony_ci};
178362306a36Sopenharmony_ci#endif
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci/* Load up routines and constants for sun4m and sun4d mmu */
178662306a36Sopenharmony_civoid __init load_mmu(void)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	/* Functions */
178962306a36Sopenharmony_ci	get_srmmu_type();
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci#ifdef CONFIG_SMP
179262306a36Sopenharmony_ci	/* El switcheroo... */
179362306a36Sopenharmony_ci	local_ops = sparc32_cachetlb_ops;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	if (sparc_cpu_model == sun4d || sparc_cpu_model == sparc_leon) {
179662306a36Sopenharmony_ci		smp_cachetlb_ops.tlb_all = local_ops->tlb_all;
179762306a36Sopenharmony_ci		smp_cachetlb_ops.tlb_mm = local_ops->tlb_mm;
179862306a36Sopenharmony_ci		smp_cachetlb_ops.tlb_range = local_ops->tlb_range;
179962306a36Sopenharmony_ci		smp_cachetlb_ops.tlb_page = local_ops->tlb_page;
180062306a36Sopenharmony_ci	}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	if (poke_srmmu == poke_viking) {
180362306a36Sopenharmony_ci		/* Avoid unnecessary cross calls. */
180462306a36Sopenharmony_ci		smp_cachetlb_ops.cache_all = local_ops->cache_all;
180562306a36Sopenharmony_ci		smp_cachetlb_ops.cache_mm = local_ops->cache_mm;
180662306a36Sopenharmony_ci		smp_cachetlb_ops.cache_range = local_ops->cache_range;
180762306a36Sopenharmony_ci		smp_cachetlb_ops.cache_page = local_ops->cache_page;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		smp_cachetlb_ops.page_to_ram = local_ops->page_to_ram;
181062306a36Sopenharmony_ci		smp_cachetlb_ops.sig_insns = local_ops->sig_insns;
181162306a36Sopenharmony_ci		smp_cachetlb_ops.page_for_dma = local_ops->page_for_dma;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	/* It really is const after this point. */
181562306a36Sopenharmony_ci	sparc32_cachetlb_ops = (const struct sparc32_cachetlb_ops *)
181662306a36Sopenharmony_ci		&smp_cachetlb_ops;
181762306a36Sopenharmony_ci#endif
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	if (sparc_cpu_model != sun4d)
182062306a36Sopenharmony_ci		ld_mmu_iommu();
182162306a36Sopenharmony_ci#ifdef CONFIG_SMP
182262306a36Sopenharmony_ci	if (sparc_cpu_model == sun4d)
182362306a36Sopenharmony_ci		sun4d_init_smp();
182462306a36Sopenharmony_ci	else if (sparc_cpu_model == sparc_leon)
182562306a36Sopenharmony_ci		leon_init_smp();
182662306a36Sopenharmony_ci	else
182762306a36Sopenharmony_ci		sun4m_init_smp();
182862306a36Sopenharmony_ci#endif
182962306a36Sopenharmony_ci}
1830