root/arch/nios2/mm/tlb.c

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

DEFINITIONS

This source file includes following definitions.
  1. get_misc_and_pid
  2. pteaddr_invalid
  3. replace_tlb_one_pid
  4. flush_tlb_one_pid
  5. reload_tlb_one_pid
  6. flush_tlb_range
  7. reload_tlb_page
  8. flush_tlb_one
  9. flush_tlb_kernel_range
  10. dump_tlb_line
  11. dump_tlb
  12. flush_tlb_pid
  13. flush_tlb_mm
  14. flush_tlb_all
  15. set_mmu_pid

   1 /*
   2  * Nios2 TLB handling
   3  *
   4  * Copyright (C) 2009, Wind River Systems Inc
   5  *   Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
   6  *
   7  * This file is subject to the terms and conditions of the GNU General Public
   8  * License.  See the file "COPYING" in the main directory of this archive
   9  * for more details.
  10  */
  11 
  12 #include <linux/init.h>
  13 #include <linux/sched.h>
  14 #include <linux/mm.h>
  15 #include <linux/pagemap.h>
  16 
  17 #include <asm/tlb.h>
  18 #include <asm/mmu_context.h>
  19 #include <asm/pgtable.h>
  20 #include <asm/cpuinfo.h>
  21 
  22 #define TLB_INDEX_MASK          \
  23         ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \
  24                 << PAGE_SHIFT)
  25 
  26 static void get_misc_and_pid(unsigned long *misc, unsigned long *pid)
  27 {
  28         *misc  = RDCTL(CTL_TLBMISC);
  29         *misc &= (TLBMISC_PID | TLBMISC_WAY);
  30         *pid  = *misc & TLBMISC_PID;
  31 }
  32 
  33 /*
  34  * This provides a PTEADDR value for addr that will cause a TLB miss
  35  * (fast TLB miss). TLB invalidation replaces entries with this value.
  36  */
  37 static unsigned long pteaddr_invalid(unsigned long addr)
  38 {
  39         return ((addr | 0xC0000000UL) >> PAGE_SHIFT) << 2;
  40 }
  41 
  42 /*
  43  * This one is only used for pages with the global bit set so we don't care
  44  * much about the ASID.
  45  */
  46 static void replace_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, unsigned long tlbacc)
  47 {
  48         unsigned int way;
  49         unsigned long org_misc, pid_misc;
  50 
  51         /* remember pid/way until we return. */
  52         get_misc_and_pid(&org_misc, &pid_misc);
  53 
  54         WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
  55 
  56         for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
  57                 unsigned long pteaddr;
  58                 unsigned long tlbmisc;
  59                 unsigned long pid;
  60 
  61                 tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
  62                 WRCTL(CTL_TLBMISC, tlbmisc);
  63 
  64                 pteaddr = RDCTL(CTL_PTEADDR);
  65                 if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT))
  66                         continue;
  67 
  68                 tlbmisc = RDCTL(CTL_TLBMISC);
  69                 pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
  70                 if (pid != mmu_pid)
  71                         continue;
  72 
  73                 tlbmisc = (mmu_pid << TLBMISC_PID_SHIFT) | TLBMISC_WE |
  74                           (way << TLBMISC_WAY_SHIFT);
  75                 WRCTL(CTL_TLBMISC, tlbmisc);
  76                 if (tlbacc == 0)
  77                         WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
  78                 WRCTL(CTL_TLBACC, tlbacc);
  79                 /*
  80                  * There should be only a single entry that maps a
  81                  * particular {address,pid} so break after a match.
  82                  */
  83                 break;
  84         }
  85 
  86         WRCTL(CTL_TLBMISC, org_misc);
  87 }
  88 
  89 static void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid)
  90 {
  91         pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
  92 
  93         replace_tlb_one_pid(addr, mmu_pid, 0);
  94 }
  95 
  96 static void reload_tlb_one_pid(unsigned long addr, unsigned long mmu_pid, pte_t pte)
  97 {
  98         pr_debug("Reload tlb-entry for vaddr=%#lx\n", addr);
  99 
 100         replace_tlb_one_pid(addr, mmu_pid, pte_val(pte));
 101 }
 102 
 103 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
 104                         unsigned long end)
 105 {
 106         unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
 107 
 108         while (start < end) {
 109                 flush_tlb_one_pid(start, mmu_pid);
 110                 start += PAGE_SIZE;
 111         }
 112 }
 113 
 114 void reload_tlb_page(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
 115 {
 116         unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
 117 
 118         reload_tlb_one_pid(addr, mmu_pid, pte);
 119 }
 120 
 121 /*
 122  * This one is only used for pages with the global bit set so we don't care
 123  * much about the ASID.
 124  */
 125 static void flush_tlb_one(unsigned long addr)
 126 {
 127         unsigned int way;
 128         unsigned long org_misc, pid_misc;
 129 
 130         pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
 131 
 132         /* remember pid/way until we return. */
 133         get_misc_and_pid(&org_misc, &pid_misc);
 134 
 135         WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
 136 
 137         for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
 138                 unsigned long pteaddr;
 139                 unsigned long tlbmisc;
 140 
 141                 tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
 142                 WRCTL(CTL_TLBMISC, tlbmisc);
 143 
 144                 pteaddr = RDCTL(CTL_PTEADDR);
 145                 if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT))
 146                         continue;
 147 
 148                 pr_debug("Flush entry by writing way=%dl pid=%ld\n",
 149                          way, (pid_misc >> TLBMISC_PID_SHIFT));
 150 
 151                 tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT);
 152                 WRCTL(CTL_TLBMISC, tlbmisc);
 153                 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
 154                 WRCTL(CTL_TLBACC, 0);
 155         }
 156 
 157         WRCTL(CTL_TLBMISC, org_misc);
 158 }
 159 
 160 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
 161 {
 162         while (start < end) {
 163                 flush_tlb_one(start);
 164                 start += PAGE_SIZE;
 165         }
 166 }
 167 
 168 void dump_tlb_line(unsigned long line)
 169 {
 170         unsigned int way;
 171         unsigned long org_misc;
 172 
 173         pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line,
 174                 line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2));
 175 
 176         /* remember pid/way until we return */
 177         org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY));
 178 
 179         WRCTL(CTL_PTEADDR, line << 2);
 180 
 181         for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
 182                 unsigned long pteaddr;
 183                 unsigned long tlbmisc;
 184                 unsigned long tlbacc;
 185 
 186                 WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT));
 187                 pteaddr = RDCTL(CTL_PTEADDR);
 188                 tlbmisc = RDCTL(CTL_TLBMISC);
 189                 tlbacc = RDCTL(CTL_TLBACC);
 190 
 191                 if ((tlbacc << PAGE_SHIFT) != 0) {
 192                         pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n",
 193                                 way,
 194                                 (pteaddr << (PAGE_SHIFT-2)),
 195                                 (tlbacc << PAGE_SHIFT),
 196                                 ((tlbmisc >> TLBMISC_PID_SHIFT) &
 197                                 TLBMISC_PID_MASK),
 198                                 (tlbacc & _PAGE_READ ? 'r' : '-'),
 199                                 (tlbacc & _PAGE_WRITE ? 'w' : '-'),
 200                                 (tlbacc & _PAGE_EXEC ? 'x' : '-'),
 201                                 (tlbacc & _PAGE_GLOBAL ? 'g' : '-'),
 202                                 (tlbacc & _PAGE_CACHED ? 'c' : '-'));
 203                 }
 204         }
 205 
 206         WRCTL(CTL_TLBMISC, org_misc);
 207 }
 208 
 209 void dump_tlb(void)
 210 {
 211         unsigned int i;
 212 
 213         for (i = 0; i < cpuinfo.tlb_num_lines; i++)
 214                 dump_tlb_line(i);
 215 }
 216 
 217 void flush_tlb_pid(unsigned long mmu_pid)
 218 {
 219         unsigned long addr = 0;
 220         unsigned int line;
 221         unsigned int way;
 222         unsigned long org_misc, pid_misc;
 223 
 224         /* remember pid/way until we return */
 225         get_misc_and_pid(&org_misc, &pid_misc);
 226 
 227         for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
 228                 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
 229 
 230                 for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
 231                         unsigned long tlbmisc;
 232                         unsigned long pid;
 233 
 234                         tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
 235                         WRCTL(CTL_TLBMISC, tlbmisc);
 236                         tlbmisc = RDCTL(CTL_TLBMISC);
 237                         pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
 238                         if (pid != mmu_pid)
 239                                 continue;
 240 
 241                         tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT);
 242                         WRCTL(CTL_TLBMISC, tlbmisc);
 243                         WRCTL(CTL_TLBACC, 0);
 244                 }
 245 
 246                 addr += PAGE_SIZE;
 247         }
 248 
 249         WRCTL(CTL_TLBMISC, org_misc);
 250 }
 251 
 252 /*
 253  * All entries common to a mm share an asid.  To effectively flush these
 254  * entries, we just bump the asid.
 255  */
 256 void flush_tlb_mm(struct mm_struct *mm)
 257 {
 258         if (current->mm == mm) {
 259                 unsigned long mmu_pid = get_pid_from_context(&mm->context);
 260                 flush_tlb_pid(mmu_pid);
 261         } else {
 262                 memset(&mm->context, 0, sizeof(mm_context_t));
 263         }
 264 }
 265 
 266 void flush_tlb_all(void)
 267 {
 268         unsigned long addr = 0;
 269         unsigned int line;
 270         unsigned int way;
 271         unsigned long org_misc, pid_misc;
 272 
 273         /* remember pid/way until we return */
 274         get_misc_and_pid(&org_misc, &pid_misc);
 275 
 276         /* Start at way 0, way is auto-incremented after each TLBACC write */
 277         WRCTL(CTL_TLBMISC, TLBMISC_WE);
 278 
 279         /* Map each TLB entry to physcal address 0 with no-access and a
 280            bad ptbase */
 281         for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
 282                 WRCTL(CTL_PTEADDR, pteaddr_invalid(addr));
 283                 for (way = 0; way < cpuinfo.tlb_num_ways; way++)
 284                         WRCTL(CTL_TLBACC, 0);
 285 
 286                 addr += PAGE_SIZE;
 287         }
 288 
 289         /* restore pid/way */
 290         WRCTL(CTL_TLBMISC, org_misc);
 291 }
 292 
 293 void set_mmu_pid(unsigned long pid)
 294 {
 295         unsigned long tlbmisc;
 296 
 297         tlbmisc = RDCTL(CTL_TLBMISC);
 298         tlbmisc = (tlbmisc & TLBMISC_WAY);
 299         tlbmisc |= (pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT;
 300         WRCTL(CTL_TLBMISC, tlbmisc);
 301 }

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