162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 1996 David S. Miller (davem@davemloft.net) 762306a36Sopenharmony_ci * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org 862306a36Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com 962306a36Sopenharmony_ci * Copyright (C) 2002 MIPS Technologies, Inc. All rights reserved. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/cpu_pm.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/sched.h> 1462306a36Sopenharmony_ci#include <linux/smp.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/hugetlb.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/cpu.h> 2062306a36Sopenharmony_ci#include <asm/cpu-type.h> 2162306a36Sopenharmony_ci#include <asm/bootinfo.h> 2262306a36Sopenharmony_ci#include <asm/hazards.h> 2362306a36Sopenharmony_ci#include <asm/mmu_context.h> 2462306a36Sopenharmony_ci#include <asm/tlb.h> 2562306a36Sopenharmony_ci#include <asm/tlbmisc.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciextern void build_tlb_refill_handler(void); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * LOONGSON-2 has a 4 entry itlb which is a subset of jtlb, LOONGSON-3 has 3162306a36Sopenharmony_ci * a 4 entry itlb and a 4 entry dtlb which are subsets of jtlb. Unfortunately, 3262306a36Sopenharmony_ci * itlb/dtlb are not totally transparent to software. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic inline void flush_micro_tlb(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci switch (current_cpu_type()) { 3762306a36Sopenharmony_ci case CPU_LOONGSON2EF: 3862306a36Sopenharmony_ci write_c0_diag(LOONGSON_DIAG_ITLB); 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci case CPU_LOONGSON64: 4162306a36Sopenharmony_ci write_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB); 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci default: 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic inline void flush_micro_tlb_vm(struct vm_area_struct *vma) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 5162306a36Sopenharmony_ci flush_micro_tlb(); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_civoid local_flush_tlb_all(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci unsigned long flags; 5762306a36Sopenharmony_ci unsigned long old_ctx; 5862306a36Sopenharmony_ci int entry, ftlbhighset; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci local_irq_save(flags); 6162306a36Sopenharmony_ci /* Save old context and create impossible VPN2 value */ 6262306a36Sopenharmony_ci old_ctx = read_c0_entryhi(); 6362306a36Sopenharmony_ci htw_stop(); 6462306a36Sopenharmony_ci write_c0_entrylo0(0); 6562306a36Sopenharmony_ci write_c0_entrylo1(0); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci entry = num_wired_entries(); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * Blast 'em all away. 7162306a36Sopenharmony_ci * If there are any wired entries, fall back to iterating 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (cpu_has_tlbinv && !entry) { 7462306a36Sopenharmony_ci if (current_cpu_data.tlbsizevtlb) { 7562306a36Sopenharmony_ci write_c0_index(0); 7662306a36Sopenharmony_ci mtc0_tlbw_hazard(); 7762306a36Sopenharmony_ci tlbinvf(); /* invalidate VTLB */ 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci ftlbhighset = current_cpu_data.tlbsizevtlb + 8062306a36Sopenharmony_ci current_cpu_data.tlbsizeftlbsets; 8162306a36Sopenharmony_ci for (entry = current_cpu_data.tlbsizevtlb; 8262306a36Sopenharmony_ci entry < ftlbhighset; 8362306a36Sopenharmony_ci entry++) { 8462306a36Sopenharmony_ci write_c0_index(entry); 8562306a36Sopenharmony_ci mtc0_tlbw_hazard(); 8662306a36Sopenharmony_ci tlbinvf(); /* invalidate one FTLB set */ 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } else { 8962306a36Sopenharmony_ci while (entry < current_cpu_data.tlbsize) { 9062306a36Sopenharmony_ci /* Make sure all entries differ. */ 9162306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(entry)); 9262306a36Sopenharmony_ci write_c0_index(entry); 9362306a36Sopenharmony_ci mtc0_tlbw_hazard(); 9462306a36Sopenharmony_ci tlb_write_indexed(); 9562306a36Sopenharmony_ci entry++; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci tlbw_use_hazard(); 9962306a36Sopenharmony_ci write_c0_entryhi(old_ctx); 10062306a36Sopenharmony_ci htw_start(); 10162306a36Sopenharmony_ci flush_micro_tlb(); 10262306a36Sopenharmony_ci local_irq_restore(flags); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ciEXPORT_SYMBOL(local_flush_tlb_all); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 10762306a36Sopenharmony_ci unsigned long end) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 11062306a36Sopenharmony_ci int cpu = smp_processor_id(); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (cpu_context(cpu, mm) != 0) { 11362306a36Sopenharmony_ci unsigned long size, flags; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci local_irq_save(flags); 11662306a36Sopenharmony_ci start = round_down(start, PAGE_SIZE << 1); 11762306a36Sopenharmony_ci end = round_up(end, PAGE_SIZE << 1); 11862306a36Sopenharmony_ci size = (end - start) >> (PAGE_SHIFT + 1); 11962306a36Sopenharmony_ci if (size <= (current_cpu_data.tlbsizeftlbsets ? 12062306a36Sopenharmony_ci current_cpu_data.tlbsize / 8 : 12162306a36Sopenharmony_ci current_cpu_data.tlbsize / 2)) { 12262306a36Sopenharmony_ci unsigned long old_entryhi, old_mmid; 12362306a36Sopenharmony_ci int newpid = cpu_asid(cpu, mm); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci old_entryhi = read_c0_entryhi(); 12662306a36Sopenharmony_ci if (cpu_has_mmid) { 12762306a36Sopenharmony_ci old_mmid = read_c0_memorymapid(); 12862306a36Sopenharmony_ci write_c0_memorymapid(newpid); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci htw_stop(); 13262306a36Sopenharmony_ci while (start < end) { 13362306a36Sopenharmony_ci int idx; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (cpu_has_mmid) 13662306a36Sopenharmony_ci write_c0_entryhi(start); 13762306a36Sopenharmony_ci else 13862306a36Sopenharmony_ci write_c0_entryhi(start | newpid); 13962306a36Sopenharmony_ci start += (PAGE_SIZE << 1); 14062306a36Sopenharmony_ci mtc0_tlbw_hazard(); 14162306a36Sopenharmony_ci tlb_probe(); 14262306a36Sopenharmony_ci tlb_probe_hazard(); 14362306a36Sopenharmony_ci idx = read_c0_index(); 14462306a36Sopenharmony_ci write_c0_entrylo0(0); 14562306a36Sopenharmony_ci write_c0_entrylo1(0); 14662306a36Sopenharmony_ci if (idx < 0) 14762306a36Sopenharmony_ci continue; 14862306a36Sopenharmony_ci /* Make sure all entries differ. */ 14962306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 15062306a36Sopenharmony_ci mtc0_tlbw_hazard(); 15162306a36Sopenharmony_ci tlb_write_indexed(); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci tlbw_use_hazard(); 15462306a36Sopenharmony_ci write_c0_entryhi(old_entryhi); 15562306a36Sopenharmony_ci if (cpu_has_mmid) 15662306a36Sopenharmony_ci write_c0_memorymapid(old_mmid); 15762306a36Sopenharmony_ci htw_start(); 15862306a36Sopenharmony_ci } else { 15962306a36Sopenharmony_ci drop_mmu_context(mm); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci flush_micro_tlb(); 16262306a36Sopenharmony_ci local_irq_restore(flags); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_civoid local_flush_tlb_kernel_range(unsigned long start, unsigned long end) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci unsigned long size, flags; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci local_irq_save(flags); 17162306a36Sopenharmony_ci size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; 17262306a36Sopenharmony_ci size = (size + 1) >> 1; 17362306a36Sopenharmony_ci if (size <= (current_cpu_data.tlbsizeftlbsets ? 17462306a36Sopenharmony_ci current_cpu_data.tlbsize / 8 : 17562306a36Sopenharmony_ci current_cpu_data.tlbsize / 2)) { 17662306a36Sopenharmony_ci int pid = read_c0_entryhi(); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci start &= (PAGE_MASK << 1); 17962306a36Sopenharmony_ci end += ((PAGE_SIZE << 1) - 1); 18062306a36Sopenharmony_ci end &= (PAGE_MASK << 1); 18162306a36Sopenharmony_ci htw_stop(); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci while (start < end) { 18462306a36Sopenharmony_ci int idx; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci write_c0_entryhi(start); 18762306a36Sopenharmony_ci start += (PAGE_SIZE << 1); 18862306a36Sopenharmony_ci mtc0_tlbw_hazard(); 18962306a36Sopenharmony_ci tlb_probe(); 19062306a36Sopenharmony_ci tlb_probe_hazard(); 19162306a36Sopenharmony_ci idx = read_c0_index(); 19262306a36Sopenharmony_ci write_c0_entrylo0(0); 19362306a36Sopenharmony_ci write_c0_entrylo1(0); 19462306a36Sopenharmony_ci if (idx < 0) 19562306a36Sopenharmony_ci continue; 19662306a36Sopenharmony_ci /* Make sure all entries differ. */ 19762306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 19862306a36Sopenharmony_ci mtc0_tlbw_hazard(); 19962306a36Sopenharmony_ci tlb_write_indexed(); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci tlbw_use_hazard(); 20262306a36Sopenharmony_ci write_c0_entryhi(pid); 20362306a36Sopenharmony_ci htw_start(); 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci local_flush_tlb_all(); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci flush_micro_tlb(); 20862306a36Sopenharmony_ci local_irq_restore(flags); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_civoid local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int cpu = smp_processor_id(); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (cpu_context(cpu, vma->vm_mm) != 0) { 21662306a36Sopenharmony_ci unsigned long old_mmid; 21762306a36Sopenharmony_ci unsigned long flags, old_entryhi; 21862306a36Sopenharmony_ci int idx; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci page &= (PAGE_MASK << 1); 22162306a36Sopenharmony_ci local_irq_save(flags); 22262306a36Sopenharmony_ci old_entryhi = read_c0_entryhi(); 22362306a36Sopenharmony_ci htw_stop(); 22462306a36Sopenharmony_ci if (cpu_has_mmid) { 22562306a36Sopenharmony_ci old_mmid = read_c0_memorymapid(); 22662306a36Sopenharmony_ci write_c0_entryhi(page); 22762306a36Sopenharmony_ci write_c0_memorymapid(cpu_asid(cpu, vma->vm_mm)); 22862306a36Sopenharmony_ci } else { 22962306a36Sopenharmony_ci write_c0_entryhi(page | cpu_asid(cpu, vma->vm_mm)); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci mtc0_tlbw_hazard(); 23262306a36Sopenharmony_ci tlb_probe(); 23362306a36Sopenharmony_ci tlb_probe_hazard(); 23462306a36Sopenharmony_ci idx = read_c0_index(); 23562306a36Sopenharmony_ci write_c0_entrylo0(0); 23662306a36Sopenharmony_ci write_c0_entrylo1(0); 23762306a36Sopenharmony_ci if (idx < 0) 23862306a36Sopenharmony_ci goto finish; 23962306a36Sopenharmony_ci /* Make sure all entries differ. */ 24062306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 24162306a36Sopenharmony_ci mtc0_tlbw_hazard(); 24262306a36Sopenharmony_ci tlb_write_indexed(); 24362306a36Sopenharmony_ci tlbw_use_hazard(); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci finish: 24662306a36Sopenharmony_ci write_c0_entryhi(old_entryhi); 24762306a36Sopenharmony_ci if (cpu_has_mmid) 24862306a36Sopenharmony_ci write_c0_memorymapid(old_mmid); 24962306a36Sopenharmony_ci htw_start(); 25062306a36Sopenharmony_ci flush_micro_tlb_vm(vma); 25162306a36Sopenharmony_ci local_irq_restore(flags); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * This one is only used for pages with the global bit set so we don't care 25762306a36Sopenharmony_ci * much about the ASID. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_civoid local_flush_tlb_one(unsigned long page) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci unsigned long flags; 26262306a36Sopenharmony_ci int oldpid, idx; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci local_irq_save(flags); 26562306a36Sopenharmony_ci oldpid = read_c0_entryhi(); 26662306a36Sopenharmony_ci htw_stop(); 26762306a36Sopenharmony_ci page &= (PAGE_MASK << 1); 26862306a36Sopenharmony_ci write_c0_entryhi(page); 26962306a36Sopenharmony_ci mtc0_tlbw_hazard(); 27062306a36Sopenharmony_ci tlb_probe(); 27162306a36Sopenharmony_ci tlb_probe_hazard(); 27262306a36Sopenharmony_ci idx = read_c0_index(); 27362306a36Sopenharmony_ci write_c0_entrylo0(0); 27462306a36Sopenharmony_ci write_c0_entrylo1(0); 27562306a36Sopenharmony_ci if (idx >= 0) { 27662306a36Sopenharmony_ci /* Make sure all entries differ. */ 27762306a36Sopenharmony_ci write_c0_entryhi(UNIQUE_ENTRYHI(idx)); 27862306a36Sopenharmony_ci mtc0_tlbw_hazard(); 27962306a36Sopenharmony_ci tlb_write_indexed(); 28062306a36Sopenharmony_ci tlbw_use_hazard(); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci write_c0_entryhi(oldpid); 28362306a36Sopenharmony_ci htw_start(); 28462306a36Sopenharmony_ci flush_micro_tlb(); 28562306a36Sopenharmony_ci local_irq_restore(flags); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * We will need multiple versions of update_mmu_cache(), one that just 29062306a36Sopenharmony_ci * updates the TLB with the new pte(s), and another which also checks 29162306a36Sopenharmony_ci * for the R4k "end of page" hardware bug and does the needy. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_civoid __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci unsigned long flags; 29662306a36Sopenharmony_ci pgd_t *pgdp; 29762306a36Sopenharmony_ci p4d_t *p4dp; 29862306a36Sopenharmony_ci pud_t *pudp; 29962306a36Sopenharmony_ci pmd_t *pmdp; 30062306a36Sopenharmony_ci pte_t *ptep, *ptemap = NULL; 30162306a36Sopenharmony_ci int idx, pid; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* 30462306a36Sopenharmony_ci * Handle debugger faulting in for debugee. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (current->active_mm != vma->vm_mm) 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci local_irq_save(flags); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci htw_stop(); 31262306a36Sopenharmony_ci address &= (PAGE_MASK << 1); 31362306a36Sopenharmony_ci if (cpu_has_mmid) { 31462306a36Sopenharmony_ci write_c0_entryhi(address); 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci pid = read_c0_entryhi() & cpu_asid_mask(¤t_cpu_data); 31762306a36Sopenharmony_ci write_c0_entryhi(address | pid); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci pgdp = pgd_offset(vma->vm_mm, address); 32062306a36Sopenharmony_ci mtc0_tlbw_hazard(); 32162306a36Sopenharmony_ci tlb_probe(); 32262306a36Sopenharmony_ci tlb_probe_hazard(); 32362306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, address); 32462306a36Sopenharmony_ci pudp = pud_offset(p4dp, address); 32562306a36Sopenharmony_ci pmdp = pmd_offset(pudp, address); 32662306a36Sopenharmony_ci idx = read_c0_index(); 32762306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT 32862306a36Sopenharmony_ci /* this could be a huge page */ 32962306a36Sopenharmony_ci if (pmd_huge(*pmdp)) { 33062306a36Sopenharmony_ci unsigned long lo; 33162306a36Sopenharmony_ci write_c0_pagemask(PM_HUGE_MASK); 33262306a36Sopenharmony_ci ptep = (pte_t *)pmdp; 33362306a36Sopenharmony_ci lo = pte_to_entrylo(pte_val(*ptep)); 33462306a36Sopenharmony_ci write_c0_entrylo0(lo); 33562306a36Sopenharmony_ci write_c0_entrylo1(lo + (HPAGE_SIZE >> 7)); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci mtc0_tlbw_hazard(); 33862306a36Sopenharmony_ci if (idx < 0) 33962306a36Sopenharmony_ci tlb_write_random(); 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci tlb_write_indexed(); 34262306a36Sopenharmony_ci tlbw_use_hazard(); 34362306a36Sopenharmony_ci write_c0_pagemask(PM_DEFAULT_MASK); 34462306a36Sopenharmony_ci } else 34562306a36Sopenharmony_ci#endif 34662306a36Sopenharmony_ci { 34762306a36Sopenharmony_ci ptemap = ptep = pte_offset_map(pmdp, address); 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * update_mmu_cache() is called between pte_offset_map_lock() 35062306a36Sopenharmony_ci * and pte_unmap_unlock(), so we can assume that ptep is not 35162306a36Sopenharmony_ci * NULL here: and what should be done below if it were NULL? 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32) 35562306a36Sopenharmony_ci#ifdef CONFIG_XPA 35662306a36Sopenharmony_ci write_c0_entrylo0(pte_to_entrylo(ptep->pte_high)); 35762306a36Sopenharmony_ci if (cpu_has_xpa) 35862306a36Sopenharmony_ci writex_c0_entrylo0(ptep->pte_low & _PFNX_MASK); 35962306a36Sopenharmony_ci ptep++; 36062306a36Sopenharmony_ci write_c0_entrylo1(pte_to_entrylo(ptep->pte_high)); 36162306a36Sopenharmony_ci if (cpu_has_xpa) 36262306a36Sopenharmony_ci writex_c0_entrylo1(ptep->pte_low & _PFNX_MASK); 36362306a36Sopenharmony_ci#else 36462306a36Sopenharmony_ci write_c0_entrylo0(ptep->pte_high); 36562306a36Sopenharmony_ci ptep++; 36662306a36Sopenharmony_ci write_c0_entrylo1(ptep->pte_high); 36762306a36Sopenharmony_ci#endif 36862306a36Sopenharmony_ci#else 36962306a36Sopenharmony_ci write_c0_entrylo0(pte_to_entrylo(pte_val(*ptep++))); 37062306a36Sopenharmony_ci write_c0_entrylo1(pte_to_entrylo(pte_val(*ptep))); 37162306a36Sopenharmony_ci#endif 37262306a36Sopenharmony_ci mtc0_tlbw_hazard(); 37362306a36Sopenharmony_ci if (idx < 0) 37462306a36Sopenharmony_ci tlb_write_random(); 37562306a36Sopenharmony_ci else 37662306a36Sopenharmony_ci tlb_write_indexed(); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci tlbw_use_hazard(); 37962306a36Sopenharmony_ci htw_start(); 38062306a36Sopenharmony_ci flush_micro_tlb_vm(vma); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (ptemap) 38362306a36Sopenharmony_ci pte_unmap(ptemap); 38462306a36Sopenharmony_ci local_irq_restore(flags); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_civoid add_wired_entry(unsigned long entrylo0, unsigned long entrylo1, 38862306a36Sopenharmony_ci unsigned long entryhi, unsigned long pagemask) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci#ifdef CONFIG_XPA 39162306a36Sopenharmony_ci panic("Broken for XPA kernels"); 39262306a36Sopenharmony_ci#else 39362306a36Sopenharmony_ci unsigned int old_mmid; 39462306a36Sopenharmony_ci unsigned long flags; 39562306a36Sopenharmony_ci unsigned long wired; 39662306a36Sopenharmony_ci unsigned long old_pagemask; 39762306a36Sopenharmony_ci unsigned long old_ctx; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci local_irq_save(flags); 40062306a36Sopenharmony_ci if (cpu_has_mmid) { 40162306a36Sopenharmony_ci old_mmid = read_c0_memorymapid(); 40262306a36Sopenharmony_ci write_c0_memorymapid(MMID_KERNEL_WIRED); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci /* Save old context and create impossible VPN2 value */ 40562306a36Sopenharmony_ci old_ctx = read_c0_entryhi(); 40662306a36Sopenharmony_ci htw_stop(); 40762306a36Sopenharmony_ci old_pagemask = read_c0_pagemask(); 40862306a36Sopenharmony_ci wired = num_wired_entries(); 40962306a36Sopenharmony_ci write_c0_wired(wired + 1); 41062306a36Sopenharmony_ci write_c0_index(wired); 41162306a36Sopenharmony_ci tlbw_use_hazard(); /* What is the hazard here? */ 41262306a36Sopenharmony_ci write_c0_pagemask(pagemask); 41362306a36Sopenharmony_ci write_c0_entryhi(entryhi); 41462306a36Sopenharmony_ci write_c0_entrylo0(entrylo0); 41562306a36Sopenharmony_ci write_c0_entrylo1(entrylo1); 41662306a36Sopenharmony_ci mtc0_tlbw_hazard(); 41762306a36Sopenharmony_ci tlb_write_indexed(); 41862306a36Sopenharmony_ci tlbw_use_hazard(); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci write_c0_entryhi(old_ctx); 42162306a36Sopenharmony_ci if (cpu_has_mmid) 42262306a36Sopenharmony_ci write_c0_memorymapid(old_mmid); 42362306a36Sopenharmony_ci tlbw_use_hazard(); /* What is the hazard here? */ 42462306a36Sopenharmony_ci htw_start(); 42562306a36Sopenharmony_ci write_c0_pagemask(old_pagemask); 42662306a36Sopenharmony_ci local_flush_tlb_all(); 42762306a36Sopenharmony_ci local_irq_restore(flags); 42862306a36Sopenharmony_ci#endif 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciint has_transparent_hugepage(void) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci static unsigned int mask = -1; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (mask == -1) { /* first call comes during __init */ 43862306a36Sopenharmony_ci unsigned long flags; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci local_irq_save(flags); 44162306a36Sopenharmony_ci write_c0_pagemask(PM_HUGE_MASK); 44262306a36Sopenharmony_ci back_to_back_c0_hazard(); 44362306a36Sopenharmony_ci mask = read_c0_pagemask(); 44462306a36Sopenharmony_ci write_c0_pagemask(PM_DEFAULT_MASK); 44562306a36Sopenharmony_ci local_irq_restore(flags); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci return mask == PM_HUGE_MASK; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ciEXPORT_SYMBOL(has_transparent_hugepage); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * Used for loading TLB entries before trap_init() has started, when we 45562306a36Sopenharmony_ci * don't actually want to add a wired entry which remains throughout the 45662306a36Sopenharmony_ci * lifetime of the system 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ciint temp_tlb_entry; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci__init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1, 46262306a36Sopenharmony_ci unsigned long entryhi, unsigned long pagemask) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci int ret = 0; 46562306a36Sopenharmony_ci unsigned long flags; 46662306a36Sopenharmony_ci unsigned long wired; 46762306a36Sopenharmony_ci unsigned long old_pagemask; 46862306a36Sopenharmony_ci unsigned long old_ctx; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci local_irq_save(flags); 47162306a36Sopenharmony_ci /* Save old context and create impossible VPN2 value */ 47262306a36Sopenharmony_ci htw_stop(); 47362306a36Sopenharmony_ci old_ctx = read_c0_entryhi(); 47462306a36Sopenharmony_ci old_pagemask = read_c0_pagemask(); 47562306a36Sopenharmony_ci wired = num_wired_entries(); 47662306a36Sopenharmony_ci if (--temp_tlb_entry < wired) { 47762306a36Sopenharmony_ci printk(KERN_WARNING 47862306a36Sopenharmony_ci "No TLB space left for add_temporary_entry\n"); 47962306a36Sopenharmony_ci ret = -ENOSPC; 48062306a36Sopenharmony_ci goto out; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci write_c0_index(temp_tlb_entry); 48462306a36Sopenharmony_ci write_c0_pagemask(pagemask); 48562306a36Sopenharmony_ci write_c0_entryhi(entryhi); 48662306a36Sopenharmony_ci write_c0_entrylo0(entrylo0); 48762306a36Sopenharmony_ci write_c0_entrylo1(entrylo1); 48862306a36Sopenharmony_ci mtc0_tlbw_hazard(); 48962306a36Sopenharmony_ci tlb_write_indexed(); 49062306a36Sopenharmony_ci tlbw_use_hazard(); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci write_c0_entryhi(old_ctx); 49362306a36Sopenharmony_ci write_c0_pagemask(old_pagemask); 49462306a36Sopenharmony_ci htw_start(); 49562306a36Sopenharmony_ciout: 49662306a36Sopenharmony_ci local_irq_restore(flags); 49762306a36Sopenharmony_ci return ret; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int ntlb; 50162306a36Sopenharmony_cistatic int __init set_ntlb(char *str) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci get_option(&str, &ntlb); 50462306a36Sopenharmony_ci return 1; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci__setup("ntlb=", set_ntlb); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci/* 51062306a36Sopenharmony_ci * Configure TLB (for init or after a CPU has been powered off). 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_cistatic void r4k_tlb_configure(void) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * You should never change this register: 51662306a36Sopenharmony_ci * - On R4600 1.7 the tlbp never hits for pages smaller than 51762306a36Sopenharmony_ci * the value in the c0_pagemask register. 51862306a36Sopenharmony_ci * - The entire mm handling assumes the c0_pagemask register to 51962306a36Sopenharmony_ci * be set to fixed-size pages. 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_ci write_c0_pagemask(PM_DEFAULT_MASK); 52262306a36Sopenharmony_ci back_to_back_c0_hazard(); 52362306a36Sopenharmony_ci if (read_c0_pagemask() != PM_DEFAULT_MASK) 52462306a36Sopenharmony_ci panic("MMU doesn't support PAGE_SIZE=0x%lx", PAGE_SIZE); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci write_c0_wired(0); 52762306a36Sopenharmony_ci if (current_cpu_type() == CPU_R10000 || 52862306a36Sopenharmony_ci current_cpu_type() == CPU_R12000 || 52962306a36Sopenharmony_ci current_cpu_type() == CPU_R14000 || 53062306a36Sopenharmony_ci current_cpu_type() == CPU_R16000) 53162306a36Sopenharmony_ci write_c0_framemask(0); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (cpu_has_rixi) { 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * Enable the no read, no exec bits, and enable large physical 53662306a36Sopenharmony_ci * address. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci#ifdef CONFIG_64BIT 53962306a36Sopenharmony_ci set_c0_pagegrain(PG_RIE | PG_XIE | PG_ELPA); 54062306a36Sopenharmony_ci#else 54162306a36Sopenharmony_ci set_c0_pagegrain(PG_RIE | PG_XIE); 54262306a36Sopenharmony_ci#endif 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci temp_tlb_entry = current_cpu_data.tlbsize - 1; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* From this point on the ARC firmware is dead. */ 54862306a36Sopenharmony_ci local_flush_tlb_all(); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Did I tell you that ARC SUCKS? */ 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_civoid tlb_init(void) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci r4k_tlb_configure(); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (ntlb) { 55862306a36Sopenharmony_ci if (ntlb > 1 && ntlb <= current_cpu_data.tlbsize) { 55962306a36Sopenharmony_ci int wired = current_cpu_data.tlbsize - ntlb; 56062306a36Sopenharmony_ci write_c0_wired(wired); 56162306a36Sopenharmony_ci write_c0_index(wired-1); 56262306a36Sopenharmony_ci printk("Restricting TLB to %d entries\n", ntlb); 56362306a36Sopenharmony_ci } else 56462306a36Sopenharmony_ci printk("Ignoring invalid argument ntlb=%d\n", ntlb); 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci build_tlb_refill_handler(); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic int r4k_tlb_pm_notifier(struct notifier_block *self, unsigned long cmd, 57162306a36Sopenharmony_ci void *v) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci switch (cmd) { 57462306a36Sopenharmony_ci case CPU_PM_ENTER_FAILED: 57562306a36Sopenharmony_ci case CPU_PM_EXIT: 57662306a36Sopenharmony_ci r4k_tlb_configure(); 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return NOTIFY_OK; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic struct notifier_block r4k_tlb_pm_notifier_block = { 58462306a36Sopenharmony_ci .notifier_call = r4k_tlb_pm_notifier, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic int __init r4k_tlb_init_pm(void) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci return cpu_pm_register_notifier(&r4k_tlb_pm_notifier_block); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ciarch_initcall(r4k_tlb_init_pm); 592