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