1c983e92fSLey Foon Tan /* 2c983e92fSLey Foon Tan * Nios2 TLB handling 3c983e92fSLey Foon Tan * 4c983e92fSLey Foon Tan * Copyright (C) 2009, Wind River Systems Inc 5c983e92fSLey Foon Tan * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com 6c983e92fSLey Foon Tan * 7c983e92fSLey Foon Tan * This file is subject to the terms and conditions of the GNU General Public 8c983e92fSLey Foon Tan * License. See the file "COPYING" in the main directory of this archive 9c983e92fSLey Foon Tan * for more details. 10c983e92fSLey Foon Tan */ 11c983e92fSLey Foon Tan 12c983e92fSLey Foon Tan #include <linux/init.h> 13c983e92fSLey Foon Tan #include <linux/sched.h> 14c983e92fSLey Foon Tan #include <linux/mm.h> 15c983e92fSLey Foon Tan #include <linux/pagemap.h> 16c983e92fSLey Foon Tan 17c983e92fSLey Foon Tan #include <asm/tlb.h> 18c983e92fSLey Foon Tan #include <asm/mmu_context.h> 19c983e92fSLey Foon Tan #include <asm/pgtable.h> 20c983e92fSLey Foon Tan #include <asm/cpuinfo.h> 21c983e92fSLey Foon Tan 22c983e92fSLey Foon Tan #define TLB_INDEX_MASK \ 23c983e92fSLey Foon Tan ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \ 24c983e92fSLey Foon Tan << PAGE_SHIFT) 25c983e92fSLey Foon Tan 26c983e92fSLey Foon Tan static void get_misc_and_pid(unsigned long *misc, unsigned long *pid) 27c983e92fSLey Foon Tan { 28c983e92fSLey Foon Tan *misc = RDCTL(CTL_TLBMISC); 29c983e92fSLey Foon Tan *misc &= (TLBMISC_PID | TLBMISC_WAY); 30c983e92fSLey Foon Tan *pid = *misc & TLBMISC_PID; 31c983e92fSLey Foon Tan } 32c983e92fSLey Foon Tan 33c983e92fSLey Foon Tan /* 343437d3c8SNicholas Piggin * This provides a PTEADDR value for addr that will cause a TLB miss 353437d3c8SNicholas Piggin * (fast TLB miss). TLB invalidation replaces entries with this value. 363437d3c8SNicholas Piggin */ 373437d3c8SNicholas Piggin static unsigned long pteaddr_invalid(unsigned long addr) 383437d3c8SNicholas Piggin { 393437d3c8SNicholas Piggin return ((addr | 0xC0000000UL) >> PAGE_SHIFT) << 2; 403437d3c8SNicholas Piggin } 413437d3c8SNicholas Piggin 423437d3c8SNicholas Piggin /* 43c983e92fSLey Foon Tan * This one is only used for pages with the global bit set so we don't care 44c983e92fSLey Foon Tan * much about the ASID. 45c983e92fSLey Foon Tan */ 46c983e92fSLey Foon Tan void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid) 47c983e92fSLey Foon Tan { 48c983e92fSLey Foon Tan unsigned int way; 49c983e92fSLey Foon Tan unsigned long org_misc, pid_misc; 50c983e92fSLey Foon Tan 51c983e92fSLey Foon Tan pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); 52c983e92fSLey Foon Tan 53c983e92fSLey Foon Tan /* remember pid/way until we return. */ 54c983e92fSLey Foon Tan get_misc_and_pid(&org_misc, &pid_misc); 55c983e92fSLey Foon Tan 56c983e92fSLey Foon Tan WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); 57c983e92fSLey Foon Tan 58c983e92fSLey Foon Tan for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 59c983e92fSLey Foon Tan unsigned long pteaddr; 60c983e92fSLey Foon Tan unsigned long tlbmisc; 61c983e92fSLey Foon Tan unsigned long pid; 62c983e92fSLey Foon Tan 637d173070SNicholas Piggin tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); 64c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, tlbmisc); 65c6b1d363SNicholas Piggin 66c983e92fSLey Foon Tan pteaddr = RDCTL(CTL_PTEADDR); 67c6b1d363SNicholas Piggin if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT)) 68c6b1d363SNicholas Piggin continue; 69c6b1d363SNicholas Piggin 70c983e92fSLey Foon Tan tlbmisc = RDCTL(CTL_TLBMISC); 71c983e92fSLey Foon Tan pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK; 72c6b1d363SNicholas Piggin if (pid != mmu_pid) 73c6b1d363SNicholas Piggin continue; 74c983e92fSLey Foon Tan 757d173070SNicholas Piggin tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); 76c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, tlbmisc); 77c6b1d363SNicholas Piggin WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); 783437d3c8SNicholas Piggin WRCTL(CTL_TLBACC, 0); 79c983e92fSLey Foon Tan } 80c983e92fSLey Foon Tan 81c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, org_misc); 82c983e92fSLey Foon Tan } 83c983e92fSLey Foon Tan 84c983e92fSLey Foon Tan void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, 85c983e92fSLey Foon Tan unsigned long end) 86c983e92fSLey Foon Tan { 87c983e92fSLey Foon Tan unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context); 88c983e92fSLey Foon Tan 89c983e92fSLey Foon Tan while (start < end) { 90c983e92fSLey Foon Tan flush_tlb_one_pid(start, mmu_pid); 91c983e92fSLey Foon Tan start += PAGE_SIZE; 92c983e92fSLey Foon Tan } 93c983e92fSLey Foon Tan } 94c983e92fSLey Foon Tan 95c983e92fSLey Foon Tan /* 96c983e92fSLey Foon Tan * This one is only used for pages with the global bit set so we don't care 97c983e92fSLey Foon Tan * much about the ASID. 98c983e92fSLey Foon Tan */ 99195568a1SNicholas Piggin static void flush_tlb_one(unsigned long addr) 100c983e92fSLey Foon Tan { 101c983e92fSLey Foon Tan unsigned int way; 102c983e92fSLey Foon Tan unsigned long org_misc, pid_misc; 103c983e92fSLey Foon Tan 104c983e92fSLey Foon Tan pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr); 105c983e92fSLey Foon Tan 106c983e92fSLey Foon Tan /* remember pid/way until we return. */ 107c983e92fSLey Foon Tan get_misc_and_pid(&org_misc, &pid_misc); 108c983e92fSLey Foon Tan 109c983e92fSLey Foon Tan WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2); 110c983e92fSLey Foon Tan 111c983e92fSLey Foon Tan for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 112c983e92fSLey Foon Tan unsigned long pteaddr; 113c983e92fSLey Foon Tan unsigned long tlbmisc; 114c983e92fSLey Foon Tan 1157d173070SNicholas Piggin tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); 116c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, tlbmisc); 117c983e92fSLey Foon Tan 118c6b1d363SNicholas Piggin pteaddr = RDCTL(CTL_PTEADDR); 119c6b1d363SNicholas Piggin if (((pteaddr >> 2) & 0xfffff) != (addr >> PAGE_SHIFT)) 120c6b1d363SNicholas Piggin continue; 121c6b1d363SNicholas Piggin 1223437d3c8SNicholas Piggin pr_debug("Flush entry by writing way=%dl pid=%ld\n", 1233437d3c8SNicholas Piggin way, (pid_misc >> TLBMISC_PID_SHIFT)); 124c983e92fSLey Foon Tan 1257d173070SNicholas Piggin tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); 126c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, tlbmisc); 127c6b1d363SNicholas Piggin WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); 1283437d3c8SNicholas Piggin WRCTL(CTL_TLBACC, 0); 129c983e92fSLey Foon Tan } 130c983e92fSLey Foon Tan 131c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, org_misc); 132c983e92fSLey Foon Tan } 133c983e92fSLey Foon Tan 134195568a1SNicholas Piggin void flush_tlb_kernel_range(unsigned long start, unsigned long end) 135195568a1SNicholas Piggin { 136195568a1SNicholas Piggin while (start < end) { 137195568a1SNicholas Piggin flush_tlb_one(start); 138195568a1SNicholas Piggin start += PAGE_SIZE; 139195568a1SNicholas Piggin } 140195568a1SNicholas Piggin } 141195568a1SNicholas Piggin 142c983e92fSLey Foon Tan void dump_tlb_line(unsigned long line) 143c983e92fSLey Foon Tan { 144c983e92fSLey Foon Tan unsigned int way; 145c983e92fSLey Foon Tan unsigned long org_misc; 146c983e92fSLey Foon Tan 147c983e92fSLey Foon Tan pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line, 148c983e92fSLey Foon Tan line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2)); 149c983e92fSLey Foon Tan 150c983e92fSLey Foon Tan /* remember pid/way until we return */ 151c983e92fSLey Foon Tan org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY)); 152c983e92fSLey Foon Tan 153c983e92fSLey Foon Tan WRCTL(CTL_PTEADDR, line << 2); 154c983e92fSLey Foon Tan 155c983e92fSLey Foon Tan for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 156c983e92fSLey Foon Tan unsigned long pteaddr; 157c983e92fSLey Foon Tan unsigned long tlbmisc; 158c983e92fSLey Foon Tan unsigned long tlbacc; 159c983e92fSLey Foon Tan 160c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT)); 161c983e92fSLey Foon Tan pteaddr = RDCTL(CTL_PTEADDR); 162c983e92fSLey Foon Tan tlbmisc = RDCTL(CTL_TLBMISC); 163c983e92fSLey Foon Tan tlbacc = RDCTL(CTL_TLBACC); 164c983e92fSLey Foon Tan 1653437d3c8SNicholas Piggin if ((tlbacc << PAGE_SHIFT) != 0) { 166c983e92fSLey Foon Tan pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n", 167c983e92fSLey Foon Tan way, 168c983e92fSLey Foon Tan (pteaddr << (PAGE_SHIFT-2)), 169c983e92fSLey Foon Tan (tlbacc << PAGE_SHIFT), 170c983e92fSLey Foon Tan ((tlbmisc >> TLBMISC_PID_SHIFT) & 171c983e92fSLey Foon Tan TLBMISC_PID_MASK), 172c983e92fSLey Foon Tan (tlbacc & _PAGE_READ ? 'r' : '-'), 173c983e92fSLey Foon Tan (tlbacc & _PAGE_WRITE ? 'w' : '-'), 174c983e92fSLey Foon Tan (tlbacc & _PAGE_EXEC ? 'x' : '-'), 175c983e92fSLey Foon Tan (tlbacc & _PAGE_GLOBAL ? 'g' : '-'), 176c983e92fSLey Foon Tan (tlbacc & _PAGE_CACHED ? 'c' : '-')); 177c983e92fSLey Foon Tan } 178c983e92fSLey Foon Tan } 179c983e92fSLey Foon Tan 180c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, org_misc); 181c983e92fSLey Foon Tan } 182c983e92fSLey Foon Tan 183c983e92fSLey Foon Tan void dump_tlb(void) 184c983e92fSLey Foon Tan { 185c983e92fSLey Foon Tan unsigned int i; 186c983e92fSLey Foon Tan 187c983e92fSLey Foon Tan for (i = 0; i < cpuinfo.tlb_num_lines; i++) 188c983e92fSLey Foon Tan dump_tlb_line(i); 189c983e92fSLey Foon Tan } 190c983e92fSLey Foon Tan 191c6b1d363SNicholas Piggin void flush_tlb_pid(unsigned long mmu_pid) 192c983e92fSLey Foon Tan { 1933437d3c8SNicholas Piggin unsigned long addr = 0; 194c983e92fSLey Foon Tan unsigned int line; 195c983e92fSLey Foon Tan unsigned int way; 196c983e92fSLey Foon Tan unsigned long org_misc, pid_misc; 197c983e92fSLey Foon Tan 198c983e92fSLey Foon Tan /* remember pid/way until we return */ 199c983e92fSLey Foon Tan get_misc_and_pid(&org_misc, &pid_misc); 200c983e92fSLey Foon Tan 201c983e92fSLey Foon Tan for (line = 0; line < cpuinfo.tlb_num_lines; line++) { 2023437d3c8SNicholas Piggin WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); 203c983e92fSLey Foon Tan 204c983e92fSLey Foon Tan for (way = 0; way < cpuinfo.tlb_num_ways; way++) { 205c983e92fSLey Foon Tan unsigned long tlbmisc; 206c6b1d363SNicholas Piggin unsigned long pid; 207c983e92fSLey Foon Tan 2087d173070SNicholas Piggin tlbmisc = TLBMISC_RD | (way << TLBMISC_WAY_SHIFT); 209c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, tlbmisc); 210c983e92fSLey Foon Tan tlbmisc = RDCTL(CTL_TLBMISC); 211c6b1d363SNicholas Piggin pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK; 212c6b1d363SNicholas Piggin if (pid != mmu_pid) 213c6b1d363SNicholas Piggin continue; 214c983e92fSLey Foon Tan 2157d173070SNicholas Piggin tlbmisc = TLBMISC_WE | (way << TLBMISC_WAY_SHIFT); 216c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, tlbmisc); 2173437d3c8SNicholas Piggin WRCTL(CTL_TLBACC, 0); 218c983e92fSLey Foon Tan } 219c983e92fSLey Foon Tan 2203437d3c8SNicholas Piggin addr += PAGE_SIZE; 22158fd4766SNicholas Piggin } 2223437d3c8SNicholas Piggin 223c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, org_misc); 224c983e92fSLey Foon Tan } 225c983e92fSLey Foon Tan 226e71c99feSNicholas Piggin /* 227e71c99feSNicholas Piggin * All entries common to a mm share an asid. To effectively flush these 228e71c99feSNicholas Piggin * entries, we just bump the asid. 229e71c99feSNicholas Piggin */ 230e71c99feSNicholas Piggin void flush_tlb_mm(struct mm_struct *mm) 231e71c99feSNicholas Piggin { 232e71c99feSNicholas Piggin if (current->mm == mm) { 233e71c99feSNicholas Piggin unsigned long mmu_pid = get_pid_from_context(&mm->context); 234e71c99feSNicholas Piggin flush_tlb_pid(mmu_pid); 235e71c99feSNicholas Piggin } else { 236e71c99feSNicholas Piggin memset(&mm->context, 0, sizeof(mm_context_t)); 237e71c99feSNicholas Piggin } 238e71c99feSNicholas Piggin } 239e71c99feSNicholas Piggin 240c983e92fSLey Foon Tan void flush_tlb_all(void) 241c983e92fSLey Foon Tan { 2423437d3c8SNicholas Piggin unsigned long addr = 0; 2433437d3c8SNicholas Piggin unsigned int line; 244c983e92fSLey Foon Tan unsigned int way; 245*737a3fa2SNicholas Piggin unsigned long org_misc, pid_misc; 246c983e92fSLey Foon Tan 247c983e92fSLey Foon Tan /* remember pid/way until we return */ 248c983e92fSLey Foon Tan get_misc_and_pid(&org_misc, &pid_misc); 249c983e92fSLey Foon Tan 250*737a3fa2SNicholas Piggin /* Start at way 0, way is auto-incremented after each TLBACC write */ 251*737a3fa2SNicholas Piggin WRCTL(CTL_TLBMISC, TLBMISC_WE); 252*737a3fa2SNicholas Piggin 253c983e92fSLey Foon Tan /* Map each TLB entry to physcal address 0 with no-access and a 254c983e92fSLey Foon Tan bad ptbase */ 2553437d3c8SNicholas Piggin for (line = 0; line < cpuinfo.tlb_num_lines; line++) { 2563437d3c8SNicholas Piggin WRCTL(CTL_PTEADDR, pteaddr_invalid(addr)); 257*737a3fa2SNicholas Piggin for (way = 0; way < cpuinfo.tlb_num_ways; way++) 2583437d3c8SNicholas Piggin WRCTL(CTL_TLBACC, 0); 2593437d3c8SNicholas Piggin 2603437d3c8SNicholas Piggin addr += PAGE_SIZE; 261c983e92fSLey Foon Tan } 262c983e92fSLey Foon Tan 263c983e92fSLey Foon Tan /* restore pid/way */ 264c983e92fSLey Foon Tan WRCTL(CTL_TLBMISC, org_misc); 265c983e92fSLey Foon Tan } 266c983e92fSLey Foon Tan 267c983e92fSLey Foon Tan void set_mmu_pid(unsigned long pid) 268c983e92fSLey Foon Tan { 269c6b1d363SNicholas Piggin unsigned long tlbmisc; 270c6b1d363SNicholas Piggin 271c6b1d363SNicholas Piggin tlbmisc = RDCTL(CTL_TLBMISC); 272c6b1d363SNicholas Piggin tlbmisc = (tlbmisc & TLBMISC_WAY); 273c6b1d363SNicholas Piggin tlbmisc |= (pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT; 274c6b1d363SNicholas Piggin WRCTL(CTL_TLBMISC, tlbmisc); 275c983e92fSLey Foon Tan } 276