162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Modifications by Matt Porter (mporter@mvista.com) to support 462306a36Sopenharmony_ci * PPC44x Book E processors. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file contains the routines for initializing the MMU 762306a36Sopenharmony_ci * on the 4xx series of chips. 862306a36Sopenharmony_ci * -- paulus 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Derived from arch/ppc/mm/init.c: 1162306a36Sopenharmony_ci * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) 1462306a36Sopenharmony_ci * and Cort Dougan (PReP) (cort@cs.nmt.edu) 1562306a36Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Derived from "arch/i386/mm/init.c" 1862306a36Sopenharmony_ci * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/memblock.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/mmu.h> 2562306a36Sopenharmony_ci#include <asm/page.h> 2662306a36Sopenharmony_ci#include <asm/cacheflush.h> 2762306a36Sopenharmony_ci#include <asm/code-patching.h> 2862306a36Sopenharmony_ci#include <asm/smp.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <mm/mmu_decl.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Used by the 44x TLB replacement exception handler. 3362306a36Sopenharmony_ci * Just needed it declared someplace. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ciunsigned int tlb_44x_index; /* = 0 */ 3662306a36Sopenharmony_ciunsigned int tlb_44x_hwater = PPC44x_TLB_SIZE - 1 - PPC44x_EARLY_TLBS; 3762306a36Sopenharmony_ciint icache_44x_need_flush; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciunsigned long tlb_47x_boltmap[1024/8]; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void __init ppc44x_update_tlb_hwater(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci /* The TLB miss handlers hard codes the watermark in a cmpli 4462306a36Sopenharmony_ci * instruction to improve performances rather than loading it 4562306a36Sopenharmony_ci * from the global variable. Thus, we patch the instructions 4662306a36Sopenharmony_ci * in the 2 TLB miss handlers when updating the value 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci modify_instruction_site(&patch__tlb_44x_hwater_D, 0xffff, tlb_44x_hwater); 4962306a36Sopenharmony_ci modify_instruction_site(&patch__tlb_44x_hwater_I, 0xffff, tlb_44x_hwater); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * "Pins" a 256MB TLB entry in AS0 for kernel lowmem for 44x type MMU 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic void __init ppc44x_pin_tlb(unsigned int virt, unsigned int phys) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci unsigned int entry = tlb_44x_hwater--; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ppc44x_update_tlb_hwater(); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci mtspr(SPRN_MMUCR, 0); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci __asm__ __volatile__( 6462306a36Sopenharmony_ci "tlbwe %2,%3,%4\n" 6562306a36Sopenharmony_ci "tlbwe %1,%3,%5\n" 6662306a36Sopenharmony_ci "tlbwe %0,%3,%6\n" 6762306a36Sopenharmony_ci : 6862306a36Sopenharmony_ci : "r" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G), 6962306a36Sopenharmony_ci "r" (phys), 7062306a36Sopenharmony_ci "r" (virt | PPC44x_TLB_VALID | PPC44x_TLB_256M), 7162306a36Sopenharmony_ci "r" (entry), 7262306a36Sopenharmony_ci "i" (PPC44x_TLB_PAGEID), 7362306a36Sopenharmony_ci "i" (PPC44x_TLB_XLAT), 7462306a36Sopenharmony_ci "i" (PPC44x_TLB_ATTRIB)); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int __init ppc47x_find_free_bolted(void) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned int mmube0 = mfspr(SPRN_MMUBE0); 8062306a36Sopenharmony_ci unsigned int mmube1 = mfspr(SPRN_MMUBE1); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!(mmube0 & MMUBE0_VBE0)) 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci if (!(mmube0 & MMUBE0_VBE1)) 8562306a36Sopenharmony_ci return 1; 8662306a36Sopenharmony_ci if (!(mmube0 & MMUBE0_VBE2)) 8762306a36Sopenharmony_ci return 2; 8862306a36Sopenharmony_ci if (!(mmube1 & MMUBE1_VBE3)) 8962306a36Sopenharmony_ci return 3; 9062306a36Sopenharmony_ci if (!(mmube1 & MMUBE1_VBE4)) 9162306a36Sopenharmony_ci return 4; 9262306a36Sopenharmony_ci if (!(mmube1 & MMUBE1_VBE5)) 9362306a36Sopenharmony_ci return 5; 9462306a36Sopenharmony_ci return -1; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void __init ppc47x_update_boltmap(void) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned int mmube0 = mfspr(SPRN_MMUBE0); 10062306a36Sopenharmony_ci unsigned int mmube1 = mfspr(SPRN_MMUBE1); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (mmube0 & MMUBE0_VBE0) 10362306a36Sopenharmony_ci __set_bit((mmube0 >> MMUBE0_IBE0_SHIFT) & 0xff, 10462306a36Sopenharmony_ci tlb_47x_boltmap); 10562306a36Sopenharmony_ci if (mmube0 & MMUBE0_VBE1) 10662306a36Sopenharmony_ci __set_bit((mmube0 >> MMUBE0_IBE1_SHIFT) & 0xff, 10762306a36Sopenharmony_ci tlb_47x_boltmap); 10862306a36Sopenharmony_ci if (mmube0 & MMUBE0_VBE2) 10962306a36Sopenharmony_ci __set_bit((mmube0 >> MMUBE0_IBE2_SHIFT) & 0xff, 11062306a36Sopenharmony_ci tlb_47x_boltmap); 11162306a36Sopenharmony_ci if (mmube1 & MMUBE1_VBE3) 11262306a36Sopenharmony_ci __set_bit((mmube1 >> MMUBE1_IBE3_SHIFT) & 0xff, 11362306a36Sopenharmony_ci tlb_47x_boltmap); 11462306a36Sopenharmony_ci if (mmube1 & MMUBE1_VBE4) 11562306a36Sopenharmony_ci __set_bit((mmube1 >> MMUBE1_IBE4_SHIFT) & 0xff, 11662306a36Sopenharmony_ci tlb_47x_boltmap); 11762306a36Sopenharmony_ci if (mmube1 & MMUBE1_VBE5) 11862306a36Sopenharmony_ci __set_bit((mmube1 >> MMUBE1_IBE5_SHIFT) & 0xff, 11962306a36Sopenharmony_ci tlb_47x_boltmap); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * "Pins" a 256MB TLB entry in AS0 for kernel lowmem for 47x type MMU 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistatic void __init ppc47x_pin_tlb(unsigned int virt, unsigned int phys) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned int rA; 12862306a36Sopenharmony_ci int bolted; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Base rA is HW way select, way 0, bolted bit set */ 13162306a36Sopenharmony_ci rA = 0x88000000; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Look for a bolted entry slot */ 13462306a36Sopenharmony_ci bolted = ppc47x_find_free_bolted(); 13562306a36Sopenharmony_ci BUG_ON(bolted < 0); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Insert bolted slot number */ 13862306a36Sopenharmony_ci rA |= bolted << 24; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci pr_debug("256M TLB entry for 0x%08x->0x%08x in bolt slot %d\n", 14162306a36Sopenharmony_ci virt, phys, bolted); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci mtspr(SPRN_MMUCR, 0); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci __asm__ __volatile__( 14662306a36Sopenharmony_ci "tlbwe %2,%3,0\n" 14762306a36Sopenharmony_ci "tlbwe %1,%3,1\n" 14862306a36Sopenharmony_ci "tlbwe %0,%3,2\n" 14962306a36Sopenharmony_ci : 15062306a36Sopenharmony_ci : "r" (PPC47x_TLB2_SW | PPC47x_TLB2_SR | 15162306a36Sopenharmony_ci PPC47x_TLB2_SX 15262306a36Sopenharmony_ci#ifdef CONFIG_SMP 15362306a36Sopenharmony_ci | PPC47x_TLB2_M 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci ), 15662306a36Sopenharmony_ci "r" (phys), 15762306a36Sopenharmony_ci "r" (virt | PPC47x_TLB0_VALID | PPC47x_TLB0_256M), 15862306a36Sopenharmony_ci "r" (rA)); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_civoid __init MMU_init_hw(void) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci /* This is not useful on 47x but won't hurt either */ 16462306a36Sopenharmony_ci ppc44x_update_tlb_hwater(); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci flush_instruction_cache(); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciunsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned long addr; 17262306a36Sopenharmony_ci unsigned long memstart = memstart_addr & ~(PPC_PIN_SIZE - 1); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Pin in enough TLBs to cover any lowmem not covered by the 17562306a36Sopenharmony_ci * initial 256M mapping established in head_44x.S */ 17662306a36Sopenharmony_ci for (addr = memstart + PPC_PIN_SIZE; addr < lowmem_end_addr; 17762306a36Sopenharmony_ci addr += PPC_PIN_SIZE) { 17862306a36Sopenharmony_ci if (mmu_has_feature(MMU_FTR_TYPE_47x)) 17962306a36Sopenharmony_ci ppc47x_pin_tlb(addr + PAGE_OFFSET, addr); 18062306a36Sopenharmony_ci else 18162306a36Sopenharmony_ci ppc44x_pin_tlb(addr + PAGE_OFFSET, addr); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci if (mmu_has_feature(MMU_FTR_TYPE_47x)) { 18462306a36Sopenharmony_ci ppc47x_update_boltmap(); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#ifdef DEBUG 18762306a36Sopenharmony_ci { 18862306a36Sopenharmony_ci int i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci printk(KERN_DEBUG "bolted entries: "); 19162306a36Sopenharmony_ci for (i = 0; i < 255; i++) { 19262306a36Sopenharmony_ci if (test_bit(i, tlb_47x_boltmap)) 19362306a36Sopenharmony_ci printk("%d ", i); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci printk("\n"); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci#endif /* DEBUG */ 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci return total_lowmem; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_civoid setup_initial_memory_limit(phys_addr_t first_memblock_base, 20362306a36Sopenharmony_ci phys_addr_t first_memblock_size) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u64 size; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci#ifndef CONFIG_NONSTATIC_KERNEL 20862306a36Sopenharmony_ci /* We don't currently support the first MEMBLOCK not mapping 0 20962306a36Sopenharmony_ci * physical on those processors 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci BUG_ON(first_memblock_base != 0); 21262306a36Sopenharmony_ci#endif 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 44x has a 256M TLB entry pinned at boot */ 21562306a36Sopenharmony_ci size = (min_t(u64, first_memblock_size, PPC_PIN_SIZE)); 21662306a36Sopenharmony_ci memblock_set_current_limit(first_memblock_base + size); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#ifdef CONFIG_SMP 22062306a36Sopenharmony_civoid __init mmu_init_secondary(int cpu) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci unsigned long addr; 22362306a36Sopenharmony_ci unsigned long memstart = memstart_addr & ~(PPC_PIN_SIZE - 1); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Pin in enough TLBs to cover any lowmem not covered by the 22662306a36Sopenharmony_ci * initial 256M mapping established in head_44x.S 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * WARNING: This is called with only the first 256M of the 22962306a36Sopenharmony_ci * linear mapping in the TLB and we can't take faults yet 23062306a36Sopenharmony_ci * so beware of what this code uses. It runs off a temporary 23162306a36Sopenharmony_ci * stack. current (r2) isn't initialized, smp_processor_id() 23262306a36Sopenharmony_ci * will not work, current thread info isn't accessible, ... 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci for (addr = memstart + PPC_PIN_SIZE; addr < lowmem_end_addr; 23562306a36Sopenharmony_ci addr += PPC_PIN_SIZE) { 23662306a36Sopenharmony_ci if (mmu_has_feature(MMU_FTR_TYPE_47x)) 23762306a36Sopenharmony_ci ppc47x_pin_tlb(addr + PAGE_OFFSET, addr); 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci ppc44x_pin_tlb(addr + PAGE_OFFSET, addr); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 243