4 * Copyright (C) 2009, Wind River Systems Inc
5 * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
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
12 #include <linux/init.h>
13 #include <linux/sched.h>
15 #include <linux/pagemap.h>
18 #include <asm/mmu_context.h>
19 #include <asm/pgtable.h>
20 #include <asm/cpuinfo.h>
22 #define TLB_INDEX_MASK \
23 ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \
26 /* Used as illegal PHYS_ADDR for TLB mappings
28 #define MAX_PHYS_ADDR 0
30 static void get_misc_and_pid(unsigned long *misc
, unsigned long *pid
)
32 *misc
= RDCTL(CTL_TLBMISC
);
33 *misc
&= (TLBMISC_PID
| TLBMISC_WAY
);
34 *pid
= *misc
& TLBMISC_PID
;
38 * All entries common to a mm share an asid. To effectively flush these
39 * entries, we just bump the asid.
41 void flush_tlb_mm(struct mm_struct
*mm
)
43 if (current
->mm
== mm
)
46 memset(&mm
->context
, 0, sizeof(mm_context_t
));
50 * This one is only used for pages with the global bit set so we don't care
51 * much about the ASID.
53 void flush_tlb_one_pid(unsigned long addr
, unsigned long mmu_pid
)
56 unsigned long org_misc
, pid_misc
;
58 pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr
);
60 /* remember pid/way until we return. */
61 get_misc_and_pid(&org_misc
, &pid_misc
);
63 WRCTL(CTL_PTEADDR
, (addr
>> PAGE_SHIFT
) << 2);
65 for (way
= 0; way
< cpuinfo
.tlb_num_ways
; way
++) {
66 unsigned long pteaddr
;
67 unsigned long tlbmisc
;
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
)) &&
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
));
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
));
91 WRCTL(CTL_TLBMISC
, org_misc
);
94 void flush_tlb_range(struct vm_area_struct
*vma
, unsigned long start
,
97 unsigned long mmu_pid
= get_pid_from_context(&vma
->vm_mm
->context
);
100 flush_tlb_one_pid(start
, mmu_pid
);
105 void flush_tlb_kernel_range(unsigned long start
, unsigned long end
)
107 while (start
< end
) {
108 flush_tlb_one(start
);
114 * This one is only used for pages with the global bit set so we don't care
115 * much about the ASID.
117 void flush_tlb_one(unsigned long addr
)
120 unsigned long org_misc
, pid_misc
;
122 pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr
);
124 /* remember pid/way until we return. */
125 get_misc_and_pid(&org_misc
, &pid_misc
);
127 WRCTL(CTL_PTEADDR
, (addr
>> PAGE_SHIFT
) << 2);
129 for (way
= 0; way
< cpuinfo
.tlb_num_ways
; way
++) {
130 unsigned long pteaddr
;
131 unsigned long tlbmisc
;
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
);
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
);
143 pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n",
144 vaddr
, way
, (pid_misc
>> TLBMISC_PID_SHIFT
));
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
));
154 WRCTL(CTL_TLBMISC
, org_misc
);
157 void dump_tlb_line(unsigned long line
)
160 unsigned long org_misc
;
162 pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line
,
163 line
<< (PAGE_SHIFT
+ cpuinfo
.tlb_num_ways_log2
));
165 /* remember pid/way until we return */
166 org_misc
= (RDCTL(CTL_TLBMISC
) & (TLBMISC_PID
| TLBMISC_WAY
));
168 WRCTL(CTL_PTEADDR
, line
<< 2);
170 for (way
= 0; way
< cpuinfo
.tlb_num_ways
; way
++) {
171 unsigned long pteaddr
;
172 unsigned long tlbmisc
;
173 unsigned long tlbacc
;
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
);
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",
183 (pteaddr
<< (PAGE_SHIFT
-2)),
184 (tlbacc
<< PAGE_SHIFT
),
185 ((tlbmisc
>> TLBMISC_PID_SHIFT
) &
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' : '-'));
195 WRCTL(CTL_TLBMISC
, org_misc
);
202 for (i
= 0; i
< cpuinfo
.tlb_num_lines
; i
++)
206 void flush_tlb_pid(unsigned long pid
)
210 unsigned long org_misc
, pid_misc
;
212 /* remember pid/way until we return */
213 get_misc_and_pid(&org_misc
, &pid_misc
);
215 for (line
= 0; line
< cpuinfo
.tlb_num_lines
; line
++) {
216 WRCTL(CTL_PTEADDR
, line
<< 2);
218 for (way
= 0; way
< cpuinfo
.tlb_num_ways
; way
++) {
219 unsigned long pteaddr
;
220 unsigned long tlbmisc
;
221 unsigned long tlbacc
;
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
);
230 if (((tlbmisc
>>TLBMISC_PID_SHIFT
) & TLBMISC_PID_MASK
)
232 tlbmisc
= pid_misc
| TLBMISC_WE
|
233 (way
<< TLBMISC_WAY_SHIFT
);
234 WRCTL(CTL_TLBMISC
, tlbmisc
);
236 (MAX_PHYS_ADDR
>> PAGE_SHIFT
));
240 WRCTL(CTL_TLBMISC
, org_misc
);
244 void flush_tlb_all(void)
247 unsigned long vaddr
= CONFIG_NIOS2_IO_REGION_BASE
;
249 unsigned long org_misc
, pid_misc
, tlbmisc
;
251 /* remember pid/way until we return */
252 get_misc_and_pid(&org_misc
, &pid_misc
);
253 pid_misc
|= TLBMISC_WE
;
255 /* Map each TLB entry to physcal address 0 with no-access and a
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
));
267 /* restore pid/way */
268 WRCTL(CTL_TLBMISC
, org_misc
);
271 void set_mmu_pid(unsigned long pid
)
273 WRCTL(CTL_TLBMISC
, (RDCTL(CTL_TLBMISC
) & TLBMISC_WAY
) |
274 ((pid
& TLBMISC_PID_MASK
) << TLBMISC_PID_SHIFT
));