Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nios2: TLB handling

This patch adds the TLB maintenance functions.

Signed-off-by: Ley Foon Tan <lftan@altera.com>

+355
+34
arch/nios2/include/asm/tlb.h
··· 1 + /* 2 + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> 3 + * Copyright (C) 2009 Wind River Systems Inc 4 + * Copyright (C) 2004 Microtronix Datacom Ltd. 5 + * 6 + * This file is subject to the terms and conditions of the GNU General Public 7 + * License. See the file "COPYING" in the main directory of this archive 8 + * for more details. 9 + */ 10 + 11 + #ifndef _ASM_NIOS2_TLB_H 12 + #define _ASM_NIOS2_TLB_H 13 + 14 + #define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) 15 + 16 + extern void set_mmu_pid(unsigned long pid); 17 + 18 + /* 19 + * NiosII doesn't need any special per-pte or per-vma handling, except 20 + * we need to flush cache for the area to be unmapped. 21 + */ 22 + #define tlb_start_vma(tlb, vma) \ 23 + do { \ 24 + if (!tlb->fullmm) \ 25 + flush_cache_range(vma, vma->vm_start, vma->vm_end); \ 26 + } while (0) 27 + 28 + #define tlb_end_vma(tlb, vma) do { } while (0) 29 + #define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0) 30 + 31 + #include <linux/pagemap.h> 32 + #include <asm-generic/tlb.h> 33 + 34 + #endif /* _ASM_NIOS2_TLB_H */
+46
arch/nios2/include/asm/tlbflush.h
··· 1 + /* 2 + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 + * 17 + */ 18 + 19 + #ifndef _ASM_NIOS2_TLBFLUSH_H 20 + #define _ASM_NIOS2_TLBFLUSH_H 21 + 22 + struct mm_struct; 23 + 24 + /* 25 + * TLB flushing: 26 + * 27 + * - flush_tlb_all() flushes all processes TLB entries 28 + * - flush_tlb_mm(mm) flushes the specified mm context TLB entries 29 + * - flush_tlb_page(vma, vmaddr) flushes one page 30 + * - flush_tlb_range(vma, start, end) flushes a range of pages 31 + * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages 32 + */ 33 + extern void flush_tlb_all(void); 34 + extern void flush_tlb_mm(struct mm_struct *mm); 35 + extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 36 + unsigned long end); 37 + extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); 38 + extern void flush_tlb_one(unsigned long vaddr); 39 + 40 + static inline void flush_tlb_page(struct vm_area_struct *vma, 41 + unsigned long addr) 42 + { 43 + flush_tlb_one(addr); 44 + } 45 + 46 + #endif /* _ASM_NIOS2_TLBFLUSH_H */
+275
arch/nios2/mm/tlb.c
··· 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 + /* Used as illegal PHYS_ADDR for TLB mappings 27 + */ 28 + #define MAX_PHYS_ADDR 0 29 + 30 + static void get_misc_and_pid(unsigned long *misc, unsigned long *pid) 31 + { 32 + *misc = RDCTL(CTL_TLBMISC); 33 + *misc &= (TLBMISC_PID | TLBMISC_WAY); 34 + *pid = *misc & TLBMISC_PID; 35 + } 36 + 37 + /* 38 + * All entries common to a mm share an asid. To effectively flush these 39 + * entries, we just bump the asid. 40 + */ 41 + void flush_tlb_mm(struct mm_struct *mm) 42 + { 43 + if (current->mm == mm) 44 + flush_tlb_all(); 45 + else 46 + memset(&mm->context, 0, sizeof(mm_context_t)); 47 + } 48 + 49 + /* 50 + * This one is only used for pages with the global bit set so we don't care 51 + * much about the ASID. 52 + */ 53 + void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) 54 + { 55 + unsigned int way; 56 + unsigned long org_misc, pid_misc; 57 + 58 + pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); 59 + 60 + /* remember pid/way until we return. */ 61 + get_misc_and_pid(&org_misc, &pid_misc); 62 + 63 + WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); 64 + 65 + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 66 + unsigned long pteaddr; 67 + unsigned long tlbmisc; 68 + unsigned long pid; 69 + 70 + tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); 71 + WRCTL(CTL_TLBMISC, tlbmisc); 72 + pteaddr = RDCTL(CTL_PTEADDR); 73 + tlbmisc = RDCTL(CTL_TLBMISC); 74 + pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK; 75 + if (((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) && 76 + pid == mmu_pid) { 77 + unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE + 78 + ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + 79 + (addr & TLB_INDEX_MASK); 80 + pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", 81 + vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT)); 82 + 83 + WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); 84 + tlbmisc = pid_misc | TLBMISC_WE | 85 + (way << TLBMISC_WAY_SHIFT); 86 + WRCTL(CTL_TLBMISC, tlbmisc); 87 + WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); 88 + } 89 + } 90 + 91 + WRCTL(CTL_TLBMISC, org_misc); 92 + } 93 + 94 + void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 95 + unsigned long end) 96 + { 97 + unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context); 98 + 99 + while (start < end) { 100 + flush_tlb_one_pid(start, mmu_pid); 101 + start += PAGE_SIZE; 102 + } 103 + } 104 + 105 + void flush_tlb_kernel_range(unsigned long start, unsigned long end) 106 + { 107 + while (start < end) { 108 + flush_tlb_one(start); 109 + start += PAGE_SIZE; 110 + } 111 + } 112 + 113 + /* 114 + * This one is only used for pages with the global bit set so we don't care 115 + * much about the ASID. 116 + */ 117 + void flush_tlb_one(unsigned long addr) 118 + { 119 + unsigned int way; 120 + unsigned long org_misc, pid_misc; 121 + 122 + pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); 123 + 124 + /* remember pid/way until we return. */ 125 + get_misc_and_pid(&org_misc, &pid_misc); 126 + 127 + WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); 128 + 129 + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 130 + unsigned long pteaddr; 131 + unsigned long tlbmisc; 132 + 133 + tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); 134 + WRCTL(CTL_TLBMISC, tlbmisc); 135 + pteaddr = RDCTL(CTL_PTEADDR); 136 + tlbmisc = RDCTL(CTL_TLBMISC); 137 + 138 + if ((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) { 139 + unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE + 140 + ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) + 141 + (addr & TLB_INDEX_MASK); 142 + 143 + pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n", 144 + vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT)); 145 + 146 + tlbmisc = pid_misc | TLBMISC_WE | 147 + (way << TLBMISC_WAY_SHIFT); 148 + WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2); 149 + WRCTL(CTL_TLBMISC, tlbmisc); 150 + WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); 151 + } 152 + } 153 + 154 + WRCTL(CTL_TLBMISC, org_misc); 155 + } 156 + 157 + void dump_tlb_line(unsigned long line) 158 + { 159 + unsigned int way; 160 + unsigned long org_misc; 161 + 162 + pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line, 163 + line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2)); 164 + 165 + /* remember pid/way until we return */ 166 + org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY)); 167 + 168 + WRCTL(CTL_PTEADDR, line << 2); 169 + 170 + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 171 + unsigned long pteaddr; 172 + unsigned long tlbmisc; 173 + unsigned long tlbacc; 174 + 175 + WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT)); 176 + pteaddr = RDCTL(CTL_PTEADDR); 177 + tlbmisc = RDCTL(CTL_TLBMISC); 178 + tlbacc = RDCTL(CTL_TLBACC); 179 + 180 + if ((tlbacc << PAGE_SHIFT) != (MAX_PHYS_ADDR & PAGE_MASK)) { 181 + pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n", 182 + way, 183 + (pteaddr << (PAGE_SHIFT-2)), 184 + (tlbacc << PAGE_SHIFT), 185 + ((tlbmisc >> TLBMISC_PID_SHIFT) & 186 + TLBMISC_PID_MASK), 187 + (tlbacc & _PAGE_READ ? 'r' : '-'), 188 + (tlbacc & _PAGE_WRITE ? 'w' : '-'), 189 + (tlbacc & _PAGE_EXEC ? 'x' : '-'), 190 + (tlbacc & _PAGE_GLOBAL ? 'g' : '-'), 191 + (tlbacc & _PAGE_CACHED ? 'c' : '-')); 192 + } 193 + } 194 + 195 + WRCTL(CTL_TLBMISC, org_misc); 196 + } 197 + 198 + void dump_tlb(void) 199 + { 200 + unsigned int i; 201 + 202 + for (i = 0; i < cpuinfo.tlb_num_lines; i++) 203 + dump_tlb_line(i); 204 + } 205 + 206 + void flush_tlb_pid(unsigned long pid) 207 + { 208 + unsigned int line; 209 + unsigned int way; 210 + unsigned long org_misc, pid_misc; 211 + 212 + /* remember pid/way until we return */ 213 + get_misc_and_pid(&org_misc, &pid_misc); 214 + 215 + for (line = 0; line < cpuinfo.tlb_num_lines; line++) { 216 + WRCTL(CTL_PTEADDR, line << 2); 217 + 218 + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 219 + unsigned long pteaddr; 220 + unsigned long tlbmisc; 221 + unsigned long tlbacc; 222 + 223 + tlbmisc = pid_misc | TLBMISC_RD | 224 + (way << TLBMISC_WAY_SHIFT); 225 + WRCTL(CTL_TLBMISC, tlbmisc); 226 + pteaddr = RDCTL(CTL_PTEADDR); 227 + tlbmisc = RDCTL(CTL_TLBMISC); 228 + tlbacc = RDCTL(CTL_TLBACC); 229 + 230 + if (((tlbmisc>>TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK) 231 + == pid) { 232 + tlbmisc = pid_misc | TLBMISC_WE | 233 + (way << TLBMISC_WAY_SHIFT); 234 + WRCTL(CTL_TLBMISC, tlbmisc); 235 + WRCTL(CTL_TLBACC, 236 + (MAX_PHYS_ADDR >> PAGE_SHIFT)); 237 + } 238 + } 239 + 240 + WRCTL(CTL_TLBMISC, org_misc); 241 + } 242 + } 243 + 244 + void flush_tlb_all(void) 245 + { 246 + int i; 247 + unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE; 248 + unsigned int way; 249 + unsigned long org_misc, pid_misc, tlbmisc; 250 + 251 + /* remember pid/way until we return */ 252 + get_misc_and_pid(&org_misc, &pid_misc); 253 + pid_misc |= TLBMISC_WE; 254 + 255 + /* Map each TLB entry to physcal address 0 with no-access and a 256 + bad ptbase */ 257 + for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 258 + tlbmisc = pid_misc | (way << TLBMISC_WAY_SHIFT); 259 + for (i = 0; i < cpuinfo.tlb_num_lines; i++) { 260 + WRCTL(CTL_PTEADDR, ((vaddr) >> PAGE_SHIFT) << 2); 261 + WRCTL(CTL_TLBMISC, tlbmisc); 262 + WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT)); 263 + vaddr += 1UL << 12; 264 + } 265 + } 266 + 267 + /* restore pid/way */ 268 + WRCTL(CTL_TLBMISC, org_misc); 269 + } 270 + 271 + void set_mmu_pid(unsigned long pid) 272 + { 273 + WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & TLBMISC_WAY) | 274 + ((pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT)); 275 + }