···11+/*22+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>33+ * Copyright (C) 2009 Wind River Systems Inc44+ * Copyright (C) 2004 Microtronix Datacom Ltd.55+ *66+ * This file is subject to the terms and conditions of the GNU General Public77+ * License. See the file "COPYING" in the main directory of this archive88+ * for more details.99+ */1010+1111+#ifndef _ASM_NIOS2_TLB_H1212+#define _ASM_NIOS2_TLB_H1313+1414+#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm)1515+1616+extern void set_mmu_pid(unsigned long pid);1717+1818+/*1919+ * NiosII doesn't need any special per-pte or per-vma handling, except2020+ * we need to flush cache for the area to be unmapped.2121+ */2222+#define tlb_start_vma(tlb, vma) \2323+ do { \2424+ if (!tlb->fullmm) \2525+ flush_cache_range(vma, vma->vm_start, vma->vm_end); \2626+ } while (0)2727+2828+#define tlb_end_vma(tlb, vma) do { } while (0)2929+#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)3030+3131+#include <linux/pagemap.h>3232+#include <asm-generic/tlb.h>3333+3434+#endif /* _ASM_NIOS2_TLB_H */
+46
arch/nios2/include/asm/tlbflush.h
···11+/*22+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>33+ *44+ * This program is free software; you can redistribute it and/or modify55+ * it under the terms of the GNU General Public License as published by66+ * the Free Software Foundation; either version 2 of the License, or77+ * (at your option) any later version.88+ *99+ * This program is distributed in the hope that it will be useful,1010+ * but WITHOUT ANY WARRANTY; without even the implied warranty of1111+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the1212+ * GNU General Public License for more details.1313+ *1414+ * You should have received a copy of the GNU General Public License1515+ * along with this program. If not, see <http://www.gnu.org/licenses/>.1616+ *1717+ */1818+1919+#ifndef _ASM_NIOS2_TLBFLUSH_H2020+#define _ASM_NIOS2_TLBFLUSH_H2121+2222+struct mm_struct;2323+2424+/*2525+ * TLB flushing:2626+ *2727+ * - flush_tlb_all() flushes all processes TLB entries2828+ * - flush_tlb_mm(mm) flushes the specified mm context TLB entries2929+ * - flush_tlb_page(vma, vmaddr) flushes one page3030+ * - flush_tlb_range(vma, start, end) flushes a range of pages3131+ * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages3232+ */3333+extern void flush_tlb_all(void);3434+extern void flush_tlb_mm(struct mm_struct *mm);3535+extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,3636+ unsigned long end);3737+extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);3838+extern void flush_tlb_one(unsigned long vaddr);3939+4040+static inline void flush_tlb_page(struct vm_area_struct *vma,4141+ unsigned long addr)4242+{4343+ flush_tlb_one(addr);4444+}4545+4646+#endif /* _ASM_NIOS2_TLBFLUSH_H */
+275
arch/nios2/mm/tlb.c
···11+/*22+ * Nios2 TLB handling33+ *44+ * Copyright (C) 2009, Wind River Systems Inc55+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com66+ *77+ * This file is subject to the terms and conditions of the GNU General Public88+ * License. See the file "COPYING" in the main directory of this archive99+ * for more details.1010+ */1111+1212+#include <linux/init.h>1313+#include <linux/sched.h>1414+#include <linux/mm.h>1515+#include <linux/pagemap.h>1616+1717+#include <asm/tlb.h>1818+#include <asm/mmu_context.h>1919+#include <asm/pgtable.h>2020+#include <asm/cpuinfo.h>2121+2222+#define TLB_INDEX_MASK \2323+ ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \2424+ << PAGE_SHIFT)2525+2626+/* Used as illegal PHYS_ADDR for TLB mappings2727+ */2828+#define MAX_PHYS_ADDR 02929+3030+static void get_misc_and_pid(unsigned long *misc, unsigned long *pid)3131+{3232+ *misc = RDCTL(CTL_TLBMISC);3333+ *misc &= (TLBMISC_PID | TLBMISC_WAY);3434+ *pid = *misc & TLBMISC_PID;3535+}3636+3737+/*3838+ * All entries common to a mm share an asid. To effectively flush these3939+ * entries, we just bump the asid.4040+ */4141+void flush_tlb_mm(struct mm_struct *mm)4242+{4343+ if (current->mm == mm)4444+ flush_tlb_all();4545+ else4646+ memset(&mm->context, 0, sizeof(mm_context_t));4747+}4848+4949+/*5050+ * This one is only used for pages with the global bit set so we don't care5151+ * much about the ASID.5252+ */5353+void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid)5454+{5555+ unsigned int way;5656+ unsigned long org_misc, pid_misc;5757+5858+ pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);5959+6060+ /* remember pid/way until we return. */6161+ get_misc_and_pid(&org_misc, &pid_misc);6262+6363+ WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);6464+6565+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {6666+ unsigned long pteaddr;6767+ unsigned long tlbmisc;6868+ unsigned long pid;6969+7070+ tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);7171+ WRCTL(CTL_TLBMISC, tlbmisc);7272+ pteaddr = RDCTL(CTL_PTEADDR);7373+ tlbmisc = RDCTL(CTL_TLBMISC);7474+ pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;7575+ if (((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) &&7676+ pid == mmu_pid) {7777+ unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE +7878+ ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) +7979+ (addr & TLB_INDEX_MASK);8080+ pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n",8181+ vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT));8282+8383+ WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2);8484+ tlbmisc = pid_misc | TLBMISC_WE |8585+ (way << TLBMISC_WAY_SHIFT);8686+ WRCTL(CTL_TLBMISC, tlbmisc);8787+ WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT));8888+ }8989+ }9090+9191+ WRCTL(CTL_TLBMISC, org_misc);9292+}9393+9494+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,9595+ unsigned long end)9696+{9797+ unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);9898+9999+ while (start < end) {100100+ flush_tlb_one_pid(start, mmu_pid);101101+ start += PAGE_SIZE;102102+ }103103+}104104+105105+void flush_tlb_kernel_range(unsigned long start, unsigned long end)106106+{107107+ while (start < end) {108108+ flush_tlb_one(start);109109+ start += PAGE_SIZE;110110+ }111111+}112112+113113+/*114114+ * This one is only used for pages with the global bit set so we don't care115115+ * much about the ASID.116116+ */117117+void flush_tlb_one(unsigned long addr)118118+{119119+ unsigned int way;120120+ unsigned long org_misc, pid_misc;121121+122122+ pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);123123+124124+ /* remember pid/way until we return. */125125+ get_misc_and_pid(&org_misc, &pid_misc);126126+127127+ WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);128128+129129+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {130130+ unsigned long pteaddr;131131+ unsigned long tlbmisc;132132+133133+ tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);134134+ WRCTL(CTL_TLBMISC, tlbmisc);135135+ pteaddr = RDCTL(CTL_PTEADDR);136136+ tlbmisc = RDCTL(CTL_TLBMISC);137137+138138+ if ((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) {139139+ unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE +140140+ ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) +141141+ (addr & TLB_INDEX_MASK);142142+143143+ pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n",144144+ vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT));145145+146146+ tlbmisc = pid_misc | TLBMISC_WE |147147+ (way << TLBMISC_WAY_SHIFT);148148+ WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2);149149+ WRCTL(CTL_TLBMISC, tlbmisc);150150+ WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT));151151+ }152152+ }153153+154154+ WRCTL(CTL_TLBMISC, org_misc);155155+}156156+157157+void dump_tlb_line(unsigned long line)158158+{159159+ unsigned int way;160160+ unsigned long org_misc;161161+162162+ pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line,163163+ line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2));164164+165165+ /* remember pid/way until we return */166166+ org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY));167167+168168+ WRCTL(CTL_PTEADDR, line << 2);169169+170170+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {171171+ unsigned long pteaddr;172172+ unsigned long tlbmisc;173173+ unsigned long tlbacc;174174+175175+ WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT));176176+ pteaddr = RDCTL(CTL_PTEADDR);177177+ tlbmisc = RDCTL(CTL_TLBMISC);178178+ tlbacc = RDCTL(CTL_TLBACC);179179+180180+ if ((tlbacc << PAGE_SHIFT) != (MAX_PHYS_ADDR & PAGE_MASK)) {181181+ pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n",182182+ way,183183+ (pteaddr << (PAGE_SHIFT-2)),184184+ (tlbacc << PAGE_SHIFT),185185+ ((tlbmisc >> TLBMISC_PID_SHIFT) &186186+ TLBMISC_PID_MASK),187187+ (tlbacc & _PAGE_READ ? 'r' : '-'),188188+ (tlbacc & _PAGE_WRITE ? 'w' : '-'),189189+ (tlbacc & _PAGE_EXEC ? 'x' : '-'),190190+ (tlbacc & _PAGE_GLOBAL ? 'g' : '-'),191191+ (tlbacc & _PAGE_CACHED ? 'c' : '-'));192192+ }193193+ }194194+195195+ WRCTL(CTL_TLBMISC, org_misc);196196+}197197+198198+void dump_tlb(void)199199+{200200+ unsigned int i;201201+202202+ for (i = 0; i < cpuinfo.tlb_num_lines; i++)203203+ dump_tlb_line(i);204204+}205205+206206+void flush_tlb_pid(unsigned long pid)207207+{208208+ unsigned int line;209209+ unsigned int way;210210+ unsigned long org_misc, pid_misc;211211+212212+ /* remember pid/way until we return */213213+ get_misc_and_pid(&org_misc, &pid_misc);214214+215215+ for (line = 0; line < cpuinfo.tlb_num_lines; line++) {216216+ WRCTL(CTL_PTEADDR, line << 2);217217+218218+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {219219+ unsigned long pteaddr;220220+ unsigned long tlbmisc;221221+ unsigned long tlbacc;222222+223223+ tlbmisc = pid_misc | TLBMISC_RD |224224+ (way << TLBMISC_WAY_SHIFT);225225+ WRCTL(CTL_TLBMISC, tlbmisc);226226+ pteaddr = RDCTL(CTL_PTEADDR);227227+ tlbmisc = RDCTL(CTL_TLBMISC);228228+ tlbacc = RDCTL(CTL_TLBACC);229229+230230+ if (((tlbmisc>>TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK)231231+ == pid) {232232+ tlbmisc = pid_misc | TLBMISC_WE |233233+ (way << TLBMISC_WAY_SHIFT);234234+ WRCTL(CTL_TLBMISC, tlbmisc);235235+ WRCTL(CTL_TLBACC,236236+ (MAX_PHYS_ADDR >> PAGE_SHIFT));237237+ }238238+ }239239+240240+ WRCTL(CTL_TLBMISC, org_misc);241241+ }242242+}243243+244244+void flush_tlb_all(void)245245+{246246+ int i;247247+ unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE;248248+ unsigned int way;249249+ unsigned long org_misc, pid_misc, tlbmisc;250250+251251+ /* remember pid/way until we return */252252+ get_misc_and_pid(&org_misc, &pid_misc);253253+ pid_misc |= TLBMISC_WE;254254+255255+ /* Map each TLB entry to physcal address 0 with no-access and a256256+ bad ptbase */257257+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {258258+ tlbmisc = pid_misc | (way << TLBMISC_WAY_SHIFT);259259+ for (i = 0; i < cpuinfo.tlb_num_lines; i++) {260260+ WRCTL(CTL_PTEADDR, ((vaddr) >> PAGE_SHIFT) << 2);261261+ WRCTL(CTL_TLBMISC, tlbmisc);262262+ WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT));263263+ vaddr += 1UL << 12;264264+ }265265+ }266266+267267+ /* restore pid/way */268268+ WRCTL(CTL_TLBMISC, org_misc);269269+}270270+271271+void set_mmu_pid(unsigned long pid)272272+{273273+ WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & TLBMISC_WAY) |274274+ ((pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT));275275+}