root/arch/mips/mm/tlb-r3k.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. local_flush_tlb_from
  2. local_flush_tlb_all
  3. local_flush_tlb_range
  4. local_flush_tlb_kernel_range
  5. local_flush_tlb_page
  6. __update_tlb
  7. add_wired_entry
  8. tlb_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * r2300.c: R2000 and R3000 specific mmu/cache code.
   4  *
   5  * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
   6  *
   7  * with a lot of changes to make this thing work for R3000s
   8  * Tx39XX R4k style caches added. HK
   9  * Copyright (C) 1998, 1999, 2000 Harald Koerfgen
  10  * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
  11  * Copyright (C) 2002  Ralf Baechle
  12  * Copyright (C) 2002  Maciej W. Rozycki
  13  */
  14 #include <linux/kernel.h>
  15 #include <linux/sched.h>
  16 #include <linux/smp.h>
  17 #include <linux/mm.h>
  18 
  19 #include <asm/page.h>
  20 #include <asm/pgtable.h>
  21 #include <asm/mmu_context.h>
  22 #include <asm/tlbmisc.h>
  23 #include <asm/isadep.h>
  24 #include <asm/io.h>
  25 #include <asm/bootinfo.h>
  26 #include <asm/cpu.h>
  27 
  28 #undef DEBUG_TLB
  29 
  30 extern void build_tlb_refill_handler(void);
  31 
  32 /* CP0 hazard avoidance. */
  33 #define BARRIER                         \
  34         __asm__ __volatile__(           \
  35                 ".set   push\n\t"       \
  36                 ".set   noreorder\n\t"  \
  37                 "nop\n\t"               \
  38                 ".set   pop\n\t")
  39 
  40 int r3k_have_wired_reg;                 /* Should be in cpu_data? */
  41 
  42 /* TLB operations. */
  43 static void local_flush_tlb_from(int entry)
  44 {
  45         unsigned long old_ctx;
  46 
  47         old_ctx = read_c0_entryhi() & cpu_asid_mask(&current_cpu_data);
  48         write_c0_entrylo0(0);
  49         while (entry < current_cpu_data.tlbsize) {
  50                 write_c0_index(entry << 8);
  51                 write_c0_entryhi((entry | 0x80000) << 12);
  52                 entry++;                                /* BARRIER */
  53                 tlb_write_indexed();
  54         }
  55         write_c0_entryhi(old_ctx);
  56 }
  57 
  58 void local_flush_tlb_all(void)
  59 {
  60         unsigned long flags;
  61 
  62 #ifdef DEBUG_TLB
  63         printk("[tlball]");
  64 #endif
  65         local_irq_save(flags);
  66         local_flush_tlb_from(r3k_have_wired_reg ? read_c0_wired() : 8);
  67         local_irq_restore(flags);
  68 }
  69 
  70 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
  71                            unsigned long end)
  72 {
  73         unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
  74         struct mm_struct *mm = vma->vm_mm;
  75         int cpu = smp_processor_id();
  76 
  77         if (cpu_context(cpu, mm) != 0) {
  78                 unsigned long size, flags;
  79 
  80 #ifdef DEBUG_TLB
  81                 printk("[tlbrange<%lu,0x%08lx,0x%08lx>]",
  82                         cpu_context(cpu, mm) & asid_mask, start, end);
  83 #endif
  84                 local_irq_save(flags);
  85                 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
  86                 if (size <= current_cpu_data.tlbsize) {
  87                         int oldpid = read_c0_entryhi() & asid_mask;
  88                         int newpid = cpu_context(cpu, mm) & asid_mask;
  89 
  90                         start &= PAGE_MASK;
  91                         end += PAGE_SIZE - 1;
  92                         end &= PAGE_MASK;
  93                         while (start < end) {
  94                                 int idx;
  95 
  96                                 write_c0_entryhi(start | newpid);
  97                                 start += PAGE_SIZE;     /* BARRIER */
  98                                 tlb_probe();
  99                                 idx = read_c0_index();
 100                                 write_c0_entrylo0(0);
 101                                 write_c0_entryhi(KSEG0);
 102                                 if (idx < 0)            /* BARRIER */
 103                                         continue;
 104                                 tlb_write_indexed();
 105                         }
 106                         write_c0_entryhi(oldpid);
 107                 } else {
 108                         drop_mmu_context(mm);
 109                 }
 110                 local_irq_restore(flags);
 111         }
 112 }
 113 
 114 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
 115 {
 116         unsigned long size, flags;
 117 
 118 #ifdef DEBUG_TLB
 119         printk("[tlbrange<%lu,0x%08lx,0x%08lx>]", start, end);
 120 #endif
 121         local_irq_save(flags);
 122         size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 123         if (size <= current_cpu_data.tlbsize) {
 124                 int pid = read_c0_entryhi();
 125 
 126                 start &= PAGE_MASK;
 127                 end += PAGE_SIZE - 1;
 128                 end &= PAGE_MASK;
 129 
 130                 while (start < end) {
 131                         int idx;
 132 
 133                         write_c0_entryhi(start);
 134                         start += PAGE_SIZE;             /* BARRIER */
 135                         tlb_probe();
 136                         idx = read_c0_index();
 137                         write_c0_entrylo0(0);
 138                         write_c0_entryhi(KSEG0);
 139                         if (idx < 0)                    /* BARRIER */
 140                                 continue;
 141                         tlb_write_indexed();
 142                 }
 143                 write_c0_entryhi(pid);
 144         } else {
 145                 local_flush_tlb_all();
 146         }
 147         local_irq_restore(flags);
 148 }
 149 
 150 void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
 151 {
 152         unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
 153         int cpu = smp_processor_id();
 154 
 155         if (cpu_context(cpu, vma->vm_mm) != 0) {
 156                 unsigned long flags;
 157                 int oldpid, newpid, idx;
 158 
 159 #ifdef DEBUG_TLB
 160                 printk("[tlbpage<%lu,0x%08lx>]", cpu_context(cpu, vma->vm_mm), page);
 161 #endif
 162                 newpid = cpu_context(cpu, vma->vm_mm) & asid_mask;
 163                 page &= PAGE_MASK;
 164                 local_irq_save(flags);
 165                 oldpid = read_c0_entryhi() & asid_mask;
 166                 write_c0_entryhi(page | newpid);
 167                 BARRIER;
 168                 tlb_probe();
 169                 idx = read_c0_index();
 170                 write_c0_entrylo0(0);
 171                 write_c0_entryhi(KSEG0);
 172                 if (idx < 0)                            /* BARRIER */
 173                         goto finish;
 174                 tlb_write_indexed();
 175 
 176 finish:
 177                 write_c0_entryhi(oldpid);
 178                 local_irq_restore(flags);
 179         }
 180 }
 181 
 182 void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
 183 {
 184         unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
 185         unsigned long flags;
 186         int idx, pid;
 187 
 188         /*
 189          * Handle debugger faulting in for debugee.
 190          */
 191         if (current->active_mm != vma->vm_mm)
 192                 return;
 193 
 194         pid = read_c0_entryhi() & asid_mask;
 195 
 196 #ifdef DEBUG_TLB
 197         if ((pid != (cpu_context(cpu, vma->vm_mm) & asid_mask)) || (cpu_context(cpu, vma->vm_mm) == 0)) {
 198                 printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%lu tlbpid=%d\n",
 199                        (cpu_context(cpu, vma->vm_mm)), pid);
 200         }
 201 #endif
 202 
 203         local_irq_save(flags);
 204         address &= PAGE_MASK;
 205         write_c0_entryhi(address | pid);
 206         BARRIER;
 207         tlb_probe();
 208         idx = read_c0_index();
 209         write_c0_entrylo0(pte_val(pte));
 210         write_c0_entryhi(address | pid);
 211         if (idx < 0) {                                  /* BARRIER */
 212                 tlb_write_random();
 213         } else {
 214                 tlb_write_indexed();
 215         }
 216         write_c0_entryhi(pid);
 217         local_irq_restore(flags);
 218 }
 219 
 220 void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
 221                      unsigned long entryhi, unsigned long pagemask)
 222 {
 223         unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
 224         unsigned long flags;
 225         unsigned long old_ctx;
 226         static unsigned long wired = 0;
 227 
 228         if (r3k_have_wired_reg) {                       /* TX39XX */
 229                 unsigned long old_pagemask;
 230                 unsigned long w;
 231 
 232 #ifdef DEBUG_TLB
 233                 printk("[tlbwired<entry lo0 %8x, hi %8x\n, pagemask %8x>]\n",
 234                        entrylo0, entryhi, pagemask);
 235 #endif
 236 
 237                 local_irq_save(flags);
 238                 /* Save old context and create impossible VPN2 value */
 239                 old_ctx = read_c0_entryhi() & asid_mask;
 240                 old_pagemask = read_c0_pagemask();
 241                 w = read_c0_wired();
 242                 write_c0_wired(w + 1);
 243                 write_c0_index(w << 8);
 244                 write_c0_pagemask(pagemask);
 245                 write_c0_entryhi(entryhi);
 246                 write_c0_entrylo0(entrylo0);
 247                 BARRIER;
 248                 tlb_write_indexed();
 249 
 250                 write_c0_entryhi(old_ctx);
 251                 write_c0_pagemask(old_pagemask);
 252                 local_flush_tlb_all();
 253                 local_irq_restore(flags);
 254 
 255         } else if (wired < 8) {
 256 #ifdef DEBUG_TLB
 257                 printk("[tlbwired<entry lo0 %8x, hi %8x\n>]\n",
 258                        entrylo0, entryhi);
 259 #endif
 260 
 261                 local_irq_save(flags);
 262                 old_ctx = read_c0_entryhi() & asid_mask;
 263                 write_c0_entrylo0(entrylo0);
 264                 write_c0_entryhi(entryhi);
 265                 write_c0_index(wired);
 266                 wired++;                                /* BARRIER */
 267                 tlb_write_indexed();
 268                 write_c0_entryhi(old_ctx);
 269                 local_flush_tlb_all();
 270                 local_irq_restore(flags);
 271         }
 272 }
 273 
 274 void tlb_init(void)
 275 {
 276         switch (current_cpu_type()) {
 277         case CPU_TX3922:
 278         case CPU_TX3927:
 279                 r3k_have_wired_reg = 1;
 280                 write_c0_wired(0);              /* Set to 8 on reset... */
 281                 break;
 282         }
 283         local_flush_tlb_from(0);
 284         build_tlb_refill_handler();
 285 }

/* [<][>][^][v][top][bottom][index][help] */