diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/arm/mm |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/arm/mm')
56 files changed, 13340 insertions, 0 deletions
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig new file mode 100644 index 000000000000..5b670c9ac5ef --- /dev/null +++ b/arch/arm/mm/Kconfig | |||
@@ -0,0 +1,411 @@ | |||
1 | comment "Processor Type" | ||
2 | |||
3 | config CPU_32 | ||
4 | bool | ||
5 | default y | ||
6 | |||
7 | # Select CPU types depending on the architecture selected. This selects | ||
8 | # which CPUs we support in the kernel image, and the compiler instruction | ||
9 | # optimiser behaviour. | ||
10 | |||
11 | # ARM610 | ||
12 | config CPU_ARM610 | ||
13 | bool "Support ARM610 processor" | ||
14 | depends on ARCH_RPC | ||
15 | select CPU_32v3 | ||
16 | select CPU_CACHE_V3 | ||
17 | select CPU_CACHE_VIVT | ||
18 | select CPU_COPY_V3 | ||
19 | select CPU_TLB_V3 | ||
20 | help | ||
21 | The ARM610 is the successor to the ARM3 processor | ||
22 | and was produced by VLSI Technology Inc. | ||
23 | |||
24 | Say Y if you want support for the ARM610 processor. | ||
25 | Otherwise, say N. | ||
26 | |||
27 | # ARM710 | ||
28 | config CPU_ARM710 | ||
29 | bool "Support ARM710 processor" if !ARCH_CLPS7500 && ARCH_RPC | ||
30 | default y if ARCH_CLPS7500 | ||
31 | select CPU_32v3 | ||
32 | select CPU_CACHE_V3 | ||
33 | select CPU_CACHE_VIVT | ||
34 | select CPU_COPY_V3 | ||
35 | select CPU_TLB_V3 | ||
36 | help | ||
37 | A 32-bit RISC microprocessor based on the ARM7 processor core | ||
38 | designed by Advanced RISC Machines Ltd. The ARM710 is the | ||
39 | successor to the ARM610 processor. It was released in | ||
40 | July 1994 by VLSI Technology Inc. | ||
41 | |||
42 | Say Y if you want support for the ARM710 processor. | ||
43 | Otherwise, say N. | ||
44 | |||
45 | # ARM720T | ||
46 | config CPU_ARM720T | ||
47 | bool "Support ARM720T processor" if !ARCH_CLPS711X && !ARCH_L7200 && !ARCH_CDB89712 && ARCH_INTEGRATOR | ||
48 | default y if ARCH_CLPS711X || ARCH_L7200 || ARCH_CDB89712 || ARCH_H720X | ||
49 | select CPU_32v4 | ||
50 | select CPU_ABRT_LV4T | ||
51 | select CPU_CACHE_V4 | ||
52 | select CPU_CACHE_VIVT | ||
53 | select CPU_COPY_V4WT | ||
54 | select CPU_TLB_V4WT | ||
55 | help | ||
56 | A 32-bit RISC processor with 8kByte Cache, Write Buffer and | ||
57 | MMU built around an ARM7TDMI core. | ||
58 | |||
59 | Say Y if you want support for the ARM720T processor. | ||
60 | Otherwise, say N. | ||
61 | |||
62 | # ARM920T | ||
63 | config CPU_ARM920T | ||
64 | bool "Support ARM920T processor" if !ARCH_S3C2410 | ||
65 | depends on ARCH_INTEGRATOR || ARCH_S3C2410 || ARCH_IMX | ||
66 | default y if ARCH_S3C2410 | ||
67 | select CPU_32v4 | ||
68 | select CPU_ABRT_EV4T | ||
69 | select CPU_CACHE_V4WT | ||
70 | select CPU_CACHE_VIVT | ||
71 | select CPU_COPY_V4WB | ||
72 | select CPU_TLB_V4WBI | ||
73 | help | ||
74 | The ARM920T is licensed to be produced by numerous vendors, | ||
75 | and is used in the Maverick EP9312 and the Samsung S3C2410. | ||
76 | |||
77 | More information on the Maverick EP9312 at | ||
78 | <http://linuxdevices.com/products/PD2382866068.html>. | ||
79 | |||
80 | Say Y if you want support for the ARM920T processor. | ||
81 | Otherwise, say N. | ||
82 | |||
83 | # ARM922T | ||
84 | config CPU_ARM922T | ||
85 | bool "Support ARM922T processor" if ARCH_INTEGRATOR | ||
86 | depends on ARCH_CAMELOT || ARCH_LH7A40X || ARCH_INTEGRATOR | ||
87 | default y if ARCH_CAMELOT || ARCH_LH7A40X | ||
88 | select CPU_32v4 | ||
89 | select CPU_ABRT_EV4T | ||
90 | select CPU_CACHE_V4WT | ||
91 | select CPU_CACHE_VIVT | ||
92 | select CPU_COPY_V4WB | ||
93 | select CPU_TLB_V4WBI | ||
94 | help | ||
95 | The ARM922T is a version of the ARM920T, but with smaller | ||
96 | instruction and data caches. It is used in Altera's | ||
97 | Excalibur XA device family. | ||
98 | |||
99 | Say Y if you want support for the ARM922T processor. | ||
100 | Otherwise, say N. | ||
101 | |||
102 | # ARM925T | ||
103 | config CPU_ARM925T | ||
104 | bool "Support ARM925T processor" if ARCH_OMAP | ||
105 | depends on ARCH_OMAP1510 | ||
106 | default y if ARCH_OMAP1510 | ||
107 | select CPU_32v4 | ||
108 | select CPU_ABRT_EV4T | ||
109 | select CPU_CACHE_V4WT | ||
110 | select CPU_CACHE_VIVT | ||
111 | select CPU_COPY_V4WB | ||
112 | select CPU_TLB_V4WBI | ||
113 | help | ||
114 | The ARM925T is a mix between the ARM920T and ARM926T, but with | ||
115 | different instruction and data caches. It is used in TI's OMAP | ||
116 | device family. | ||
117 | |||
118 | Say Y if you want support for the ARM925T processor. | ||
119 | Otherwise, say N. | ||
120 | |||
121 | # ARM926T | ||
122 | config CPU_ARM926T | ||
123 | bool "Support ARM926T processor" if ARCH_INTEGRATOR | ||
124 | depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX | ||
125 | default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX | ||
126 | select CPU_32v5 | ||
127 | select CPU_ABRT_EV5TJ | ||
128 | select CPU_CACHE_VIVT | ||
129 | select CPU_COPY_V4WB | ||
130 | select CPU_TLB_V4WBI | ||
131 | help | ||
132 | This is a variant of the ARM920. It has slightly different | ||
133 | instruction sequences for cache and TLB operations. Curiously, | ||
134 | there is no documentation on it at the ARM corporate website. | ||
135 | |||
136 | Say Y if you want support for the ARM926T processor. | ||
137 | Otherwise, say N. | ||
138 | |||
139 | # ARM1020 - needs validating | ||
140 | config CPU_ARM1020 | ||
141 | bool "Support ARM1020T (rev 0) processor" | ||
142 | depends on ARCH_INTEGRATOR | ||
143 | select CPU_32v5 | ||
144 | select CPU_ABRT_EV4T | ||
145 | select CPU_CACHE_V4WT | ||
146 | select CPU_CACHE_VIVT | ||
147 | select CPU_COPY_V4WB | ||
148 | select CPU_TLB_V4WBI | ||
149 | help | ||
150 | The ARM1020 is the 32K cached version of the ARM10 processor, | ||
151 | with an addition of a floating-point unit. | ||
152 | |||
153 | Say Y if you want support for the ARM1020 processor. | ||
154 | Otherwise, say N. | ||
155 | |||
156 | # ARM1020E - needs validating | ||
157 | config CPU_ARM1020E | ||
158 | bool "Support ARM1020E processor" | ||
159 | depends on ARCH_INTEGRATOR | ||
160 | select CPU_32v5 | ||
161 | select CPU_ABRT_EV4T | ||
162 | select CPU_CACHE_V4WT | ||
163 | select CPU_CACHE_VIVT | ||
164 | select CPU_COPY_V4WB | ||
165 | select CPU_TLB_V4WBI | ||
166 | depends on n | ||
167 | |||
168 | # ARM1022E | ||
169 | config CPU_ARM1022 | ||
170 | bool "Support ARM1022E processor" | ||
171 | depends on ARCH_INTEGRATOR | ||
172 | select CPU_32v5 | ||
173 | select CPU_ABRT_EV4T | ||
174 | select CPU_CACHE_VIVT | ||
175 | select CPU_COPY_V4WB # can probably do better | ||
176 | select CPU_TLB_V4WBI | ||
177 | help | ||
178 | The ARM1022E is an implementation of the ARMv5TE architecture | ||
179 | based upon the ARM10 integer core with a 16KiB L1 Harvard cache, | ||
180 | embedded trace macrocell, and a floating-point unit. | ||
181 | |||
182 | Say Y if you want support for the ARM1022E processor. | ||
183 | Otherwise, say N. | ||
184 | |||
185 | # ARM1026EJ-S | ||
186 | config CPU_ARM1026 | ||
187 | bool "Support ARM1026EJ-S processor" | ||
188 | depends on ARCH_INTEGRATOR | ||
189 | select CPU_32v5 | ||
190 | select CPU_ABRT_EV5T # But need Jazelle, but EV5TJ ignores bit 10 | ||
191 | select CPU_CACHE_VIVT | ||
192 | select CPU_COPY_V4WB # can probably do better | ||
193 | select CPU_TLB_V4WBI | ||
194 | help | ||
195 | The ARM1026EJ-S is an implementation of the ARMv5TEJ architecture | ||
196 | based upon the ARM10 integer core. | ||
197 | |||
198 | Say Y if you want support for the ARM1026EJ-S processor. | ||
199 | Otherwise, say N. | ||
200 | |||
201 | # SA110 | ||
202 | config CPU_SA110 | ||
203 | bool "Support StrongARM(R) SA-110 processor" if !ARCH_EBSA110 && !FOOTBRIDGE && !ARCH_TBOX && !ARCH_SHARK && !ARCH_NEXUSPCI && ARCH_RPC | ||
204 | default y if ARCH_EBSA110 || FOOTBRIDGE || ARCH_TBOX || ARCH_SHARK || ARCH_NEXUSPCI | ||
205 | select CPU_32v3 if ARCH_RPC | ||
206 | select CPU_32v4 if !ARCH_RPC | ||
207 | select CPU_ABRT_EV4 | ||
208 | select CPU_CACHE_V4WB | ||
209 | select CPU_CACHE_VIVT | ||
210 | select CPU_COPY_V4WB | ||
211 | select CPU_TLB_V4WB | ||
212 | help | ||
213 | The Intel StrongARM(R) SA-110 is a 32-bit microprocessor and | ||
214 | is available at five speeds ranging from 100 MHz to 233 MHz. | ||
215 | More information is available at | ||
216 | <http://developer.intel.com/design/strong/sa110.htm>. | ||
217 | |||
218 | Say Y if you want support for the SA-110 processor. | ||
219 | Otherwise, say N. | ||
220 | |||
221 | # SA1100 | ||
222 | config CPU_SA1100 | ||
223 | bool | ||
224 | depends on ARCH_SA1100 | ||
225 | default y | ||
226 | select CPU_32v4 | ||
227 | select CPU_ABRT_EV4 | ||
228 | select CPU_CACHE_V4WB | ||
229 | select CPU_CACHE_VIVT | ||
230 | select CPU_TLB_V4WB | ||
231 | select CPU_MINICACHE | ||
232 | |||
233 | # XScale | ||
234 | config CPU_XSCALE | ||
235 | bool | ||
236 | depends on ARCH_IOP3XX || ARCH_PXA || ARCH_IXP4XX || ARCH_IXP2000 | ||
237 | default y | ||
238 | select CPU_32v5 | ||
239 | select CPU_ABRT_EV5T | ||
240 | select CPU_CACHE_VIVT | ||
241 | select CPU_TLB_V4WBI | ||
242 | select CPU_MINICACHE | ||
243 | |||
244 | # ARMv6 | ||
245 | config CPU_V6 | ||
246 | bool "Support ARM V6 processor" | ||
247 | depends on ARCH_INTEGRATOR | ||
248 | select CPU_32v6 | ||
249 | select CPU_ABRT_EV6 | ||
250 | select CPU_CACHE_V6 | ||
251 | select CPU_CACHE_VIPT | ||
252 | select CPU_COPY_V6 | ||
253 | select CPU_TLB_V6 | ||
254 | |||
255 | # Figure out what processor architecture version we should be using. | ||
256 | # This defines the compiler instruction set which depends on the machine type. | ||
257 | config CPU_32v3 | ||
258 | bool | ||
259 | |||
260 | config CPU_32v4 | ||
261 | bool | ||
262 | |||
263 | config CPU_32v5 | ||
264 | bool | ||
265 | |||
266 | config CPU_32v6 | ||
267 | bool | ||
268 | |||
269 | # The abort model | ||
270 | config CPU_ABRT_EV4 | ||
271 | bool | ||
272 | |||
273 | config CPU_ABRT_EV4T | ||
274 | bool | ||
275 | |||
276 | config CPU_ABRT_LV4T | ||
277 | bool | ||
278 | |||
279 | config CPU_ABRT_EV5T | ||
280 | bool | ||
281 | |||
282 | config CPU_ABRT_EV5TJ | ||
283 | bool | ||
284 | |||
285 | config CPU_ABRT_EV6 | ||
286 | bool | ||
287 | |||
288 | # The cache model | ||
289 | config CPU_CACHE_V3 | ||
290 | bool | ||
291 | |||
292 | config CPU_CACHE_V4 | ||
293 | bool | ||
294 | |||
295 | config CPU_CACHE_V4WT | ||
296 | bool | ||
297 | |||
298 | config CPU_CACHE_V4WB | ||
299 | bool | ||
300 | |||
301 | config CPU_CACHE_V6 | ||
302 | bool | ||
303 | |||
304 | config CPU_CACHE_VIVT | ||
305 | bool | ||
306 | |||
307 | config CPU_CACHE_VIPT | ||
308 | bool | ||
309 | |||
310 | # The copy-page model | ||
311 | config CPU_COPY_V3 | ||
312 | bool | ||
313 | |||
314 | config CPU_COPY_V4WT | ||
315 | bool | ||
316 | |||
317 | config CPU_COPY_V4WB | ||
318 | bool | ||
319 | |||
320 | config CPU_COPY_V6 | ||
321 | bool | ||
322 | |||
323 | # This selects the TLB model | ||
324 | config CPU_TLB_V3 | ||
325 | bool | ||
326 | help | ||
327 | ARM Architecture Version 3 TLB. | ||
328 | |||
329 | config CPU_TLB_V4WT | ||
330 | bool | ||
331 | help | ||
332 | ARM Architecture Version 4 TLB with writethrough cache. | ||
333 | |||
334 | config CPU_TLB_V4WB | ||
335 | bool | ||
336 | help | ||
337 | ARM Architecture Version 4 TLB with writeback cache. | ||
338 | |||
339 | config CPU_TLB_V4WBI | ||
340 | bool | ||
341 | help | ||
342 | ARM Architecture Version 4 TLB with writeback cache and invalidate | ||
343 | instruction cache entry. | ||
344 | |||
345 | config CPU_TLB_V6 | ||
346 | bool | ||
347 | |||
348 | config CPU_MINICACHE | ||
349 | bool | ||
350 | help | ||
351 | Processor has a minicache. | ||
352 | |||
353 | comment "Processor Features" | ||
354 | |||
355 | config ARM_THUMB | ||
356 | bool "Support Thumb user binaries" | ||
357 | depends on CPU_ARM720T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || CPU_XSCALE || CPU_V6 | ||
358 | default y | ||
359 | help | ||
360 | Say Y if you want to include kernel support for running user space | ||
361 | Thumb binaries. | ||
362 | |||
363 | The Thumb instruction set is a compressed form of the standard ARM | ||
364 | instruction set resulting in smaller binaries at the expense of | ||
365 | slightly less efficient code. | ||
366 | |||
367 | If you don't know what this all is, saying Y is a safe choice. | ||
368 | |||
369 | config CPU_BIG_ENDIAN | ||
370 | bool "Build big-endian kernel" | ||
371 | depends on ARCH_SUPPORTS_BIG_ENDIAN | ||
372 | help | ||
373 | Say Y if you plan on running a kernel in big-endian mode. | ||
374 | Note that your board must be properly built and your board | ||
375 | port must properly enable any big-endian related features | ||
376 | of your chipset/board/processor. | ||
377 | |||
378 | config CPU_ICACHE_DISABLE | ||
379 | bool "Disable I-Cache" | ||
380 | depends on CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM1020 | ||
381 | help | ||
382 | Say Y here to disable the processor instruction cache. Unless | ||
383 | you have a reason not to or are unsure, say N. | ||
384 | |||
385 | config CPU_DCACHE_DISABLE | ||
386 | bool "Disable D-Cache" | ||
387 | depends on CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM1020 | ||
388 | help | ||
389 | Say Y here to disable the processor data cache. Unless | ||
390 | you have a reason not to or are unsure, say N. | ||
391 | |||
392 | config CPU_DCACHE_WRITETHROUGH | ||
393 | bool "Force write through D-cache" | ||
394 | depends on (CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM1020) && !CPU_DISABLE_DCACHE | ||
395 | default y if CPU_ARM925T | ||
396 | help | ||
397 | Say Y here to use the data cache in writethrough mode. Unless you | ||
398 | specifically require this or are unsure, say N. | ||
399 | |||
400 | config CPU_CACHE_ROUND_ROBIN | ||
401 | bool "Round robin I and D cache replacement algorithm" | ||
402 | depends on (CPU_ARM926T || CPU_ARM1020) && (!CPU_ICACHE_DISABLE || !CPU_DCACHE_DISABLE) | ||
403 | help | ||
404 | Say Y here to use the predictable round-robin cache replacement | ||
405 | policy. Unless you specifically require this or are unsure, say N. | ||
406 | |||
407 | config CPU_BPREDICT_DISABLE | ||
408 | bool "Disable branch prediction" | ||
409 | depends on CPU_ARM1020 | ||
410 | help | ||
411 | Say Y here to disable branch prediction. If unsure, say N. | ||
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile new file mode 100644 index 000000000000..ccf316c11e02 --- /dev/null +++ b/arch/arm/mm/Makefile | |||
@@ -0,0 +1,56 @@ | |||
1 | # | ||
2 | # Makefile for the linux arm-specific parts of the memory manager. | ||
3 | # | ||
4 | |||
5 | obj-y := consistent.o extable.o fault-armv.o \ | ||
6 | fault.o flush.o init.o ioremap.o mmap.o \ | ||
7 | mm-armv.o | ||
8 | |||
9 | obj-$(CONFIG_MODULES) += proc-syms.o | ||
10 | |||
11 | obj-$(CONFIG_ALIGNMENT_TRAP) += alignment.o | ||
12 | obj-$(CONFIG_DISCONTIGMEM) += discontig.o | ||
13 | |||
14 | obj-$(CONFIG_CPU_ABRT_EV4) += abort-ev4.o | ||
15 | obj-$(CONFIG_CPU_ABRT_EV4T) += abort-ev4t.o | ||
16 | obj-$(CONFIG_CPU_ABRT_LV4T) += abort-lv4t.o | ||
17 | obj-$(CONFIG_CPU_ABRT_EV5T) += abort-ev5t.o | ||
18 | obj-$(CONFIG_CPU_ABRT_EV5TJ) += abort-ev5tj.o | ||
19 | obj-$(CONFIG_CPU_ABRT_EV6) += abort-ev6.o | ||
20 | |||
21 | obj-$(CONFIG_CPU_CACHE_V3) += cache-v3.o | ||
22 | obj-$(CONFIG_CPU_CACHE_V4) += cache-v4.o | ||
23 | obj-$(CONFIG_CPU_CACHE_V4WT) += cache-v4wt.o | ||
24 | obj-$(CONFIG_CPU_CACHE_V4WB) += cache-v4wb.o | ||
25 | obj-$(CONFIG_CPU_CACHE_V6) += cache-v6.o | ||
26 | |||
27 | obj-$(CONFIG_CPU_COPY_V3) += copypage-v3.o | ||
28 | obj-$(CONFIG_CPU_COPY_V4WT) += copypage-v4wt.o | ||
29 | obj-$(CONFIG_CPU_COPY_V4WB) += copypage-v4wb.o | ||
30 | obj-$(CONFIG_CPU_COPY_V6) += copypage-v6.o mmu.o | ||
31 | obj-$(CONFIG_CPU_SA1100) += copypage-v4mc.o | ||
32 | obj-$(CONFIG_CPU_XSCALE) += copypage-xscale.o | ||
33 | |||
34 | obj-$(CONFIG_CPU_MINICACHE) += minicache.o | ||
35 | |||
36 | obj-$(CONFIG_CPU_TLB_V3) += tlb-v3.o | ||
37 | obj-$(CONFIG_CPU_TLB_V4WT) += tlb-v4.o | ||
38 | obj-$(CONFIG_CPU_TLB_V4WB) += tlb-v4wb.o | ||
39 | obj-$(CONFIG_CPU_TLB_V4WBI) += tlb-v4wbi.o | ||
40 | obj-$(CONFIG_CPU_TLB_V6) += tlb-v6.o | ||
41 | |||
42 | obj-$(CONFIG_CPU_ARM610) += proc-arm6_7.o | ||
43 | obj-$(CONFIG_CPU_ARM710) += proc-arm6_7.o | ||
44 | obj-$(CONFIG_CPU_ARM720T) += proc-arm720.o | ||
45 | obj-$(CONFIG_CPU_ARM920T) += proc-arm920.o | ||
46 | obj-$(CONFIG_CPU_ARM922T) += proc-arm922.o | ||
47 | obj-$(CONFIG_CPU_ARM925T) += proc-arm925.o | ||
48 | obj-$(CONFIG_CPU_ARM926T) += proc-arm926.o | ||
49 | obj-$(CONFIG_CPU_ARM1020) += proc-arm1020.o | ||
50 | obj-$(CONFIG_CPU_ARM1020E) += proc-arm1020e.o | ||
51 | obj-$(CONFIG_CPU_ARM1022) += proc-arm1022.o | ||
52 | obj-$(CONFIG_CPU_ARM1026) += proc-arm1026.o | ||
53 | obj-$(CONFIG_CPU_SA110) += proc-sa110.o | ||
54 | obj-$(CONFIG_CPU_SA1100) += proc-sa1100.o | ||
55 | obj-$(CONFIG_CPU_XSCALE) += proc-xscale.o | ||
56 | obj-$(CONFIG_CPU_V6) += proc-v6.o blockops.o | ||
diff --git a/arch/arm/mm/abort-ev4.S b/arch/arm/mm/abort-ev4.S new file mode 100644 index 000000000000..4f18f9e87bae --- /dev/null +++ b/arch/arm/mm/abort-ev4.S | |||
@@ -0,0 +1,30 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <asm/assembler.h> | ||
3 | /* | ||
4 | * Function: v4_early_abort | ||
5 | * | ||
6 | * Params : r2 = address of aborted instruction | ||
7 | * : r3 = saved SPSR | ||
8 | * | ||
9 | * Returns : r0 = address of abort | ||
10 | * : r1 = FSR, bit 11 = write | ||
11 | * : r2-r8 = corrupted | ||
12 | * : r9 = preserved | ||
13 | * : sp = pointer to registers | ||
14 | * | ||
15 | * Purpose : obtain information about current aborted instruction. | ||
16 | * Note: we read user space. This means we might cause a data | ||
17 | * abort here if the I-TLB and D-TLB aren't seeing the same | ||
18 | * picture. Unfortunately, this does happen. We live with it. | ||
19 | */ | ||
20 | .align 5 | ||
21 | ENTRY(v4_early_abort) | ||
22 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
23 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
24 | ldr r3, [r2] @ read aborted ARM instruction | ||
25 | bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR | ||
26 | tst r3, #1 << 20 @ L = 1 -> write? | ||
27 | orreq r1, r1, #1 << 11 @ yes. | ||
28 | mov pc, lr | ||
29 | |||
30 | |||
diff --git a/arch/arm/mm/abort-ev4t.S b/arch/arm/mm/abort-ev4t.S new file mode 100644 index 000000000000..b6282548f922 --- /dev/null +++ b/arch/arm/mm/abort-ev4t.S | |||
@@ -0,0 +1,30 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <asm/assembler.h> | ||
3 | #include "abort-macro.S" | ||
4 | /* | ||
5 | * Function: v4t_early_abort | ||
6 | * | ||
7 | * Params : r2 = address of aborted instruction | ||
8 | * : r3 = saved SPSR | ||
9 | * | ||
10 | * Returns : r0 = address of abort | ||
11 | * : r1 = FSR, bit 11 = write | ||
12 | * : r2-r8 = corrupted | ||
13 | * : r9 = preserved | ||
14 | * : sp = pointer to registers | ||
15 | * | ||
16 | * Purpose : obtain information about current aborted instruction. | ||
17 | * Note: we read user space. This means we might cause a data | ||
18 | * abort here if the I-TLB and D-TLB aren't seeing the same | ||
19 | * picture. Unfortunately, this does happen. We live with it. | ||
20 | */ | ||
21 | .align 5 | ||
22 | ENTRY(v4t_early_abort) | ||
23 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
24 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
25 | do_thumb_abort | ||
26 | ldreq r3, [r2] @ read aborted ARM instruction | ||
27 | bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR | ||
28 | tst r3, #1 << 20 @ check write | ||
29 | orreq r1, r1, #1 << 11 | ||
30 | mov pc, lr | ||
diff --git a/arch/arm/mm/abort-ev5t.S b/arch/arm/mm/abort-ev5t.S new file mode 100644 index 000000000000..02251b526c0d --- /dev/null +++ b/arch/arm/mm/abort-ev5t.S | |||
@@ -0,0 +1,31 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <asm/assembler.h> | ||
3 | #include "abort-macro.S" | ||
4 | /* | ||
5 | * Function: v5t_early_abort | ||
6 | * | ||
7 | * Params : r2 = address of aborted instruction | ||
8 | * : r3 = saved SPSR | ||
9 | * | ||
10 | * Returns : r0 = address of abort | ||
11 | * : r1 = FSR, bit 11 = write | ||
12 | * : r2-r8 = corrupted | ||
13 | * : r9 = preserved | ||
14 | * : sp = pointer to registers | ||
15 | * | ||
16 | * Purpose : obtain information about current aborted instruction. | ||
17 | * Note: we read user space. This means we might cause a data | ||
18 | * abort here if the I-TLB and D-TLB aren't seeing the same | ||
19 | * picture. Unfortunately, this does happen. We live with it. | ||
20 | */ | ||
21 | .align 5 | ||
22 | ENTRY(v5t_early_abort) | ||
23 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
24 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
25 | do_thumb_abort | ||
26 | ldreq r3, [r2] @ read aborted ARM instruction | ||
27 | bic r1, r1, #1 << 11 @ clear bits 11 of FSR | ||
28 | do_ldrd_abort | ||
29 | tst r3, #1 << 20 @ check write | ||
30 | orreq r1, r1, #1 << 11 | ||
31 | mov pc, lr | ||
diff --git a/arch/arm/mm/abort-ev5tj.S b/arch/arm/mm/abort-ev5tj.S new file mode 100644 index 000000000000..bce68d601c8b --- /dev/null +++ b/arch/arm/mm/abort-ev5tj.S | |||
@@ -0,0 +1,35 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <asm/assembler.h> | ||
3 | #include "abort-macro.S" | ||
4 | /* | ||
5 | * Function: v5tj_early_abort | ||
6 | * | ||
7 | * Params : r2 = address of aborted instruction | ||
8 | * : r3 = saved SPSR | ||
9 | * | ||
10 | * Returns : r0 = address of abort | ||
11 | * : r1 = FSR, bit 11 = write | ||
12 | * : r2-r8 = corrupted | ||
13 | * : r9 = preserved | ||
14 | * : sp = pointer to registers | ||
15 | * | ||
16 | * Purpose : obtain information about current aborted instruction. | ||
17 | * Note: we read user space. This means we might cause a data | ||
18 | * abort here if the I-TLB and D-TLB aren't seeing the same | ||
19 | * picture. Unfortunately, this does happen. We live with it. | ||
20 | */ | ||
21 | .align 5 | ||
22 | ENTRY(v5tj_early_abort) | ||
23 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
24 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
25 | bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR | ||
26 | tst r3, #PSR_J_BIT @ Java? | ||
27 | movne pc, lr | ||
28 | do_thumb_abort | ||
29 | ldreq r3, [r2] @ read aborted ARM instruction | ||
30 | do_ldrd_abort | ||
31 | tst r3, #1 << 20 @ L = 0 -> write | ||
32 | orreq r1, r1, #1 << 11 @ yes. | ||
33 | mov pc, lr | ||
34 | |||
35 | |||
diff --git a/arch/arm/mm/abort-ev6.S b/arch/arm/mm/abort-ev6.S new file mode 100644 index 000000000000..38b2cbb89beb --- /dev/null +++ b/arch/arm/mm/abort-ev6.S | |||
@@ -0,0 +1,23 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <asm/assembler.h> | ||
3 | /* | ||
4 | * Function: v6_early_abort | ||
5 | * | ||
6 | * Params : r2 = address of aborted instruction | ||
7 | * : r3 = saved SPSR | ||
8 | * | ||
9 | * Returns : r0 = address of abort | ||
10 | * : r1 = FSR, bit 11 = write | ||
11 | * : r2-r8 = corrupted | ||
12 | * : r9 = preserved | ||
13 | * : sp = pointer to registers | ||
14 | * | ||
15 | * Purpose : obtain information about current aborted instruction. | ||
16 | */ | ||
17 | .align 5 | ||
18 | ENTRY(v6_early_abort) | ||
19 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
20 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
21 | mov pc, lr | ||
22 | |||
23 | |||
diff --git a/arch/arm/mm/abort-lv4t.S b/arch/arm/mm/abort-lv4t.S new file mode 100644 index 000000000000..db743e510214 --- /dev/null +++ b/arch/arm/mm/abort-lv4t.S | |||
@@ -0,0 +1,220 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | #include <asm/assembler.h> | ||
3 | /* | ||
4 | * Function: v4t_late_abort | ||
5 | * | ||
6 | * Params : r2 = address of aborted instruction | ||
7 | * : r3 = saved SPSR | ||
8 | * | ||
9 | * Returns : r0 = address of abort | ||
10 | * : r1 = FSR, bit 11 = write | ||
11 | * : r2-r8 = corrupted | ||
12 | * : r9 = preserved | ||
13 | * : sp = pointer to registers | ||
14 | * | ||
15 | * Purpose : obtain information about current aborted instruction. | ||
16 | * Note: we read user space. This means we might cause a data | ||
17 | * abort here if the I-TLB and D-TLB aren't seeing the same | ||
18 | * picture. Unfortunately, this does happen. We live with it. | ||
19 | */ | ||
20 | ENTRY(v4t_late_abort) | ||
21 | tst r3, #PSR_T_BIT @ check for thumb mode | ||
22 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
23 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
24 | bne .data_thumb_abort | ||
25 | ldr r8, [r2] @ read arm instruction | ||
26 | bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR | ||
27 | tst r8, #1 << 20 @ L = 1 -> write? | ||
28 | orreq r1, r1, #1 << 11 @ yes. | ||
29 | and r7, r8, #15 << 24 | ||
30 | add pc, pc, r7, lsr #22 @ Now branch to the relevant processing routine | ||
31 | nop | ||
32 | |||
33 | /* 0 */ b .data_arm_lateldrhpost @ ldrh rd, [rn], #m/rm | ||
34 | /* 1 */ b .data_arm_lateldrhpre @ ldrh rd, [rn, #m/rm] | ||
35 | /* 2 */ b .data_unknown | ||
36 | /* 3 */ b .data_unknown | ||
37 | /* 4 */ b .data_arm_lateldrpostconst @ ldr rd, [rn], #m | ||
38 | /* 5 */ b .data_arm_lateldrpreconst @ ldr rd, [rn, #m] | ||
39 | /* 6 */ b .data_arm_lateldrpostreg @ ldr rd, [rn], rm | ||
40 | /* 7 */ b .data_arm_lateldrprereg @ ldr rd, [rn, rm] | ||
41 | /* 8 */ b .data_arm_ldmstm @ ldm*a rn, <rlist> | ||
42 | /* 9 */ b .data_arm_ldmstm @ ldm*b rn, <rlist> | ||
43 | /* a */ b .data_unknown | ||
44 | /* b */ b .data_unknown | ||
45 | /* c */ mov pc, lr @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m | ||
46 | /* d */ mov pc, lr @ ldc rd, [rn, #m] | ||
47 | /* e */ b .data_unknown | ||
48 | /* f */ | ||
49 | .data_unknown: @ Part of jumptable | ||
50 | mov r0, r2 | ||
51 | mov r1, r8 | ||
52 | mov r2, sp | ||
53 | bl baddataabort | ||
54 | b ret_from_exception | ||
55 | |||
56 | .data_arm_ldmstm: | ||
57 | tst r8, #1 << 21 @ check writeback bit | ||
58 | moveq pc, lr @ no writeback -> no fixup | ||
59 | mov r7, #0x11 | ||
60 | orr r7, r7, #0x1100 | ||
61 | and r6, r8, r7 | ||
62 | and r2, r8, r7, lsl #1 | ||
63 | add r6, r6, r2, lsr #1 | ||
64 | and r2, r8, r7, lsl #2 | ||
65 | add r6, r6, r2, lsr #2 | ||
66 | and r2, r8, r7, lsl #3 | ||
67 | add r6, r6, r2, lsr #3 | ||
68 | add r6, r6, r6, lsr #8 | ||
69 | add r6, r6, r6, lsr #4 | ||
70 | and r6, r6, #15 @ r6 = no. of registers to transfer. | ||
71 | and r5, r8, #15 << 16 @ Extract 'n' from instruction | ||
72 | ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' | ||
73 | tst r8, #1 << 23 @ Check U bit | ||
74 | subne r7, r7, r6, lsl #2 @ Undo increment | ||
75 | addeq r7, r7, r6, lsl #2 @ Undo decrement | ||
76 | str r7, [sp, r5, lsr #14] @ Put register 'Rn' | ||
77 | mov pc, lr | ||
78 | |||
79 | .data_arm_lateldrhpre: | ||
80 | tst r8, #1 << 21 @ Check writeback bit | ||
81 | moveq pc, lr @ No writeback -> no fixup | ||
82 | .data_arm_lateldrhpost: | ||
83 | and r5, r8, #0x00f @ get Rm / low nibble of immediate value | ||
84 | tst r8, #1 << 22 @ if (immediate offset) | ||
85 | andne r6, r8, #0xf00 @ { immediate high nibble | ||
86 | orrne r6, r5, r6, lsr #4 @ combine nibbles } else | ||
87 | ldreq r6, [sp, r5, lsl #2] @ { load Rm value } | ||
88 | .data_arm_apply_r6_and_rn: | ||
89 | and r5, r8, #15 << 16 @ Extract 'n' from instruction | ||
90 | ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' | ||
91 | tst r8, #1 << 23 @ Check U bit | ||
92 | subne r7, r7, r6 @ Undo incrmenet | ||
93 | addeq r7, r7, r6 @ Undo decrement | ||
94 | str r7, [sp, r5, lsr #14] @ Put register 'Rn' | ||
95 | mov pc, lr | ||
96 | |||
97 | .data_arm_lateldrpreconst: | ||
98 | tst r8, #1 << 21 @ check writeback bit | ||
99 | moveq pc, lr @ no writeback -> no fixup | ||
100 | .data_arm_lateldrpostconst: | ||
101 | movs r2, r8, lsl #20 @ Get offset | ||
102 | moveq pc, lr @ zero -> no fixup | ||
103 | and r5, r8, #15 << 16 @ Extract 'n' from instruction | ||
104 | ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' | ||
105 | tst r8, #1 << 23 @ Check U bit | ||
106 | subne r7, r7, r2, lsr #20 @ Undo increment | ||
107 | addeq r7, r7, r2, lsr #20 @ Undo decrement | ||
108 | str r7, [sp, r5, lsr #14] @ Put register 'Rn' | ||
109 | mov pc, lr | ||
110 | |||
111 | .data_arm_lateldrprereg: | ||
112 | tst r8, #1 << 21 @ check writeback bit | ||
113 | moveq pc, lr @ no writeback -> no fixup | ||
114 | .data_arm_lateldrpostreg: | ||
115 | and r7, r8, #15 @ Extract 'm' from instruction | ||
116 | ldr r6, [sp, r7, lsl #2] @ Get register 'Rm' | ||
117 | mov r5, r8, lsr #7 @ get shift count | ||
118 | ands r5, r5, #31 | ||
119 | and r7, r8, #0x70 @ get shift type | ||
120 | orreq r7, r7, #8 @ shift count = 0 | ||
121 | add pc, pc, r7 | ||
122 | nop | ||
123 | |||
124 | mov r6, r6, lsl r5 @ 0: LSL #!0 | ||
125 | b .data_arm_apply_r6_and_rn | ||
126 | b .data_arm_apply_r6_and_rn @ 1: LSL #0 | ||
127 | nop | ||
128 | b .data_unknown @ 2: MUL? | ||
129 | nop | ||
130 | b .data_unknown @ 3: MUL? | ||
131 | nop | ||
132 | mov r6, r6, lsr r5 @ 4: LSR #!0 | ||
133 | b .data_arm_apply_r6_and_rn | ||
134 | mov r6, r6, lsr #32 @ 5: LSR #32 | ||
135 | b .data_arm_apply_r6_and_rn | ||
136 | b .data_unknown @ 6: MUL? | ||
137 | nop | ||
138 | b .data_unknown @ 7: MUL? | ||
139 | nop | ||
140 | mov r6, r6, asr r5 @ 8: ASR #!0 | ||
141 | b .data_arm_apply_r6_and_rn | ||
142 | mov r6, r6, asr #32 @ 9: ASR #32 | ||
143 | b .data_arm_apply_r6_and_rn | ||
144 | b .data_unknown @ A: MUL? | ||
145 | nop | ||
146 | b .data_unknown @ B: MUL? | ||
147 | nop | ||
148 | mov r6, r6, ror r5 @ C: ROR #!0 | ||
149 | b .data_arm_apply_r6_and_rn | ||
150 | mov r6, r6, rrx @ D: RRX | ||
151 | b .data_arm_apply_r6_and_rn | ||
152 | b .data_unknown @ E: MUL? | ||
153 | nop | ||
154 | b .data_unknown @ F: MUL? | ||
155 | |||
156 | .data_thumb_abort: | ||
157 | ldrh r8, [r2] @ read instruction | ||
158 | tst r8, #1 << 11 @ L = 1 -> write? | ||
159 | orreq r1, r1, #1 << 8 @ yes | ||
160 | and r7, r8, #15 << 12 | ||
161 | add pc, pc, r7, lsr #10 @ lookup in table | ||
162 | nop | ||
163 | |||
164 | /* 0 */ b .data_unknown | ||
165 | /* 1 */ b .data_unknown | ||
166 | /* 2 */ b .data_unknown | ||
167 | /* 3 */ b .data_unknown | ||
168 | /* 4 */ b .data_unknown | ||
169 | /* 5 */ b .data_thumb_reg | ||
170 | /* 6 */ mov pc, lr | ||
171 | /* 7 */ mov pc, lr | ||
172 | /* 8 */ mov pc, lr | ||
173 | /* 9 */ mov pc, lr | ||
174 | /* A */ b .data_unknown | ||
175 | /* B */ b .data_thumb_pushpop | ||
176 | /* C */ b .data_thumb_ldmstm | ||
177 | /* D */ b .data_unknown | ||
178 | /* E */ b .data_unknown | ||
179 | /* F */ b .data_unknown | ||
180 | |||
181 | .data_thumb_reg: | ||
182 | tst r8, #1 << 9 | ||
183 | moveq pc, lr | ||
184 | tst r8, #1 << 10 @ If 'S' (signed) bit is set | ||
185 | movne r1, #0 @ it must be a load instr | ||
186 | mov pc, lr | ||
187 | |||
188 | .data_thumb_pushpop: | ||
189 | tst r8, #1 << 10 | ||
190 | beq .data_unknown | ||
191 | and r6, r8, #0x55 @ hweight8(r8) + R bit | ||
192 | and r2, r8, #0xaa | ||
193 | add r6, r6, r2, lsr #1 | ||
194 | and r2, r6, #0xcc | ||
195 | and r6, r6, #0x33 | ||
196 | add r6, r6, r2, lsr #2 | ||
197 | movs r7, r8, lsr #9 @ C = r8 bit 8 (R bit) | ||
198 | adc r6, r6, r6, lsr #4 @ high + low nibble + R bit | ||
199 | and r6, r6, #15 @ number of regs to transfer | ||
200 | ldr r7, [sp, #13 << 2] | ||
201 | tst r8, #1 << 11 | ||
202 | addeq r7, r7, r6, lsl #2 @ increment SP if PUSH | ||
203 | subne r7, r7, r6, lsl #2 @ decrement SP if POP | ||
204 | str r7, [sp, #13 << 2] | ||
205 | mov pc, lr | ||
206 | |||
207 | .data_thumb_ldmstm: | ||
208 | and r6, r8, #0x55 @ hweight8(r8) | ||
209 | and r2, r8, #0xaa | ||
210 | add r6, r6, r2, lsr #1 | ||
211 | and r2, r6, #0xcc | ||
212 | and r6, r6, #0x33 | ||
213 | add r6, r6, r2, lsr #2 | ||
214 | add r6, r6, r6, lsr #4 | ||
215 | and r5, r8, #7 << 8 | ||
216 | ldr r7, [sp, r5, lsr #6] | ||
217 | and r6, r6, #15 @ number of regs to transfer | ||
218 | sub r7, r7, r6, lsl #2 @ always decrement | ||
219 | str r7, [sp, r5, lsr #6] | ||
220 | mov pc, lr | ||
diff --git a/arch/arm/mm/abort-macro.S b/arch/arm/mm/abort-macro.S new file mode 100644 index 000000000000..d7cb1bfa51a4 --- /dev/null +++ b/arch/arm/mm/abort-macro.S | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * The ARM LDRD and Thumb LDRSB instructions use bit 20/11 (ARM/Thumb) | ||
3 | * differently than every other instruction, so it is set to 0 (write) | ||
4 | * even though the instructions are read instructions. This means that | ||
5 | * during an abort the instructions will be treated as a write and the | ||
6 | * handler will raise a signal from unwriteable locations if they | ||
7 | * fault. We have to specifically check for these instructions | ||
8 | * from the abort handlers to treat them properly. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | .macro do_thumb_abort | ||
13 | tst r3, #PSR_T_BIT | ||
14 | beq not_thumb | ||
15 | ldrh r3, [r2] @ Read aborted Thumb instruction | ||
16 | and r3, r3, # 0xfe00 @ Mask opcode field | ||
17 | cmp r3, # 0x5600 @ Is it ldrsb? | ||
18 | orreq r3, r3, #1 << 11 @ Set L-bit if yes | ||
19 | tst r3, #1 << 11 @ L = 0 -> write | ||
20 | orreq r1, r1, #1 << 11 @ yes. | ||
21 | mov pc, lr | ||
22 | not_thumb: | ||
23 | .endm | ||
24 | |||
25 | /* | ||
26 | * We check for the following insturction encoding for LDRD. | ||
27 | * | ||
28 | * [27:25] == 0 | ||
29 | * [7:4] == 1101 | ||
30 | * [20] == 0 | ||
31 | */ | ||
32 | .macro do_ldrd_abort | ||
33 | tst r3, #0x0e000000 @ [27:25] == 0 | ||
34 | bne not_ldrd | ||
35 | and r2, r3, #0x000000f0 @ [7:4] == 1101 | ||
36 | cmp r2, #0x000000d0 | ||
37 | bne not_ldrd | ||
38 | tst r3, #1 << 20 @ [20] == 0 | ||
39 | moveq pc, lr | ||
40 | not_ldrd: | ||
41 | .endm | ||
42 | |||
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c new file mode 100644 index 000000000000..81f4a8a2d34b --- /dev/null +++ b/arch/arm/mm/alignment.c | |||
@@ -0,0 +1,756 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/alignment.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Modifications for ARM processor (c) 1995-2001 Russell King | ||
6 | * Thumb aligment fault fixups (c) 2004 MontaVista Software, Inc. | ||
7 | * - Adapted from gdb/sim/arm/thumbemu.c -- Thumb instruction emulation. | ||
8 | * Copyright (C) 1996, Cygnus Software Technologies Ltd. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | #include <linux/config.h> | ||
15 | #include <linux/compiler.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | #include <linux/proc_fs.h> | ||
21 | #include <linux/init.h> | ||
22 | |||
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/unaligned.h> | ||
25 | |||
26 | #include "fault.h" | ||
27 | |||
28 | /* | ||
29 | * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 | ||
30 | * /proc/sys/debug/alignment, modified and integrated into | ||
31 | * Linux 2.1 by Russell King | ||
32 | * | ||
33 | * Speed optimisations and better fault handling by Russell King. | ||
34 | * | ||
35 | * *** NOTE *** | ||
36 | * This code is not portable to processors with late data abort handling. | ||
37 | */ | ||
38 | #define CODING_BITS(i) (i & 0x0e000000) | ||
39 | |||
40 | #define LDST_I_BIT(i) (i & (1 << 26)) /* Immediate constant */ | ||
41 | #define LDST_P_BIT(i) (i & (1 << 24)) /* Preindex */ | ||
42 | #define LDST_U_BIT(i) (i & (1 << 23)) /* Add offset */ | ||
43 | #define LDST_W_BIT(i) (i & (1 << 21)) /* Writeback */ | ||
44 | #define LDST_L_BIT(i) (i & (1 << 20)) /* Load */ | ||
45 | |||
46 | #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 23)) == 0) | ||
47 | |||
48 | #define LDSTH_I_BIT(i) (i & (1 << 22)) /* half-word immed */ | ||
49 | #define LDM_S_BIT(i) (i & (1 << 22)) /* write CPSR from SPSR */ | ||
50 | |||
51 | #define RN_BITS(i) ((i >> 16) & 15) /* Rn */ | ||
52 | #define RD_BITS(i) ((i >> 12) & 15) /* Rd */ | ||
53 | #define RM_BITS(i) (i & 15) /* Rm */ | ||
54 | |||
55 | #define REGMASK_BITS(i) (i & 0xffff) | ||
56 | #define OFFSET_BITS(i) (i & 0x0fff) | ||
57 | |||
58 | #define IS_SHIFT(i) (i & 0x0ff0) | ||
59 | #define SHIFT_BITS(i) ((i >> 7) & 0x1f) | ||
60 | #define SHIFT_TYPE(i) (i & 0x60) | ||
61 | #define SHIFT_LSL 0x00 | ||
62 | #define SHIFT_LSR 0x20 | ||
63 | #define SHIFT_ASR 0x40 | ||
64 | #define SHIFT_RORRRX 0x60 | ||
65 | |||
66 | static unsigned long ai_user; | ||
67 | static unsigned long ai_sys; | ||
68 | static unsigned long ai_skipped; | ||
69 | static unsigned long ai_half; | ||
70 | static unsigned long ai_word; | ||
71 | static unsigned long ai_multi; | ||
72 | static int ai_usermode; | ||
73 | |||
74 | #ifdef CONFIG_PROC_FS | ||
75 | static const char *usermode_action[] = { | ||
76 | "ignored", | ||
77 | "warn", | ||
78 | "fixup", | ||
79 | "fixup+warn", | ||
80 | "signal", | ||
81 | "signal+warn" | ||
82 | }; | ||
83 | |||
84 | static int | ||
85 | proc_alignment_read(char *page, char **start, off_t off, int count, int *eof, | ||
86 | void *data) | ||
87 | { | ||
88 | char *p = page; | ||
89 | int len; | ||
90 | |||
91 | p += sprintf(p, "User:\t\t%lu\n", ai_user); | ||
92 | p += sprintf(p, "System:\t\t%lu\n", ai_sys); | ||
93 | p += sprintf(p, "Skipped:\t%lu\n", ai_skipped); | ||
94 | p += sprintf(p, "Half:\t\t%lu\n", ai_half); | ||
95 | p += sprintf(p, "Word:\t\t%lu\n", ai_word); | ||
96 | p += sprintf(p, "Multi:\t\t%lu\n", ai_multi); | ||
97 | p += sprintf(p, "User faults:\t%i (%s)\n", ai_usermode, | ||
98 | usermode_action[ai_usermode]); | ||
99 | |||
100 | len = (p - page) - off; | ||
101 | if (len < 0) | ||
102 | len = 0; | ||
103 | |||
104 | *eof = (len <= count) ? 1 : 0; | ||
105 | *start = page + off; | ||
106 | |||
107 | return len; | ||
108 | } | ||
109 | |||
110 | static int proc_alignment_write(struct file *file, const char __user *buffer, | ||
111 | unsigned long count, void *data) | ||
112 | { | ||
113 | char mode; | ||
114 | |||
115 | if (count > 0) { | ||
116 | if (get_user(mode, buffer)) | ||
117 | return -EFAULT; | ||
118 | if (mode >= '0' && mode <= '5') | ||
119 | ai_usermode = mode - '0'; | ||
120 | } | ||
121 | return count; | ||
122 | } | ||
123 | |||
124 | #endif /* CONFIG_PROC_FS */ | ||
125 | |||
126 | union offset_union { | ||
127 | unsigned long un; | ||
128 | signed long sn; | ||
129 | }; | ||
130 | |||
131 | #define TYPE_ERROR 0 | ||
132 | #define TYPE_FAULT 1 | ||
133 | #define TYPE_LDST 2 | ||
134 | #define TYPE_DONE 3 | ||
135 | |||
136 | #ifdef __ARMEB__ | ||
137 | #define BE 1 | ||
138 | #define FIRST_BYTE_16 "mov %1, %1, ror #8\n" | ||
139 | #define FIRST_BYTE_32 "mov %1, %1, ror #24\n" | ||
140 | #define NEXT_BYTE "ror #24" | ||
141 | #else | ||
142 | #define BE 0 | ||
143 | #define FIRST_BYTE_16 | ||
144 | #define FIRST_BYTE_32 | ||
145 | #define NEXT_BYTE "lsr #8" | ||
146 | #endif | ||
147 | |||
148 | #define __get8_unaligned_check(ins,val,addr,err) \ | ||
149 | __asm__( \ | ||
150 | "1: "ins" %1, [%2], #1\n" \ | ||
151 | "2:\n" \ | ||
152 | " .section .fixup,\"ax\"\n" \ | ||
153 | " .align 2\n" \ | ||
154 | "3: mov %0, #1\n" \ | ||
155 | " b 2b\n" \ | ||
156 | " .previous\n" \ | ||
157 | " .section __ex_table,\"a\"\n" \ | ||
158 | " .align 3\n" \ | ||
159 | " .long 1b, 3b\n" \ | ||
160 | " .previous\n" \ | ||
161 | : "=r" (err), "=&r" (val), "=r" (addr) \ | ||
162 | : "0" (err), "2" (addr)) | ||
163 | |||
164 | #define __get16_unaligned_check(ins,val,addr) \ | ||
165 | do { \ | ||
166 | unsigned int err = 0, v, a = addr; \ | ||
167 | __get8_unaligned_check(ins,v,a,err); \ | ||
168 | val = v << ((BE) ? 8 : 0); \ | ||
169 | __get8_unaligned_check(ins,v,a,err); \ | ||
170 | val |= v << ((BE) ? 0 : 8); \ | ||
171 | if (err) \ | ||
172 | goto fault; \ | ||
173 | } while (0) | ||
174 | |||
175 | #define get16_unaligned_check(val,addr) \ | ||
176 | __get16_unaligned_check("ldrb",val,addr) | ||
177 | |||
178 | #define get16t_unaligned_check(val,addr) \ | ||
179 | __get16_unaligned_check("ldrbt",val,addr) | ||
180 | |||
181 | #define __get32_unaligned_check(ins,val,addr) \ | ||
182 | do { \ | ||
183 | unsigned int err = 0, v, a = addr; \ | ||
184 | __get8_unaligned_check(ins,v,a,err); \ | ||
185 | val = v << ((BE) ? 24 : 0); \ | ||
186 | __get8_unaligned_check(ins,v,a,err); \ | ||
187 | val |= v << ((BE) ? 16 : 8); \ | ||
188 | __get8_unaligned_check(ins,v,a,err); \ | ||
189 | val |= v << ((BE) ? 8 : 16); \ | ||
190 | __get8_unaligned_check(ins,v,a,err); \ | ||
191 | val |= v << ((BE) ? 0 : 24); \ | ||
192 | if (err) \ | ||
193 | goto fault; \ | ||
194 | } while (0) | ||
195 | |||
196 | #define get32_unaligned_check(val,addr) \ | ||
197 | __get32_unaligned_check("ldrb",val,addr) | ||
198 | |||
199 | #define get32t_unaligned_check(val,addr) \ | ||
200 | __get32_unaligned_check("ldrbt",val,addr) | ||
201 | |||
202 | #define __put16_unaligned_check(ins,val,addr) \ | ||
203 | do { \ | ||
204 | unsigned int err = 0, v = val, a = addr; \ | ||
205 | __asm__( FIRST_BYTE_16 \ | ||
206 | "1: "ins" %1, [%2], #1\n" \ | ||
207 | " mov %1, %1, "NEXT_BYTE"\n" \ | ||
208 | "2: "ins" %1, [%2]\n" \ | ||
209 | "3:\n" \ | ||
210 | " .section .fixup,\"ax\"\n" \ | ||
211 | " .align 2\n" \ | ||
212 | "4: mov %0, #1\n" \ | ||
213 | " b 3b\n" \ | ||
214 | " .previous\n" \ | ||
215 | " .section __ex_table,\"a\"\n" \ | ||
216 | " .align 3\n" \ | ||
217 | " .long 1b, 4b\n" \ | ||
218 | " .long 2b, 4b\n" \ | ||
219 | " .previous\n" \ | ||
220 | : "=r" (err), "=&r" (v), "=&r" (a) \ | ||
221 | : "0" (err), "1" (v), "2" (a)); \ | ||
222 | if (err) \ | ||
223 | goto fault; \ | ||
224 | } while (0) | ||
225 | |||
226 | #define put16_unaligned_check(val,addr) \ | ||
227 | __put16_unaligned_check("strb",val,addr) | ||
228 | |||
229 | #define put16t_unaligned_check(val,addr) \ | ||
230 | __put16_unaligned_check("strbt",val,addr) | ||
231 | |||
232 | #define __put32_unaligned_check(ins,val,addr) \ | ||
233 | do { \ | ||
234 | unsigned int err = 0, v = val, a = addr; \ | ||
235 | __asm__( FIRST_BYTE_32 \ | ||
236 | "1: "ins" %1, [%2], #1\n" \ | ||
237 | " mov %1, %1, "NEXT_BYTE"\n" \ | ||
238 | "2: "ins" %1, [%2], #1\n" \ | ||
239 | " mov %1, %1, "NEXT_BYTE"\n" \ | ||
240 | "3: "ins" %1, [%2], #1\n" \ | ||
241 | " mov %1, %1, "NEXT_BYTE"\n" \ | ||
242 | "4: "ins" %1, [%2]\n" \ | ||
243 | "5:\n" \ | ||
244 | " .section .fixup,\"ax\"\n" \ | ||
245 | " .align 2\n" \ | ||
246 | "6: mov %0, #1\n" \ | ||
247 | " b 5b\n" \ | ||
248 | " .previous\n" \ | ||
249 | " .section __ex_table,\"a\"\n" \ | ||
250 | " .align 3\n" \ | ||
251 | " .long 1b, 6b\n" \ | ||
252 | " .long 2b, 6b\n" \ | ||
253 | " .long 3b, 6b\n" \ | ||
254 | " .long 4b, 6b\n" \ | ||
255 | " .previous\n" \ | ||
256 | : "=r" (err), "=&r" (v), "=&r" (a) \ | ||
257 | : "0" (err), "1" (v), "2" (a)); \ | ||
258 | if (err) \ | ||
259 | goto fault; \ | ||
260 | } while (0) | ||
261 | |||
262 | #define put32_unaligned_check(val,addr) \ | ||
263 | __put32_unaligned_check("strb", val, addr) | ||
264 | |||
265 | #define put32t_unaligned_check(val,addr) \ | ||
266 | __put32_unaligned_check("strbt", val, addr) | ||
267 | |||
268 | static void | ||
269 | do_alignment_finish_ldst(unsigned long addr, unsigned long instr, struct pt_regs *regs, union offset_union offset) | ||
270 | { | ||
271 | if (!LDST_U_BIT(instr)) | ||
272 | offset.un = -offset.un; | ||
273 | |||
274 | if (!LDST_P_BIT(instr)) | ||
275 | addr += offset.un; | ||
276 | |||
277 | if (!LDST_P_BIT(instr) || LDST_W_BIT(instr)) | ||
278 | regs->uregs[RN_BITS(instr)] = addr; | ||
279 | } | ||
280 | |||
281 | static int | ||
282 | do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, struct pt_regs *regs) | ||
283 | { | ||
284 | unsigned int rd = RD_BITS(instr); | ||
285 | |||
286 | if ((instr & 0x01f00ff0) == 0x01000090) | ||
287 | goto swp; | ||
288 | |||
289 | if ((instr & 0x90) != 0x90 || (instr & 0x60) == 0) | ||
290 | goto bad; | ||
291 | |||
292 | ai_half += 1; | ||
293 | |||
294 | if (user_mode(regs)) | ||
295 | goto user; | ||
296 | |||
297 | if (LDST_L_BIT(instr)) { | ||
298 | unsigned long val; | ||
299 | get16_unaligned_check(val, addr); | ||
300 | |||
301 | /* signed half-word? */ | ||
302 | if (instr & 0x40) | ||
303 | val = (signed long)((signed short) val); | ||
304 | |||
305 | regs->uregs[rd] = val; | ||
306 | } else | ||
307 | put16_unaligned_check(regs->uregs[rd], addr); | ||
308 | |||
309 | return TYPE_LDST; | ||
310 | |||
311 | user: | ||
312 | if (LDST_L_BIT(instr)) { | ||
313 | unsigned long val; | ||
314 | get16t_unaligned_check(val, addr); | ||
315 | |||
316 | /* signed half-word? */ | ||
317 | if (instr & 0x40) | ||
318 | val = (signed long)((signed short) val); | ||
319 | |||
320 | regs->uregs[rd] = val; | ||
321 | } else | ||
322 | put16t_unaligned_check(regs->uregs[rd], addr); | ||
323 | |||
324 | return TYPE_LDST; | ||
325 | |||
326 | swp: | ||
327 | printk(KERN_ERR "Alignment trap: not handling swp instruction\n"); | ||
328 | bad: | ||
329 | return TYPE_ERROR; | ||
330 | |||
331 | fault: | ||
332 | return TYPE_FAULT; | ||
333 | } | ||
334 | |||
335 | static int | ||
336 | do_alignment_ldrstr(unsigned long addr, unsigned long instr, struct pt_regs *regs) | ||
337 | { | ||
338 | unsigned int rd = RD_BITS(instr); | ||
339 | |||
340 | ai_word += 1; | ||
341 | |||
342 | if ((!LDST_P_BIT(instr) && LDST_W_BIT(instr)) || user_mode(regs)) | ||
343 | goto trans; | ||
344 | |||
345 | if (LDST_L_BIT(instr)) { | ||
346 | unsigned int val; | ||
347 | get32_unaligned_check(val, addr); | ||
348 | regs->uregs[rd] = val; | ||
349 | } else | ||
350 | put32_unaligned_check(regs->uregs[rd], addr); | ||
351 | return TYPE_LDST; | ||
352 | |||
353 | trans: | ||
354 | if (LDST_L_BIT(instr)) { | ||
355 | unsigned int val; | ||
356 | get32t_unaligned_check(val, addr); | ||
357 | regs->uregs[rd] = val; | ||
358 | } else | ||
359 | put32t_unaligned_check(regs->uregs[rd], addr); | ||
360 | return TYPE_LDST; | ||
361 | |||
362 | fault: | ||
363 | return TYPE_FAULT; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * LDM/STM alignment handler. | ||
368 | * | ||
369 | * There are 4 variants of this instruction: | ||
370 | * | ||
371 | * B = rn pointer before instruction, A = rn pointer after instruction | ||
372 | * ------ increasing address -----> | ||
373 | * | | r0 | r1 | ... | rx | | | ||
374 | * PU = 01 B A | ||
375 | * PU = 11 B A | ||
376 | * PU = 00 A B | ||
377 | * PU = 10 A B | ||
378 | */ | ||
379 | static int | ||
380 | do_alignment_ldmstm(unsigned long addr, unsigned long instr, struct pt_regs *regs) | ||
381 | { | ||
382 | unsigned int rd, rn, correction, nr_regs, regbits; | ||
383 | unsigned long eaddr, newaddr; | ||
384 | |||
385 | if (LDM_S_BIT(instr)) | ||
386 | goto bad; | ||
387 | |||
388 | correction = 4; /* processor implementation defined */ | ||
389 | regs->ARM_pc += correction; | ||
390 | |||
391 | ai_multi += 1; | ||
392 | |||
393 | /* count the number of registers in the mask to be transferred */ | ||
394 | nr_regs = hweight16(REGMASK_BITS(instr)) * 4; | ||
395 | |||
396 | rn = RN_BITS(instr); | ||
397 | newaddr = eaddr = regs->uregs[rn]; | ||
398 | |||
399 | if (!LDST_U_BIT(instr)) | ||
400 | nr_regs = -nr_regs; | ||
401 | newaddr += nr_regs; | ||
402 | if (!LDST_U_BIT(instr)) | ||
403 | eaddr = newaddr; | ||
404 | |||
405 | if (LDST_P_EQ_U(instr)) /* U = P */ | ||
406 | eaddr += 4; | ||
407 | |||
408 | /* | ||
409 | * For alignment faults on the ARM922T/ARM920T the MMU makes | ||
410 | * the FSR (and hence addr) equal to the updated base address | ||
411 | * of the multiple access rather than the restored value. | ||
412 | * Switch this message off if we've got a ARM92[02], otherwise | ||
413 | * [ls]dm alignment faults are noisy! | ||
414 | */ | ||
415 | #if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T) | ||
416 | /* | ||
417 | * This is a "hint" - we already have eaddr worked out by the | ||
418 | * processor for us. | ||
419 | */ | ||
420 | if (addr != eaddr) { | ||
421 | printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, " | ||
422 | "addr = %08lx, eaddr = %08lx\n", | ||
423 | instruction_pointer(regs), instr, addr, eaddr); | ||
424 | show_regs(regs); | ||
425 | } | ||
426 | #endif | ||
427 | |||
428 | if (user_mode(regs)) { | ||
429 | for (regbits = REGMASK_BITS(instr), rd = 0; regbits; | ||
430 | regbits >>= 1, rd += 1) | ||
431 | if (regbits & 1) { | ||
432 | if (LDST_L_BIT(instr)) { | ||
433 | unsigned int val; | ||
434 | get32t_unaligned_check(val, eaddr); | ||
435 | regs->uregs[rd] = val; | ||
436 | } else | ||
437 | put32t_unaligned_check(regs->uregs[rd], eaddr); | ||
438 | eaddr += 4; | ||
439 | } | ||
440 | } else { | ||
441 | for (regbits = REGMASK_BITS(instr), rd = 0; regbits; | ||
442 | regbits >>= 1, rd += 1) | ||
443 | if (regbits & 1) { | ||
444 | if (LDST_L_BIT(instr)) { | ||
445 | unsigned int val; | ||
446 | get32_unaligned_check(val, eaddr); | ||
447 | regs->uregs[rd] = val; | ||
448 | } else | ||
449 | put32_unaligned_check(regs->uregs[rd], eaddr); | ||
450 | eaddr += 4; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | if (LDST_W_BIT(instr)) | ||
455 | regs->uregs[rn] = newaddr; | ||
456 | if (!LDST_L_BIT(instr) || !(REGMASK_BITS(instr) & (1 << 15))) | ||
457 | regs->ARM_pc -= correction; | ||
458 | return TYPE_DONE; | ||
459 | |||
460 | fault: | ||
461 | regs->ARM_pc -= correction; | ||
462 | return TYPE_FAULT; | ||
463 | |||
464 | bad: | ||
465 | printk(KERN_ERR "Alignment trap: not handling ldm with s-bit set\n"); | ||
466 | return TYPE_ERROR; | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * Convert Thumb ld/st instruction forms to equivalent ARM instructions so | ||
471 | * we can reuse ARM userland alignment fault fixups for Thumb. | ||
472 | * | ||
473 | * This implementation was initially based on the algorithm found in | ||
474 | * gdb/sim/arm/thumbemu.c. It is basically just a code reduction of same | ||
475 | * to convert only Thumb ld/st instruction forms to equivalent ARM forms. | ||
476 | * | ||
477 | * NOTES: | ||
478 | * 1. Comments below refer to ARM ARM DDI0100E Thumb Instruction sections. | ||
479 | * 2. If for some reason we're passed an non-ld/st Thumb instruction to | ||
480 | * decode, we return 0xdeadc0de. This should never happen under normal | ||
481 | * circumstances but if it does, we've got other problems to deal with | ||
482 | * elsewhere and we obviously can't fix those problems here. | ||
483 | */ | ||
484 | |||
485 | static unsigned long | ||
486 | thumb2arm(u16 tinstr) | ||
487 | { | ||
488 | u32 L = (tinstr & (1<<11)) >> 11; | ||
489 | |||
490 | switch ((tinstr & 0xf800) >> 11) { | ||
491 | /* 6.5.1 Format 1: */ | ||
492 | case 0x6000 >> 11: /* 7.1.52 STR(1) */ | ||
493 | case 0x6800 >> 11: /* 7.1.26 LDR(1) */ | ||
494 | case 0x7000 >> 11: /* 7.1.55 STRB(1) */ | ||
495 | case 0x7800 >> 11: /* 7.1.30 LDRB(1) */ | ||
496 | return 0xe5800000 | | ||
497 | ((tinstr & (1<<12)) << (22-12)) | /* fixup */ | ||
498 | (L<<20) | /* L==1? */ | ||
499 | ((tinstr & (7<<0)) << (12-0)) | /* Rd */ | ||
500 | ((tinstr & (7<<3)) << (16-3)) | /* Rn */ | ||
501 | ((tinstr & (31<<6)) >> /* immed_5 */ | ||
502 | (6 - ((tinstr & (1<<12)) ? 0 : 2))); | ||
503 | case 0x8000 >> 11: /* 7.1.57 STRH(1) */ | ||
504 | case 0x8800 >> 11: /* 7.1.32 LDRH(1) */ | ||
505 | return 0xe1c000b0 | | ||
506 | (L<<20) | /* L==1? */ | ||
507 | ((tinstr & (7<<0)) << (12-0)) | /* Rd */ | ||
508 | ((tinstr & (7<<3)) << (16-3)) | /* Rn */ | ||
509 | ((tinstr & (7<<6)) >> (6-1)) | /* immed_5[2:0] */ | ||
510 | ((tinstr & (3<<9)) >> (9-8)); /* immed_5[4:3] */ | ||
511 | |||
512 | /* 6.5.1 Format 2: */ | ||
513 | case 0x5000 >> 11: | ||
514 | case 0x5800 >> 11: | ||
515 | { | ||
516 | static const u32 subset[8] = { | ||
517 | 0xe7800000, /* 7.1.53 STR(2) */ | ||
518 | 0xe18000b0, /* 7.1.58 STRH(2) */ | ||
519 | 0xe7c00000, /* 7.1.56 STRB(2) */ | ||
520 | 0xe19000d0, /* 7.1.34 LDRSB */ | ||
521 | 0xe7900000, /* 7.1.27 LDR(2) */ | ||
522 | 0xe19000b0, /* 7.1.33 LDRH(2) */ | ||
523 | 0xe7d00000, /* 7.1.31 LDRB(2) */ | ||
524 | 0xe19000f0 /* 7.1.35 LDRSH */ | ||
525 | }; | ||
526 | return subset[(tinstr & (7<<9)) >> 9] | | ||
527 | ((tinstr & (7<<0)) << (12-0)) | /* Rd */ | ||
528 | ((tinstr & (7<<3)) << (16-3)) | /* Rn */ | ||
529 | ((tinstr & (7<<6)) >> (6-0)); /* Rm */ | ||
530 | } | ||
531 | |||
532 | /* 6.5.1 Format 3: */ | ||
533 | case 0x4800 >> 11: /* 7.1.28 LDR(3) */ | ||
534 | /* NOTE: This case is not technically possible. We're | ||
535 | * loading 32-bit memory data via PC relative | ||
536 | * addressing mode. So we can and should eliminate | ||
537 | * this case. But I'll leave it here for now. | ||
538 | */ | ||
539 | return 0xe59f0000 | | ||
540 | ((tinstr & (7<<8)) << (12-8)) | /* Rd */ | ||
541 | ((tinstr & 255) << (2-0)); /* immed_8 */ | ||
542 | |||
543 | /* 6.5.1 Format 4: */ | ||
544 | case 0x9000 >> 11: /* 7.1.54 STR(3) */ | ||
545 | case 0x9800 >> 11: /* 7.1.29 LDR(4) */ | ||
546 | return 0xe58d0000 | | ||
547 | (L<<20) | /* L==1? */ | ||
548 | ((tinstr & (7<<8)) << (12-8)) | /* Rd */ | ||
549 | ((tinstr & 255) << 2); /* immed_8 */ | ||
550 | |||
551 | /* 6.6.1 Format 1: */ | ||
552 | case 0xc000 >> 11: /* 7.1.51 STMIA */ | ||
553 | case 0xc800 >> 11: /* 7.1.25 LDMIA */ | ||
554 | { | ||
555 | u32 Rn = (tinstr & (7<<8)) >> 8; | ||
556 | u32 W = ((L<<Rn) & (tinstr&255)) ? 0 : 1<<21; | ||
557 | |||
558 | return 0xe8800000 | W | (L<<20) | (Rn<<16) | | ||
559 | (tinstr&255); | ||
560 | } | ||
561 | |||
562 | /* 6.6.1 Format 2: */ | ||
563 | case 0xb000 >> 11: /* 7.1.48 PUSH */ | ||
564 | case 0xb800 >> 11: /* 7.1.47 POP */ | ||
565 | if ((tinstr & (3 << 9)) == 0x0400) { | ||
566 | static const u32 subset[4] = { | ||
567 | 0xe92d0000, /* STMDB sp!,{registers} */ | ||
568 | 0xe92d4000, /* STMDB sp!,{registers,lr} */ | ||
569 | 0xe8bd0000, /* LDMIA sp!,{registers} */ | ||
570 | 0xe8bd8000 /* LDMIA sp!,{registers,pc} */ | ||
571 | }; | ||
572 | return subset[(L<<1) | ((tinstr & (1<<8)) >> 8)] | | ||
573 | (tinstr & 255); /* register_list */ | ||
574 | } | ||
575 | /* Else fall through for illegal instruction case */ | ||
576 | |||
577 | default: | ||
578 | return 0xdeadc0de; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | static int | ||
583 | do_alignment(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
584 | { | ||
585 | union offset_union offset; | ||
586 | unsigned long instr = 0, instrptr; | ||
587 | int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs *regs); | ||
588 | unsigned int type; | ||
589 | mm_segment_t fs; | ||
590 | unsigned int fault; | ||
591 | u16 tinstr = 0; | ||
592 | |||
593 | instrptr = instruction_pointer(regs); | ||
594 | |||
595 | fs = get_fs(); | ||
596 | set_fs(KERNEL_DS); | ||
597 | if thumb_mode(regs) { | ||
598 | fault = __get_user(tinstr, (u16 *)(instrptr & ~1)); | ||
599 | if (!(fault)) | ||
600 | instr = thumb2arm(tinstr); | ||
601 | } else | ||
602 | fault = __get_user(instr, (u32 *)instrptr); | ||
603 | set_fs(fs); | ||
604 | |||
605 | if (fault) { | ||
606 | type = TYPE_FAULT; | ||
607 | goto bad_or_fault; | ||
608 | } | ||
609 | |||
610 | if (user_mode(regs)) | ||
611 | goto user; | ||
612 | |||
613 | ai_sys += 1; | ||
614 | |||
615 | fixup: | ||
616 | |||
617 | regs->ARM_pc += thumb_mode(regs) ? 2 : 4; | ||
618 | |||
619 | switch (CODING_BITS(instr)) { | ||
620 | case 0x00000000: /* ldrh or strh */ | ||
621 | if (LDSTH_I_BIT(instr)) | ||
622 | offset.un = (instr & 0xf00) >> 4 | (instr & 15); | ||
623 | else | ||
624 | offset.un = regs->uregs[RM_BITS(instr)]; | ||
625 | handler = do_alignment_ldrhstrh; | ||
626 | break; | ||
627 | |||
628 | case 0x04000000: /* ldr or str immediate */ | ||
629 | offset.un = OFFSET_BITS(instr); | ||
630 | handler = do_alignment_ldrstr; | ||
631 | break; | ||
632 | |||
633 | case 0x06000000: /* ldr or str register */ | ||
634 | offset.un = regs->uregs[RM_BITS(instr)]; | ||
635 | |||
636 | if (IS_SHIFT(instr)) { | ||
637 | unsigned int shiftval = SHIFT_BITS(instr); | ||
638 | |||
639 | switch(SHIFT_TYPE(instr)) { | ||
640 | case SHIFT_LSL: | ||
641 | offset.un <<= shiftval; | ||
642 | break; | ||
643 | |||
644 | case SHIFT_LSR: | ||
645 | offset.un >>= shiftval; | ||
646 | break; | ||
647 | |||
648 | case SHIFT_ASR: | ||
649 | offset.sn >>= shiftval; | ||
650 | break; | ||
651 | |||
652 | case SHIFT_RORRRX: | ||
653 | if (shiftval == 0) { | ||
654 | offset.un >>= 1; | ||
655 | if (regs->ARM_cpsr & PSR_C_BIT) | ||
656 | offset.un |= 1 << 31; | ||
657 | } else | ||
658 | offset.un = offset.un >> shiftval | | ||
659 | offset.un << (32 - shiftval); | ||
660 | break; | ||
661 | } | ||
662 | } | ||
663 | handler = do_alignment_ldrstr; | ||
664 | break; | ||
665 | |||
666 | case 0x08000000: /* ldm or stm */ | ||
667 | handler = do_alignment_ldmstm; | ||
668 | break; | ||
669 | |||
670 | default: | ||
671 | goto bad; | ||
672 | } | ||
673 | |||
674 | type = handler(addr, instr, regs); | ||
675 | |||
676 | if (type == TYPE_ERROR || type == TYPE_FAULT) | ||
677 | goto bad_or_fault; | ||
678 | |||
679 | if (type == TYPE_LDST) | ||
680 | do_alignment_finish_ldst(addr, instr, regs, offset); | ||
681 | |||
682 | return 0; | ||
683 | |||
684 | bad_or_fault: | ||
685 | if (type == TYPE_ERROR) | ||
686 | goto bad; | ||
687 | regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; | ||
688 | /* | ||
689 | * We got a fault - fix it up, or die. | ||
690 | */ | ||
691 | do_bad_area(current, current->mm, addr, fsr, regs); | ||
692 | return 0; | ||
693 | |||
694 | bad: | ||
695 | /* | ||
696 | * Oops, we didn't handle the instruction. | ||
697 | */ | ||
698 | printk(KERN_ERR "Alignment trap: not handling instruction " | ||
699 | "%0*lx at [<%08lx>]\n", | ||
700 | thumb_mode(regs) ? 4 : 8, | ||
701 | thumb_mode(regs) ? tinstr : instr, instrptr); | ||
702 | ai_skipped += 1; | ||
703 | return 1; | ||
704 | |||
705 | user: | ||
706 | ai_user += 1; | ||
707 | |||
708 | if (ai_usermode & 1) | ||
709 | printk("Alignment trap: %s (%d) PC=0x%08lx Instr=0x%0*lx " | ||
710 | "Address=0x%08lx FSR 0x%03x\n", current->comm, | ||
711 | current->pid, instrptr, | ||
712 | thumb_mode(regs) ? 4 : 8, | ||
713 | thumb_mode(regs) ? tinstr : instr, | ||
714 | addr, fsr); | ||
715 | |||
716 | if (ai_usermode & 2) | ||
717 | goto fixup; | ||
718 | |||
719 | if (ai_usermode & 4) | ||
720 | force_sig(SIGBUS, current); | ||
721 | else | ||
722 | set_cr(cr_no_alignment); | ||
723 | |||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | /* | ||
728 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
729 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
730 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
731 | * We now locate it in /proc/cpu/alignment instead. | ||
732 | */ | ||
733 | static int __init alignment_init(void) | ||
734 | { | ||
735 | #ifdef CONFIG_PROC_FS | ||
736 | struct proc_dir_entry *res; | ||
737 | |||
738 | res = proc_mkdir("cpu", NULL); | ||
739 | if (!res) | ||
740 | return -ENOMEM; | ||
741 | |||
742 | res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, res); | ||
743 | if (!res) | ||
744 | return -ENOMEM; | ||
745 | |||
746 | res->read_proc = proc_alignment_read; | ||
747 | res->write_proc = proc_alignment_write; | ||
748 | #endif | ||
749 | |||
750 | hook_fault_code(1, do_alignment, SIGILL, "alignment exception"); | ||
751 | hook_fault_code(3, do_alignment, SIGILL, "alignment exception"); | ||
752 | |||
753 | return 0; | ||
754 | } | ||
755 | |||
756 | fs_initcall(alignment_init); | ||
diff --git a/arch/arm/mm/blockops.c b/arch/arm/mm/blockops.c new file mode 100644 index 000000000000..806c6eeb1b0c --- /dev/null +++ b/arch/arm/mm/blockops.c | |||
@@ -0,0 +1,184 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <linux/init.h> | ||
3 | #include <linux/errno.h> | ||
4 | #include <linux/mm.h> | ||
5 | |||
6 | #include <asm/memory.h> | ||
7 | #include <asm/ptrace.h> | ||
8 | #include <asm/cacheflush.h> | ||
9 | #include <asm/traps.h> | ||
10 | |||
11 | extern struct cpu_cache_fns blk_cache_fns; | ||
12 | |||
13 | #define HARVARD_CACHE | ||
14 | |||
15 | /* | ||
16 | * blk_flush_kern_dcache_page(kaddr) | ||
17 | * | ||
18 | * Ensure that the data held in the page kaddr is written back | ||
19 | * to the page in question. | ||
20 | * | ||
21 | * - kaddr - kernel address (guaranteed to be page aligned) | ||
22 | */ | ||
23 | static void __attribute__((naked)) | ||
24 | blk_flush_kern_dcache_page(void *kaddr) | ||
25 | { | ||
26 | asm( | ||
27 | "add r1, r0, %0 \n\ | ||
28 | 1: .word 0xec401f0e @ mcrr p15, 0, r0, r1, c14, 0 @ blocking \n\ | ||
29 | mov r0, #0 \n\ | ||
30 | mcr p15, 0, r0, c7, c5, 0 \n\ | ||
31 | mcr p15, 0, r0, c7, c10, 4 \n\ | ||
32 | mov pc, lr" | ||
33 | : | ||
34 | : "I" (PAGE_SIZE)); | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * blk_dma_inv_range(start,end) | ||
39 | * | ||
40 | * Invalidate the data cache within the specified region; we will | ||
41 | * be performing a DMA operation in this region and we want to | ||
42 | * purge old data in the cache. | ||
43 | * | ||
44 | * - start - virtual start address of region | ||
45 | * - end - virtual end address of region | ||
46 | */ | ||
47 | static void __attribute__((naked)) | ||
48 | blk_dma_inv_range_unified(unsigned long start, unsigned long end) | ||
49 | { | ||
50 | asm( | ||
51 | "tst r0, %0 \n\ | ||
52 | mcrne p15, 0, r0, c7, c11, 1 @ clean unified line \n\ | ||
53 | tst r1, %0 \n\ | ||
54 | mcrne p15, 0, r1, c7, c15, 1 @ clean & invalidate unified line\n\ | ||
55 | .word 0xec401f06 @ mcrr p15, 0, r1, r0, c6, 0 @ blocking \n\ | ||
56 | mov r0, #0 \n\ | ||
57 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer \n\ | ||
58 | mov pc, lr" | ||
59 | : | ||
60 | : "I" (L1_CACHE_BYTES - 1)); | ||
61 | } | ||
62 | |||
63 | static void __attribute__((naked)) | ||
64 | blk_dma_inv_range_harvard(unsigned long start, unsigned long end) | ||
65 | { | ||
66 | asm( | ||
67 | "tst r0, %0 \n\ | ||
68 | mcrne p15, 0, r0, c7, c10, 1 @ clean D line \n\ | ||
69 | tst r1, %0 \n\ | ||
70 | mcrne p15, 0, r1, c7, c14, 1 @ clean & invalidate D line \n\ | ||
71 | .word 0xec401f06 @ mcrr p15, 0, r1, r0, c6, 0 @ blocking \n\ | ||
72 | mov r0, #0 \n\ | ||
73 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer \n\ | ||
74 | mov pc, lr" | ||
75 | : | ||
76 | : "I" (L1_CACHE_BYTES - 1)); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * blk_dma_clean_range(start,end) | ||
81 | * - start - virtual start address of region | ||
82 | * - end - virtual end address of region | ||
83 | */ | ||
84 | static void __attribute__((naked)) | ||
85 | blk_dma_clean_range(unsigned long start, unsigned long end) | ||
86 | { | ||
87 | asm( | ||
88 | ".word 0xec401f0c @ mcrr p15, 0, r1, r0, c12, 0 @ blocking \n\ | ||
89 | mov r0, #0 \n\ | ||
90 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer \n\ | ||
91 | mov pc, lr"); | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * blk_dma_flush_range(start,end) | ||
96 | * - start - virtual start address of region | ||
97 | * - end - virtual end address of region | ||
98 | */ | ||
99 | static void __attribute__((naked)) | ||
100 | blk_dma_flush_range(unsigned long start, unsigned long end) | ||
101 | { | ||
102 | asm( | ||
103 | ".word 0xec401f0e @ mcrr p15, 0, r1, r0, c14, 0 @ blocking \n\ | ||
104 | mov pc, lr"); | ||
105 | } | ||
106 | |||
107 | static int blockops_trap(struct pt_regs *regs, unsigned int instr) | ||
108 | { | ||
109 | regs->ARM_r4 |= regs->ARM_r2; | ||
110 | regs->ARM_pc += 4; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static char *func[] = { | ||
115 | "Prefetch data range", | ||
116 | "Clean+Invalidate data range", | ||
117 | "Clean data range", | ||
118 | "Invalidate data range", | ||
119 | "Invalidate instr range" | ||
120 | }; | ||
121 | |||
122 | static struct undef_hook blockops_hook __initdata = { | ||
123 | .instr_mask = 0x0fffffd0, | ||
124 | .instr_val = 0x0c401f00, | ||
125 | .cpsr_mask = PSR_T_BIT, | ||
126 | .cpsr_val = 0, | ||
127 | .fn = blockops_trap, | ||
128 | }; | ||
129 | |||
130 | static int __init blockops_check(void) | ||
131 | { | ||
132 | register unsigned int err asm("r4") = 0; | ||
133 | unsigned int err_pos = 1; | ||
134 | unsigned int cache_type; | ||
135 | int i; | ||
136 | |||
137 | asm("mrc p15, 0, %0, c0, c0, 1" : "=r" (cache_type)); | ||
138 | |||
139 | printk("Checking V6 block cache operations:\n"); | ||
140 | register_undef_hook(&blockops_hook); | ||
141 | |||
142 | __asm__ ("mov r0, %0\n\t" | ||
143 | "mov r1, %1\n\t" | ||
144 | "mov r2, #1\n\t" | ||
145 | ".word 0xec401f2c @ mcrr p15, 0, r1, r0, c12, 2\n\t" | ||
146 | "mov r2, #2\n\t" | ||
147 | ".word 0xec401f0e @ mcrr p15, 0, r1, r0, c14, 0\n\t" | ||
148 | "mov r2, #4\n\t" | ||
149 | ".word 0xec401f0c @ mcrr p15, 0, r1, r0, c12, 0\n\t" | ||
150 | "mov r2, #8\n\t" | ||
151 | ".word 0xec401f06 @ mcrr p15, 0, r1, r0, c6, 0\n\t" | ||
152 | "mov r2, #16\n\t" | ||
153 | ".word 0xec401f05 @ mcrr p15, 0, r1, r0, c5, 0\n\t" | ||
154 | : | ||
155 | : "r" (PAGE_OFFSET), "r" (PAGE_OFFSET + 128) | ||
156 | : "r0", "r1", "r2"); | ||
157 | |||
158 | unregister_undef_hook(&blockops_hook); | ||
159 | |||
160 | for (i = 0; i < ARRAY_SIZE(func); i++, err_pos <<= 1) | ||
161 | printk("%30s: %ssupported\n", func[i], err & err_pos ? "not " : ""); | ||
162 | |||
163 | if ((err & 8) == 0) { | ||
164 | printk(" --> Using %s block cache invalidate\n", | ||
165 | cache_type & (1 << 24) ? "harvard" : "unified"); | ||
166 | if (cache_type & (1 << 24)) | ||
167 | cpu_cache.dma_inv_range = blk_dma_inv_range_harvard; | ||
168 | else | ||
169 | cpu_cache.dma_inv_range = blk_dma_inv_range_unified; | ||
170 | } | ||
171 | if ((err & 4) == 0) { | ||
172 | printk(" --> Using block cache clean\n"); | ||
173 | cpu_cache.dma_clean_range = blk_dma_clean_range; | ||
174 | } | ||
175 | if ((err & 2) == 0) { | ||
176 | printk(" --> Using block cache clean+invalidate\n"); | ||
177 | cpu_cache.dma_flush_range = blk_dma_flush_range; | ||
178 | cpu_cache.flush_kern_dcache_page = blk_flush_kern_dcache_page; | ||
179 | } | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | __initcall(blockops_check); | ||
diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S new file mode 100644 index 000000000000..e1994788cf0e --- /dev/null +++ b/arch/arm/mm/cache-v3.S | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/cache-v3.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell king | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/linkage.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <asm/hardware.h> | ||
13 | #include <asm/page.h> | ||
14 | #include "proc-macros.S" | ||
15 | |||
16 | /* | ||
17 | * flush_user_cache_all() | ||
18 | * | ||
19 | * Invalidate all cache entries in a particular address | ||
20 | * space. | ||
21 | * | ||
22 | * - mm - mm_struct describing address space | ||
23 | */ | ||
24 | ENTRY(v3_flush_user_cache_all) | ||
25 | /* FALLTHROUGH */ | ||
26 | /* | ||
27 | * flush_kern_cache_all() | ||
28 | * | ||
29 | * Clean and invalidate the entire cache. | ||
30 | */ | ||
31 | ENTRY(v3_flush_kern_cache_all) | ||
32 | /* FALLTHROUGH */ | ||
33 | |||
34 | /* | ||
35 | * flush_user_cache_range(start, end, flags) | ||
36 | * | ||
37 | * Invalidate a range of cache entries in the specified | ||
38 | * address space. | ||
39 | * | ||
40 | * - start - start address (may not be aligned) | ||
41 | * - end - end address (exclusive, may not be aligned) | ||
42 | * - flags - vma_area_struct flags describing address space | ||
43 | */ | ||
44 | ENTRY(v3_flush_user_cache_range) | ||
45 | mov ip, #0 | ||
46 | mcreq p15, 0, ip, c7, c0, 0 @ flush ID cache | ||
47 | mov pc, lr | ||
48 | |||
49 | /* | ||
50 | * coherent_kern_range(start, end) | ||
51 | * | ||
52 | * Ensure coherency between the Icache and the Dcache in the | ||
53 | * region described by start. If you have non-snooping | ||
54 | * Harvard caches, you need to implement this function. | ||
55 | * | ||
56 | * - start - virtual start address | ||
57 | * - end - virtual end address | ||
58 | */ | ||
59 | ENTRY(v3_coherent_kern_range) | ||
60 | /* FALLTHROUGH */ | ||
61 | |||
62 | /* | ||
63 | * coherent_user_range(start, end) | ||
64 | * | ||
65 | * Ensure coherency between the Icache and the Dcache in the | ||
66 | * region described by start. If you have non-snooping | ||
67 | * Harvard caches, you need to implement this function. | ||
68 | * | ||
69 | * - start - virtual start address | ||
70 | * - end - virtual end address | ||
71 | */ | ||
72 | ENTRY(v3_coherent_user_range) | ||
73 | mov pc, lr | ||
74 | |||
75 | /* | ||
76 | * flush_kern_dcache_page(void *page) | ||
77 | * | ||
78 | * Ensure no D cache aliasing occurs, either with itself or | ||
79 | * the I cache | ||
80 | * | ||
81 | * - addr - page aligned address | ||
82 | */ | ||
83 | ENTRY(v3_flush_kern_dcache_page) | ||
84 | /* FALLTHROUGH */ | ||
85 | |||
86 | /* | ||
87 | * dma_inv_range(start, end) | ||
88 | * | ||
89 | * Invalidate (discard) the specified virtual address range. | ||
90 | * May not write back any entries. If 'start' or 'end' | ||
91 | * are not cache line aligned, those lines must be written | ||
92 | * back. | ||
93 | * | ||
94 | * - start - virtual start address | ||
95 | * - end - virtual end address | ||
96 | */ | ||
97 | ENTRY(v3_dma_inv_range) | ||
98 | /* FALLTHROUGH */ | ||
99 | |||
100 | /* | ||
101 | * dma_flush_range(start, end) | ||
102 | * | ||
103 | * Clean and invalidate the specified virtual address range. | ||
104 | * | ||
105 | * - start - virtual start address | ||
106 | * - end - virtual end address | ||
107 | */ | ||
108 | ENTRY(v3_dma_flush_range) | ||
109 | mov r0, #0 | ||
110 | mcr p15, 0, r0, c7, c0, 0 @ flush ID cache | ||
111 | /* FALLTHROUGH */ | ||
112 | |||
113 | /* | ||
114 | * dma_clean_range(start, end) | ||
115 | * | ||
116 | * Clean (write back) the specified virtual address range. | ||
117 | * | ||
118 | * - start - virtual start address | ||
119 | * - end - virtual end address | ||
120 | */ | ||
121 | ENTRY(v3_dma_clean_range) | ||
122 | mov pc, lr | ||
123 | |||
124 | __INITDATA | ||
125 | |||
126 | .type v3_cache_fns, #object | ||
127 | ENTRY(v3_cache_fns) | ||
128 | .long v3_flush_kern_cache_all | ||
129 | .long v3_flush_user_cache_all | ||
130 | .long v3_flush_user_cache_range | ||
131 | .long v3_coherent_kern_range | ||
132 | .long v3_coherent_user_range | ||
133 | .long v3_flush_kern_dcache_page | ||
134 | .long v3_dma_inv_range | ||
135 | .long v3_dma_clean_range | ||
136 | .long v3_dma_flush_range | ||
137 | .size v3_cache_fns, . - v3_cache_fns | ||
diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S new file mode 100644 index 000000000000..b8ad5d58ebe2 --- /dev/null +++ b/arch/arm/mm/cache-v4.S | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/cache-v4.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell king | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/linkage.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <asm/hardware.h> | ||
13 | #include <asm/page.h> | ||
14 | #include "proc-macros.S" | ||
15 | |||
16 | /* | ||
17 | * flush_user_cache_all() | ||
18 | * | ||
19 | * Invalidate all cache entries in a particular address | ||
20 | * space. | ||
21 | * | ||
22 | * - mm - mm_struct describing address space | ||
23 | */ | ||
24 | ENTRY(v4_flush_user_cache_all) | ||
25 | /* FALLTHROUGH */ | ||
26 | /* | ||
27 | * flush_kern_cache_all() | ||
28 | * | ||
29 | * Clean and invalidate the entire cache. | ||
30 | */ | ||
31 | ENTRY(v4_flush_kern_cache_all) | ||
32 | mov r0, #0 | ||
33 | mcr p15, 0, r0, c7, c7, 0 @ flush ID cache | ||
34 | mov pc, lr | ||
35 | |||
36 | /* | ||
37 | * flush_user_cache_range(start, end, flags) | ||
38 | * | ||
39 | * Invalidate a range of cache entries in the specified | ||
40 | * address space. | ||
41 | * | ||
42 | * - start - start address (may not be aligned) | ||
43 | * - end - end address (exclusive, may not be aligned) | ||
44 | * - flags - vma_area_struct flags describing address space | ||
45 | */ | ||
46 | ENTRY(v4_flush_user_cache_range) | ||
47 | mov ip, #0 | ||
48 | mcreq p15, 0, ip, c7, c7, 0 @ flush ID cache | ||
49 | mov pc, lr | ||
50 | |||
51 | /* | ||
52 | * coherent_kern_range(start, end) | ||
53 | * | ||
54 | * Ensure coherency between the Icache and the Dcache in the | ||
55 | * region described by start. If you have non-snooping | ||
56 | * Harvard caches, you need to implement this function. | ||
57 | * | ||
58 | * - start - virtual start address | ||
59 | * - end - virtual end address | ||
60 | */ | ||
61 | ENTRY(v4_coherent_kern_range) | ||
62 | /* FALLTHROUGH */ | ||
63 | |||
64 | /* | ||
65 | * coherent_user_range(start, end) | ||
66 | * | ||
67 | * Ensure coherency between the Icache and the Dcache in the | ||
68 | * region described by start. If you have non-snooping | ||
69 | * Harvard caches, you need to implement this function. | ||
70 | * | ||
71 | * - start - virtual start address | ||
72 | * - end - virtual end address | ||
73 | */ | ||
74 | ENTRY(v4_coherent_user_range) | ||
75 | mov pc, lr | ||
76 | |||
77 | /* | ||
78 | * flush_kern_dcache_page(void *page) | ||
79 | * | ||
80 | * Ensure no D cache aliasing occurs, either with itself or | ||
81 | * the I cache | ||
82 | * | ||
83 | * - addr - page aligned address | ||
84 | */ | ||
85 | ENTRY(v4_flush_kern_dcache_page) | ||
86 | /* FALLTHROUGH */ | ||
87 | |||
88 | /* | ||
89 | * dma_inv_range(start, end) | ||
90 | * | ||
91 | * Invalidate (discard) the specified virtual address range. | ||
92 | * May not write back any entries. If 'start' or 'end' | ||
93 | * are not cache line aligned, those lines must be written | ||
94 | * back. | ||
95 | * | ||
96 | * - start - virtual start address | ||
97 | * - end - virtual end address | ||
98 | */ | ||
99 | ENTRY(v4_dma_inv_range) | ||
100 | /* FALLTHROUGH */ | ||
101 | |||
102 | /* | ||
103 | * dma_flush_range(start, end) | ||
104 | * | ||
105 | * Clean and invalidate the specified virtual address range. | ||
106 | * | ||
107 | * - start - virtual start address | ||
108 | * - end - virtual end address | ||
109 | */ | ||
110 | ENTRY(v4_dma_flush_range) | ||
111 | mov r0, #0 | ||
112 | mcr p15, 0, r0, c7, c7, 0 @ flush ID cache | ||
113 | /* FALLTHROUGH */ | ||
114 | |||
115 | /* | ||
116 | * dma_clean_range(start, end) | ||
117 | * | ||
118 | * Clean (write back) the specified virtual address range. | ||
119 | * | ||
120 | * - start - virtual start address | ||
121 | * - end - virtual end address | ||
122 | */ | ||
123 | ENTRY(v4_dma_clean_range) | ||
124 | mov pc, lr | ||
125 | |||
126 | __INITDATA | ||
127 | |||
128 | .type v4_cache_fns, #object | ||
129 | ENTRY(v4_cache_fns) | ||
130 | .long v4_flush_kern_cache_all | ||
131 | .long v4_flush_user_cache_all | ||
132 | .long v4_flush_user_cache_range | ||
133 | .long v4_coherent_kern_range | ||
134 | .long v4_coherent_user_range | ||
135 | .long v4_flush_kern_dcache_page | ||
136 | .long v4_dma_inv_range | ||
137 | .long v4_dma_clean_range | ||
138 | .long v4_dma_flush_range | ||
139 | .size v4_cache_fns, . - v4_cache_fns | ||
diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S new file mode 100644 index 000000000000..5c4055b62d97 --- /dev/null +++ b/arch/arm/mm/cache-v4wb.S | |||
@@ -0,0 +1,216 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/cache-v4wb.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell king | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/config.h> | ||
11 | #include <linux/linkage.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <asm/hardware.h> | ||
14 | #include <asm/page.h> | ||
15 | #include "proc-macros.S" | ||
16 | |||
17 | /* | ||
18 | * The size of one data cache line. | ||
19 | */ | ||
20 | #define CACHE_DLINESIZE 32 | ||
21 | |||
22 | /* | ||
23 | * The total size of the data cache. | ||
24 | */ | ||
25 | #if defined(CONFIG_CPU_SA110) | ||
26 | # define CACHE_DSIZE 16384 | ||
27 | #elif defined(CONFIG_CPU_SA1100) | ||
28 | # define CACHE_DSIZE 8192 | ||
29 | #else | ||
30 | # error Unknown cache size | ||
31 | #endif | ||
32 | |||
33 | /* | ||
34 | * This is the size at which it becomes more efficient to | ||
35 | * clean the whole cache, rather than using the individual | ||
36 | * cache line maintainence instructions. | ||
37 | * | ||
38 | * Size Clean (ticks) Dirty (ticks) | ||
39 | * 4096 21 20 21 53 55 54 | ||
40 | * 8192 40 41 40 106 100 102 | ||
41 | * 16384 77 77 76 140 140 138 | ||
42 | * 32768 150 149 150 214 216 212 <--- | ||
43 | * 65536 296 297 296 351 358 361 | ||
44 | * 131072 591 591 591 656 657 651 | ||
45 | * Whole 132 136 132 221 217 207 <--- | ||
46 | */ | ||
47 | #define CACHE_DLIMIT (CACHE_DSIZE * 4) | ||
48 | |||
49 | /* | ||
50 | * flush_user_cache_all() | ||
51 | * | ||
52 | * Clean and invalidate all cache entries in a particular address | ||
53 | * space. | ||
54 | */ | ||
55 | ENTRY(v4wb_flush_user_cache_all) | ||
56 | /* FALLTHROUGH */ | ||
57 | /* | ||
58 | * flush_kern_cache_all() | ||
59 | * | ||
60 | * Clean and invalidate the entire cache. | ||
61 | */ | ||
62 | ENTRY(v4wb_flush_kern_cache_all) | ||
63 | mov ip, #0 | ||
64 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
65 | __flush_whole_cache: | ||
66 | mov r0, #FLUSH_BASE | ||
67 | add r1, r0, #CACHE_DSIZE | ||
68 | 1: ldr r2, [r0], #32 | ||
69 | cmp r0, r1 | ||
70 | blo 1b | ||
71 | mcr p15, 0, ip, c7, c10, 4 @ drain write buffer | ||
72 | mov pc, lr | ||
73 | |||
74 | /* | ||
75 | * flush_user_cache_range(start, end, flags) | ||
76 | * | ||
77 | * Invalidate a range of cache entries in the specified | ||
78 | * address space. | ||
79 | * | ||
80 | * - start - start address (inclusive, page aligned) | ||
81 | * - end - end address (exclusive, page aligned) | ||
82 | * - flags - vma_area_struct flags describing address space | ||
83 | */ | ||
84 | ENTRY(v4wb_flush_user_cache_range) | ||
85 | sub r3, r1, r0 @ calculate total size | ||
86 | tst r2, #VM_EXEC @ executable region? | ||
87 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
88 | |||
89 | cmp r3, #CACHE_DLIMIT @ total size >= limit? | ||
90 | bhs __flush_whole_cache @ flush whole D cache | ||
91 | |||
92 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
93 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
94 | add r0, r0, #CACHE_DLINESIZE | ||
95 | cmp r0, r1 | ||
96 | blo 1b | ||
97 | tst r2, #VM_EXEC | ||
98 | mcrne p15, 0, ip, c7, c10, 4 @ drain write buffer | ||
99 | mov pc, lr | ||
100 | |||
101 | /* | ||
102 | * flush_kern_dcache_page(void *page) | ||
103 | * | ||
104 | * Ensure no D cache aliasing occurs, either with itself or | ||
105 | * the I cache | ||
106 | * | ||
107 | * - addr - page aligned address | ||
108 | */ | ||
109 | ENTRY(v4wb_flush_kern_dcache_page) | ||
110 | add r1, r0, #PAGE_SZ | ||
111 | /* fall through */ | ||
112 | |||
113 | /* | ||
114 | * coherent_kern_range(start, end) | ||
115 | * | ||
116 | * Ensure coherency between the Icache and the Dcache in the | ||
117 | * region described by start. If you have non-snooping | ||
118 | * Harvard caches, you need to implement this function. | ||
119 | * | ||
120 | * - start - virtual start address | ||
121 | * - end - virtual end address | ||
122 | */ | ||
123 | ENTRY(v4wb_coherent_kern_range) | ||
124 | /* fall through */ | ||
125 | |||
126 | /* | ||
127 | * coherent_user_range(start, end) | ||
128 | * | ||
129 | * Ensure coherency between the Icache and the Dcache in the | ||
130 | * region described by start. If you have non-snooping | ||
131 | * Harvard caches, you need to implement this function. | ||
132 | * | ||
133 | * - start - virtual start address | ||
134 | * - end - virtual end address | ||
135 | */ | ||
136 | ENTRY(v4wb_coherent_user_range) | ||
137 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
138 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
139 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
140 | add r0, r0, #CACHE_DLINESIZE | ||
141 | cmp r0, r1 | ||
142 | blo 1b | ||
143 | mov ip, #0 | ||
144 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
145 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
146 | mov pc, lr | ||
147 | |||
148 | |||
149 | /* | ||
150 | * dma_inv_range(start, end) | ||
151 | * | ||
152 | * Invalidate (discard) the specified virtual address range. | ||
153 | * May not write back any entries. If 'start' or 'end' | ||
154 | * are not cache line aligned, those lines must be written | ||
155 | * back. | ||
156 | * | ||
157 | * - start - virtual start address | ||
158 | * - end - virtual end address | ||
159 | */ | ||
160 | ENTRY(v4wb_dma_inv_range) | ||
161 | tst r0, #CACHE_DLINESIZE - 1 | ||
162 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
163 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
164 | tst r1, #CACHE_DLINESIZE - 1 | ||
165 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
166 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
167 | add r0, r0, #CACHE_DLINESIZE | ||
168 | cmp r0, r1 | ||
169 | blo 1b | ||
170 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
171 | mov pc, lr | ||
172 | |||
173 | /* | ||
174 | * dma_clean_range(start, end) | ||
175 | * | ||
176 | * Clean (write back) the specified virtual address range. | ||
177 | * | ||
178 | * - start - virtual start address | ||
179 | * - end - virtual end address | ||
180 | */ | ||
181 | ENTRY(v4wb_dma_clean_range) | ||
182 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
183 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
184 | add r0, r0, #CACHE_DLINESIZE | ||
185 | cmp r0, r1 | ||
186 | blo 1b | ||
187 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
188 | mov pc, lr | ||
189 | |||
190 | /* | ||
191 | * dma_flush_range(start, end) | ||
192 | * | ||
193 | * Clean and invalidate the specified virtual address range. | ||
194 | * | ||
195 | * - start - virtual start address | ||
196 | * - end - virtual end address | ||
197 | * | ||
198 | * This is actually the same as v4wb_coherent_kern_range() | ||
199 | */ | ||
200 | .globl v4wb_dma_flush_range | ||
201 | .set v4wb_dma_flush_range, v4wb_coherent_kern_range | ||
202 | |||
203 | __INITDATA | ||
204 | |||
205 | .type v4wb_cache_fns, #object | ||
206 | ENTRY(v4wb_cache_fns) | ||
207 | .long v4wb_flush_kern_cache_all | ||
208 | .long v4wb_flush_user_cache_all | ||
209 | .long v4wb_flush_user_cache_range | ||
210 | .long v4wb_coherent_kern_range | ||
211 | .long v4wb_coherent_user_range | ||
212 | .long v4wb_flush_kern_dcache_page | ||
213 | .long v4wb_dma_inv_range | ||
214 | .long v4wb_dma_clean_range | ||
215 | .long v4wb_dma_flush_range | ||
216 | .size v4wb_cache_fns, . - v4wb_cache_fns | ||
diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S new file mode 100644 index 000000000000..9bcabd86c6f3 --- /dev/null +++ b/arch/arm/mm/cache-v4wt.S | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/cache-v4wt.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell king | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARMv4 write through cache operations support. | ||
11 | * | ||
12 | * We assume that the write buffer is not enabled. | ||
13 | */ | ||
14 | #include <linux/linkage.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <asm/hardware.h> | ||
17 | #include <asm/page.h> | ||
18 | #include "proc-macros.S" | ||
19 | |||
20 | /* | ||
21 | * The size of one data cache line. | ||
22 | */ | ||
23 | #define CACHE_DLINESIZE 32 | ||
24 | |||
25 | /* | ||
26 | * The number of data cache segments. | ||
27 | */ | ||
28 | #define CACHE_DSEGMENTS 8 | ||
29 | |||
30 | /* | ||
31 | * The number of lines in a cache segment. | ||
32 | */ | ||
33 | #define CACHE_DENTRIES 64 | ||
34 | |||
35 | /* | ||
36 | * This is the size at which it becomes more efficient to | ||
37 | * clean the whole cache, rather than using the individual | ||
38 | * cache line maintainence instructions. | ||
39 | * | ||
40 | * *** This needs benchmarking | ||
41 | */ | ||
42 | #define CACHE_DLIMIT 16384 | ||
43 | |||
44 | /* | ||
45 | * flush_user_cache_all() | ||
46 | * | ||
47 | * Invalidate all cache entries in a particular address | ||
48 | * space. | ||
49 | */ | ||
50 | ENTRY(v4wt_flush_user_cache_all) | ||
51 | /* FALLTHROUGH */ | ||
52 | /* | ||
53 | * flush_kern_cache_all() | ||
54 | * | ||
55 | * Clean and invalidate the entire cache. | ||
56 | */ | ||
57 | ENTRY(v4wt_flush_kern_cache_all) | ||
58 | mov r2, #VM_EXEC | ||
59 | mov ip, #0 | ||
60 | __flush_whole_cache: | ||
61 | tst r2, #VM_EXEC | ||
62 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
63 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
64 | mov pc, lr | ||
65 | |||
66 | /* | ||
67 | * flush_user_cache_range(start, end, flags) | ||
68 | * | ||
69 | * Clean and invalidate a range of cache entries in the specified | ||
70 | * address space. | ||
71 | * | ||
72 | * - start - start address (inclusive, page aligned) | ||
73 | * - end - end address (exclusive, page aligned) | ||
74 | * - flags - vma_area_struct flags describing address space | ||
75 | */ | ||
76 | ENTRY(v4wt_flush_user_cache_range) | ||
77 | sub r3, r1, r0 @ calculate total size | ||
78 | cmp r3, #CACHE_DLIMIT | ||
79 | bhs __flush_whole_cache | ||
80 | |||
81 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
82 | tst r2, #VM_EXEC | ||
83 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
84 | add r0, r0, #CACHE_DLINESIZE | ||
85 | cmp r0, r1 | ||
86 | blo 1b | ||
87 | mov pc, lr | ||
88 | |||
89 | /* | ||
90 | * coherent_kern_range(start, end) | ||
91 | * | ||
92 | * Ensure coherency between the Icache and the Dcache in the | ||
93 | * region described by start. If you have non-snooping | ||
94 | * Harvard caches, you need to implement this function. | ||
95 | * | ||
96 | * - start - virtual start address | ||
97 | * - end - virtual end address | ||
98 | */ | ||
99 | ENTRY(v4wt_coherent_kern_range) | ||
100 | /* FALLTRHOUGH */ | ||
101 | |||
102 | /* | ||
103 | * coherent_user_range(start, end) | ||
104 | * | ||
105 | * Ensure coherency between the Icache and the Dcache in the | ||
106 | * region described by start. If you have non-snooping | ||
107 | * Harvard caches, you need to implement this function. | ||
108 | * | ||
109 | * - start - virtual start address | ||
110 | * - end - virtual end address | ||
111 | */ | ||
112 | ENTRY(v4wt_coherent_user_range) | ||
113 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
114 | 1: mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
115 | add r0, r0, #CACHE_DLINESIZE | ||
116 | cmp r0, r1 | ||
117 | blo 1b | ||
118 | mov pc, lr | ||
119 | |||
120 | /* | ||
121 | * flush_kern_dcache_page(void *page) | ||
122 | * | ||
123 | * Ensure no D cache aliasing occurs, either with itself or | ||
124 | * the I cache | ||
125 | * | ||
126 | * - addr - page aligned address | ||
127 | */ | ||
128 | ENTRY(v4wt_flush_kern_dcache_page) | ||
129 | mov r2, #0 | ||
130 | mcr p15, 0, r2, c7, c5, 0 @ invalidate I cache | ||
131 | add r1, r0, #PAGE_SZ | ||
132 | /* fallthrough */ | ||
133 | |||
134 | /* | ||
135 | * dma_inv_range(start, end) | ||
136 | * | ||
137 | * Invalidate (discard) the specified virtual address range. | ||
138 | * May not write back any entries. If 'start' or 'end' | ||
139 | * are not cache line aligned, those lines must be written | ||
140 | * back. | ||
141 | * | ||
142 | * - start - virtual start address | ||
143 | * - end - virtual end address | ||
144 | */ | ||
145 | ENTRY(v4wt_dma_inv_range) | ||
146 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
147 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
148 | add r0, r0, #CACHE_DLINESIZE | ||
149 | cmp r0, r1 | ||
150 | blo 1b | ||
151 | /* FALLTHROUGH */ | ||
152 | |||
153 | /* | ||
154 | * dma_clean_range(start, end) | ||
155 | * | ||
156 | * Clean the specified virtual address range. | ||
157 | * | ||
158 | * - start - virtual start address | ||
159 | * - end - virtual end address | ||
160 | */ | ||
161 | ENTRY(v4wt_dma_clean_range) | ||
162 | mov pc, lr | ||
163 | |||
164 | /* | ||
165 | * dma_flush_range(start, end) | ||
166 | * | ||
167 | * Clean and invalidate the specified virtual address range. | ||
168 | * | ||
169 | * - start - virtual start address | ||
170 | * - end - virtual end address | ||
171 | */ | ||
172 | .globl v4wt_dma_flush_range | ||
173 | .equ v4wt_dma_flush_range, v4wt_dma_inv_range | ||
174 | |||
175 | __INITDATA | ||
176 | |||
177 | .type v4wt_cache_fns, #object | ||
178 | ENTRY(v4wt_cache_fns) | ||
179 | .long v4wt_flush_kern_cache_all | ||
180 | .long v4wt_flush_user_cache_all | ||
181 | .long v4wt_flush_user_cache_range | ||
182 | .long v4wt_coherent_kern_range | ||
183 | .long v4wt_coherent_user_range | ||
184 | .long v4wt_flush_kern_dcache_page | ||
185 | .long v4wt_dma_inv_range | ||
186 | .long v4wt_dma_clean_range | ||
187 | .long v4wt_dma_flush_range | ||
188 | .size v4wt_cache_fns, . - v4wt_cache_fns | ||
diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S new file mode 100644 index 000000000000..85c10a71e7c6 --- /dev/null +++ b/arch/arm/mm/cache-v6.S | |||
@@ -0,0 +1,227 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/cache-v6.S | ||
3 | * | ||
4 | * Copyright (C) 2001 Deep Blue Solutions Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This is the "shell" of the ARMv6 processor support. | ||
11 | */ | ||
12 | #include <linux/linkage.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <asm/assembler.h> | ||
15 | |||
16 | #include "proc-macros.S" | ||
17 | |||
18 | #define HARVARD_CACHE | ||
19 | #define CACHE_LINE_SIZE 32 | ||
20 | #define D_CACHE_LINE_SIZE 32 | ||
21 | |||
22 | /* | ||
23 | * v6_flush_cache_all() | ||
24 | * | ||
25 | * Flush the entire cache. | ||
26 | * | ||
27 | * It is assumed that: | ||
28 | */ | ||
29 | ENTRY(v6_flush_kern_cache_all) | ||
30 | mov r0, #0 | ||
31 | #ifdef HARVARD_CACHE | ||
32 | mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate | ||
33 | mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate | ||
34 | #else | ||
35 | mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate | ||
36 | #endif | ||
37 | mov pc, lr | ||
38 | |||
39 | /* | ||
40 | * v6_flush_cache_all() | ||
41 | * | ||
42 | * Flush all TLB entries in a particular address space | ||
43 | * | ||
44 | * - mm - mm_struct describing address space | ||
45 | */ | ||
46 | ENTRY(v6_flush_user_cache_all) | ||
47 | /*FALLTHROUGH*/ | ||
48 | |||
49 | /* | ||
50 | * v6_flush_cache_range(start, end, flags) | ||
51 | * | ||
52 | * Flush a range of TLB entries in the specified address space. | ||
53 | * | ||
54 | * - start - start address (may not be aligned) | ||
55 | * - end - end address (exclusive, may not be aligned) | ||
56 | * - flags - vm_area_struct flags describing address space | ||
57 | * | ||
58 | * It is assumed that: | ||
59 | * - we have a VIPT cache. | ||
60 | */ | ||
61 | ENTRY(v6_flush_user_cache_range) | ||
62 | mov pc, lr | ||
63 | |||
64 | /* | ||
65 | * v6_coherent_kern_range(start,end) | ||
66 | * | ||
67 | * Ensure that the I and D caches are coherent within specified | ||
68 | * region. This is typically used when code has been written to | ||
69 | * a memory region, and will be executed. | ||
70 | * | ||
71 | * - start - virtual start address of region | ||
72 | * - end - virtual end address of region | ||
73 | * | ||
74 | * It is assumed that: | ||
75 | * - the Icache does not read data from the write buffer | ||
76 | */ | ||
77 | ENTRY(v6_coherent_kern_range) | ||
78 | /* FALLTHROUGH */ | ||
79 | |||
80 | /* | ||
81 | * v6_coherent_user_range(start,end) | ||
82 | * | ||
83 | * Ensure that the I and D caches are coherent within specified | ||
84 | * region. This is typically used when code has been written to | ||
85 | * a memory region, and will be executed. | ||
86 | * | ||
87 | * - start - virtual start address of region | ||
88 | * - end - virtual end address of region | ||
89 | * | ||
90 | * It is assumed that: | ||
91 | * - the Icache does not read data from the write buffer | ||
92 | */ | ||
93 | ENTRY(v6_coherent_user_range) | ||
94 | bic r0, r0, #CACHE_LINE_SIZE - 1 | ||
95 | 1: | ||
96 | #ifdef HARVARD_CACHE | ||
97 | mcr p15, 0, r0, c7, c10, 1 @ clean D line | ||
98 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I line | ||
99 | #endif | ||
100 | mcr p15, 0, r0, c7, c5, 7 @ invalidate BTB entry | ||
101 | add r0, r0, #CACHE_LINE_SIZE | ||
102 | cmp r0, r1 | ||
103 | blo 1b | ||
104 | #ifdef HARVARD_CACHE | ||
105 | mov r0, #0 | ||
106 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
107 | #endif | ||
108 | mov pc, lr | ||
109 | |||
110 | /* | ||
111 | * v6_flush_kern_dcache_page(kaddr) | ||
112 | * | ||
113 | * Ensure that the data held in the page kaddr is written back | ||
114 | * to the page in question. | ||
115 | * | ||
116 | * - kaddr - kernel address (guaranteed to be page aligned) | ||
117 | */ | ||
118 | ENTRY(v6_flush_kern_dcache_page) | ||
119 | add r1, r0, #PAGE_SZ | ||
120 | 1: | ||
121 | #ifdef HARVARD_CACHE | ||
122 | mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line | ||
123 | #else | ||
124 | mcr p15, 0, r0, c7, c15, 1 @ clean & invalidate unified line | ||
125 | #endif | ||
126 | add r0, r0, #D_CACHE_LINE_SIZE | ||
127 | cmp r0, r1 | ||
128 | blo 1b | ||
129 | #ifdef HARVARD_CACHE | ||
130 | mov r0, #0 | ||
131 | mcr p15, 0, r0, c7, c10, 4 | ||
132 | #endif | ||
133 | mov pc, lr | ||
134 | |||
135 | |||
136 | /* | ||
137 | * v6_dma_inv_range(start,end) | ||
138 | * | ||
139 | * Invalidate the data cache within the specified region; we will | ||
140 | * be performing a DMA operation in this region and we want to | ||
141 | * purge old data in the cache. | ||
142 | * | ||
143 | * - start - virtual start address of region | ||
144 | * - end - virtual end address of region | ||
145 | */ | ||
146 | ENTRY(v6_dma_inv_range) | ||
147 | tst r0, #D_CACHE_LINE_SIZE - 1 | ||
148 | bic r0, r0, #D_CACHE_LINE_SIZE - 1 | ||
149 | #ifdef HARVARD_CACHE | ||
150 | mcrne p15, 0, r0, c7, c10, 1 @ clean D line | ||
151 | #else | ||
152 | mcrne p15, 0, r0, c7, c11, 1 @ clean unified line | ||
153 | #endif | ||
154 | tst r1, #D_CACHE_LINE_SIZE - 1 | ||
155 | bic r1, r1, #D_CACHE_LINE_SIZE - 1 | ||
156 | #ifdef HARVARD_CACHE | ||
157 | mcrne p15, 0, r1, c7, c14, 1 @ clean & invalidate D line | ||
158 | #else | ||
159 | mcrne p15, 0, r1, c7, c15, 1 @ clean & invalidate unified line | ||
160 | #endif | ||
161 | 1: | ||
162 | #ifdef HARVARD_CACHE | ||
163 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D line | ||
164 | #else | ||
165 | mcr p15, 0, r0, c7, c7, 1 @ invalidate unified line | ||
166 | #endif | ||
167 | add r0, r0, #D_CACHE_LINE_SIZE | ||
168 | cmp r0, r1 | ||
169 | blo 1b | ||
170 | mov r0, #0 | ||
171 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
172 | mov pc, lr | ||
173 | |||
174 | /* | ||
175 | * v6_dma_clean_range(start,end) | ||
176 | * - start - virtual start address of region | ||
177 | * - end - virtual end address of region | ||
178 | */ | ||
179 | ENTRY(v6_dma_clean_range) | ||
180 | bic r0, r0, #D_CACHE_LINE_SIZE - 1 | ||
181 | 1: | ||
182 | #ifdef HARVARD_CACHE | ||
183 | mcr p15, 0, r0, c7, c10, 1 @ clean D line | ||
184 | #else | ||
185 | mcr p15, 0, r0, c7, c11, 1 @ clean unified line | ||
186 | #endif | ||
187 | add r0, r0, #D_CACHE_LINE_SIZE | ||
188 | cmp r0, r1 | ||
189 | blo 1b | ||
190 | mov r0, #0 | ||
191 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
192 | mov pc, lr | ||
193 | |||
194 | /* | ||
195 | * v6_dma_flush_range(start,end) | ||
196 | * - start - virtual start address of region | ||
197 | * - end - virtual end address of region | ||
198 | */ | ||
199 | ENTRY(v6_dma_flush_range) | ||
200 | bic r0, r0, #D_CACHE_LINE_SIZE - 1 | ||
201 | 1: | ||
202 | #ifdef HARVARD_CACHE | ||
203 | mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line | ||
204 | #else | ||
205 | mcr p15, 0, r0, c7, c15, 1 @ clean & invalidate line | ||
206 | #endif | ||
207 | add r0, r0, #D_CACHE_LINE_SIZE | ||
208 | cmp r0, r1 | ||
209 | blo 1b | ||
210 | mov r0, #0 | ||
211 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
212 | mov pc, lr | ||
213 | |||
214 | __INITDATA | ||
215 | |||
216 | .type v6_cache_fns, #object | ||
217 | ENTRY(v6_cache_fns) | ||
218 | .long v6_flush_kern_cache_all | ||
219 | .long v6_flush_user_cache_all | ||
220 | .long v6_flush_user_cache_range | ||
221 | .long v6_coherent_kern_range | ||
222 | .long v6_coherent_user_range | ||
223 | .long v6_flush_kern_dcache_page | ||
224 | .long v6_dma_inv_range | ||
225 | .long v6_dma_clean_range | ||
226 | .long v6_dma_flush_range | ||
227 | .size v6_cache_fns, . - v6_cache_fns | ||
diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c new file mode 100644 index 000000000000..26356ce4da54 --- /dev/null +++ b/arch/arm/mm/consistent.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/consistent.c | ||
3 | * | ||
4 | * Copyright (C) 2000-2004 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * DMA uncached mapping support. | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/list.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/dma-mapping.h> | ||
20 | |||
21 | #include <asm/cacheflush.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/tlbflush.h> | ||
24 | |||
25 | #define CONSISTENT_BASE (0xffc00000) | ||
26 | #define CONSISTENT_END (0xffe00000) | ||
27 | #define CONSISTENT_OFFSET(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PAGE_SHIFT) | ||
28 | |||
29 | /* | ||
30 | * This is the page table (2MB) covering uncached, DMA consistent allocations | ||
31 | */ | ||
32 | static pte_t *consistent_pte; | ||
33 | static DEFINE_SPINLOCK(consistent_lock); | ||
34 | |||
35 | /* | ||
36 | * VM region handling support. | ||
37 | * | ||
38 | * This should become something generic, handling VM region allocations for | ||
39 | * vmalloc and similar (ioremap, module space, etc). | ||
40 | * | ||
41 | * I envisage vmalloc()'s supporting vm_struct becoming: | ||
42 | * | ||
43 | * struct vm_struct { | ||
44 | * struct vm_region region; | ||
45 | * unsigned long flags; | ||
46 | * struct page **pages; | ||
47 | * unsigned int nr_pages; | ||
48 | * unsigned long phys_addr; | ||
49 | * }; | ||
50 | * | ||
51 | * get_vm_area() would then call vm_region_alloc with an appropriate | ||
52 | * struct vm_region head (eg): | ||
53 | * | ||
54 | * struct vm_region vmalloc_head = { | ||
55 | * .vm_list = LIST_HEAD_INIT(vmalloc_head.vm_list), | ||
56 | * .vm_start = VMALLOC_START, | ||
57 | * .vm_end = VMALLOC_END, | ||
58 | * }; | ||
59 | * | ||
60 | * However, vmalloc_head.vm_start is variable (typically, it is dependent on | ||
61 | * the amount of RAM found at boot time.) I would imagine that get_vm_area() | ||
62 | * would have to initialise this each time prior to calling vm_region_alloc(). | ||
63 | */ | ||
64 | struct vm_region { | ||
65 | struct list_head vm_list; | ||
66 | unsigned long vm_start; | ||
67 | unsigned long vm_end; | ||
68 | struct page *vm_pages; | ||
69 | }; | ||
70 | |||
71 | static struct vm_region consistent_head = { | ||
72 | .vm_list = LIST_HEAD_INIT(consistent_head.vm_list), | ||
73 | .vm_start = CONSISTENT_BASE, | ||
74 | .vm_end = CONSISTENT_END, | ||
75 | }; | ||
76 | |||
77 | static struct vm_region * | ||
78 | vm_region_alloc(struct vm_region *head, size_t size, int gfp) | ||
79 | { | ||
80 | unsigned long addr = head->vm_start, end = head->vm_end - size; | ||
81 | unsigned long flags; | ||
82 | struct vm_region *c, *new; | ||
83 | |||
84 | new = kmalloc(sizeof(struct vm_region), gfp); | ||
85 | if (!new) | ||
86 | goto out; | ||
87 | |||
88 | spin_lock_irqsave(&consistent_lock, flags); | ||
89 | |||
90 | list_for_each_entry(c, &head->vm_list, vm_list) { | ||
91 | if ((addr + size) < addr) | ||
92 | goto nospc; | ||
93 | if ((addr + size) <= c->vm_start) | ||
94 | goto found; | ||
95 | addr = c->vm_end; | ||
96 | if (addr > end) | ||
97 | goto nospc; | ||
98 | } | ||
99 | |||
100 | found: | ||
101 | /* | ||
102 | * Insert this entry _before_ the one we found. | ||
103 | */ | ||
104 | list_add_tail(&new->vm_list, &c->vm_list); | ||
105 | new->vm_start = addr; | ||
106 | new->vm_end = addr + size; | ||
107 | |||
108 | spin_unlock_irqrestore(&consistent_lock, flags); | ||
109 | return new; | ||
110 | |||
111 | nospc: | ||
112 | spin_unlock_irqrestore(&consistent_lock, flags); | ||
113 | kfree(new); | ||
114 | out: | ||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | static struct vm_region *vm_region_find(struct vm_region *head, unsigned long addr) | ||
119 | { | ||
120 | struct vm_region *c; | ||
121 | |||
122 | list_for_each_entry(c, &head->vm_list, vm_list) { | ||
123 | if (c->vm_start == addr) | ||
124 | goto out; | ||
125 | } | ||
126 | c = NULL; | ||
127 | out: | ||
128 | return c; | ||
129 | } | ||
130 | |||
131 | #ifdef CONFIG_HUGETLB_PAGE | ||
132 | #error ARM Coherent DMA allocator does not (yet) support huge TLB | ||
133 | #endif | ||
134 | |||
135 | static void * | ||
136 | __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, int gfp, | ||
137 | pgprot_t prot) | ||
138 | { | ||
139 | struct page *page; | ||
140 | struct vm_region *c; | ||
141 | unsigned long order; | ||
142 | u64 mask = ISA_DMA_THRESHOLD, limit; | ||
143 | |||
144 | if (!consistent_pte) { | ||
145 | printk(KERN_ERR "%s: not initialised\n", __func__); | ||
146 | dump_stack(); | ||
147 | return NULL; | ||
148 | } | ||
149 | |||
150 | if (dev) { | ||
151 | mask = dev->coherent_dma_mask; | ||
152 | |||
153 | /* | ||
154 | * Sanity check the DMA mask - it must be non-zero, and | ||
155 | * must be able to be satisfied by a DMA allocation. | ||
156 | */ | ||
157 | if (mask == 0) { | ||
158 | dev_warn(dev, "coherent DMA mask is unset\n"); | ||
159 | goto no_page; | ||
160 | } | ||
161 | |||
162 | if ((~mask) & ISA_DMA_THRESHOLD) { | ||
163 | dev_warn(dev, "coherent DMA mask %#llx is smaller " | ||
164 | "than system GFP_DMA mask %#llx\n", | ||
165 | mask, (unsigned long long)ISA_DMA_THRESHOLD); | ||
166 | goto no_page; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Sanity check the allocation size. | ||
172 | */ | ||
173 | size = PAGE_ALIGN(size); | ||
174 | limit = (mask + 1) & ~mask; | ||
175 | if ((limit && size >= limit) || | ||
176 | size >= (CONSISTENT_END - CONSISTENT_BASE)) { | ||
177 | printk(KERN_WARNING "coherent allocation too big " | ||
178 | "(requested %#x mask %#llx)\n", size, mask); | ||
179 | goto no_page; | ||
180 | } | ||
181 | |||
182 | order = get_order(size); | ||
183 | |||
184 | if (mask != 0xffffffff) | ||
185 | gfp |= GFP_DMA; | ||
186 | |||
187 | page = alloc_pages(gfp, order); | ||
188 | if (!page) | ||
189 | goto no_page; | ||
190 | |||
191 | /* | ||
192 | * Invalidate any data that might be lurking in the | ||
193 | * kernel direct-mapped region for device DMA. | ||
194 | */ | ||
195 | { | ||
196 | unsigned long kaddr = (unsigned long)page_address(page); | ||
197 | memset(page_address(page), 0, size); | ||
198 | dmac_flush_range(kaddr, kaddr + size); | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Allocate a virtual address in the consistent mapping region. | ||
203 | */ | ||
204 | c = vm_region_alloc(&consistent_head, size, | ||
205 | gfp & ~(__GFP_DMA | __GFP_HIGHMEM)); | ||
206 | if (c) { | ||
207 | pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); | ||
208 | struct page *end = page + (1 << order); | ||
209 | |||
210 | c->vm_pages = page; | ||
211 | |||
212 | /* | ||
213 | * Set the "dma handle" | ||
214 | */ | ||
215 | *handle = page_to_dma(dev, page); | ||
216 | |||
217 | do { | ||
218 | BUG_ON(!pte_none(*pte)); | ||
219 | |||
220 | set_page_count(page, 1); | ||
221 | /* | ||
222 | * x86 does not mark the pages reserved... | ||
223 | */ | ||
224 | SetPageReserved(page); | ||
225 | set_pte(pte, mk_pte(page, prot)); | ||
226 | page++; | ||
227 | pte++; | ||
228 | } while (size -= PAGE_SIZE); | ||
229 | |||
230 | /* | ||
231 | * Free the otherwise unused pages. | ||
232 | */ | ||
233 | while (page < end) { | ||
234 | set_page_count(page, 1); | ||
235 | __free_page(page); | ||
236 | page++; | ||
237 | } | ||
238 | |||
239 | return (void *)c->vm_start; | ||
240 | } | ||
241 | |||
242 | if (page) | ||
243 | __free_pages(page, order); | ||
244 | no_page: | ||
245 | *handle = ~0; | ||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Allocate DMA-coherent memory space and return both the kernel remapped | ||
251 | * virtual and bus address for that space. | ||
252 | */ | ||
253 | void * | ||
254 | dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp) | ||
255 | { | ||
256 | return __dma_alloc(dev, size, handle, gfp, | ||
257 | pgprot_noncached(pgprot_kernel)); | ||
258 | } | ||
259 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
260 | |||
261 | /* | ||
262 | * Allocate a writecombining region, in much the same way as | ||
263 | * dma_alloc_coherent above. | ||
264 | */ | ||
265 | void * | ||
266 | dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, int gfp) | ||
267 | { | ||
268 | return __dma_alloc(dev, size, handle, gfp, | ||
269 | pgprot_writecombine(pgprot_kernel)); | ||
270 | } | ||
271 | EXPORT_SYMBOL(dma_alloc_writecombine); | ||
272 | |||
273 | static int dma_mmap(struct device *dev, struct vm_area_struct *vma, | ||
274 | void *cpu_addr, dma_addr_t dma_addr, size_t size) | ||
275 | { | ||
276 | unsigned long flags, user_size, kern_size; | ||
277 | struct vm_region *c; | ||
278 | int ret = -ENXIO; | ||
279 | |||
280 | user_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
281 | |||
282 | spin_lock_irqsave(&consistent_lock, flags); | ||
283 | c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); | ||
284 | spin_unlock_irqrestore(&consistent_lock, flags); | ||
285 | |||
286 | if (c) { | ||
287 | unsigned long off = vma->vm_pgoff; | ||
288 | |||
289 | kern_size = (c->vm_end - c->vm_start) >> PAGE_SHIFT; | ||
290 | |||
291 | if (off < kern_size && | ||
292 | user_size <= (kern_size - off)) { | ||
293 | vma->vm_flags |= VM_RESERVED; | ||
294 | ret = remap_pfn_range(vma, vma->vm_start, | ||
295 | page_to_pfn(c->vm_pages) + off, | ||
296 | user_size << PAGE_SHIFT, | ||
297 | vma->vm_page_prot); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | int dma_mmap_coherent(struct device *dev, struct vm_area_struct *vma, | ||
305 | void *cpu_addr, dma_addr_t dma_addr, size_t size) | ||
306 | { | ||
307 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
308 | return dma_mmap(dev, vma, cpu_addr, dma_addr, size); | ||
309 | } | ||
310 | EXPORT_SYMBOL(dma_mmap_coherent); | ||
311 | |||
312 | int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, | ||
313 | void *cpu_addr, dma_addr_t dma_addr, size_t size) | ||
314 | { | ||
315 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | ||
316 | return dma_mmap(dev, vma, cpu_addr, dma_addr, size); | ||
317 | } | ||
318 | EXPORT_SYMBOL(dma_mmap_writecombine); | ||
319 | |||
320 | /* | ||
321 | * free a page as defined by the above mapping. | ||
322 | */ | ||
323 | void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle) | ||
324 | { | ||
325 | struct vm_region *c; | ||
326 | unsigned long flags, addr; | ||
327 | pte_t *ptep; | ||
328 | |||
329 | size = PAGE_ALIGN(size); | ||
330 | |||
331 | spin_lock_irqsave(&consistent_lock, flags); | ||
332 | |||
333 | c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); | ||
334 | if (!c) | ||
335 | goto no_area; | ||
336 | |||
337 | if ((c->vm_end - c->vm_start) != size) { | ||
338 | printk(KERN_ERR "%s: freeing wrong coherent size (%ld != %d)\n", | ||
339 | __func__, c->vm_end - c->vm_start, size); | ||
340 | dump_stack(); | ||
341 | size = c->vm_end - c->vm_start; | ||
342 | } | ||
343 | |||
344 | ptep = consistent_pte + CONSISTENT_OFFSET(c->vm_start); | ||
345 | addr = c->vm_start; | ||
346 | do { | ||
347 | pte_t pte = ptep_get_and_clear(&init_mm, addr, ptep); | ||
348 | unsigned long pfn; | ||
349 | |||
350 | ptep++; | ||
351 | addr += PAGE_SIZE; | ||
352 | |||
353 | if (!pte_none(pte) && pte_present(pte)) { | ||
354 | pfn = pte_pfn(pte); | ||
355 | |||
356 | if (pfn_valid(pfn)) { | ||
357 | struct page *page = pfn_to_page(pfn); | ||
358 | |||
359 | /* | ||
360 | * x86 does not mark the pages reserved... | ||
361 | */ | ||
362 | ClearPageReserved(page); | ||
363 | |||
364 | __free_page(page); | ||
365 | continue; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | printk(KERN_CRIT "%s: bad page in kernel page table\n", | ||
370 | __func__); | ||
371 | } while (size -= PAGE_SIZE); | ||
372 | |||
373 | flush_tlb_kernel_range(c->vm_start, c->vm_end); | ||
374 | |||
375 | list_del(&c->vm_list); | ||
376 | |||
377 | spin_unlock_irqrestore(&consistent_lock, flags); | ||
378 | |||
379 | kfree(c); | ||
380 | return; | ||
381 | |||
382 | no_area: | ||
383 | spin_unlock_irqrestore(&consistent_lock, flags); | ||
384 | printk(KERN_ERR "%s: trying to free invalid coherent area: %p\n", | ||
385 | __func__, cpu_addr); | ||
386 | dump_stack(); | ||
387 | } | ||
388 | EXPORT_SYMBOL(dma_free_coherent); | ||
389 | |||
390 | /* | ||
391 | * Initialise the consistent memory allocation. | ||
392 | */ | ||
393 | static int __init consistent_init(void) | ||
394 | { | ||
395 | pgd_t *pgd; | ||
396 | pmd_t *pmd; | ||
397 | pte_t *pte; | ||
398 | int ret = 0; | ||
399 | |||
400 | spin_lock(&init_mm.page_table_lock); | ||
401 | |||
402 | do { | ||
403 | pgd = pgd_offset(&init_mm, CONSISTENT_BASE); | ||
404 | pmd = pmd_alloc(&init_mm, pgd, CONSISTENT_BASE); | ||
405 | if (!pmd) { | ||
406 | printk(KERN_ERR "%s: no pmd tables\n", __func__); | ||
407 | ret = -ENOMEM; | ||
408 | break; | ||
409 | } | ||
410 | WARN_ON(!pmd_none(*pmd)); | ||
411 | |||
412 | pte = pte_alloc_kernel(&init_mm, pmd, CONSISTENT_BASE); | ||
413 | if (!pte) { | ||
414 | printk(KERN_ERR "%s: no pte tables\n", __func__); | ||
415 | ret = -ENOMEM; | ||
416 | break; | ||
417 | } | ||
418 | |||
419 | consistent_pte = pte; | ||
420 | } while (0); | ||
421 | |||
422 | spin_unlock(&init_mm.page_table_lock); | ||
423 | |||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | core_initcall(consistent_init); | ||
428 | |||
429 | /* | ||
430 | * Make an area consistent for devices. | ||
431 | */ | ||
432 | void consistent_sync(void *vaddr, size_t size, int direction) | ||
433 | { | ||
434 | unsigned long start = (unsigned long)vaddr; | ||
435 | unsigned long end = start + size; | ||
436 | |||
437 | switch (direction) { | ||
438 | case DMA_FROM_DEVICE: /* invalidate only */ | ||
439 | dmac_inv_range(start, end); | ||
440 | break; | ||
441 | case DMA_TO_DEVICE: /* writeback only */ | ||
442 | dmac_clean_range(start, end); | ||
443 | break; | ||
444 | case DMA_BIDIRECTIONAL: /* writeback and invalidate */ | ||
445 | dmac_flush_range(start, end); | ||
446 | break; | ||
447 | default: | ||
448 | BUG(); | ||
449 | } | ||
450 | } | ||
451 | EXPORT_SYMBOL(consistent_sync); | ||
diff --git a/arch/arm/mm/copypage-v3.S b/arch/arm/mm/copypage-v3.S new file mode 100644 index 000000000000..4940f1908316 --- /dev/null +++ b/arch/arm/mm/copypage-v3.S | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/lib/copypage.S | ||
3 | * | ||
4 | * Copyright (C) 1995-1999 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ASM optimised string functions | ||
11 | */ | ||
12 | #include <linux/linkage.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <asm/assembler.h> | ||
15 | #include <asm/constants.h> | ||
16 | |||
17 | .text | ||
18 | .align 5 | ||
19 | /* | ||
20 | * ARMv3 optimised copy_user_page | ||
21 | * | ||
22 | * FIXME: do we need to handle cache stuff... | ||
23 | */ | ||
24 | ENTRY(v3_copy_user_page) | ||
25 | stmfd sp!, {r4, lr} @ 2 | ||
26 | mov r2, #PAGE_SZ/64 @ 1 | ||
27 | ldmia r1!, {r3, r4, ip, lr} @ 4+1 | ||
28 | 1: stmia r0!, {r3, r4, ip, lr} @ 4 | ||
29 | ldmia r1!, {r3, r4, ip, lr} @ 4+1 | ||
30 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
31 | ldmia r1!, {r3, r4, ip, lr} @ 4+1 | ||
32 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
33 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
34 | subs r2, r2, #1 @ 1 | ||
35 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
36 | ldmneia r1!, {r3, r4, ip, lr} @ 4 | ||
37 | bne 1b @ 1 | ||
38 | LOADREGS(fd, sp!, {r4, pc}) @ 3 | ||
39 | |||
40 | .align 5 | ||
41 | /* | ||
42 | * ARMv3 optimised clear_user_page | ||
43 | * | ||
44 | * FIXME: do we need to handle cache stuff... | ||
45 | */ | ||
46 | ENTRY(v3_clear_user_page) | ||
47 | str lr, [sp, #-4]! | ||
48 | mov r1, #PAGE_SZ/64 @ 1 | ||
49 | mov r2, #0 @ 1 | ||
50 | mov r3, #0 @ 1 | ||
51 | mov ip, #0 @ 1 | ||
52 | mov lr, #0 @ 1 | ||
53 | 1: stmia r0!, {r2, r3, ip, lr} @ 4 | ||
54 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
55 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
56 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
57 | subs r1, r1, #1 @ 1 | ||
58 | bne 1b @ 1 | ||
59 | ldr pc, [sp], #4 | ||
60 | |||
61 | __INITDATA | ||
62 | |||
63 | .type v3_user_fns, #object | ||
64 | ENTRY(v3_user_fns) | ||
65 | .long v3_clear_user_page | ||
66 | .long v3_copy_user_page | ||
67 | .size v3_user_fns, . - v3_user_fns | ||
diff --git a/arch/arm/mm/copypage-v4mc.S b/arch/arm/mm/copypage-v4mc.S new file mode 100644 index 000000000000..305af3dab3d8 --- /dev/null +++ b/arch/arm/mm/copypage-v4mc.S | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/lib/copy_page-armv4mc.S | ||
3 | * | ||
4 | * Copyright (C) 1995-2001 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ASM optimised string functions | ||
11 | */ | ||
12 | #include <linux/linkage.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <asm/constants.h> | ||
15 | |||
16 | .text | ||
17 | .align 5 | ||
18 | /* | ||
19 | * ARMv4 mini-dcache optimised copy_user_page | ||
20 | * | ||
21 | * We flush the destination cache lines just before we write the data into the | ||
22 | * corresponding address. Since the Dcache is read-allocate, this removes the | ||
23 | * Dcache aliasing issue. The writes will be forwarded to the write buffer, | ||
24 | * and merged as appropriate. | ||
25 | * | ||
26 | * Note: We rely on all ARMv4 processors implementing the "invalidate D line" | ||
27 | * instruction. If your processor does not supply this, you have to write your | ||
28 | * own copy_user_page that does the right thing. | ||
29 | */ | ||
30 | ENTRY(v4_mc_copy_user_page) | ||
31 | stmfd sp!, {r4, lr} @ 2 | ||
32 | mov r4, r0 | ||
33 | mov r0, r1 | ||
34 | bl map_page_minicache | ||
35 | mov r1, #PAGE_SZ/64 @ 1 | ||
36 | ldmia r0!, {r2, r3, ip, lr} @ 4 | ||
37 | 1: mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line | ||
38 | stmia r4!, {r2, r3, ip, lr} @ 4 | ||
39 | ldmia r0!, {r2, r3, ip, lr} @ 4+1 | ||
40 | stmia r4!, {r2, r3, ip, lr} @ 4 | ||
41 | ldmia r0!, {r2, r3, ip, lr} @ 4 | ||
42 | mcr p15, 0, r4, c7, c6, 1 @ 1 invalidate D line | ||
43 | stmia r4!, {r2, r3, ip, lr} @ 4 | ||
44 | ldmia r0!, {r2, r3, ip, lr} @ 4 | ||
45 | subs r1, r1, #1 @ 1 | ||
46 | stmia r4!, {r2, r3, ip, lr} @ 4 | ||
47 | ldmneia r0!, {r2, r3, ip, lr} @ 4 | ||
48 | bne 1b @ 1 | ||
49 | ldmfd sp!, {r4, pc} @ 3 | ||
50 | |||
51 | .align 5 | ||
52 | /* | ||
53 | * ARMv4 optimised clear_user_page | ||
54 | * | ||
55 | * Same story as above. | ||
56 | */ | ||
57 | ENTRY(v4_mc_clear_user_page) | ||
58 | str lr, [sp, #-4]! | ||
59 | mov r1, #PAGE_SZ/64 @ 1 | ||
60 | mov r2, #0 @ 1 | ||
61 | mov r3, #0 @ 1 | ||
62 | mov ip, #0 @ 1 | ||
63 | mov lr, #0 @ 1 | ||
64 | 1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line | ||
65 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
66 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
67 | mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line | ||
68 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
69 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
70 | subs r1, r1, #1 @ 1 | ||
71 | bne 1b @ 1 | ||
72 | ldr pc, [sp], #4 | ||
73 | |||
74 | __INITDATA | ||
75 | |||
76 | .type v4_mc_user_fns, #object | ||
77 | ENTRY(v4_mc_user_fns) | ||
78 | .long v4_mc_clear_user_page | ||
79 | .long v4_mc_copy_user_page | ||
80 | .size v4_mc_user_fns, . - v4_mc_user_fns | ||
diff --git a/arch/arm/mm/copypage-v4wb.S b/arch/arm/mm/copypage-v4wb.S new file mode 100644 index 000000000000..b94c345ceb94 --- /dev/null +++ b/arch/arm/mm/copypage-v4wb.S | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/lib/copypage.S | ||
3 | * | ||
4 | * Copyright (C) 1995-1999 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ASM optimised string functions | ||
11 | */ | ||
12 | #include <linux/linkage.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <asm/constants.h> | ||
15 | |||
16 | .text | ||
17 | .align 5 | ||
18 | /* | ||
19 | * ARMv4 optimised copy_user_page | ||
20 | * | ||
21 | * We flush the destination cache lines just before we write the data into the | ||
22 | * corresponding address. Since the Dcache is read-allocate, this removes the | ||
23 | * Dcache aliasing issue. The writes will be forwarded to the write buffer, | ||
24 | * and merged as appropriate. | ||
25 | * | ||
26 | * Note: We rely on all ARMv4 processors implementing the "invalidate D line" | ||
27 | * instruction. If your processor does not supply this, you have to write your | ||
28 | * own copy_user_page that does the right thing. | ||
29 | */ | ||
30 | ENTRY(v4wb_copy_user_page) | ||
31 | stmfd sp!, {r4, lr} @ 2 | ||
32 | mov r2, #PAGE_SZ/64 @ 1 | ||
33 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
34 | 1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line | ||
35 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
36 | ldmia r1!, {r3, r4, ip, lr} @ 4+1 | ||
37 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
38 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
39 | mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line | ||
40 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
41 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
42 | subs r2, r2, #1 @ 1 | ||
43 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
44 | ldmneia r1!, {r3, r4, ip, lr} @ 4 | ||
45 | bne 1b @ 1 | ||
46 | mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB | ||
47 | ldmfd sp!, {r4, pc} @ 3 | ||
48 | |||
49 | .align 5 | ||
50 | /* | ||
51 | * ARMv4 optimised clear_user_page | ||
52 | * | ||
53 | * Same story as above. | ||
54 | */ | ||
55 | ENTRY(v4wb_clear_user_page) | ||
56 | str lr, [sp, #-4]! | ||
57 | mov r1, #PAGE_SZ/64 @ 1 | ||
58 | mov r2, #0 @ 1 | ||
59 | mov r3, #0 @ 1 | ||
60 | mov ip, #0 @ 1 | ||
61 | mov lr, #0 @ 1 | ||
62 | 1: mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line | ||
63 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
64 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
65 | mcr p15, 0, r0, c7, c6, 1 @ 1 invalidate D line | ||
66 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
67 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
68 | subs r1, r1, #1 @ 1 | ||
69 | bne 1b @ 1 | ||
70 | mcr p15, 0, r1, c7, c10, 4 @ 1 drain WB | ||
71 | ldr pc, [sp], #4 | ||
72 | |||
73 | __INITDATA | ||
74 | |||
75 | .type v4wb_user_fns, #object | ||
76 | ENTRY(v4wb_user_fns) | ||
77 | .long v4wb_clear_user_page | ||
78 | .long v4wb_copy_user_page | ||
79 | .size v4wb_user_fns, . - v4wb_user_fns | ||
diff --git a/arch/arm/mm/copypage-v4wt.S b/arch/arm/mm/copypage-v4wt.S new file mode 100644 index 000000000000..976793937a93 --- /dev/null +++ b/arch/arm/mm/copypage-v4wt.S | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/lib/copypage-v4.S | ||
3 | * | ||
4 | * Copyright (C) 1995-1999 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ASM optimised string functions | ||
11 | * | ||
12 | * This is for CPUs with a writethrough cache and 'flush ID cache' is | ||
13 | * the only supported cache operation. | ||
14 | */ | ||
15 | #include <linux/linkage.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <asm/constants.h> | ||
18 | |||
19 | .text | ||
20 | .align 5 | ||
21 | /* | ||
22 | * ARMv4 optimised copy_user_page | ||
23 | * | ||
24 | * Since we have writethrough caches, we don't have to worry about | ||
25 | * dirty data in the cache. However, we do have to ensure that | ||
26 | * subsequent reads are up to date. | ||
27 | */ | ||
28 | ENTRY(v4wt_copy_user_page) | ||
29 | stmfd sp!, {r4, lr} @ 2 | ||
30 | mov r2, #PAGE_SZ/64 @ 1 | ||
31 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
32 | 1: stmia r0!, {r3, r4, ip, lr} @ 4 | ||
33 | ldmia r1!, {r3, r4, ip, lr} @ 4+1 | ||
34 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
35 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
36 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
37 | ldmia r1!, {r3, r4, ip, lr} @ 4 | ||
38 | subs r2, r2, #1 @ 1 | ||
39 | stmia r0!, {r3, r4, ip, lr} @ 4 | ||
40 | ldmneia r1!, {r3, r4, ip, lr} @ 4 | ||
41 | bne 1b @ 1 | ||
42 | mcr p15, 0, r2, c7, c7, 0 @ flush ID cache | ||
43 | ldmfd sp!, {r4, pc} @ 3 | ||
44 | |||
45 | .align 5 | ||
46 | /* | ||
47 | * ARMv4 optimised clear_user_page | ||
48 | * | ||
49 | * Same story as above. | ||
50 | */ | ||
51 | ENTRY(v4wt_clear_user_page) | ||
52 | str lr, [sp, #-4]! | ||
53 | mov r1, #PAGE_SZ/64 @ 1 | ||
54 | mov r2, #0 @ 1 | ||
55 | mov r3, #0 @ 1 | ||
56 | mov ip, #0 @ 1 | ||
57 | mov lr, #0 @ 1 | ||
58 | 1: stmia r0!, {r2, r3, ip, lr} @ 4 | ||
59 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
60 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
61 | stmia r0!, {r2, r3, ip, lr} @ 4 | ||
62 | subs r1, r1, #1 @ 1 | ||
63 | bne 1b @ 1 | ||
64 | mcr p15, 0, r2, c7, c7, 0 @ flush ID cache | ||
65 | ldr pc, [sp], #4 | ||
66 | |||
67 | __INITDATA | ||
68 | |||
69 | .type v4wt_user_fns, #object | ||
70 | ENTRY(v4wt_user_fns) | ||
71 | .long v4wt_clear_user_page | ||
72 | .long v4wt_copy_user_page | ||
73 | .size v4wt_user_fns, . - v4wt_user_fns | ||
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c new file mode 100644 index 000000000000..694ac8208858 --- /dev/null +++ b/arch/arm/mm/copypage-v6.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/copypage-v6.c | ||
3 | * | ||
4 | * Copyright (C) 2002 Deep Blue Solutions Ltd, All Rights Reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/spinlock.h> | ||
12 | #include <linux/mm.h> | ||
13 | |||
14 | #include <asm/page.h> | ||
15 | #include <asm/pgtable.h> | ||
16 | #include <asm/shmparam.h> | ||
17 | #include <asm/tlbflush.h> | ||
18 | #include <asm/cacheflush.h> | ||
19 | |||
20 | #if SHMLBA > 16384 | ||
21 | #error FIX ME | ||
22 | #endif | ||
23 | |||
24 | #define from_address (0xffff8000) | ||
25 | #define from_pgprot PAGE_KERNEL | ||
26 | #define to_address (0xffffc000) | ||
27 | #define to_pgprot PAGE_KERNEL | ||
28 | |||
29 | static pte_t *from_pte; | ||
30 | static pte_t *to_pte; | ||
31 | static DEFINE_SPINLOCK(v6_lock); | ||
32 | |||
33 | #define DCACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) | ||
34 | |||
35 | /* | ||
36 | * Copy the user page. No aliasing to deal with so we can just | ||
37 | * attack the kernel's existing mapping of these pages. | ||
38 | */ | ||
39 | void v6_copy_user_page_nonaliasing(void *kto, const void *kfrom, unsigned long vaddr) | ||
40 | { | ||
41 | copy_page(kto, kfrom); | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * Clear the user page. No aliasing to deal with so we can just | ||
46 | * attack the kernel's existing mapping of this page. | ||
47 | */ | ||
48 | void v6_clear_user_page_nonaliasing(void *kaddr, unsigned long vaddr) | ||
49 | { | ||
50 | clear_page(kaddr); | ||
51 | } | ||
52 | |||
53 | /* | ||
54 | * Copy the page, taking account of the cache colour. | ||
55 | */ | ||
56 | void v6_copy_user_page_aliasing(void *kto, const void *kfrom, unsigned long vaddr) | ||
57 | { | ||
58 | unsigned int offset = DCACHE_COLOUR(vaddr); | ||
59 | unsigned long from, to; | ||
60 | |||
61 | /* | ||
62 | * Discard data in the kernel mapping for the new page. | ||
63 | * FIXME: needs this MCRR to be supported. | ||
64 | */ | ||
65 | __asm__("mcrr p15, 0, %1, %0, c6 @ 0xec401f06" | ||
66 | : | ||
67 | : "r" (kto), | ||
68 | "r" ((unsigned long)kto + PAGE_SIZE - L1_CACHE_BYTES) | ||
69 | : "cc"); | ||
70 | |||
71 | /* | ||
72 | * Now copy the page using the same cache colour as the | ||
73 | * pages ultimate destination. | ||
74 | */ | ||
75 | spin_lock(&v6_lock); | ||
76 | |||
77 | set_pte(from_pte + offset, pfn_pte(__pa(kfrom) >> PAGE_SHIFT, from_pgprot)); | ||
78 | set_pte(to_pte + offset, pfn_pte(__pa(kto) >> PAGE_SHIFT, to_pgprot)); | ||
79 | |||
80 | from = from_address + (offset << PAGE_SHIFT); | ||
81 | to = to_address + (offset << PAGE_SHIFT); | ||
82 | |||
83 | flush_tlb_kernel_page(from); | ||
84 | flush_tlb_kernel_page(to); | ||
85 | |||
86 | copy_page((void *)to, (void *)from); | ||
87 | |||
88 | spin_unlock(&v6_lock); | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * Clear the user page. We need to deal with the aliasing issues, | ||
93 | * so remap the kernel page into the same cache colour as the user | ||
94 | * page. | ||
95 | */ | ||
96 | void v6_clear_user_page_aliasing(void *kaddr, unsigned long vaddr) | ||
97 | { | ||
98 | unsigned int offset = DCACHE_COLOUR(vaddr); | ||
99 | unsigned long to = to_address + (offset << PAGE_SHIFT); | ||
100 | |||
101 | /* | ||
102 | * Discard data in the kernel mapping for the new page | ||
103 | * FIXME: needs this MCRR to be supported. | ||
104 | */ | ||
105 | __asm__("mcrr p15, 0, %1, %0, c6 @ 0xec401f06" | ||
106 | : | ||
107 | : "r" (kaddr), | ||
108 | "r" ((unsigned long)kaddr + PAGE_SIZE - L1_CACHE_BYTES) | ||
109 | : "cc"); | ||
110 | |||
111 | /* | ||
112 | * Now clear the page using the same cache colour as | ||
113 | * the pages ultimate destination. | ||
114 | */ | ||
115 | spin_lock(&v6_lock); | ||
116 | |||
117 | set_pte(to_pte + offset, pfn_pte(__pa(kaddr) >> PAGE_SHIFT, to_pgprot)); | ||
118 | flush_tlb_kernel_page(to); | ||
119 | clear_page((void *)to); | ||
120 | |||
121 | spin_unlock(&v6_lock); | ||
122 | } | ||
123 | |||
124 | struct cpu_user_fns v6_user_fns __initdata = { | ||
125 | .cpu_clear_user_page = v6_clear_user_page_nonaliasing, | ||
126 | .cpu_copy_user_page = v6_copy_user_page_nonaliasing, | ||
127 | }; | ||
128 | |||
129 | static int __init v6_userpage_init(void) | ||
130 | { | ||
131 | if (cache_is_vipt_aliasing()) { | ||
132 | pgd_t *pgd; | ||
133 | pmd_t *pmd; | ||
134 | |||
135 | pgd = pgd_offset_k(from_address); | ||
136 | pmd = pmd_alloc(&init_mm, pgd, from_address); | ||
137 | if (!pmd) | ||
138 | BUG(); | ||
139 | from_pte = pte_alloc_kernel(&init_mm, pmd, from_address); | ||
140 | if (!from_pte) | ||
141 | BUG(); | ||
142 | |||
143 | to_pte = pte_alloc_kernel(&init_mm, pmd, to_address); | ||
144 | if (!to_pte) | ||
145 | BUG(); | ||
146 | |||
147 | cpu_user.cpu_clear_user_page = v6_clear_user_page_aliasing; | ||
148 | cpu_user.cpu_copy_user_page = v6_copy_user_page_aliasing; | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | __initcall(v6_userpage_init); | ||
155 | |||
diff --git a/arch/arm/mm/copypage-xscale.S b/arch/arm/mm/copypage-xscale.S new file mode 100644 index 000000000000..bb277316ef52 --- /dev/null +++ b/arch/arm/mm/copypage-xscale.S | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/lib/copypage-xscale.S | ||
3 | * | ||
4 | * Copyright (C) 2001 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/linkage.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <asm/constants.h> | ||
13 | |||
14 | /* | ||
15 | * General note: | ||
16 | * We don't really want write-allocate cache behaviour for these functions | ||
17 | * since that will just eat through 8K of the cache. | ||
18 | */ | ||
19 | |||
20 | .text | ||
21 | .align 5 | ||
22 | /* | ||
23 | * XScale optimised copy_user_page | ||
24 | * r0 = destination | ||
25 | * r1 = source | ||
26 | * r2 = virtual user address of ultimate destination page | ||
27 | * | ||
28 | * The source page may have some clean entries in the cache already, but we | ||
29 | * can safely ignore them - break_cow() will flush them out of the cache | ||
30 | * if we eventually end up using our copied page. | ||
31 | * | ||
32 | * What we could do is use the mini-cache to buffer reads from the source | ||
33 | * page. We rely on the mini-cache being smaller than one page, so we'll | ||
34 | * cycle through the complete cache anyway. | ||
35 | */ | ||
36 | ENTRY(xscale_mc_copy_user_page) | ||
37 | stmfd sp!, {r4, r5, lr} | ||
38 | mov r5, r0 | ||
39 | mov r0, r1 | ||
40 | bl map_page_minicache | ||
41 | mov r1, r5 | ||
42 | mov lr, #PAGE_SZ/64-1 | ||
43 | |||
44 | /* | ||
45 | * Strangely enough, best performance is achieved | ||
46 | * when prefetching destination as well. (NP) | ||
47 | */ | ||
48 | pld [r0, #0] | ||
49 | pld [r0, #32] | ||
50 | pld [r1, #0] | ||
51 | pld [r1, #32] | ||
52 | |||
53 | 1: pld [r0, #64] | ||
54 | pld [r0, #96] | ||
55 | pld [r1, #64] | ||
56 | pld [r1, #96] | ||
57 | |||
58 | 2: ldrd r2, [r0], #8 | ||
59 | ldrd r4, [r0], #8 | ||
60 | mov ip, r1 | ||
61 | strd r2, [r1], #8 | ||
62 | ldrd r2, [r0], #8 | ||
63 | strd r4, [r1], #8 | ||
64 | ldrd r4, [r0], #8 | ||
65 | strd r2, [r1], #8 | ||
66 | strd r4, [r1], #8 | ||
67 | mcr p15, 0, ip, c7, c10, 1 @ clean D line | ||
68 | ldrd r2, [r0], #8 | ||
69 | mcr p15, 0, ip, c7, c6, 1 @ invalidate D line | ||
70 | ldrd r4, [r0], #8 | ||
71 | mov ip, r1 | ||
72 | strd r2, [r1], #8 | ||
73 | ldrd r2, [r0], #8 | ||
74 | strd r4, [r1], #8 | ||
75 | ldrd r4, [r0], #8 | ||
76 | strd r2, [r1], #8 | ||
77 | strd r4, [r1], #8 | ||
78 | mcr p15, 0, ip, c7, c10, 1 @ clean D line | ||
79 | subs lr, lr, #1 | ||
80 | mcr p15, 0, ip, c7, c6, 1 @ invalidate D line | ||
81 | bgt 1b | ||
82 | beq 2b | ||
83 | |||
84 | ldmfd sp!, {r4, r5, pc} | ||
85 | |||
86 | .align 5 | ||
87 | /* | ||
88 | * XScale optimised clear_user_page | ||
89 | * r0 = destination | ||
90 | * r1 = virtual user address of ultimate destination page | ||
91 | */ | ||
92 | ENTRY(xscale_mc_clear_user_page) | ||
93 | mov r1, #PAGE_SZ/32 | ||
94 | mov r2, #0 | ||
95 | mov r3, #0 | ||
96 | 1: mov ip, r0 | ||
97 | strd r2, [r0], #8 | ||
98 | strd r2, [r0], #8 | ||
99 | strd r2, [r0], #8 | ||
100 | strd r2, [r0], #8 | ||
101 | mcr p15, 0, ip, c7, c10, 1 @ clean D line | ||
102 | subs r1, r1, #1 | ||
103 | mcr p15, 0, ip, c7, c6, 1 @ invalidate D line | ||
104 | bne 1b | ||
105 | mov pc, lr | ||
106 | |||
107 | __INITDATA | ||
108 | |||
109 | .type xscale_mc_user_fns, #object | ||
110 | ENTRY(xscale_mc_user_fns) | ||
111 | .long xscale_mc_clear_user_page | ||
112 | .long xscale_mc_copy_user_page | ||
113 | .size xscale_mc_user_fns, . - xscale_mc_user_fns | ||
diff --git a/arch/arm/mm/discontig.c b/arch/arm/mm/discontig.c new file mode 100644 index 000000000000..0d097bb1bc4d --- /dev/null +++ b/arch/arm/mm/discontig.c | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/discontig.c | ||
3 | * | ||
4 | * Discontiguous memory support. | ||
5 | * | ||
6 | * Initial code: Copyright (C) 1999-2000 Nicolas Pitre | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/bootmem.h> | ||
17 | |||
18 | #if MAX_NUMNODES != 4 && MAX_NUMNODES != 16 | ||
19 | # error Fix Me Please | ||
20 | #endif | ||
21 | |||
22 | /* | ||
23 | * Our node_data structure for discontiguous memory. | ||
24 | */ | ||
25 | |||
26 | static bootmem_data_t node_bootmem_data[MAX_NUMNODES]; | ||
27 | |||
28 | pg_data_t discontig_node_data[MAX_NUMNODES] = { | ||
29 | { .bdata = &node_bootmem_data[0] }, | ||
30 | { .bdata = &node_bootmem_data[1] }, | ||
31 | { .bdata = &node_bootmem_data[2] }, | ||
32 | { .bdata = &node_bootmem_data[3] }, | ||
33 | #if MAX_NUMNODES == 16 | ||
34 | { .bdata = &node_bootmem_data[4] }, | ||
35 | { .bdata = &node_bootmem_data[5] }, | ||
36 | { .bdata = &node_bootmem_data[6] }, | ||
37 | { .bdata = &node_bootmem_data[7] }, | ||
38 | { .bdata = &node_bootmem_data[8] }, | ||
39 | { .bdata = &node_bootmem_data[9] }, | ||
40 | { .bdata = &node_bootmem_data[10] }, | ||
41 | { .bdata = &node_bootmem_data[11] }, | ||
42 | { .bdata = &node_bootmem_data[12] }, | ||
43 | { .bdata = &node_bootmem_data[13] }, | ||
44 | { .bdata = &node_bootmem_data[14] }, | ||
45 | { .bdata = &node_bootmem_data[15] }, | ||
46 | #endif | ||
47 | }; | ||
48 | |||
49 | EXPORT_SYMBOL(discontig_node_data); | ||
diff --git a/arch/arm/mm/extable.c b/arch/arm/mm/extable.c new file mode 100644 index 000000000000..9592c3ee4cb2 --- /dev/null +++ b/arch/arm/mm/extable.c | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/extable.c | ||
3 | */ | ||
4 | #include <linux/module.h> | ||
5 | #include <asm/uaccess.h> | ||
6 | |||
7 | int fixup_exception(struct pt_regs *regs) | ||
8 | { | ||
9 | const struct exception_table_entry *fixup; | ||
10 | |||
11 | fixup = search_exception_tables(instruction_pointer(regs)); | ||
12 | if (fixup) | ||
13 | regs->ARM_pc = fixup->fixup; | ||
14 | |||
15 | return fixup != NULL; | ||
16 | } | ||
diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c new file mode 100644 index 000000000000..01967ddeef53 --- /dev/null +++ b/arch/arm/mm/fault-armv.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/fault-armv.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Modifications for ARM processor (c) 1995-2002 Russell King | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/bitops.h> | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/pagemap.h> | ||
19 | |||
20 | #include <asm/cacheflush.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/tlbflush.h> | ||
23 | |||
24 | static unsigned long shared_pte_mask = L_PTE_CACHEABLE; | ||
25 | |||
26 | /* | ||
27 | * We take the easy way out of this problem - we make the | ||
28 | * PTE uncacheable. However, we leave the write buffer on. | ||
29 | */ | ||
30 | static int adjust_pte(struct vm_area_struct *vma, unsigned long address) | ||
31 | { | ||
32 | pgd_t *pgd; | ||
33 | pmd_t *pmd; | ||
34 | pte_t *pte, entry; | ||
35 | int ret = 0; | ||
36 | |||
37 | pgd = pgd_offset(vma->vm_mm, address); | ||
38 | if (pgd_none(*pgd)) | ||
39 | goto no_pgd; | ||
40 | if (pgd_bad(*pgd)) | ||
41 | goto bad_pgd; | ||
42 | |||
43 | pmd = pmd_offset(pgd, address); | ||
44 | if (pmd_none(*pmd)) | ||
45 | goto no_pmd; | ||
46 | if (pmd_bad(*pmd)) | ||
47 | goto bad_pmd; | ||
48 | |||
49 | pte = pte_offset_map(pmd, address); | ||
50 | entry = *pte; | ||
51 | |||
52 | /* | ||
53 | * If this page isn't present, or is already setup to | ||
54 | * fault (ie, is old), we can safely ignore any issues. | ||
55 | */ | ||
56 | if (pte_present(entry) && pte_val(entry) & shared_pte_mask) { | ||
57 | flush_cache_page(vma, address, pte_pfn(entry)); | ||
58 | pte_val(entry) &= ~shared_pte_mask; | ||
59 | set_pte(pte, entry); | ||
60 | flush_tlb_page(vma, address); | ||
61 | ret = 1; | ||
62 | } | ||
63 | pte_unmap(pte); | ||
64 | return ret; | ||
65 | |||
66 | bad_pgd: | ||
67 | pgd_ERROR(*pgd); | ||
68 | pgd_clear(pgd); | ||
69 | no_pgd: | ||
70 | return 0; | ||
71 | |||
72 | bad_pmd: | ||
73 | pmd_ERROR(*pmd); | ||
74 | pmd_clear(pmd); | ||
75 | no_pmd: | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static void | ||
80 | make_coherent(struct vm_area_struct *vma, unsigned long addr, struct page *page, int dirty) | ||
81 | { | ||
82 | struct address_space *mapping = page_mapping(page); | ||
83 | struct mm_struct *mm = vma->vm_mm; | ||
84 | struct vm_area_struct *mpnt; | ||
85 | struct prio_tree_iter iter; | ||
86 | unsigned long offset; | ||
87 | pgoff_t pgoff; | ||
88 | int aliases = 0; | ||
89 | |||
90 | if (!mapping) | ||
91 | return; | ||
92 | |||
93 | pgoff = vma->vm_pgoff + ((addr - vma->vm_start) >> PAGE_SHIFT); | ||
94 | |||
95 | /* | ||
96 | * If we have any shared mappings that are in the same mm | ||
97 | * space, then we need to handle them specially to maintain | ||
98 | * cache coherency. | ||
99 | */ | ||
100 | flush_dcache_mmap_lock(mapping); | ||
101 | vma_prio_tree_foreach(mpnt, &iter, &mapping->i_mmap, pgoff, pgoff) { | ||
102 | /* | ||
103 | * If this VMA is not in our MM, we can ignore it. | ||
104 | * Note that we intentionally mask out the VMA | ||
105 | * that we are fixing up. | ||
106 | */ | ||
107 | if (mpnt->vm_mm != mm || mpnt == vma) | ||
108 | continue; | ||
109 | if (!(mpnt->vm_flags & VM_MAYSHARE)) | ||
110 | continue; | ||
111 | offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; | ||
112 | aliases += adjust_pte(mpnt, mpnt->vm_start + offset); | ||
113 | } | ||
114 | flush_dcache_mmap_unlock(mapping); | ||
115 | if (aliases) | ||
116 | adjust_pte(vma, addr); | ||
117 | else | ||
118 | flush_cache_page(vma, addr, page_to_pfn(page)); | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Take care of architecture specific things when placing a new PTE into | ||
123 | * a page table, or changing an existing PTE. Basically, there are two | ||
124 | * things that we need to take care of: | ||
125 | * | ||
126 | * 1. If PG_dcache_dirty is set for the page, we need to ensure | ||
127 | * that any cache entries for the kernels virtual memory | ||
128 | * range are written back to the page. | ||
129 | * 2. If we have multiple shared mappings of the same space in | ||
130 | * an object, we need to deal with the cache aliasing issues. | ||
131 | * | ||
132 | * Note that the page_table_lock will be held. | ||
133 | */ | ||
134 | void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) | ||
135 | { | ||
136 | unsigned long pfn = pte_pfn(pte); | ||
137 | struct page *page; | ||
138 | |||
139 | if (!pfn_valid(pfn)) | ||
140 | return; | ||
141 | page = pfn_to_page(pfn); | ||
142 | if (page_mapping(page)) { | ||
143 | int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags); | ||
144 | |||
145 | if (dirty) { | ||
146 | /* | ||
147 | * This is our first userspace mapping of this page. | ||
148 | * Ensure that the physical page is coherent with | ||
149 | * the kernel mapping. | ||
150 | * | ||
151 | * FIXME: only need to do this on VIVT and aliasing | ||
152 | * VIPT cache architectures. We can do that | ||
153 | * by choosing whether to set this bit... | ||
154 | */ | ||
155 | __cpuc_flush_dcache_page(page_address(page)); | ||
156 | } | ||
157 | |||
158 | if (cache_is_vivt()) | ||
159 | make_coherent(vma, addr, page, dirty); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Check whether the write buffer has physical address aliasing | ||
165 | * issues. If it has, we need to avoid them for the case where | ||
166 | * we have several shared mappings of the same object in user | ||
167 | * space. | ||
168 | */ | ||
169 | static int __init check_writebuffer(unsigned long *p1, unsigned long *p2) | ||
170 | { | ||
171 | register unsigned long zero = 0, one = 1, val; | ||
172 | |||
173 | local_irq_disable(); | ||
174 | mb(); | ||
175 | *p1 = one; | ||
176 | mb(); | ||
177 | *p2 = zero; | ||
178 | mb(); | ||
179 | val = *p1; | ||
180 | mb(); | ||
181 | local_irq_enable(); | ||
182 | return val != zero; | ||
183 | } | ||
184 | |||
185 | void __init check_writebuffer_bugs(void) | ||
186 | { | ||
187 | struct page *page; | ||
188 | const char *reason; | ||
189 | unsigned long v = 1; | ||
190 | |||
191 | printk(KERN_INFO "CPU: Testing write buffer coherency: "); | ||
192 | |||
193 | page = alloc_page(GFP_KERNEL); | ||
194 | if (page) { | ||
195 | unsigned long *p1, *p2; | ||
196 | pgprot_t prot = __pgprot(L_PTE_PRESENT|L_PTE_YOUNG| | ||
197 | L_PTE_DIRTY|L_PTE_WRITE| | ||
198 | L_PTE_BUFFERABLE); | ||
199 | |||
200 | p1 = vmap(&page, 1, VM_IOREMAP, prot); | ||
201 | p2 = vmap(&page, 1, VM_IOREMAP, prot); | ||
202 | |||
203 | if (p1 && p2) { | ||
204 | v = check_writebuffer(p1, p2); | ||
205 | reason = "enabling work-around"; | ||
206 | } else { | ||
207 | reason = "unable to map memory\n"; | ||
208 | } | ||
209 | |||
210 | vunmap(p1); | ||
211 | vunmap(p2); | ||
212 | put_page(page); | ||
213 | } else { | ||
214 | reason = "unable to grab page\n"; | ||
215 | } | ||
216 | |||
217 | if (v) { | ||
218 | printk("failed, %s\n", reason); | ||
219 | shared_pte_mask |= L_PTE_BUFFERABLE; | ||
220 | } else { | ||
221 | printk("ok\n"); | ||
222 | } | ||
223 | } | ||
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c new file mode 100644 index 000000000000..29be1c018949 --- /dev/null +++ b/arch/arm/mm/fault.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/fault.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Modifications for ARM processor (c) 1995-2004 Russell King | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/signal.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/init.h> | ||
17 | |||
18 | #include <asm/system.h> | ||
19 | #include <asm/pgtable.h> | ||
20 | #include <asm/tlbflush.h> | ||
21 | #include <asm/uaccess.h> | ||
22 | |||
23 | #include "fault.h" | ||
24 | |||
25 | /* | ||
26 | * This is useful to dump out the page tables associated with | ||
27 | * 'addr' in mm 'mm'. | ||
28 | */ | ||
29 | void show_pte(struct mm_struct *mm, unsigned long addr) | ||
30 | { | ||
31 | pgd_t *pgd; | ||
32 | |||
33 | if (!mm) | ||
34 | mm = &init_mm; | ||
35 | |||
36 | printk(KERN_ALERT "pgd = %p\n", mm->pgd); | ||
37 | pgd = pgd_offset(mm, addr); | ||
38 | printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd)); | ||
39 | |||
40 | do { | ||
41 | pmd_t *pmd; | ||
42 | pte_t *pte; | ||
43 | |||
44 | if (pgd_none(*pgd)) | ||
45 | break; | ||
46 | |||
47 | if (pgd_bad(*pgd)) { | ||
48 | printk("(bad)"); | ||
49 | break; | ||
50 | } | ||
51 | |||
52 | pmd = pmd_offset(pgd, addr); | ||
53 | #if PTRS_PER_PMD != 1 | ||
54 | printk(", *pmd=%08lx", pmd_val(*pmd)); | ||
55 | #endif | ||
56 | |||
57 | if (pmd_none(*pmd)) | ||
58 | break; | ||
59 | |||
60 | if (pmd_bad(*pmd)) { | ||
61 | printk("(bad)"); | ||
62 | break; | ||
63 | } | ||
64 | |||
65 | #ifndef CONFIG_HIGHMEM | ||
66 | /* We must not map this if we have highmem enabled */ | ||
67 | pte = pte_offset_map(pmd, addr); | ||
68 | printk(", *pte=%08lx", pte_val(*pte)); | ||
69 | printk(", *ppte=%08lx", pte_val(pte[-PTRS_PER_PTE])); | ||
70 | pte_unmap(pte); | ||
71 | #endif | ||
72 | } while(0); | ||
73 | |||
74 | printk("\n"); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Oops. The kernel tried to access some page that wasn't present. | ||
79 | */ | ||
80 | static void | ||
81 | __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, | ||
82 | struct pt_regs *regs) | ||
83 | { | ||
84 | /* | ||
85 | * Are we prepared to handle this kernel fault? | ||
86 | */ | ||
87 | if (fixup_exception(regs)) | ||
88 | return; | ||
89 | |||
90 | /* | ||
91 | * No handler, we'll have to terminate things with extreme prejudice. | ||
92 | */ | ||
93 | bust_spinlocks(1); | ||
94 | printk(KERN_ALERT | ||
95 | "Unable to handle kernel %s at virtual address %08lx\n", | ||
96 | (addr < PAGE_SIZE) ? "NULL pointer dereference" : | ||
97 | "paging request", addr); | ||
98 | |||
99 | show_pte(mm, addr); | ||
100 | die("Oops", regs, fsr); | ||
101 | bust_spinlocks(0); | ||
102 | do_exit(SIGKILL); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Something tried to access memory that isn't in our memory map.. | ||
107 | * User mode accesses just cause a SIGSEGV | ||
108 | */ | ||
109 | static void | ||
110 | __do_user_fault(struct task_struct *tsk, unsigned long addr, | ||
111 | unsigned int fsr, int code, struct pt_regs *regs) | ||
112 | { | ||
113 | struct siginfo si; | ||
114 | |||
115 | #ifdef CONFIG_DEBUG_USER | ||
116 | if (user_debug & UDBG_SEGV) { | ||
117 | printk(KERN_DEBUG "%s: unhandled page fault at 0x%08lx, code 0x%03x\n", | ||
118 | tsk->comm, addr, fsr); | ||
119 | show_pte(tsk->mm, addr); | ||
120 | show_regs(regs); | ||
121 | } | ||
122 | #endif | ||
123 | |||
124 | tsk->thread.address = addr; | ||
125 | tsk->thread.error_code = fsr; | ||
126 | tsk->thread.trap_no = 14; | ||
127 | si.si_signo = SIGSEGV; | ||
128 | si.si_errno = 0; | ||
129 | si.si_code = code; | ||
130 | si.si_addr = (void __user *)addr; | ||
131 | force_sig_info(SIGSEGV, &si, tsk); | ||
132 | } | ||
133 | |||
134 | void | ||
135 | do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr, | ||
136 | unsigned int fsr, struct pt_regs *regs) | ||
137 | { | ||
138 | /* | ||
139 | * If we are in kernel mode at this point, we | ||
140 | * have no context to handle this fault with. | ||
141 | */ | ||
142 | if (user_mode(regs)) | ||
143 | __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs); | ||
144 | else | ||
145 | __do_kernel_fault(mm, addr, fsr, regs); | ||
146 | } | ||
147 | |||
148 | #define VM_FAULT_BADMAP (-20) | ||
149 | #define VM_FAULT_BADACCESS (-21) | ||
150 | |||
151 | static int | ||
152 | __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, | ||
153 | struct task_struct *tsk) | ||
154 | { | ||
155 | struct vm_area_struct *vma; | ||
156 | int fault, mask; | ||
157 | |||
158 | vma = find_vma(mm, addr); | ||
159 | fault = VM_FAULT_BADMAP; | ||
160 | if (!vma) | ||
161 | goto out; | ||
162 | if (vma->vm_start > addr) | ||
163 | goto check_stack; | ||
164 | |||
165 | /* | ||
166 | * Ok, we have a good vm_area for this | ||
167 | * memory access, so we can handle it. | ||
168 | */ | ||
169 | good_area: | ||
170 | if (fsr & (1 << 11)) /* write? */ | ||
171 | mask = VM_WRITE; | ||
172 | else | ||
173 | mask = VM_READ|VM_EXEC; | ||
174 | |||
175 | fault = VM_FAULT_BADACCESS; | ||
176 | if (!(vma->vm_flags & mask)) | ||
177 | goto out; | ||
178 | |||
179 | /* | ||
180 | * If for any reason at all we couldn't handle | ||
181 | * the fault, make sure we exit gracefully rather | ||
182 | * than endlessly redo the fault. | ||
183 | */ | ||
184 | survive: | ||
185 | fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, fsr & (1 << 11)); | ||
186 | |||
187 | /* | ||
188 | * Handle the "normal" cases first - successful and sigbus | ||
189 | */ | ||
190 | switch (fault) { | ||
191 | case VM_FAULT_MAJOR: | ||
192 | tsk->maj_flt++; | ||
193 | return fault; | ||
194 | case VM_FAULT_MINOR: | ||
195 | tsk->min_flt++; | ||
196 | case VM_FAULT_SIGBUS: | ||
197 | return fault; | ||
198 | } | ||
199 | |||
200 | if (tsk->pid != 1) | ||
201 | goto out; | ||
202 | |||
203 | /* | ||
204 | * If we are out of memory for pid1, | ||
205 | * sleep for a while and retry | ||
206 | */ | ||
207 | yield(); | ||
208 | goto survive; | ||
209 | |||
210 | check_stack: | ||
211 | if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) | ||
212 | goto good_area; | ||
213 | out: | ||
214 | return fault; | ||
215 | } | ||
216 | |||
217 | static int | ||
218 | do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
219 | { | ||
220 | struct task_struct *tsk; | ||
221 | struct mm_struct *mm; | ||
222 | int fault; | ||
223 | |||
224 | tsk = current; | ||
225 | mm = tsk->mm; | ||
226 | |||
227 | /* | ||
228 | * If we're in an interrupt or have no user | ||
229 | * context, we must not take the fault.. | ||
230 | */ | ||
231 | if (in_interrupt() || !mm) | ||
232 | goto no_context; | ||
233 | |||
234 | down_read(&mm->mmap_sem); | ||
235 | fault = __do_page_fault(mm, addr, fsr, tsk); | ||
236 | up_read(&mm->mmap_sem); | ||
237 | |||
238 | /* | ||
239 | * Handle the "normal" case first | ||
240 | */ | ||
241 | if (fault > 0) | ||
242 | return 0; | ||
243 | |||
244 | /* | ||
245 | * We had some memory, but were unable to | ||
246 | * successfully fix up this page fault. | ||
247 | */ | ||
248 | if (fault == 0) | ||
249 | goto do_sigbus; | ||
250 | |||
251 | /* | ||
252 | * If we are in kernel mode at this point, we | ||
253 | * have no context to handle this fault with. | ||
254 | */ | ||
255 | if (!user_mode(regs)) | ||
256 | goto no_context; | ||
257 | |||
258 | if (fault == VM_FAULT_OOM) { | ||
259 | /* | ||
260 | * We ran out of memory, or some other thing happened to | ||
261 | * us that made us unable to handle the page fault gracefully. | ||
262 | */ | ||
263 | printk("VM: killing process %s\n", tsk->comm); | ||
264 | do_exit(SIGKILL); | ||
265 | } else | ||
266 | __do_user_fault(tsk, addr, fsr, fault == VM_FAULT_BADACCESS ? | ||
267 | SEGV_ACCERR : SEGV_MAPERR, regs); | ||
268 | return 0; | ||
269 | |||
270 | |||
271 | /* | ||
272 | * We ran out of memory, or some other thing happened to us that made | ||
273 | * us unable to handle the page fault gracefully. | ||
274 | */ | ||
275 | do_sigbus: | ||
276 | /* | ||
277 | * Send a sigbus, regardless of whether we were in kernel | ||
278 | * or user mode. | ||
279 | */ | ||
280 | tsk->thread.address = addr; | ||
281 | tsk->thread.error_code = fsr; | ||
282 | tsk->thread.trap_no = 14; | ||
283 | force_sig(SIGBUS, tsk); | ||
284 | #ifdef CONFIG_DEBUG_USER | ||
285 | if (user_debug & UDBG_BUS) { | ||
286 | printk(KERN_DEBUG "%s: sigbus at 0x%08lx, pc=0x%08lx\n", | ||
287 | current->comm, addr, instruction_pointer(regs)); | ||
288 | } | ||
289 | #endif | ||
290 | |||
291 | /* Kernel mode? Handle exceptions or die */ | ||
292 | if (user_mode(regs)) | ||
293 | return 0; | ||
294 | |||
295 | no_context: | ||
296 | __do_kernel_fault(mm, addr, fsr, regs); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * First Level Translation Fault Handler | ||
302 | * | ||
303 | * We enter here because the first level page table doesn't contain | ||
304 | * a valid entry for the address. | ||
305 | * | ||
306 | * If the address is in kernel space (>= TASK_SIZE), then we are | ||
307 | * probably faulting in the vmalloc() area. | ||
308 | * | ||
309 | * If the init_task's first level page tables contains the relevant | ||
310 | * entry, we copy the it to this task. If not, we send the process | ||
311 | * a signal, fixup the exception, or oops the kernel. | ||
312 | * | ||
313 | * NOTE! We MUST NOT take any locks for this case. We may be in an | ||
314 | * interrupt or a critical region, and should only copy the information | ||
315 | * from the master page table, nothing more. | ||
316 | */ | ||
317 | static int | ||
318 | do_translation_fault(unsigned long addr, unsigned int fsr, | ||
319 | struct pt_regs *regs) | ||
320 | { | ||
321 | struct task_struct *tsk; | ||
322 | unsigned int index; | ||
323 | pgd_t *pgd, *pgd_k; | ||
324 | pmd_t *pmd, *pmd_k; | ||
325 | |||
326 | if (addr < TASK_SIZE) | ||
327 | return do_page_fault(addr, fsr, regs); | ||
328 | |||
329 | index = pgd_index(addr); | ||
330 | |||
331 | /* | ||
332 | * FIXME: CP15 C1 is write only on ARMv3 architectures. | ||
333 | */ | ||
334 | pgd = cpu_get_pgd() + index; | ||
335 | pgd_k = init_mm.pgd + index; | ||
336 | |||
337 | if (pgd_none(*pgd_k)) | ||
338 | goto bad_area; | ||
339 | |||
340 | if (!pgd_present(*pgd)) | ||
341 | set_pgd(pgd, *pgd_k); | ||
342 | |||
343 | pmd_k = pmd_offset(pgd_k, addr); | ||
344 | pmd = pmd_offset(pgd, addr); | ||
345 | |||
346 | if (pmd_none(*pmd_k)) | ||
347 | goto bad_area; | ||
348 | |||
349 | copy_pmd(pmd, pmd_k); | ||
350 | return 0; | ||
351 | |||
352 | bad_area: | ||
353 | tsk = current; | ||
354 | |||
355 | do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Some section permission faults need to be handled gracefully. | ||
361 | * They can happen due to a __{get,put}_user during an oops. | ||
362 | */ | ||
363 | static int | ||
364 | do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
365 | { | ||
366 | struct task_struct *tsk = current; | ||
367 | do_bad_area(tsk, tsk->active_mm, addr, fsr, regs); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * This abort handler always returns "fault". | ||
373 | */ | ||
374 | static int | ||
375 | do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
376 | { | ||
377 | return 1; | ||
378 | } | ||
379 | |||
380 | static struct fsr_info { | ||
381 | int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); | ||
382 | int sig; | ||
383 | const char *name; | ||
384 | } fsr_info[] = { | ||
385 | /* | ||
386 | * The following are the standard ARMv3 and ARMv4 aborts. ARMv5 | ||
387 | * defines these to be "precise" aborts. | ||
388 | */ | ||
389 | { do_bad, SIGSEGV, "vector exception" }, | ||
390 | { do_bad, SIGILL, "alignment exception" }, | ||
391 | { do_bad, SIGKILL, "terminal exception" }, | ||
392 | { do_bad, SIGILL, "alignment exception" }, | ||
393 | { do_bad, SIGBUS, "external abort on linefetch" }, | ||
394 | { do_translation_fault, SIGSEGV, "section translation fault" }, | ||
395 | { do_bad, SIGBUS, "external abort on linefetch" }, | ||
396 | { do_page_fault, SIGSEGV, "page translation fault" }, | ||
397 | { do_bad, SIGBUS, "external abort on non-linefetch" }, | ||
398 | { do_bad, SIGSEGV, "section domain fault" }, | ||
399 | { do_bad, SIGBUS, "external abort on non-linefetch" }, | ||
400 | { do_bad, SIGSEGV, "page domain fault" }, | ||
401 | { do_bad, SIGBUS, "external abort on translation" }, | ||
402 | { do_sect_fault, SIGSEGV, "section permission fault" }, | ||
403 | { do_bad, SIGBUS, "external abort on translation" }, | ||
404 | { do_page_fault, SIGSEGV, "page permission fault" }, | ||
405 | /* | ||
406 | * The following are "imprecise" aborts, which are signalled by bit | ||
407 | * 10 of the FSR, and may not be recoverable. These are only | ||
408 | * supported if the CPU abort handler supports bit 10. | ||
409 | */ | ||
410 | { do_bad, SIGBUS, "unknown 16" }, | ||
411 | { do_bad, SIGBUS, "unknown 17" }, | ||
412 | { do_bad, SIGBUS, "unknown 18" }, | ||
413 | { do_bad, SIGBUS, "unknown 19" }, | ||
414 | { do_bad, SIGBUS, "lock abort" }, /* xscale */ | ||
415 | { do_bad, SIGBUS, "unknown 21" }, | ||
416 | { do_bad, SIGBUS, "imprecise external abort" }, /* xscale */ | ||
417 | { do_bad, SIGBUS, "unknown 23" }, | ||
418 | { do_bad, SIGBUS, "dcache parity error" }, /* xscale */ | ||
419 | { do_bad, SIGBUS, "unknown 25" }, | ||
420 | { do_bad, SIGBUS, "unknown 26" }, | ||
421 | { do_bad, SIGBUS, "unknown 27" }, | ||
422 | { do_bad, SIGBUS, "unknown 28" }, | ||
423 | { do_bad, SIGBUS, "unknown 29" }, | ||
424 | { do_bad, SIGBUS, "unknown 30" }, | ||
425 | { do_bad, SIGBUS, "unknown 31" } | ||
426 | }; | ||
427 | |||
428 | void __init | ||
429 | hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *), | ||
430 | int sig, const char *name) | ||
431 | { | ||
432 | if (nr >= 0 && nr < ARRAY_SIZE(fsr_info)) { | ||
433 | fsr_info[nr].fn = fn; | ||
434 | fsr_info[nr].sig = sig; | ||
435 | fsr_info[nr].name = name; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | /* | ||
440 | * Dispatch a data abort to the relevant handler. | ||
441 | */ | ||
442 | asmlinkage void | ||
443 | do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
444 | { | ||
445 | const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); | ||
446 | |||
447 | if (!inf->fn(addr, fsr, regs)) | ||
448 | return; | ||
449 | |||
450 | printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", | ||
451 | inf->name, fsr, addr); | ||
452 | force_sig(inf->sig, current); | ||
453 | show_pte(current->mm, addr); | ||
454 | die_if_kernel("Oops", regs, 0); | ||
455 | } | ||
456 | |||
457 | asmlinkage void | ||
458 | do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) | ||
459 | { | ||
460 | do_translation_fault(addr, 0, regs); | ||
461 | } | ||
462 | |||
diff --git a/arch/arm/mm/fault.h b/arch/arm/mm/fault.h new file mode 100644 index 000000000000..73b59e83227f --- /dev/null +++ b/arch/arm/mm/fault.h | |||
@@ -0,0 +1,6 @@ | |||
1 | void do_bad_area(struct task_struct *tsk, struct mm_struct *mm, | ||
2 | unsigned long addr, unsigned int fsr, struct pt_regs *regs); | ||
3 | |||
4 | void show_pte(struct mm_struct *mm, unsigned long addr); | ||
5 | |||
6 | unsigned long search_exception_table(unsigned long addr); | ||
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c new file mode 100644 index 000000000000..c6de48d89503 --- /dev/null +++ b/arch/arm/mm/flush.c | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/flush.c | ||
3 | * | ||
4 | * Copyright (C) 1995-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/pagemap.h> | ||
13 | |||
14 | #include <asm/cacheflush.h> | ||
15 | #include <asm/system.h> | ||
16 | |||
17 | static void __flush_dcache_page(struct address_space *mapping, struct page *page) | ||
18 | { | ||
19 | struct mm_struct *mm = current->active_mm; | ||
20 | struct vm_area_struct *mpnt; | ||
21 | struct prio_tree_iter iter; | ||
22 | pgoff_t pgoff; | ||
23 | |||
24 | /* | ||
25 | * Writeback any data associated with the kernel mapping of this | ||
26 | * page. This ensures that data in the physical page is mutually | ||
27 | * coherent with the kernels mapping. | ||
28 | */ | ||
29 | __cpuc_flush_dcache_page(page_address(page)); | ||
30 | |||
31 | /* | ||
32 | * If there's no mapping pointer here, then this page isn't | ||
33 | * visible to userspace yet, so there are no cache lines | ||
34 | * associated with any other aliases. | ||
35 | */ | ||
36 | if (!mapping) | ||
37 | return; | ||
38 | |||
39 | /* | ||
40 | * There are possible user space mappings of this page: | ||
41 | * - VIVT cache: we need to also write back and invalidate all user | ||
42 | * data in the current VM view associated with this page. | ||
43 | * - aliasing VIPT: we only need to find one mapping of this page. | ||
44 | */ | ||
45 | pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); | ||
46 | |||
47 | flush_dcache_mmap_lock(mapping); | ||
48 | vma_prio_tree_foreach(mpnt, &iter, &mapping->i_mmap, pgoff, pgoff) { | ||
49 | unsigned long offset; | ||
50 | |||
51 | /* | ||
52 | * If this VMA is not in our MM, we can ignore it. | ||
53 | */ | ||
54 | if (mpnt->vm_mm != mm) | ||
55 | continue; | ||
56 | if (!(mpnt->vm_flags & VM_MAYSHARE)) | ||
57 | continue; | ||
58 | offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; | ||
59 | flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page)); | ||
60 | if (cache_is_vipt()) | ||
61 | break; | ||
62 | } | ||
63 | flush_dcache_mmap_unlock(mapping); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Ensure cache coherency between kernel mapping and userspace mapping | ||
68 | * of this page. | ||
69 | * | ||
70 | * We have three cases to consider: | ||
71 | * - VIPT non-aliasing cache: fully coherent so nothing required. | ||
72 | * - VIVT: fully aliasing, so we need to handle every alias in our | ||
73 | * current VM view. | ||
74 | * - VIPT aliasing: need to handle one alias in our current VM view. | ||
75 | * | ||
76 | * If we need to handle aliasing: | ||
77 | * If the page only exists in the page cache and there are no user | ||
78 | * space mappings, we can be lazy and remember that we may have dirty | ||
79 | * kernel cache lines for later. Otherwise, we assume we have | ||
80 | * aliasing mappings. | ||
81 | */ | ||
82 | void flush_dcache_page(struct page *page) | ||
83 | { | ||
84 | struct address_space *mapping = page_mapping(page); | ||
85 | |||
86 | if (cache_is_vipt_nonaliasing()) | ||
87 | return; | ||
88 | |||
89 | if (mapping && !mapping_mapped(mapping)) | ||
90 | set_bit(PG_dcache_dirty, &page->flags); | ||
91 | else | ||
92 | __flush_dcache_page(mapping, page); | ||
93 | } | ||
94 | EXPORT_SYMBOL(flush_dcache_page); | ||
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c new file mode 100644 index 000000000000..41156c5370f7 --- /dev/null +++ b/arch/arm/mm/init.c | |||
@@ -0,0 +1,621 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/init.c | ||
3 | * | ||
4 | * Copyright (C) 1995-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/config.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/errno.h> | ||
13 | #include <linux/ptrace.h> | ||
14 | #include <linux/swap.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/bootmem.h> | ||
17 | #include <linux/mman.h> | ||
18 | #include <linux/nodemask.h> | ||
19 | #include <linux/initrd.h> | ||
20 | |||
21 | #include <asm/mach-types.h> | ||
22 | #include <asm/hardware.h> | ||
23 | #include <asm/setup.h> | ||
24 | #include <asm/tlb.h> | ||
25 | |||
26 | #include <asm/mach/arch.h> | ||
27 | #include <asm/mach/map.h> | ||
28 | |||
29 | #define TABLE_SIZE (2 * PTRS_PER_PTE * sizeof(pte_t)) | ||
30 | |||
31 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
32 | |||
33 | extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; | ||
34 | extern void _stext, _text, _etext, __data_start, _end, __init_begin, __init_end; | ||
35 | extern unsigned long phys_initrd_start; | ||
36 | extern unsigned long phys_initrd_size; | ||
37 | |||
38 | /* | ||
39 | * The sole use of this is to pass memory configuration | ||
40 | * data from paging_init to mem_init. | ||
41 | */ | ||
42 | static struct meminfo meminfo __initdata = { 0, }; | ||
43 | |||
44 | /* | ||
45 | * empty_zero_page is a special page that is used for | ||
46 | * zero-initialized data and COW. | ||
47 | */ | ||
48 | struct page *empty_zero_page; | ||
49 | |||
50 | void show_mem(void) | ||
51 | { | ||
52 | int free = 0, total = 0, reserved = 0; | ||
53 | int shared = 0, cached = 0, slab = 0, node; | ||
54 | |||
55 | printk("Mem-info:\n"); | ||
56 | show_free_areas(); | ||
57 | printk("Free swap: %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10)); | ||
58 | |||
59 | for_each_online_node(node) { | ||
60 | struct page *page, *end; | ||
61 | |||
62 | page = NODE_MEM_MAP(node); | ||
63 | end = page + NODE_DATA(node)->node_spanned_pages; | ||
64 | |||
65 | do { | ||
66 | total++; | ||
67 | if (PageReserved(page)) | ||
68 | reserved++; | ||
69 | else if (PageSwapCache(page)) | ||
70 | cached++; | ||
71 | else if (PageSlab(page)) | ||
72 | slab++; | ||
73 | else if (!page_count(page)) | ||
74 | free++; | ||
75 | else | ||
76 | shared += page_count(page) - 1; | ||
77 | page++; | ||
78 | } while (page < end); | ||
79 | } | ||
80 | |||
81 | printk("%d pages of RAM\n", total); | ||
82 | printk("%d free pages\n", free); | ||
83 | printk("%d reserved pages\n", reserved); | ||
84 | printk("%d slab pages\n", slab); | ||
85 | printk("%d pages shared\n", shared); | ||
86 | printk("%d pages swap cached\n", cached); | ||
87 | } | ||
88 | |||
89 | struct node_info { | ||
90 | unsigned int start; | ||
91 | unsigned int end; | ||
92 | int bootmap_pages; | ||
93 | }; | ||
94 | |||
95 | #define O_PFN_DOWN(x) ((x) >> PAGE_SHIFT) | ||
96 | #define V_PFN_DOWN(x) O_PFN_DOWN(__pa(x)) | ||
97 | |||
98 | #define O_PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT) | ||
99 | #define V_PFN_UP(x) O_PFN_UP(__pa(x)) | ||
100 | |||
101 | #define PFN_SIZE(x) ((x) >> PAGE_SHIFT) | ||
102 | #define PFN_RANGE(s,e) PFN_SIZE(PAGE_ALIGN((unsigned long)(e)) - \ | ||
103 | (((unsigned long)(s)) & PAGE_MASK)) | ||
104 | |||
105 | /* | ||
106 | * FIXME: We really want to avoid allocating the bootmap bitmap | ||
107 | * over the top of the initrd. Hopefully, this is located towards | ||
108 | * the start of a bank, so if we allocate the bootmap bitmap at | ||
109 | * the end, we won't clash. | ||
110 | */ | ||
111 | static unsigned int __init | ||
112 | find_bootmap_pfn(int node, struct meminfo *mi, unsigned int bootmap_pages) | ||
113 | { | ||
114 | unsigned int start_pfn, bank, bootmap_pfn; | ||
115 | |||
116 | start_pfn = V_PFN_UP(&_end); | ||
117 | bootmap_pfn = 0; | ||
118 | |||
119 | for (bank = 0; bank < mi->nr_banks; bank ++) { | ||
120 | unsigned int start, end; | ||
121 | |||
122 | if (mi->bank[bank].node != node) | ||
123 | continue; | ||
124 | |||
125 | start = O_PFN_UP(mi->bank[bank].start); | ||
126 | end = O_PFN_DOWN(mi->bank[bank].size + | ||
127 | mi->bank[bank].start); | ||
128 | |||
129 | if (end < start_pfn) | ||
130 | continue; | ||
131 | |||
132 | if (start < start_pfn) | ||
133 | start = start_pfn; | ||
134 | |||
135 | if (end <= start) | ||
136 | continue; | ||
137 | |||
138 | if (end - start >= bootmap_pages) { | ||
139 | bootmap_pfn = start; | ||
140 | break; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | if (bootmap_pfn == 0) | ||
145 | BUG(); | ||
146 | |||
147 | return bootmap_pfn; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Scan the memory info structure and pull out: | ||
152 | * - the end of memory | ||
153 | * - the number of nodes | ||
154 | * - the pfn range of each node | ||
155 | * - the number of bootmem bitmap pages | ||
156 | */ | ||
157 | static unsigned int __init | ||
158 | find_memend_and_nodes(struct meminfo *mi, struct node_info *np) | ||
159 | { | ||
160 | unsigned int i, bootmem_pages = 0, memend_pfn = 0; | ||
161 | |||
162 | for (i = 0; i < MAX_NUMNODES; i++) { | ||
163 | np[i].start = -1U; | ||
164 | np[i].end = 0; | ||
165 | np[i].bootmap_pages = 0; | ||
166 | } | ||
167 | |||
168 | for (i = 0; i < mi->nr_banks; i++) { | ||
169 | unsigned long start, end; | ||
170 | int node; | ||
171 | |||
172 | if (mi->bank[i].size == 0) { | ||
173 | /* | ||
174 | * Mark this bank with an invalid node number | ||
175 | */ | ||
176 | mi->bank[i].node = -1; | ||
177 | continue; | ||
178 | } | ||
179 | |||
180 | node = mi->bank[i].node; | ||
181 | |||
182 | /* | ||
183 | * Make sure we haven't exceeded the maximum number of nodes | ||
184 | * that we have in this configuration. If we have, we're in | ||
185 | * trouble. (maybe we ought to limit, instead of bugging?) | ||
186 | */ | ||
187 | if (node >= MAX_NUMNODES) | ||
188 | BUG(); | ||
189 | node_set_online(node); | ||
190 | |||
191 | /* | ||
192 | * Get the start and end pfns for this bank | ||
193 | */ | ||
194 | start = O_PFN_UP(mi->bank[i].start); | ||
195 | end = O_PFN_DOWN(mi->bank[i].start + mi->bank[i].size); | ||
196 | |||
197 | if (np[node].start > start) | ||
198 | np[node].start = start; | ||
199 | |||
200 | if (np[node].end < end) | ||
201 | np[node].end = end; | ||
202 | |||
203 | if (memend_pfn < end) | ||
204 | memend_pfn = end; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Calculate the number of pages we require to | ||
209 | * store the bootmem bitmaps. | ||
210 | */ | ||
211 | for_each_online_node(i) { | ||
212 | if (np[i].end == 0) | ||
213 | continue; | ||
214 | |||
215 | np[i].bootmap_pages = bootmem_bootmap_pages(np[i].end - | ||
216 | np[i].start); | ||
217 | bootmem_pages += np[i].bootmap_pages; | ||
218 | } | ||
219 | |||
220 | high_memory = __va(memend_pfn << PAGE_SHIFT); | ||
221 | |||
222 | /* | ||
223 | * This doesn't seem to be used by the Linux memory | ||
224 | * manager any more. If we can get rid of it, we | ||
225 | * also get rid of some of the stuff above as well. | ||
226 | */ | ||
227 | max_low_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET); | ||
228 | max_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET); | ||
229 | |||
230 | return bootmem_pages; | ||
231 | } | ||
232 | |||
233 | static int __init check_initrd(struct meminfo *mi) | ||
234 | { | ||
235 | int initrd_node = -2; | ||
236 | #ifdef CONFIG_BLK_DEV_INITRD | ||
237 | unsigned long end = phys_initrd_start + phys_initrd_size; | ||
238 | |||
239 | /* | ||
240 | * Make sure that the initrd is within a valid area of | ||
241 | * memory. | ||
242 | */ | ||
243 | if (phys_initrd_size) { | ||
244 | unsigned int i; | ||
245 | |||
246 | initrd_node = -1; | ||
247 | |||
248 | for (i = 0; i < mi->nr_banks; i++) { | ||
249 | unsigned long bank_end; | ||
250 | |||
251 | bank_end = mi->bank[i].start + mi->bank[i].size; | ||
252 | |||
253 | if (mi->bank[i].start <= phys_initrd_start && | ||
254 | end <= bank_end) | ||
255 | initrd_node = mi->bank[i].node; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | if (initrd_node == -1) { | ||
260 | printk(KERN_ERR "initrd (0x%08lx - 0x%08lx) extends beyond " | ||
261 | "physical memory - disabling initrd\n", | ||
262 | phys_initrd_start, end); | ||
263 | phys_initrd_start = phys_initrd_size = 0; | ||
264 | } | ||
265 | #endif | ||
266 | |||
267 | return initrd_node; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | * Reserve the various regions of node 0 | ||
272 | */ | ||
273 | static __init void reserve_node_zero(unsigned int bootmap_pfn, unsigned int bootmap_pages) | ||
274 | { | ||
275 | pg_data_t *pgdat = NODE_DATA(0); | ||
276 | unsigned long res_size = 0; | ||
277 | |||
278 | /* | ||
279 | * Register the kernel text and data with bootmem. | ||
280 | * Note that this can only be in node 0. | ||
281 | */ | ||
282 | #ifdef CONFIG_XIP_KERNEL | ||
283 | reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start); | ||
284 | #else | ||
285 | reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); | ||
286 | #endif | ||
287 | |||
288 | /* | ||
289 | * Reserve the page tables. These are already in use, | ||
290 | * and can only be in node 0. | ||
291 | */ | ||
292 | reserve_bootmem_node(pgdat, __pa(swapper_pg_dir), | ||
293 | PTRS_PER_PGD * sizeof(pgd_t)); | ||
294 | |||
295 | /* | ||
296 | * And don't forget to reserve the allocator bitmap, | ||
297 | * which will be freed later. | ||
298 | */ | ||
299 | reserve_bootmem_node(pgdat, bootmap_pfn << PAGE_SHIFT, | ||
300 | bootmap_pages << PAGE_SHIFT); | ||
301 | |||
302 | /* | ||
303 | * Hmm... This should go elsewhere, but we really really need to | ||
304 | * stop things allocating the low memory; ideally we need a better | ||
305 | * implementation of GFP_DMA which does not assume that DMA-able | ||
306 | * memory starts at zero. | ||
307 | */ | ||
308 | if (machine_is_integrator() || machine_is_cintegrator()) | ||
309 | res_size = __pa(swapper_pg_dir) - PHYS_OFFSET; | ||
310 | |||
311 | /* | ||
312 | * These should likewise go elsewhere. They pre-reserve the | ||
313 | * screen memory region at the start of main system memory. | ||
314 | */ | ||
315 | if (machine_is_edb7211()) | ||
316 | res_size = 0x00020000; | ||
317 | if (machine_is_p720t()) | ||
318 | res_size = 0x00014000; | ||
319 | |||
320 | #ifdef CONFIG_SA1111 | ||
321 | /* | ||
322 | * Because of the SA1111 DMA bug, we want to preserve our | ||
323 | * precious DMA-able memory... | ||
324 | */ | ||
325 | res_size = __pa(swapper_pg_dir) - PHYS_OFFSET; | ||
326 | #endif | ||
327 | if (res_size) | ||
328 | reserve_bootmem_node(pgdat, PHYS_OFFSET, res_size); | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Register all available RAM in this node with the bootmem allocator. | ||
333 | */ | ||
334 | static inline void free_bootmem_node_bank(int node, struct meminfo *mi) | ||
335 | { | ||
336 | pg_data_t *pgdat = NODE_DATA(node); | ||
337 | int bank; | ||
338 | |||
339 | for (bank = 0; bank < mi->nr_banks; bank++) | ||
340 | if (mi->bank[bank].node == node) | ||
341 | free_bootmem_node(pgdat, mi->bank[bank].start, | ||
342 | mi->bank[bank].size); | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * Initialise the bootmem allocator for all nodes. This is called | ||
347 | * early during the architecture specific initialisation. | ||
348 | */ | ||
349 | static void __init bootmem_init(struct meminfo *mi) | ||
350 | { | ||
351 | struct node_info node_info[MAX_NUMNODES], *np = node_info; | ||
352 | unsigned int bootmap_pages, bootmap_pfn, map_pg; | ||
353 | int node, initrd_node; | ||
354 | |||
355 | bootmap_pages = find_memend_and_nodes(mi, np); | ||
356 | bootmap_pfn = find_bootmap_pfn(0, mi, bootmap_pages); | ||
357 | initrd_node = check_initrd(mi); | ||
358 | |||
359 | map_pg = bootmap_pfn; | ||
360 | |||
361 | /* | ||
362 | * Initialise the bootmem nodes. | ||
363 | * | ||
364 | * What we really want to do is: | ||
365 | * | ||
366 | * unmap_all_regions_except_kernel(); | ||
367 | * for_each_node_in_reverse_order(node) { | ||
368 | * map_node(node); | ||
369 | * allocate_bootmem_map(node); | ||
370 | * init_bootmem_node(node); | ||
371 | * free_bootmem_node(node); | ||
372 | * } | ||
373 | * | ||
374 | * but this is a 2.5-type change. For now, we just set | ||
375 | * the nodes up in reverse order. | ||
376 | * | ||
377 | * (we could also do with rolling bootmem_init and paging_init | ||
378 | * into one generic "memory_init" type function). | ||
379 | */ | ||
380 | np += num_online_nodes() - 1; | ||
381 | for (node = num_online_nodes() - 1; node >= 0; node--, np--) { | ||
382 | /* | ||
383 | * If there are no pages in this node, ignore it. | ||
384 | * Note that node 0 must always have some pages. | ||
385 | */ | ||
386 | if (np->end == 0 || !node_online(node)) { | ||
387 | if (node == 0) | ||
388 | BUG(); | ||
389 | continue; | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | * Initialise the bootmem allocator. | ||
394 | */ | ||
395 | init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end); | ||
396 | free_bootmem_node_bank(node, mi); | ||
397 | map_pg += np->bootmap_pages; | ||
398 | |||
399 | /* | ||
400 | * If this is node 0, we need to reserve some areas ASAP - | ||
401 | * we may use bootmem on node 0 to setup the other nodes. | ||
402 | */ | ||
403 | if (node == 0) | ||
404 | reserve_node_zero(bootmap_pfn, bootmap_pages); | ||
405 | } | ||
406 | |||
407 | |||
408 | #ifdef CONFIG_BLK_DEV_INITRD | ||
409 | if (phys_initrd_size && initrd_node >= 0) { | ||
410 | reserve_bootmem_node(NODE_DATA(initrd_node), phys_initrd_start, | ||
411 | phys_initrd_size); | ||
412 | initrd_start = __phys_to_virt(phys_initrd_start); | ||
413 | initrd_end = initrd_start + phys_initrd_size; | ||
414 | } | ||
415 | #endif | ||
416 | |||
417 | BUG_ON(map_pg != bootmap_pfn + bootmap_pages); | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * paging_init() sets up the page tables, initialises the zone memory | ||
422 | * maps, and sets up the zero page, bad page and bad page tables. | ||
423 | */ | ||
424 | void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc) | ||
425 | { | ||
426 | void *zero_page; | ||
427 | int node; | ||
428 | |||
429 | bootmem_init(mi); | ||
430 | |||
431 | memcpy(&meminfo, mi, sizeof(meminfo)); | ||
432 | |||
433 | /* | ||
434 | * allocate the zero page. Note that we count on this going ok. | ||
435 | */ | ||
436 | zero_page = alloc_bootmem_low_pages(PAGE_SIZE); | ||
437 | |||
438 | /* | ||
439 | * initialise the page tables. | ||
440 | */ | ||
441 | memtable_init(mi); | ||
442 | if (mdesc->map_io) | ||
443 | mdesc->map_io(); | ||
444 | flush_tlb_all(); | ||
445 | |||
446 | /* | ||
447 | * initialise the zones within each node | ||
448 | */ | ||
449 | for_each_online_node(node) { | ||
450 | unsigned long zone_size[MAX_NR_ZONES]; | ||
451 | unsigned long zhole_size[MAX_NR_ZONES]; | ||
452 | struct bootmem_data *bdata; | ||
453 | pg_data_t *pgdat; | ||
454 | int i; | ||
455 | |||
456 | /* | ||
457 | * Initialise the zone size information. | ||
458 | */ | ||
459 | for (i = 0; i < MAX_NR_ZONES; i++) { | ||
460 | zone_size[i] = 0; | ||
461 | zhole_size[i] = 0; | ||
462 | } | ||
463 | |||
464 | pgdat = NODE_DATA(node); | ||
465 | bdata = pgdat->bdata; | ||
466 | |||
467 | /* | ||
468 | * The size of this node has already been determined. | ||
469 | * If we need to do anything fancy with the allocation | ||
470 | * of this memory to the zones, now is the time to do | ||
471 | * it. | ||
472 | */ | ||
473 | zone_size[0] = bdata->node_low_pfn - | ||
474 | (bdata->node_boot_start >> PAGE_SHIFT); | ||
475 | |||
476 | /* | ||
477 | * If this zone has zero size, skip it. | ||
478 | */ | ||
479 | if (!zone_size[0]) | ||
480 | continue; | ||
481 | |||
482 | /* | ||
483 | * For each bank in this node, calculate the size of the | ||
484 | * holes. holes = node_size - sum(bank_sizes_in_node) | ||
485 | */ | ||
486 | zhole_size[0] = zone_size[0]; | ||
487 | for (i = 0; i < mi->nr_banks; i++) { | ||
488 | if (mi->bank[i].node != node) | ||
489 | continue; | ||
490 | |||
491 | zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT; | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * Adjust the sizes according to any special | ||
496 | * requirements for this machine type. | ||
497 | */ | ||
498 | arch_adjust_zones(node, zone_size, zhole_size); | ||
499 | |||
500 | free_area_init_node(node, pgdat, zone_size, | ||
501 | bdata->node_boot_start >> PAGE_SHIFT, zhole_size); | ||
502 | } | ||
503 | |||
504 | /* | ||
505 | * finish off the bad pages once | ||
506 | * the mem_map is initialised | ||
507 | */ | ||
508 | memzero(zero_page, PAGE_SIZE); | ||
509 | empty_zero_page = virt_to_page(zero_page); | ||
510 | flush_dcache_page(empty_zero_page); | ||
511 | } | ||
512 | |||
513 | static inline void free_area(unsigned long addr, unsigned long end, char *s) | ||
514 | { | ||
515 | unsigned int size = (end - addr) >> 10; | ||
516 | |||
517 | for (; addr < end; addr += PAGE_SIZE) { | ||
518 | struct page *page = virt_to_page(addr); | ||
519 | ClearPageReserved(page); | ||
520 | set_page_count(page, 1); | ||
521 | free_page(addr); | ||
522 | totalram_pages++; | ||
523 | } | ||
524 | |||
525 | if (size && s) | ||
526 | printk(KERN_INFO "Freeing %s memory: %dK\n", s, size); | ||
527 | } | ||
528 | |||
529 | /* | ||
530 | * mem_init() marks the free areas in the mem_map and tells us how much | ||
531 | * memory is free. This is done after various parts of the system have | ||
532 | * claimed their memory after the kernel image. | ||
533 | */ | ||
534 | void __init mem_init(void) | ||
535 | { | ||
536 | unsigned int codepages, datapages, initpages; | ||
537 | int i, node; | ||
538 | |||
539 | codepages = &_etext - &_text; | ||
540 | datapages = &_end - &__data_start; | ||
541 | initpages = &__init_end - &__init_begin; | ||
542 | |||
543 | #ifndef CONFIG_DISCONTIGMEM | ||
544 | max_mapnr = virt_to_page(high_memory) - mem_map; | ||
545 | #endif | ||
546 | |||
547 | /* | ||
548 | * We may have non-contiguous memory. | ||
549 | */ | ||
550 | if (meminfo.nr_banks != 1) | ||
551 | create_memmap_holes(&meminfo); | ||
552 | |||
553 | /* this will put all unused low memory onto the freelists */ | ||
554 | for_each_online_node(node) { | ||
555 | pg_data_t *pgdat = NODE_DATA(node); | ||
556 | |||
557 | if (pgdat->node_spanned_pages != 0) | ||
558 | totalram_pages += free_all_bootmem_node(pgdat); | ||
559 | } | ||
560 | |||
561 | #ifdef CONFIG_SA1111 | ||
562 | /* now that our DMA memory is actually so designated, we can free it */ | ||
563 | free_area(PAGE_OFFSET, (unsigned long)swapper_pg_dir, NULL); | ||
564 | #endif | ||
565 | |||
566 | /* | ||
567 | * Since our memory may not be contiguous, calculate the | ||
568 | * real number of pages we have in this system | ||
569 | */ | ||
570 | printk(KERN_INFO "Memory:"); | ||
571 | |||
572 | num_physpages = 0; | ||
573 | for (i = 0; i < meminfo.nr_banks; i++) { | ||
574 | num_physpages += meminfo.bank[i].size >> PAGE_SHIFT; | ||
575 | printk(" %ldMB", meminfo.bank[i].size >> 20); | ||
576 | } | ||
577 | |||
578 | printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); | ||
579 | printk(KERN_NOTICE "Memory: %luKB available (%dK code, " | ||
580 | "%dK data, %dK init)\n", | ||
581 | (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), | ||
582 | codepages >> 10, datapages >> 10, initpages >> 10); | ||
583 | |||
584 | if (PAGE_SIZE >= 16384 && num_physpages <= 128) { | ||
585 | extern int sysctl_overcommit_memory; | ||
586 | /* | ||
587 | * On a machine this small we won't get | ||
588 | * anywhere without overcommit, so turn | ||
589 | * it on by default. | ||
590 | */ | ||
591 | sysctl_overcommit_memory = OVERCOMMIT_ALWAYS; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | void free_initmem(void) | ||
596 | { | ||
597 | if (!machine_is_integrator() && !machine_is_cintegrator()) { | ||
598 | free_area((unsigned long)(&__init_begin), | ||
599 | (unsigned long)(&__init_end), | ||
600 | "init"); | ||
601 | } | ||
602 | } | ||
603 | |||
604 | #ifdef CONFIG_BLK_DEV_INITRD | ||
605 | |||
606 | static int keep_initrd; | ||
607 | |||
608 | void free_initrd_mem(unsigned long start, unsigned long end) | ||
609 | { | ||
610 | if (!keep_initrd) | ||
611 | free_area(start, end, "initrd"); | ||
612 | } | ||
613 | |||
614 | static int __init keepinitrd_setup(char *__unused) | ||
615 | { | ||
616 | keep_initrd = 1; | ||
617 | return 1; | ||
618 | } | ||
619 | |||
620 | __setup("keepinitrd", keepinitrd_setup); | ||
621 | #endif | ||
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c new file mode 100644 index 000000000000..00bb8fd37a59 --- /dev/null +++ b/arch/arm/mm/ioremap.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/ioremap.c | ||
3 | * | ||
4 | * Re-map IO memory to kernel address space so that we can access it. | ||
5 | * | ||
6 | * (C) Copyright 1995 1996 Linus Torvalds | ||
7 | * | ||
8 | * Hacked for ARM by Phil Blundell <philb@gnu.org> | ||
9 | * Hacked to allow all architectures to build, and various cleanups | ||
10 | * by Russell King | ||
11 | * | ||
12 | * This allows a driver to remap an arbitrary region of bus memory into | ||
13 | * virtual space. One should *only* use readl, writel, memcpy_toio and | ||
14 | * so on with such remapped areas. | ||
15 | * | ||
16 | * Because the ARM only has a 32-bit address space we can't address the | ||
17 | * whole of the (physical) PCI space at once. PCI huge-mode addressing | ||
18 | * allows us to circumvent this restriction by splitting PCI space into | ||
19 | * two 2GB chunks and mapping only one at a time into processor memory. | ||
20 | * We use MMU protection domains to trap any attempt to access the bank | ||
21 | * that is not currently mapped. (This isn't fully implemented yet.) | ||
22 | */ | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/mm.h> | ||
26 | #include <linux/vmalloc.h> | ||
27 | |||
28 | #include <asm/cacheflush.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/tlbflush.h> | ||
31 | |||
32 | static inline void | ||
33 | remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, | ||
34 | unsigned long phys_addr, pgprot_t pgprot) | ||
35 | { | ||
36 | unsigned long end; | ||
37 | |||
38 | address &= ~PMD_MASK; | ||
39 | end = address + size; | ||
40 | if (end > PMD_SIZE) | ||
41 | end = PMD_SIZE; | ||
42 | BUG_ON(address >= end); | ||
43 | do { | ||
44 | if (!pte_none(*pte)) | ||
45 | goto bad; | ||
46 | |||
47 | set_pte(pte, pfn_pte(phys_addr >> PAGE_SHIFT, pgprot)); | ||
48 | address += PAGE_SIZE; | ||
49 | phys_addr += PAGE_SIZE; | ||
50 | pte++; | ||
51 | } while (address && (address < end)); | ||
52 | return; | ||
53 | |||
54 | bad: | ||
55 | printk("remap_area_pte: page already exists\n"); | ||
56 | BUG(); | ||
57 | } | ||
58 | |||
59 | static inline int | ||
60 | remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, | ||
61 | unsigned long phys_addr, unsigned long flags) | ||
62 | { | ||
63 | unsigned long end; | ||
64 | pgprot_t pgprot; | ||
65 | |||
66 | address &= ~PGDIR_MASK; | ||
67 | end = address + size; | ||
68 | |||
69 | if (end > PGDIR_SIZE) | ||
70 | end = PGDIR_SIZE; | ||
71 | |||
72 | phys_addr -= address; | ||
73 | BUG_ON(address >= end); | ||
74 | |||
75 | pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags); | ||
76 | do { | ||
77 | pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); | ||
78 | if (!pte) | ||
79 | return -ENOMEM; | ||
80 | remap_area_pte(pte, address, end - address, address + phys_addr, pgprot); | ||
81 | address = (address + PMD_SIZE) & PMD_MASK; | ||
82 | pmd++; | ||
83 | } while (address && (address < end)); | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int | ||
88 | remap_area_pages(unsigned long start, unsigned long phys_addr, | ||
89 | unsigned long size, unsigned long flags) | ||
90 | { | ||
91 | unsigned long address = start; | ||
92 | unsigned long end = start + size; | ||
93 | int err = 0; | ||
94 | pgd_t * dir; | ||
95 | |||
96 | phys_addr -= address; | ||
97 | dir = pgd_offset(&init_mm, address); | ||
98 | BUG_ON(address >= end); | ||
99 | spin_lock(&init_mm.page_table_lock); | ||
100 | do { | ||
101 | pmd_t *pmd = pmd_alloc(&init_mm, dir, address); | ||
102 | if (!pmd) { | ||
103 | err = -ENOMEM; | ||
104 | break; | ||
105 | } | ||
106 | if (remap_area_pmd(pmd, address, end - address, | ||
107 | phys_addr + address, flags)) { | ||
108 | err = -ENOMEM; | ||
109 | break; | ||
110 | } | ||
111 | |||
112 | address = (address + PGDIR_SIZE) & PGDIR_MASK; | ||
113 | dir++; | ||
114 | } while (address && (address < end)); | ||
115 | |||
116 | spin_unlock(&init_mm.page_table_lock); | ||
117 | flush_cache_vmap(start, end); | ||
118 | return err; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Remap an arbitrary physical address space into the kernel virtual | ||
123 | * address space. Needed when the kernel wants to access high addresses | ||
124 | * directly. | ||
125 | * | ||
126 | * NOTE! We need to allow non-page-aligned mappings too: we will obviously | ||
127 | * have to convert them into an offset in a page-aligned mapping, but the | ||
128 | * caller shouldn't need to know that small detail. | ||
129 | * | ||
130 | * 'flags' are the extra L_PTE_ flags that you want to specify for this | ||
131 | * mapping. See include/asm-arm/proc-armv/pgtable.h for more information. | ||
132 | */ | ||
133 | void __iomem * | ||
134 | __ioremap(unsigned long phys_addr, size_t size, unsigned long flags, | ||
135 | unsigned long align) | ||
136 | { | ||
137 | void * addr; | ||
138 | struct vm_struct * area; | ||
139 | unsigned long offset, last_addr; | ||
140 | |||
141 | /* Don't allow wraparound or zero size */ | ||
142 | last_addr = phys_addr + size - 1; | ||
143 | if (!size || last_addr < phys_addr) | ||
144 | return NULL; | ||
145 | |||
146 | /* | ||
147 | * Mappings have to be page-aligned | ||
148 | */ | ||
149 | offset = phys_addr & ~PAGE_MASK; | ||
150 | phys_addr &= PAGE_MASK; | ||
151 | size = PAGE_ALIGN(last_addr + 1) - phys_addr; | ||
152 | |||
153 | /* | ||
154 | * Ok, go for it.. | ||
155 | */ | ||
156 | area = get_vm_area(size, VM_IOREMAP); | ||
157 | if (!area) | ||
158 | return NULL; | ||
159 | addr = area->addr; | ||
160 | if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { | ||
161 | vfree(addr); | ||
162 | return NULL; | ||
163 | } | ||
164 | return (void __iomem *) (offset + (char *)addr); | ||
165 | } | ||
166 | EXPORT_SYMBOL(__ioremap); | ||
167 | |||
168 | void __iounmap(void __iomem *addr) | ||
169 | { | ||
170 | vfree((void *) (PAGE_MASK & (unsigned long) addr)); | ||
171 | } | ||
172 | EXPORT_SYMBOL(__iounmap); | ||
diff --git a/arch/arm/mm/minicache.c b/arch/arm/mm/minicache.c new file mode 100644 index 000000000000..dedf2ab01b2a --- /dev/null +++ b/arch/arm/mm/minicache.c | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/minicache.c | ||
3 | * | ||
4 | * Copyright (C) 2001 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This handles the mini data cache, as found on SA11x0 and XScale | ||
11 | * processors. When we copy a user page page, we map it in such a way | ||
12 | * that accesses to this page will not touch the main data cache, but | ||
13 | * will be cached in the mini data cache. This prevents us thrashing | ||
14 | * the main data cache on page faults. | ||
15 | */ | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/mm.h> | ||
18 | |||
19 | #include <asm/page.h> | ||
20 | #include <asm/pgtable.h> | ||
21 | #include <asm/tlbflush.h> | ||
22 | |||
23 | /* | ||
24 | * 0xffff8000 to 0xffffffff is reserved for any ARM architecture | ||
25 | * specific hacks for copying pages efficiently. | ||
26 | */ | ||
27 | #define minicache_address (0xffff8000) | ||
28 | #define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \ | ||
29 | L_PTE_CACHEABLE) | ||
30 | |||
31 | static pte_t *minicache_pte; | ||
32 | |||
33 | /* | ||
34 | * Note that this is intended to be called only from the copy_user_page | ||
35 | * asm code; anything else will require special locking to prevent the | ||
36 | * mini-cache space being re-used. (Note: probably preempt unsafe). | ||
37 | * | ||
38 | * We rely on the fact that the minicache is 2K, and we'll be pushing | ||
39 | * 4K of data through it, so we don't actually have to specifically | ||
40 | * flush the minicache when we change the mapping. | ||
41 | * | ||
42 | * Note also: assert(PAGE_OFFSET <= virt < high_memory). | ||
43 | * Unsafe: preempt, kmap. | ||
44 | */ | ||
45 | unsigned long map_page_minicache(unsigned long virt) | ||
46 | { | ||
47 | set_pte(minicache_pte, pfn_pte(__pa(virt) >> PAGE_SHIFT, minicache_pgprot)); | ||
48 | flush_tlb_kernel_page(minicache_address); | ||
49 | |||
50 | return minicache_address; | ||
51 | } | ||
52 | |||
53 | static int __init minicache_init(void) | ||
54 | { | ||
55 | pgd_t *pgd; | ||
56 | pmd_t *pmd; | ||
57 | |||
58 | spin_lock(&init_mm.page_table_lock); | ||
59 | |||
60 | pgd = pgd_offset_k(minicache_address); | ||
61 | pmd = pmd_alloc(&init_mm, pgd, minicache_address); | ||
62 | if (!pmd) | ||
63 | BUG(); | ||
64 | minicache_pte = pte_alloc_kernel(&init_mm, pmd, minicache_address); | ||
65 | if (!minicache_pte) | ||
66 | BUG(); | ||
67 | |||
68 | spin_unlock(&init_mm.page_table_lock); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | core_initcall(minicache_init); | ||
diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c new file mode 100644 index 000000000000..f5a87db8b498 --- /dev/null +++ b/arch/arm/mm/mm-armv.c | |||
@@ -0,0 +1,760 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/mm-armv.c | ||
3 | * | ||
4 | * Copyright (C) 1998-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Page table sludge for ARM v3 and v4 processor architectures. | ||
11 | */ | ||
12 | #include <linux/config.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/bootmem.h> | ||
17 | #include <linux/highmem.h> | ||
18 | #include <linux/nodemask.h> | ||
19 | |||
20 | #include <asm/pgalloc.h> | ||
21 | #include <asm/page.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/setup.h> | ||
24 | #include <asm/tlbflush.h> | ||
25 | |||
26 | #include <asm/mach/map.h> | ||
27 | |||
28 | #define CPOLICY_UNCACHED 0 | ||
29 | #define CPOLICY_BUFFERED 1 | ||
30 | #define CPOLICY_WRITETHROUGH 2 | ||
31 | #define CPOLICY_WRITEBACK 3 | ||
32 | #define CPOLICY_WRITEALLOC 4 | ||
33 | |||
34 | static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK; | ||
35 | static unsigned int ecc_mask __initdata = 0; | ||
36 | pgprot_t pgprot_kernel; | ||
37 | |||
38 | EXPORT_SYMBOL(pgprot_kernel); | ||
39 | |||
40 | struct cachepolicy { | ||
41 | const char policy[16]; | ||
42 | unsigned int cr_mask; | ||
43 | unsigned int pmd; | ||
44 | unsigned int pte; | ||
45 | }; | ||
46 | |||
47 | static struct cachepolicy cache_policies[] __initdata = { | ||
48 | { | ||
49 | .policy = "uncached", | ||
50 | .cr_mask = CR_W|CR_C, | ||
51 | .pmd = PMD_SECT_UNCACHED, | ||
52 | .pte = 0, | ||
53 | }, { | ||
54 | .policy = "buffered", | ||
55 | .cr_mask = CR_C, | ||
56 | .pmd = PMD_SECT_BUFFERED, | ||
57 | .pte = PTE_BUFFERABLE, | ||
58 | }, { | ||
59 | .policy = "writethrough", | ||
60 | .cr_mask = 0, | ||
61 | .pmd = PMD_SECT_WT, | ||
62 | .pte = PTE_CACHEABLE, | ||
63 | }, { | ||
64 | .policy = "writeback", | ||
65 | .cr_mask = 0, | ||
66 | .pmd = PMD_SECT_WB, | ||
67 | .pte = PTE_BUFFERABLE|PTE_CACHEABLE, | ||
68 | }, { | ||
69 | .policy = "writealloc", | ||
70 | .cr_mask = 0, | ||
71 | .pmd = PMD_SECT_WBWA, | ||
72 | .pte = PTE_BUFFERABLE|PTE_CACHEABLE, | ||
73 | } | ||
74 | }; | ||
75 | |||
76 | /* | ||
77 | * These are useful for identifing cache coherency | ||
78 | * problems by allowing the cache or the cache and | ||
79 | * writebuffer to be turned off. (Note: the write | ||
80 | * buffer should not be on and the cache off). | ||
81 | */ | ||
82 | static void __init early_cachepolicy(char **p) | ||
83 | { | ||
84 | int i; | ||
85 | |||
86 | for (i = 0; i < ARRAY_SIZE(cache_policies); i++) { | ||
87 | int len = strlen(cache_policies[i].policy); | ||
88 | |||
89 | if (memcmp(*p, cache_policies[i].policy, len) == 0) { | ||
90 | cachepolicy = i; | ||
91 | cr_alignment &= ~cache_policies[i].cr_mask; | ||
92 | cr_no_alignment &= ~cache_policies[i].cr_mask; | ||
93 | *p += len; | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | if (i == ARRAY_SIZE(cache_policies)) | ||
98 | printk(KERN_ERR "ERROR: unknown or unsupported cache policy\n"); | ||
99 | flush_cache_all(); | ||
100 | set_cr(cr_alignment); | ||
101 | } | ||
102 | |||
103 | static void __init early_nocache(char **__unused) | ||
104 | { | ||
105 | char *p = "buffered"; | ||
106 | printk(KERN_WARNING "nocache is deprecated; use cachepolicy=%s\n", p); | ||
107 | early_cachepolicy(&p); | ||
108 | } | ||
109 | |||
110 | static void __init early_nowrite(char **__unused) | ||
111 | { | ||
112 | char *p = "uncached"; | ||
113 | printk(KERN_WARNING "nowb is deprecated; use cachepolicy=%s\n", p); | ||
114 | early_cachepolicy(&p); | ||
115 | } | ||
116 | |||
117 | static void __init early_ecc(char **p) | ||
118 | { | ||
119 | if (memcmp(*p, "on", 2) == 0) { | ||
120 | ecc_mask = PMD_PROTECTION; | ||
121 | *p += 2; | ||
122 | } else if (memcmp(*p, "off", 3) == 0) { | ||
123 | ecc_mask = 0; | ||
124 | *p += 3; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | __early_param("nocache", early_nocache); | ||
129 | __early_param("nowb", early_nowrite); | ||
130 | __early_param("cachepolicy=", early_cachepolicy); | ||
131 | __early_param("ecc=", early_ecc); | ||
132 | |||
133 | static int __init noalign_setup(char *__unused) | ||
134 | { | ||
135 | cr_alignment &= ~CR_A; | ||
136 | cr_no_alignment &= ~CR_A; | ||
137 | set_cr(cr_alignment); | ||
138 | return 1; | ||
139 | } | ||
140 | |||
141 | __setup("noalign", noalign_setup); | ||
142 | |||
143 | #define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD) | ||
144 | |||
145 | /* | ||
146 | * need to get a 16k page for level 1 | ||
147 | */ | ||
148 | pgd_t *get_pgd_slow(struct mm_struct *mm) | ||
149 | { | ||
150 | pgd_t *new_pgd, *init_pgd; | ||
151 | pmd_t *new_pmd, *init_pmd; | ||
152 | pte_t *new_pte, *init_pte; | ||
153 | |||
154 | new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2); | ||
155 | if (!new_pgd) | ||
156 | goto no_pgd; | ||
157 | |||
158 | memzero(new_pgd, FIRST_KERNEL_PGD_NR * sizeof(pgd_t)); | ||
159 | |||
160 | init_pgd = pgd_offset_k(0); | ||
161 | |||
162 | if (!vectors_high()) { | ||
163 | /* | ||
164 | * This lock is here just to satisfy pmd_alloc and pte_lock | ||
165 | */ | ||
166 | spin_lock(&mm->page_table_lock); | ||
167 | |||
168 | /* | ||
169 | * On ARM, first page must always be allocated since it | ||
170 | * contains the machine vectors. | ||
171 | */ | ||
172 | new_pmd = pmd_alloc(mm, new_pgd, 0); | ||
173 | if (!new_pmd) | ||
174 | goto no_pmd; | ||
175 | |||
176 | new_pte = pte_alloc_map(mm, new_pmd, 0); | ||
177 | if (!new_pte) | ||
178 | goto no_pte; | ||
179 | |||
180 | init_pmd = pmd_offset(init_pgd, 0); | ||
181 | init_pte = pte_offset_map_nested(init_pmd, 0); | ||
182 | set_pte(new_pte, *init_pte); | ||
183 | pte_unmap_nested(init_pte); | ||
184 | pte_unmap(new_pte); | ||
185 | |||
186 | spin_unlock(&mm->page_table_lock); | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * Copy over the kernel and IO PGD entries | ||
191 | */ | ||
192 | memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR, | ||
193 | (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t)); | ||
194 | |||
195 | clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t)); | ||
196 | |||
197 | return new_pgd; | ||
198 | |||
199 | no_pte: | ||
200 | spin_unlock(&mm->page_table_lock); | ||
201 | pmd_free(new_pmd); | ||
202 | free_pages((unsigned long)new_pgd, 2); | ||
203 | return NULL; | ||
204 | |||
205 | no_pmd: | ||
206 | spin_unlock(&mm->page_table_lock); | ||
207 | free_pages((unsigned long)new_pgd, 2); | ||
208 | return NULL; | ||
209 | |||
210 | no_pgd: | ||
211 | return NULL; | ||
212 | } | ||
213 | |||
214 | void free_pgd_slow(pgd_t *pgd) | ||
215 | { | ||
216 | pmd_t *pmd; | ||
217 | struct page *pte; | ||
218 | |||
219 | if (!pgd) | ||
220 | return; | ||
221 | |||
222 | /* pgd is always present and good */ | ||
223 | pmd = (pmd_t *)pgd; | ||
224 | if (pmd_none(*pmd)) | ||
225 | goto free; | ||
226 | if (pmd_bad(*pmd)) { | ||
227 | pmd_ERROR(*pmd); | ||
228 | pmd_clear(pmd); | ||
229 | goto free; | ||
230 | } | ||
231 | |||
232 | pte = pmd_page(*pmd); | ||
233 | pmd_clear(pmd); | ||
234 | dec_page_state(nr_page_table_pages); | ||
235 | pte_free(pte); | ||
236 | pmd_free(pmd); | ||
237 | free: | ||
238 | free_pages((unsigned long) pgd, 2); | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Create a SECTION PGD between VIRT and PHYS in domain | ||
243 | * DOMAIN with protection PROT. This operates on half- | ||
244 | * pgdir entry increments. | ||
245 | */ | ||
246 | static inline void | ||
247 | alloc_init_section(unsigned long virt, unsigned long phys, int prot) | ||
248 | { | ||
249 | pmd_t *pmdp; | ||
250 | |||
251 | pmdp = pmd_offset(pgd_offset_k(virt), virt); | ||
252 | if (virt & (1 << 20)) | ||
253 | pmdp++; | ||
254 | |||
255 | *pmdp = __pmd(phys | prot); | ||
256 | flush_pmd_entry(pmdp); | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * Create a SUPER SECTION PGD between VIRT and PHYS with protection PROT | ||
261 | */ | ||
262 | static inline void | ||
263 | alloc_init_supersection(unsigned long virt, unsigned long phys, int prot) | ||
264 | { | ||
265 | int i; | ||
266 | |||
267 | for (i = 0; i < 16; i += 1) { | ||
268 | alloc_init_section(virt, phys & SUPERSECTION_MASK, | ||
269 | prot | PMD_SECT_SUPER); | ||
270 | |||
271 | virt += (PGDIR_SIZE / 2); | ||
272 | phys += (PGDIR_SIZE / 2); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Add a PAGE mapping between VIRT and PHYS in domain | ||
278 | * DOMAIN with protection PROT. Note that due to the | ||
279 | * way we map the PTEs, we must allocate two PTE_SIZE'd | ||
280 | * blocks - one for the Linux pte table, and one for | ||
281 | * the hardware pte table. | ||
282 | */ | ||
283 | static inline void | ||
284 | alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pgprot_t prot) | ||
285 | { | ||
286 | pmd_t *pmdp; | ||
287 | pte_t *ptep; | ||
288 | |||
289 | pmdp = pmd_offset(pgd_offset_k(virt), virt); | ||
290 | |||
291 | if (pmd_none(*pmdp)) { | ||
292 | unsigned long pmdval; | ||
293 | ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * | ||
294 | sizeof(pte_t)); | ||
295 | |||
296 | pmdval = __pa(ptep) | prot_l1; | ||
297 | pmdp[0] = __pmd(pmdval); | ||
298 | pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); | ||
299 | flush_pmd_entry(pmdp); | ||
300 | } | ||
301 | ptep = pte_offset_kernel(pmdp, virt); | ||
302 | |||
303 | set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, prot)); | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * Clear any PGD mapping. On a two-level page table system, | ||
308 | * the clearance is done by the middle-level functions (pmd) | ||
309 | * rather than the top-level (pgd) functions. | ||
310 | */ | ||
311 | static inline void clear_mapping(unsigned long virt) | ||
312 | { | ||
313 | pmd_clear(pmd_offset(pgd_offset_k(virt), virt)); | ||
314 | } | ||
315 | |||
316 | struct mem_types { | ||
317 | unsigned int prot_pte; | ||
318 | unsigned int prot_l1; | ||
319 | unsigned int prot_sect; | ||
320 | unsigned int domain; | ||
321 | }; | ||
322 | |||
323 | static struct mem_types mem_types[] __initdata = { | ||
324 | [MT_DEVICE] = { | ||
325 | .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | | ||
326 | L_PTE_WRITE, | ||
327 | .prot_l1 = PMD_TYPE_TABLE, | ||
328 | .prot_sect = PMD_TYPE_SECT | PMD_SECT_UNCACHED | | ||
329 | PMD_SECT_AP_WRITE, | ||
330 | .domain = DOMAIN_IO, | ||
331 | }, | ||
332 | [MT_CACHECLEAN] = { | ||
333 | .prot_sect = PMD_TYPE_SECT, | ||
334 | .domain = DOMAIN_KERNEL, | ||
335 | }, | ||
336 | [MT_MINICLEAN] = { | ||
337 | .prot_sect = PMD_TYPE_SECT | PMD_SECT_MINICACHE, | ||
338 | .domain = DOMAIN_KERNEL, | ||
339 | }, | ||
340 | [MT_LOW_VECTORS] = { | ||
341 | .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | | ||
342 | L_PTE_EXEC, | ||
343 | .prot_l1 = PMD_TYPE_TABLE, | ||
344 | .domain = DOMAIN_USER, | ||
345 | }, | ||
346 | [MT_HIGH_VECTORS] = { | ||
347 | .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | | ||
348 | L_PTE_USER | L_PTE_EXEC, | ||
349 | .prot_l1 = PMD_TYPE_TABLE, | ||
350 | .domain = DOMAIN_USER, | ||
351 | }, | ||
352 | [MT_MEMORY] = { | ||
353 | .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE, | ||
354 | .domain = DOMAIN_KERNEL, | ||
355 | }, | ||
356 | [MT_ROM] = { | ||
357 | .prot_sect = PMD_TYPE_SECT, | ||
358 | .domain = DOMAIN_KERNEL, | ||
359 | }, | ||
360 | [MT_IXP2000_DEVICE] = { /* IXP2400 requires XCB=101 for on-chip I/O */ | ||
361 | .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | | ||
362 | L_PTE_WRITE, | ||
363 | .prot_l1 = PMD_TYPE_TABLE, | ||
364 | .prot_sect = PMD_TYPE_SECT | PMD_SECT_UNCACHED | | ||
365 | PMD_SECT_AP_WRITE | PMD_SECT_BUFFERABLE | | ||
366 | PMD_SECT_TEX(1), | ||
367 | .domain = DOMAIN_IO, | ||
368 | } | ||
369 | }; | ||
370 | |||
371 | /* | ||
372 | * Adjust the PMD section entries according to the CPU in use. | ||
373 | */ | ||
374 | static void __init build_mem_type_table(void) | ||
375 | { | ||
376 | struct cachepolicy *cp; | ||
377 | unsigned int cr = get_cr(); | ||
378 | int cpu_arch = cpu_architecture(); | ||
379 | int i; | ||
380 | |||
381 | #if defined(CONFIG_CPU_DCACHE_DISABLE) | ||
382 | if (cachepolicy > CPOLICY_BUFFERED) | ||
383 | cachepolicy = CPOLICY_BUFFERED; | ||
384 | #elif defined(CONFIG_CPU_DCACHE_WRITETHROUGH) | ||
385 | if (cachepolicy > CPOLICY_WRITETHROUGH) | ||
386 | cachepolicy = CPOLICY_WRITETHROUGH; | ||
387 | #endif | ||
388 | if (cpu_arch < CPU_ARCH_ARMv5) { | ||
389 | if (cachepolicy >= CPOLICY_WRITEALLOC) | ||
390 | cachepolicy = CPOLICY_WRITEBACK; | ||
391 | ecc_mask = 0; | ||
392 | } | ||
393 | |||
394 | if (cpu_arch <= CPU_ARCH_ARMv5) { | ||
395 | for (i = 0; i < ARRAY_SIZE(mem_types); i++) { | ||
396 | if (mem_types[i].prot_l1) | ||
397 | mem_types[i].prot_l1 |= PMD_BIT4; | ||
398 | if (mem_types[i].prot_sect) | ||
399 | mem_types[i].prot_sect |= PMD_BIT4; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | /* | ||
404 | * ARMv6 and above have extended page tables. | ||
405 | */ | ||
406 | if (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP)) { | ||
407 | /* | ||
408 | * bit 4 becomes XN which we must clear for the | ||
409 | * kernel memory mapping. | ||
410 | */ | ||
411 | mem_types[MT_MEMORY].prot_sect &= ~PMD_BIT4; | ||
412 | mem_types[MT_ROM].prot_sect &= ~PMD_BIT4; | ||
413 | /* | ||
414 | * Mark cache clean areas read only from SVC mode | ||
415 | * and no access from userspace. | ||
416 | */ | ||
417 | mem_types[MT_MINICLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; | ||
418 | mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; | ||
419 | } | ||
420 | |||
421 | cp = &cache_policies[cachepolicy]; | ||
422 | |||
423 | if (cpu_arch >= CPU_ARCH_ARMv5) { | ||
424 | mem_types[MT_LOW_VECTORS].prot_pte |= cp->pte & PTE_CACHEABLE; | ||
425 | mem_types[MT_HIGH_VECTORS].prot_pte |= cp->pte & PTE_CACHEABLE; | ||
426 | } else { | ||
427 | mem_types[MT_LOW_VECTORS].prot_pte |= cp->pte; | ||
428 | mem_types[MT_HIGH_VECTORS].prot_pte |= cp->pte; | ||
429 | mem_types[MT_MINICLEAN].prot_sect &= ~PMD_SECT_TEX(1); | ||
430 | } | ||
431 | |||
432 | mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask; | ||
433 | mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask; | ||
434 | mem_types[MT_MEMORY].prot_sect |= ecc_mask | cp->pmd; | ||
435 | mem_types[MT_ROM].prot_sect |= cp->pmd; | ||
436 | |||
437 | for (i = 0; i < 16; i++) { | ||
438 | unsigned long v = pgprot_val(protection_map[i]); | ||
439 | v &= (~(PTE_BUFFERABLE|PTE_CACHEABLE)) | cp->pte; | ||
440 | protection_map[i] = __pgprot(v); | ||
441 | } | ||
442 | |||
443 | pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | | ||
444 | L_PTE_DIRTY | L_PTE_WRITE | | ||
445 | L_PTE_EXEC | cp->pte); | ||
446 | |||
447 | switch (cp->pmd) { | ||
448 | case PMD_SECT_WT: | ||
449 | mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WT; | ||
450 | break; | ||
451 | case PMD_SECT_WB: | ||
452 | case PMD_SECT_WBWA: | ||
453 | mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB; | ||
454 | break; | ||
455 | } | ||
456 | printk("Memory policy: ECC %sabled, Data cache %s\n", | ||
457 | ecc_mask ? "en" : "dis", cp->policy); | ||
458 | } | ||
459 | |||
460 | #define vectors_base() (vectors_high() ? 0xffff0000 : 0) | ||
461 | |||
462 | /* | ||
463 | * Create the page directory entries and any necessary | ||
464 | * page tables for the mapping specified by `md'. We | ||
465 | * are able to cope here with varying sizes and address | ||
466 | * offsets, and we take full advantage of sections and | ||
467 | * supersections. | ||
468 | */ | ||
469 | static void __init create_mapping(struct map_desc *md) | ||
470 | { | ||
471 | unsigned long virt, length; | ||
472 | int prot_sect, prot_l1, domain; | ||
473 | pgprot_t prot_pte; | ||
474 | long off; | ||
475 | |||
476 | if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) { | ||
477 | printk(KERN_WARNING "BUG: not creating mapping for " | ||
478 | "0x%08lx at 0x%08lx in user region\n", | ||
479 | md->physical, md->virtual); | ||
480 | return; | ||
481 | } | ||
482 | |||
483 | if ((md->type == MT_DEVICE || md->type == MT_ROM) && | ||
484 | md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) { | ||
485 | printk(KERN_WARNING "BUG: mapping for 0x%08lx at 0x%08lx " | ||
486 | "overlaps vmalloc space\n", | ||
487 | md->physical, md->virtual); | ||
488 | } | ||
489 | |||
490 | domain = mem_types[md->type].domain; | ||
491 | prot_pte = __pgprot(mem_types[md->type].prot_pte); | ||
492 | prot_l1 = mem_types[md->type].prot_l1 | PMD_DOMAIN(domain); | ||
493 | prot_sect = mem_types[md->type].prot_sect | PMD_DOMAIN(domain); | ||
494 | |||
495 | virt = md->virtual; | ||
496 | off = md->physical - virt; | ||
497 | length = md->length; | ||
498 | |||
499 | if (mem_types[md->type].prot_l1 == 0 && | ||
500 | (virt & 0xfffff || (virt + off) & 0xfffff || (virt + length) & 0xfffff)) { | ||
501 | printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not " | ||
502 | "be mapped using pages, ignoring.\n", | ||
503 | md->physical, md->virtual); | ||
504 | return; | ||
505 | } | ||
506 | |||
507 | while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) { | ||
508 | alloc_init_page(virt, virt + off, prot_l1, prot_pte); | ||
509 | |||
510 | virt += PAGE_SIZE; | ||
511 | length -= PAGE_SIZE; | ||
512 | } | ||
513 | |||
514 | /* N.B. ARMv6 supersections are only defined to work with domain 0. | ||
515 | * Since domain assignments can in fact be arbitrary, the | ||
516 | * 'domain == 0' check below is required to insure that ARMv6 | ||
517 | * supersections are only allocated for domain 0 regardless | ||
518 | * of the actual domain assignments in use. | ||
519 | */ | ||
520 | if (cpu_architecture() >= CPU_ARCH_ARMv6 && domain == 0) { | ||
521 | /* Align to supersection boundary */ | ||
522 | while ((virt & ~SUPERSECTION_MASK || (virt + off) & | ||
523 | ~SUPERSECTION_MASK) && length >= (PGDIR_SIZE / 2)) { | ||
524 | alloc_init_section(virt, virt + off, prot_sect); | ||
525 | |||
526 | virt += (PGDIR_SIZE / 2); | ||
527 | length -= (PGDIR_SIZE / 2); | ||
528 | } | ||
529 | |||
530 | while (length >= SUPERSECTION_SIZE) { | ||
531 | alloc_init_supersection(virt, virt + off, prot_sect); | ||
532 | |||
533 | virt += SUPERSECTION_SIZE; | ||
534 | length -= SUPERSECTION_SIZE; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | * A section mapping covers half a "pgdir" entry. | ||
540 | */ | ||
541 | while (length >= (PGDIR_SIZE / 2)) { | ||
542 | alloc_init_section(virt, virt + off, prot_sect); | ||
543 | |||
544 | virt += (PGDIR_SIZE / 2); | ||
545 | length -= (PGDIR_SIZE / 2); | ||
546 | } | ||
547 | |||
548 | while (length >= PAGE_SIZE) { | ||
549 | alloc_init_page(virt, virt + off, prot_l1, prot_pte); | ||
550 | |||
551 | virt += PAGE_SIZE; | ||
552 | length -= PAGE_SIZE; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | * In order to soft-boot, we need to insert a 1:1 mapping in place of | ||
558 | * the user-mode pages. This will then ensure that we have predictable | ||
559 | * results when turning the mmu off | ||
560 | */ | ||
561 | void setup_mm_for_reboot(char mode) | ||
562 | { | ||
563 | unsigned long pmdval; | ||
564 | pgd_t *pgd; | ||
565 | pmd_t *pmd; | ||
566 | int i; | ||
567 | int cpu_arch = cpu_architecture(); | ||
568 | |||
569 | if (current->mm && current->mm->pgd) | ||
570 | pgd = current->mm->pgd; | ||
571 | else | ||
572 | pgd = init_mm.pgd; | ||
573 | |||
574 | for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++) { | ||
575 | pmdval = (i << PGDIR_SHIFT) | | ||
576 | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | | ||
577 | PMD_TYPE_SECT; | ||
578 | if (cpu_arch <= CPU_ARCH_ARMv5) | ||
579 | pmdval |= PMD_BIT4; | ||
580 | pmd = pmd_offset(pgd + i, i << PGDIR_SHIFT); | ||
581 | pmd[0] = __pmd(pmdval); | ||
582 | pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); | ||
583 | flush_pmd_entry(pmd); | ||
584 | } | ||
585 | } | ||
586 | |||
587 | extern void _stext, _etext; | ||
588 | |||
589 | /* | ||
590 | * Setup initial mappings. We use the page we allocated for zero page to hold | ||
591 | * the mappings, which will get overwritten by the vectors in traps_init(). | ||
592 | * The mappings must be in virtual address order. | ||
593 | */ | ||
594 | void __init memtable_init(struct meminfo *mi) | ||
595 | { | ||
596 | struct map_desc *init_maps, *p, *q; | ||
597 | unsigned long address = 0; | ||
598 | int i; | ||
599 | |||
600 | build_mem_type_table(); | ||
601 | |||
602 | init_maps = p = alloc_bootmem_low_pages(PAGE_SIZE); | ||
603 | |||
604 | #ifdef CONFIG_XIP_KERNEL | ||
605 | p->physical = CONFIG_XIP_PHYS_ADDR & PMD_MASK; | ||
606 | p->virtual = (unsigned long)&_stext & PMD_MASK; | ||
607 | p->length = ((unsigned long)&_etext - p->virtual + ~PMD_MASK) & PMD_MASK; | ||
608 | p->type = MT_ROM; | ||
609 | p ++; | ||
610 | #endif | ||
611 | |||
612 | for (i = 0; i < mi->nr_banks; i++) { | ||
613 | if (mi->bank[i].size == 0) | ||
614 | continue; | ||
615 | |||
616 | p->physical = mi->bank[i].start; | ||
617 | p->virtual = __phys_to_virt(p->physical); | ||
618 | p->length = mi->bank[i].size; | ||
619 | p->type = MT_MEMORY; | ||
620 | p ++; | ||
621 | } | ||
622 | |||
623 | #ifdef FLUSH_BASE | ||
624 | p->physical = FLUSH_BASE_PHYS; | ||
625 | p->virtual = FLUSH_BASE; | ||
626 | p->length = PGDIR_SIZE; | ||
627 | p->type = MT_CACHECLEAN; | ||
628 | p ++; | ||
629 | #endif | ||
630 | |||
631 | #ifdef FLUSH_BASE_MINICACHE | ||
632 | p->physical = FLUSH_BASE_PHYS + PGDIR_SIZE; | ||
633 | p->virtual = FLUSH_BASE_MINICACHE; | ||
634 | p->length = PGDIR_SIZE; | ||
635 | p->type = MT_MINICLEAN; | ||
636 | p ++; | ||
637 | #endif | ||
638 | |||
639 | /* | ||
640 | * Go through the initial mappings, but clear out any | ||
641 | * pgdir entries that are not in the description. | ||
642 | */ | ||
643 | q = init_maps; | ||
644 | do { | ||
645 | if (address < q->virtual || q == p) { | ||
646 | clear_mapping(address); | ||
647 | address += PGDIR_SIZE; | ||
648 | } else { | ||
649 | create_mapping(q); | ||
650 | |||
651 | address = q->virtual + q->length; | ||
652 | address = (address + PGDIR_SIZE - 1) & PGDIR_MASK; | ||
653 | |||
654 | q ++; | ||
655 | } | ||
656 | } while (address != 0); | ||
657 | |||
658 | /* | ||
659 | * Create a mapping for the machine vectors at the high-vectors | ||
660 | * location (0xffff0000). If we aren't using high-vectors, also | ||
661 | * create a mapping at the low-vectors virtual address. | ||
662 | */ | ||
663 | init_maps->physical = virt_to_phys(init_maps); | ||
664 | init_maps->virtual = 0xffff0000; | ||
665 | init_maps->length = PAGE_SIZE; | ||
666 | init_maps->type = MT_HIGH_VECTORS; | ||
667 | create_mapping(init_maps); | ||
668 | |||
669 | if (!vectors_high()) { | ||
670 | init_maps->virtual = 0; | ||
671 | init_maps->type = MT_LOW_VECTORS; | ||
672 | create_mapping(init_maps); | ||
673 | } | ||
674 | |||
675 | flush_cache_all(); | ||
676 | flush_tlb_all(); | ||
677 | } | ||
678 | |||
679 | /* | ||
680 | * Create the architecture specific mappings | ||
681 | */ | ||
682 | void __init iotable_init(struct map_desc *io_desc, int nr) | ||
683 | { | ||
684 | int i; | ||
685 | |||
686 | for (i = 0; i < nr; i++) | ||
687 | create_mapping(io_desc + i); | ||
688 | } | ||
689 | |||
690 | static inline void | ||
691 | free_memmap(int node, unsigned long start_pfn, unsigned long end_pfn) | ||
692 | { | ||
693 | struct page *start_pg, *end_pg; | ||
694 | unsigned long pg, pgend; | ||
695 | |||
696 | /* | ||
697 | * Convert start_pfn/end_pfn to a struct page pointer. | ||
698 | */ | ||
699 | start_pg = pfn_to_page(start_pfn); | ||
700 | end_pg = pfn_to_page(end_pfn); | ||
701 | |||
702 | /* | ||
703 | * Convert to physical addresses, and | ||
704 | * round start upwards and end downwards. | ||
705 | */ | ||
706 | pg = PAGE_ALIGN(__pa(start_pg)); | ||
707 | pgend = __pa(end_pg) & PAGE_MASK; | ||
708 | |||
709 | /* | ||
710 | * If there are free pages between these, | ||
711 | * free the section of the memmap array. | ||
712 | */ | ||
713 | if (pg < pgend) | ||
714 | free_bootmem_node(NODE_DATA(node), pg, pgend - pg); | ||
715 | } | ||
716 | |||
717 | static inline void free_unused_memmap_node(int node, struct meminfo *mi) | ||
718 | { | ||
719 | unsigned long bank_start, prev_bank_end = 0; | ||
720 | unsigned int i; | ||
721 | |||
722 | /* | ||
723 | * [FIXME] This relies on each bank being in address order. This | ||
724 | * may not be the case, especially if the user has provided the | ||
725 | * information on the command line. | ||
726 | */ | ||
727 | for (i = 0; i < mi->nr_banks; i++) { | ||
728 | if (mi->bank[i].size == 0 || mi->bank[i].node != node) | ||
729 | continue; | ||
730 | |||
731 | bank_start = mi->bank[i].start >> PAGE_SHIFT; | ||
732 | if (bank_start < prev_bank_end) { | ||
733 | printk(KERN_ERR "MEM: unordered memory banks. " | ||
734 | "Not freeing memmap.\n"); | ||
735 | break; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * If we had a previous bank, and there is a space | ||
740 | * between the current bank and the previous, free it. | ||
741 | */ | ||
742 | if (prev_bank_end && prev_bank_end != bank_start) | ||
743 | free_memmap(node, prev_bank_end, bank_start); | ||
744 | |||
745 | prev_bank_end = PAGE_ALIGN(mi->bank[i].start + | ||
746 | mi->bank[i].size) >> PAGE_SHIFT; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | /* | ||
751 | * The mem_map array can get very big. Free | ||
752 | * the unused area of the memory map. | ||
753 | */ | ||
754 | void __init create_memmap_holes(struct meminfo *mi) | ||
755 | { | ||
756 | int node; | ||
757 | |||
758 | for_each_online_node(node) | ||
759 | free_unused_memmap_node(node, mi); | ||
760 | } | ||
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c new file mode 100644 index 000000000000..32c4b0e35b37 --- /dev/null +++ b/arch/arm/mm/mmap.c | |||
@@ -0,0 +1,109 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/mmap.c | ||
3 | */ | ||
4 | #include <linux/config.h> | ||
5 | #include <linux/fs.h> | ||
6 | #include <linux/mm.h> | ||
7 | #include <linux/mman.h> | ||
8 | #include <linux/shm.h> | ||
9 | |||
10 | #include <asm/system.h> | ||
11 | |||
12 | #define COLOUR_ALIGN(addr,pgoff) \ | ||
13 | ((((addr)+SHMLBA-1)&~(SHMLBA-1)) + \ | ||
14 | (((pgoff)<<PAGE_SHIFT) & (SHMLBA-1))) | ||
15 | |||
16 | /* | ||
17 | * We need to ensure that shared mappings are correctly aligned to | ||
18 | * avoid aliasing issues with VIPT caches. We need to ensure that | ||
19 | * a specific page of an object is always mapped at a multiple of | ||
20 | * SHMLBA bytes. | ||
21 | * | ||
22 | * We unconditionally provide this function for all cases, however | ||
23 | * in the VIVT case, we optimise out the alignment rules. | ||
24 | */ | ||
25 | unsigned long | ||
26 | arch_get_unmapped_area(struct file *filp, unsigned long addr, | ||
27 | unsigned long len, unsigned long pgoff, unsigned long flags) | ||
28 | { | ||
29 | struct mm_struct *mm = current->mm; | ||
30 | struct vm_area_struct *vma; | ||
31 | unsigned long start_addr; | ||
32 | #ifdef CONFIG_CPU_V6 | ||
33 | unsigned int cache_type; | ||
34 | int do_align = 0, aliasing = 0; | ||
35 | |||
36 | /* | ||
37 | * We only need to do colour alignment if either the I or D | ||
38 | * caches alias. This is indicated by bits 9 and 21 of the | ||
39 | * cache type register. | ||
40 | */ | ||
41 | cache_type = read_cpuid(CPUID_CACHETYPE); | ||
42 | if (cache_type != read_cpuid(CPUID_ID)) { | ||
43 | aliasing = (cache_type | cache_type >> 12) & (1 << 11); | ||
44 | if (aliasing) | ||
45 | do_align = filp || flags & MAP_SHARED; | ||
46 | } | ||
47 | #else | ||
48 | #define do_align 0 | ||
49 | #define aliasing 0 | ||
50 | #endif | ||
51 | |||
52 | /* | ||
53 | * We should enforce the MAP_FIXED case. However, currently | ||
54 | * the generic kernel code doesn't allow us to handle this. | ||
55 | */ | ||
56 | if (flags & MAP_FIXED) { | ||
57 | if (aliasing && flags & MAP_SHARED && addr & (SHMLBA - 1)) | ||
58 | return -EINVAL; | ||
59 | return addr; | ||
60 | } | ||
61 | |||
62 | if (len > TASK_SIZE) | ||
63 | return -ENOMEM; | ||
64 | |||
65 | if (addr) { | ||
66 | if (do_align) | ||
67 | addr = COLOUR_ALIGN(addr, pgoff); | ||
68 | else | ||
69 | addr = PAGE_ALIGN(addr); | ||
70 | |||
71 | vma = find_vma(mm, addr); | ||
72 | if (TASK_SIZE - len >= addr && | ||
73 | (!vma || addr + len <= vma->vm_start)) | ||
74 | return addr; | ||
75 | } | ||
76 | start_addr = addr = mm->free_area_cache; | ||
77 | |||
78 | full_search: | ||
79 | if (do_align) | ||
80 | addr = COLOUR_ALIGN(addr, pgoff); | ||
81 | else | ||
82 | addr = PAGE_ALIGN(addr); | ||
83 | |||
84 | for (vma = find_vma(mm, addr); ; vma = vma->vm_next) { | ||
85 | /* At this point: (!vma || addr < vma->vm_end). */ | ||
86 | if (TASK_SIZE - len < addr) { | ||
87 | /* | ||
88 | * Start a new search - just in case we missed | ||
89 | * some holes. | ||
90 | */ | ||
91 | if (start_addr != TASK_UNMAPPED_BASE) { | ||
92 | start_addr = addr = TASK_UNMAPPED_BASE; | ||
93 | goto full_search; | ||
94 | } | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | if (!vma || addr + len <= vma->vm_start) { | ||
98 | /* | ||
99 | * Remember the place where we stopped the search: | ||
100 | */ | ||
101 | mm->free_area_cache = addr + len; | ||
102 | return addr; | ||
103 | } | ||
104 | addr = vma->vm_end; | ||
105 | if (do_align) | ||
106 | addr = COLOUR_ALIGN(addr, pgoff); | ||
107 | } | ||
108 | } | ||
109 | |||
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c new file mode 100644 index 000000000000..0d90227a0a32 --- /dev/null +++ b/arch/arm/mm/mmu.c | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/mmu.c | ||
3 | * | ||
4 | * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | |||
14 | #include <asm/mmu_context.h> | ||
15 | #include <asm/tlbflush.h> | ||
16 | |||
17 | unsigned int cpu_last_asid = { 1 << ASID_BITS }; | ||
18 | |||
19 | /* | ||
20 | * We fork()ed a process, and we need a new context for the child | ||
21 | * to run in. We reserve version 0 for initial tasks so we will | ||
22 | * always allocate an ASID. | ||
23 | */ | ||
24 | void __init_new_context(struct task_struct *tsk, struct mm_struct *mm) | ||
25 | { | ||
26 | mm->context.id = 0; | ||
27 | } | ||
28 | |||
29 | void __new_context(struct mm_struct *mm) | ||
30 | { | ||
31 | unsigned int asid; | ||
32 | |||
33 | asid = ++cpu_last_asid; | ||
34 | if (asid == 0) | ||
35 | asid = cpu_last_asid = 1 << ASID_BITS; | ||
36 | |||
37 | /* | ||
38 | * If we've used up all our ASIDs, we need | ||
39 | * to start a new version and flush the TLB. | ||
40 | */ | ||
41 | if ((asid & ~ASID_MASK) == 0) | ||
42 | flush_tlb_all(); | ||
43 | |||
44 | mm->context.id = asid; | ||
45 | } | ||
diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S new file mode 100644 index 000000000000..1f325231b9e4 --- /dev/null +++ b/arch/arm/mm/proc-arm1020.S | |||
@@ -0,0 +1,530 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm1020.S: MMU functions for ARM1020 | ||
3 | * | ||
4 | * Copyright (C) 2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * These are the low level assembler for performing cache and TLB | ||
23 | * functions on the arm1020. | ||
24 | * | ||
25 | * CONFIG_CPU_ARM1020_CPU_IDLE -> nohlt | ||
26 | */ | ||
27 | #include <linux/linkage.h> | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <asm/assembler.h> | ||
31 | #include <asm/constants.h> | ||
32 | #include <asm/pgtable.h> | ||
33 | #include <asm/procinfo.h> | ||
34 | #include <asm/ptrace.h> | ||
35 | #include <asm/hardware.h> | ||
36 | |||
37 | /* | ||
38 | * This is the maximum size of an area which will be invalidated | ||
39 | * using the single invalidate entry instructions. Anything larger | ||
40 | * than this, and we go for the whole cache. | ||
41 | * | ||
42 | * This value should be chosen such that we choose the cheapest | ||
43 | * alternative. | ||
44 | */ | ||
45 | #define MAX_AREA_SIZE 32768 | ||
46 | |||
47 | /* | ||
48 | * The size of one data cache line. | ||
49 | */ | ||
50 | #define CACHE_DLINESIZE 32 | ||
51 | |||
52 | /* | ||
53 | * The number of data cache segments. | ||
54 | */ | ||
55 | #define CACHE_DSEGMENTS 16 | ||
56 | |||
57 | /* | ||
58 | * The number of lines in a cache segment. | ||
59 | */ | ||
60 | #define CACHE_DENTRIES 64 | ||
61 | |||
62 | /* | ||
63 | * This is the size at which it becomes more efficient to | ||
64 | * clean the whole cache, rather than using the individual | ||
65 | * cache line maintainence instructions. | ||
66 | */ | ||
67 | #define CACHE_DLIMIT 32768 | ||
68 | |||
69 | .text | ||
70 | /* | ||
71 | * cpu_arm1020_proc_init() | ||
72 | */ | ||
73 | ENTRY(cpu_arm1020_proc_init) | ||
74 | mov pc, lr | ||
75 | |||
76 | /* | ||
77 | * cpu_arm1020_proc_fin() | ||
78 | */ | ||
79 | ENTRY(cpu_arm1020_proc_fin) | ||
80 | stmfd sp!, {lr} | ||
81 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
82 | msr cpsr_c, ip | ||
83 | bl arm1020_flush_kern_cache_all | ||
84 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
85 | bic r0, r0, #0x1000 @ ...i............ | ||
86 | bic r0, r0, #0x000e @ ............wca. | ||
87 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
88 | ldmfd sp!, {pc} | ||
89 | |||
90 | /* | ||
91 | * cpu_arm1020_reset(loc) | ||
92 | * | ||
93 | * Perform a soft reset of the system. Put the CPU into the | ||
94 | * same state as it would be if it had been reset, and branch | ||
95 | * to what would be the reset vector. | ||
96 | * | ||
97 | * loc: location to jump to for soft reset | ||
98 | */ | ||
99 | .align 5 | ||
100 | ENTRY(cpu_arm1020_reset) | ||
101 | mov ip, #0 | ||
102 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
103 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
104 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
105 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
106 | bic ip, ip, #0x000f @ ............wcam | ||
107 | bic ip, ip, #0x1100 @ ...i...s........ | ||
108 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
109 | mov pc, r0 | ||
110 | |||
111 | /* | ||
112 | * cpu_arm1020_do_idle() | ||
113 | */ | ||
114 | .align 5 | ||
115 | ENTRY(cpu_arm1020_do_idle) | ||
116 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
117 | mov pc, lr | ||
118 | |||
119 | /* ================================= CACHE ================================ */ | ||
120 | |||
121 | .align 5 | ||
122 | /* | ||
123 | * flush_user_cache_all() | ||
124 | * | ||
125 | * Invalidate all cache entries in a particular address | ||
126 | * space. | ||
127 | */ | ||
128 | ENTRY(arm1020_flush_user_cache_all) | ||
129 | /* FALLTHROUGH */ | ||
130 | /* | ||
131 | * flush_kern_cache_all() | ||
132 | * | ||
133 | * Clean and invalidate the entire cache. | ||
134 | */ | ||
135 | ENTRY(arm1020_flush_kern_cache_all) | ||
136 | mov r2, #VM_EXEC | ||
137 | mov ip, #0 | ||
138 | __flush_whole_cache: | ||
139 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
140 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
141 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 16 segments | ||
142 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
143 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
144 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
145 | subs r3, r3, #1 << 26 | ||
146 | bcs 2b @ entries 63 to 0 | ||
147 | subs r1, r1, #1 << 5 | ||
148 | bcs 1b @ segments 15 to 0 | ||
149 | #endif | ||
150 | tst r2, #VM_EXEC | ||
151 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
152 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
153 | #endif | ||
154 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
155 | mov pc, lr | ||
156 | |||
157 | /* | ||
158 | * flush_user_cache_range(start, end, flags) | ||
159 | * | ||
160 | * Invalidate a range of cache entries in the specified | ||
161 | * address space. | ||
162 | * | ||
163 | * - start - start address (inclusive) | ||
164 | * - end - end address (exclusive) | ||
165 | * - flags - vm_flags for this space | ||
166 | */ | ||
167 | ENTRY(arm1020_flush_user_cache_range) | ||
168 | mov ip, #0 | ||
169 | sub r3, r1, r0 @ calculate total size | ||
170 | cmp r3, #CACHE_DLIMIT | ||
171 | bhs __flush_whole_cache | ||
172 | |||
173 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
174 | mcr p15, 0, ip, c7, c10, 4 | ||
175 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
176 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
177 | add r0, r0, #CACHE_DLINESIZE | ||
178 | cmp r0, r1 | ||
179 | blo 1b | ||
180 | #endif | ||
181 | tst r2, #VM_EXEC | ||
182 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
183 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
184 | #endif | ||
185 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
186 | mov pc, lr | ||
187 | |||
188 | /* | ||
189 | * coherent_kern_range(start, end) | ||
190 | * | ||
191 | * Ensure coherency between the Icache and the Dcache in the | ||
192 | * region described by start. If you have non-snooping | ||
193 | * Harvard caches, you need to implement this function. | ||
194 | * | ||
195 | * - start - virtual start address | ||
196 | * - end - virtual end address | ||
197 | */ | ||
198 | ENTRY(arm1020_coherent_kern_range) | ||
199 | /* FALLTRHOUGH */ | ||
200 | |||
201 | /* | ||
202 | * coherent_user_range(start, end) | ||
203 | * | ||
204 | * Ensure coherency between the Icache and the Dcache in the | ||
205 | * region described by start. If you have non-snooping | ||
206 | * Harvard caches, you need to implement this function. | ||
207 | * | ||
208 | * - start - virtual start address | ||
209 | * - end - virtual end address | ||
210 | */ | ||
211 | ENTRY(arm1020_coherent_user_range) | ||
212 | mov ip, #0 | ||
213 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
214 | mcr p15, 0, ip, c7, c10, 4 | ||
215 | 1: | ||
216 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
217 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
218 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
219 | #endif | ||
220 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
221 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
222 | #endif | ||
223 | add r0, r0, #CACHE_DLINESIZE | ||
224 | cmp r0, r1 | ||
225 | blo 1b | ||
226 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
227 | mov pc, lr | ||
228 | |||
229 | /* | ||
230 | * flush_kern_dcache_page(void *page) | ||
231 | * | ||
232 | * Ensure no D cache aliasing occurs, either with itself or | ||
233 | * the I cache | ||
234 | * | ||
235 | * - page - page aligned address | ||
236 | */ | ||
237 | ENTRY(arm1020_flush_kern_dcache_page) | ||
238 | mov ip, #0 | ||
239 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
240 | add r1, r0, #PAGE_SZ | ||
241 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
242 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
243 | add r0, r0, #CACHE_DLINESIZE | ||
244 | cmp r0, r1 | ||
245 | blo 1b | ||
246 | #endif | ||
247 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
248 | mov pc, lr | ||
249 | |||
250 | /* | ||
251 | * dma_inv_range(start, end) | ||
252 | * | ||
253 | * Invalidate (discard) the specified virtual address range. | ||
254 | * May not write back any entries. If 'start' or 'end' | ||
255 | * are not cache line aligned, those lines must be written | ||
256 | * back. | ||
257 | * | ||
258 | * - start - virtual start address | ||
259 | * - end - virtual end address | ||
260 | * | ||
261 | * (same as v4wb) | ||
262 | */ | ||
263 | ENTRY(arm1020_dma_inv_range) | ||
264 | mov ip, #0 | ||
265 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
266 | tst r0, #CACHE_DLINESIZE - 1 | ||
267 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
268 | mcrne p15, 0, ip, c7, c10, 4 | ||
269 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
270 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
271 | tst r1, #CACHE_DLINESIZE - 1 | ||
272 | mcrne p15, 0, ip, c7, c10, 4 | ||
273 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
274 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
275 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
276 | add r0, r0, #CACHE_DLINESIZE | ||
277 | cmp r0, r1 | ||
278 | blo 1b | ||
279 | #endif | ||
280 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
281 | mov pc, lr | ||
282 | |||
283 | /* | ||
284 | * dma_clean_range(start, end) | ||
285 | * | ||
286 | * Clean the specified virtual address range. | ||
287 | * | ||
288 | * - start - virtual start address | ||
289 | * - end - virtual end address | ||
290 | * | ||
291 | * (same as v4wb) | ||
292 | */ | ||
293 | ENTRY(arm1020_dma_clean_range) | ||
294 | mov ip, #0 | ||
295 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
296 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
297 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
298 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
299 | add r0, r0, #CACHE_DLINESIZE | ||
300 | cmp r0, r1 | ||
301 | blo 1b | ||
302 | #endif | ||
303 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
304 | mov pc, lr | ||
305 | |||
306 | /* | ||
307 | * dma_flush_range(start, end) | ||
308 | * | ||
309 | * Clean and invalidate the specified virtual address range. | ||
310 | * | ||
311 | * - start - virtual start address | ||
312 | * - end - virtual end address | ||
313 | */ | ||
314 | ENTRY(arm1020_dma_flush_range) | ||
315 | mov ip, #0 | ||
316 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
317 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
318 | mcr p15, 0, ip, c7, c10, 4 | ||
319 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
320 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
321 | add r0, r0, #CACHE_DLINESIZE | ||
322 | cmp r0, r1 | ||
323 | blo 1b | ||
324 | #endif | ||
325 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
326 | mov pc, lr | ||
327 | |||
328 | ENTRY(arm1020_cache_fns) | ||
329 | .long arm1020_flush_kern_cache_all | ||
330 | .long arm1020_flush_user_cache_all | ||
331 | .long arm1020_flush_user_cache_range | ||
332 | .long arm1020_coherent_kern_range | ||
333 | .long arm1020_coherent_user_range | ||
334 | .long arm1020_flush_kern_dcache_page | ||
335 | .long arm1020_dma_inv_range | ||
336 | .long arm1020_dma_clean_range | ||
337 | .long arm1020_dma_flush_range | ||
338 | |||
339 | .align 5 | ||
340 | ENTRY(cpu_arm1020_dcache_clean_area) | ||
341 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
342 | mov ip, #0 | ||
343 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
344 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
345 | add r0, r0, #CACHE_DLINESIZE | ||
346 | subs r1, r1, #CACHE_DLINESIZE | ||
347 | bhi 1b | ||
348 | #endif | ||
349 | mov pc, lr | ||
350 | |||
351 | /* =============================== PageTable ============================== */ | ||
352 | |||
353 | /* | ||
354 | * cpu_arm1020_switch_mm(pgd) | ||
355 | * | ||
356 | * Set the translation base pointer to be as described by pgd. | ||
357 | * | ||
358 | * pgd: new page tables | ||
359 | */ | ||
360 | .align 5 | ||
361 | ENTRY(cpu_arm1020_switch_mm) | ||
362 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
363 | mcr p15, 0, r3, c7, c10, 4 | ||
364 | mov r1, #0xF @ 16 segments | ||
365 | 1: mov r3, #0x3F @ 64 entries | ||
366 | 2: mov ip, r3, LSL #26 @ shift up entry | ||
367 | orr ip, ip, r1, LSL #5 @ shift in/up index | ||
368 | mcr p15, 0, ip, c7, c14, 2 @ Clean & Inval DCache entry | ||
369 | mov ip, #0 | ||
370 | mcr p15, 0, ip, c7, c10, 4 | ||
371 | subs r3, r3, #1 | ||
372 | cmp r3, #0 | ||
373 | bge 2b @ entries 3F to 0 | ||
374 | subs r1, r1, #1 | ||
375 | cmp r1, #0 | ||
376 | bge 1b @ segments 15 to 0 | ||
377 | |||
378 | #endif | ||
379 | mov r1, #0 | ||
380 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
381 | mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache | ||
382 | #endif | ||
383 | mcr p15, 0, r1, c7, c10, 4 @ drain WB | ||
384 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
385 | mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs | ||
386 | mov pc, lr | ||
387 | |||
388 | /* | ||
389 | * cpu_arm1020_set_pte(ptep, pte) | ||
390 | * | ||
391 | * Set a PTE and flush it out | ||
392 | */ | ||
393 | .align 5 | ||
394 | ENTRY(cpu_arm1020_set_pte) | ||
395 | str r1, [r0], #-2048 @ linux version | ||
396 | |||
397 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
398 | |||
399 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
400 | bic r2, r2, #PTE_TYPE_MASK | ||
401 | orr r2, r2, #PTE_TYPE_SMALL | ||
402 | |||
403 | tst r1, #L_PTE_USER @ User? | ||
404 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
405 | |||
406 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
407 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
408 | |||
409 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
410 | movne r2, #0 | ||
411 | |||
412 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
413 | eor r3, r1, #0x0a @ C & small page? | ||
414 | tst r3, #0x0b | ||
415 | biceq r2, r2, #4 | ||
416 | #endif | ||
417 | str r2, [r0] @ hardware version | ||
418 | mov r0, r0 | ||
419 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
420 | mcr p15, 0, r0, c7, c10, 4 | ||
421 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
422 | #endif | ||
423 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
424 | mov pc, lr | ||
425 | |||
426 | __INIT | ||
427 | |||
428 | .type __arm1020_setup, #function | ||
429 | __arm1020_setup: | ||
430 | mov r0, #0 | ||
431 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
432 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
433 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
434 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
435 | ldr r5, arm1020_cr1_clear | ||
436 | bic r0, r0, r5 | ||
437 | ldr r5, arm1020_cr1_set | ||
438 | orr r0, r0, r5 | ||
439 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
440 | orr r0, r0, #0x4000 @ .R.. .... .... .... | ||
441 | #endif | ||
442 | mov pc, lr | ||
443 | .size __arm1020_setup, . - __arm1020_setup | ||
444 | |||
445 | /* | ||
446 | * R | ||
447 | * .RVI ZFRS BLDP WCAM | ||
448 | * .0.1 1001 ..11 0101 /* FIXME: why no V bit? */ | ||
449 | */ | ||
450 | .type arm1020_cr1_clear, #object | ||
451 | .type arm1020_cr1_set, #object | ||
452 | arm1020_cr1_clear: | ||
453 | .word 0x593f | ||
454 | arm1020_cr1_set: | ||
455 | .word 0x1935 | ||
456 | |||
457 | __INITDATA | ||
458 | |||
459 | /* | ||
460 | * Purpose : Function pointers used to access above functions - all calls | ||
461 | * come through these | ||
462 | */ | ||
463 | .type arm1020_processor_functions, #object | ||
464 | arm1020_processor_functions: | ||
465 | .word v4t_early_abort | ||
466 | .word cpu_arm1020_proc_init | ||
467 | .word cpu_arm1020_proc_fin | ||
468 | .word cpu_arm1020_reset | ||
469 | .word cpu_arm1020_do_idle | ||
470 | .word cpu_arm1020_dcache_clean_area | ||
471 | .word cpu_arm1020_switch_mm | ||
472 | .word cpu_arm1020_set_pte | ||
473 | .size arm1020_processor_functions, . - arm1020_processor_functions | ||
474 | |||
475 | .section ".rodata" | ||
476 | |||
477 | .type cpu_arch_name, #object | ||
478 | cpu_arch_name: | ||
479 | .asciz "armv5t" | ||
480 | .size cpu_arch_name, . - cpu_arch_name | ||
481 | |||
482 | .type cpu_elf_name, #object | ||
483 | cpu_elf_name: | ||
484 | .asciz "v5" | ||
485 | .size cpu_elf_name, . - cpu_elf_name | ||
486 | |||
487 | .type cpu_arm1020_name, #object | ||
488 | cpu_arm1020_name: | ||
489 | .ascii "ARM1020" | ||
490 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
491 | .ascii "i" | ||
492 | #endif | ||
493 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
494 | .ascii "d" | ||
495 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
496 | .ascii "(wt)" | ||
497 | #else | ||
498 | .ascii "(wb)" | ||
499 | #endif | ||
500 | #endif | ||
501 | #ifndef CONFIG_CPU_BPREDICT_DISABLE | ||
502 | .ascii "B" | ||
503 | #endif | ||
504 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
505 | .ascii "RR" | ||
506 | #endif | ||
507 | .ascii "\0" | ||
508 | .size cpu_arm1020_name, . - cpu_arm1020_name | ||
509 | |||
510 | .align | ||
511 | |||
512 | .section ".proc.info", #alloc, #execinstr | ||
513 | |||
514 | .type __arm1020_proc_info,#object | ||
515 | __arm1020_proc_info: | ||
516 | .long 0x4104a200 @ ARM 1020T (Architecture v5T) | ||
517 | .long 0xff0ffff0 | ||
518 | .long PMD_TYPE_SECT | \ | ||
519 | PMD_SECT_AP_WRITE | \ | ||
520 | PMD_SECT_AP_READ | ||
521 | b __arm1020_setup | ||
522 | .long cpu_arch_name | ||
523 | .long cpu_elf_name | ||
524 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | ||
525 | .long cpu_arm1020_name | ||
526 | .long arm1020_processor_functions | ||
527 | .long v4wbi_tlb_fns | ||
528 | .long v4wb_user_fns | ||
529 | .long arm1020_cache_fns | ||
530 | .size __arm1020_proc_info, . - __arm1020_proc_info | ||
diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S new file mode 100644 index 000000000000..142a2c2d6f0b --- /dev/null +++ b/arch/arm/mm/proc-arm1020e.S | |||
@@ -0,0 +1,513 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm1020e.S: MMU functions for ARM1020 | ||
3 | * | ||
4 | * Copyright (C) 2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * These are the low level assembler for performing cache and TLB | ||
23 | * functions on the arm1020e. | ||
24 | * | ||
25 | * CONFIG_CPU_ARM1020_CPU_IDLE -> nohlt | ||
26 | */ | ||
27 | #include <linux/linkage.h> | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <asm/assembler.h> | ||
31 | #include <asm/constants.h> | ||
32 | #include <asm/pgtable.h> | ||
33 | #include <asm/procinfo.h> | ||
34 | #include <asm/ptrace.h> | ||
35 | #include <asm/hardware.h> | ||
36 | |||
37 | /* | ||
38 | * This is the maximum size of an area which will be invalidated | ||
39 | * using the single invalidate entry instructions. Anything larger | ||
40 | * than this, and we go for the whole cache. | ||
41 | * | ||
42 | * This value should be chosen such that we choose the cheapest | ||
43 | * alternative. | ||
44 | */ | ||
45 | #define MAX_AREA_SIZE 32768 | ||
46 | |||
47 | /* | ||
48 | * The size of one data cache line. | ||
49 | */ | ||
50 | #define CACHE_DLINESIZE 32 | ||
51 | |||
52 | /* | ||
53 | * The number of data cache segments. | ||
54 | */ | ||
55 | #define CACHE_DSEGMENTS 16 | ||
56 | |||
57 | /* | ||
58 | * The number of lines in a cache segment. | ||
59 | */ | ||
60 | #define CACHE_DENTRIES 64 | ||
61 | |||
62 | /* | ||
63 | * This is the size at which it becomes more efficient to | ||
64 | * clean the whole cache, rather than using the individual | ||
65 | * cache line maintainence instructions. | ||
66 | */ | ||
67 | #define CACHE_DLIMIT 32768 | ||
68 | |||
69 | .text | ||
70 | /* | ||
71 | * cpu_arm1020e_proc_init() | ||
72 | */ | ||
73 | ENTRY(cpu_arm1020e_proc_init) | ||
74 | mov pc, lr | ||
75 | |||
76 | /* | ||
77 | * cpu_arm1020e_proc_fin() | ||
78 | */ | ||
79 | ENTRY(cpu_arm1020e_proc_fin) | ||
80 | stmfd sp!, {lr} | ||
81 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
82 | msr cpsr_c, ip | ||
83 | bl arm1020e_flush_kern_cache_all | ||
84 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
85 | bic r0, r0, #0x1000 @ ...i............ | ||
86 | bic r0, r0, #0x000e @ ............wca. | ||
87 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
88 | ldmfd sp!, {pc} | ||
89 | |||
90 | /* | ||
91 | * cpu_arm1020e_reset(loc) | ||
92 | * | ||
93 | * Perform a soft reset of the system. Put the CPU into the | ||
94 | * same state as it would be if it had been reset, and branch | ||
95 | * to what would be the reset vector. | ||
96 | * | ||
97 | * loc: location to jump to for soft reset | ||
98 | */ | ||
99 | .align 5 | ||
100 | ENTRY(cpu_arm1020e_reset) | ||
101 | mov ip, #0 | ||
102 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
103 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
104 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
105 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
106 | bic ip, ip, #0x000f @ ............wcam | ||
107 | bic ip, ip, #0x1100 @ ...i...s........ | ||
108 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
109 | mov pc, r0 | ||
110 | |||
111 | /* | ||
112 | * cpu_arm1020e_do_idle() | ||
113 | */ | ||
114 | .align 5 | ||
115 | ENTRY(cpu_arm1020e_do_idle) | ||
116 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
117 | mov pc, lr | ||
118 | |||
119 | /* ================================= CACHE ================================ */ | ||
120 | |||
121 | .align 5 | ||
122 | /* | ||
123 | * flush_user_cache_all() | ||
124 | * | ||
125 | * Invalidate all cache entries in a particular address | ||
126 | * space. | ||
127 | */ | ||
128 | ENTRY(arm1020e_flush_user_cache_all) | ||
129 | /* FALLTHROUGH */ | ||
130 | /* | ||
131 | * flush_kern_cache_all() | ||
132 | * | ||
133 | * Clean and invalidate the entire cache. | ||
134 | */ | ||
135 | ENTRY(arm1020e_flush_kern_cache_all) | ||
136 | mov r2, #VM_EXEC | ||
137 | mov ip, #0 | ||
138 | __flush_whole_cache: | ||
139 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
140 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
141 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 16 segments | ||
142 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
143 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
144 | subs r3, r3, #1 << 26 | ||
145 | bcs 2b @ entries 63 to 0 | ||
146 | subs r1, r1, #1 << 5 | ||
147 | bcs 1b @ segments 15 to 0 | ||
148 | #endif | ||
149 | tst r2, #VM_EXEC | ||
150 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
151 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
152 | #endif | ||
153 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
154 | mov pc, lr | ||
155 | |||
156 | /* | ||
157 | * flush_user_cache_range(start, end, flags) | ||
158 | * | ||
159 | * Invalidate a range of cache entries in the specified | ||
160 | * address space. | ||
161 | * | ||
162 | * - start - start address (inclusive) | ||
163 | * - end - end address (exclusive) | ||
164 | * - flags - vm_flags for this space | ||
165 | */ | ||
166 | ENTRY(arm1020e_flush_user_cache_range) | ||
167 | mov ip, #0 | ||
168 | sub r3, r1, r0 @ calculate total size | ||
169 | cmp r3, #CACHE_DLIMIT | ||
170 | bhs __flush_whole_cache | ||
171 | |||
172 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
173 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
174 | add r0, r0, #CACHE_DLINESIZE | ||
175 | cmp r0, r1 | ||
176 | blo 1b | ||
177 | #endif | ||
178 | tst r2, #VM_EXEC | ||
179 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
180 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
181 | #endif | ||
182 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
183 | mov pc, lr | ||
184 | |||
185 | /* | ||
186 | * coherent_kern_range(start, end) | ||
187 | * | ||
188 | * Ensure coherency between the Icache and the Dcache in the | ||
189 | * region described by start. If you have non-snooping | ||
190 | * Harvard caches, you need to implement this function. | ||
191 | * | ||
192 | * - start - virtual start address | ||
193 | * - end - virtual end address | ||
194 | */ | ||
195 | ENTRY(arm1020e_coherent_kern_range) | ||
196 | /* FALLTHROUGH */ | ||
197 | /* | ||
198 | * coherent_user_range(start, end) | ||
199 | * | ||
200 | * Ensure coherency between the Icache and the Dcache in the | ||
201 | * region described by start. If you have non-snooping | ||
202 | * Harvard caches, you need to implement this function. | ||
203 | * | ||
204 | * - start - virtual start address | ||
205 | * - end - virtual end address | ||
206 | */ | ||
207 | ENTRY(arm1020e_coherent_user_range) | ||
208 | mov ip, #0 | ||
209 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
210 | 1: | ||
211 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
212 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
213 | #endif | ||
214 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
215 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
216 | #endif | ||
217 | add r0, r0, #CACHE_DLINESIZE | ||
218 | cmp r0, r1 | ||
219 | blo 1b | ||
220 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
221 | mov pc, lr | ||
222 | |||
223 | /* | ||
224 | * flush_kern_dcache_page(void *page) | ||
225 | * | ||
226 | * Ensure no D cache aliasing occurs, either with itself or | ||
227 | * the I cache | ||
228 | * | ||
229 | * - page - page aligned address | ||
230 | */ | ||
231 | ENTRY(arm1020e_flush_kern_dcache_page) | ||
232 | mov ip, #0 | ||
233 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
234 | add r1, r0, #PAGE_SZ | ||
235 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
236 | add r0, r0, #CACHE_DLINESIZE | ||
237 | cmp r0, r1 | ||
238 | blo 1b | ||
239 | #endif | ||
240 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
241 | mov pc, lr | ||
242 | |||
243 | /* | ||
244 | * dma_inv_range(start, end) | ||
245 | * | ||
246 | * Invalidate (discard) the specified virtual address range. | ||
247 | * May not write back any entries. If 'start' or 'end' | ||
248 | * are not cache line aligned, those lines must be written | ||
249 | * back. | ||
250 | * | ||
251 | * - start - virtual start address | ||
252 | * - end - virtual end address | ||
253 | * | ||
254 | * (same as v4wb) | ||
255 | */ | ||
256 | ENTRY(arm1020e_dma_inv_range) | ||
257 | mov ip, #0 | ||
258 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
259 | tst r0, #CACHE_DLINESIZE - 1 | ||
260 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
261 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
262 | tst r1, #CACHE_DLINESIZE - 1 | ||
263 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
264 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
265 | add r0, r0, #CACHE_DLINESIZE | ||
266 | cmp r0, r1 | ||
267 | blo 1b | ||
268 | #endif | ||
269 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
270 | mov pc, lr | ||
271 | |||
272 | /* | ||
273 | * dma_clean_range(start, end) | ||
274 | * | ||
275 | * Clean the specified virtual address range. | ||
276 | * | ||
277 | * - start - virtual start address | ||
278 | * - end - virtual end address | ||
279 | * | ||
280 | * (same as v4wb) | ||
281 | */ | ||
282 | ENTRY(arm1020e_dma_clean_range) | ||
283 | mov ip, #0 | ||
284 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
285 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
286 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
287 | add r0, r0, #CACHE_DLINESIZE | ||
288 | cmp r0, r1 | ||
289 | blo 1b | ||
290 | #endif | ||
291 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
292 | mov pc, lr | ||
293 | |||
294 | /* | ||
295 | * dma_flush_range(start, end) | ||
296 | * | ||
297 | * Clean and invalidate the specified virtual address range. | ||
298 | * | ||
299 | * - start - virtual start address | ||
300 | * - end - virtual end address | ||
301 | */ | ||
302 | ENTRY(arm1020e_dma_flush_range) | ||
303 | mov ip, #0 | ||
304 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
305 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
306 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
307 | add r0, r0, #CACHE_DLINESIZE | ||
308 | cmp r0, r1 | ||
309 | blo 1b | ||
310 | #endif | ||
311 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
312 | mov pc, lr | ||
313 | |||
314 | ENTRY(arm1020e_cache_fns) | ||
315 | .long arm1020e_flush_kern_cache_all | ||
316 | .long arm1020e_flush_user_cache_all | ||
317 | .long arm1020e_flush_user_cache_range | ||
318 | .long arm1020e_coherent_kern_range | ||
319 | .long arm1020e_coherent_user_range | ||
320 | .long arm1020e_flush_kern_dcache_page | ||
321 | .long arm1020e_dma_inv_range | ||
322 | .long arm1020e_dma_clean_range | ||
323 | .long arm1020e_dma_flush_range | ||
324 | |||
325 | .align 5 | ||
326 | ENTRY(cpu_arm1020e_dcache_clean_area) | ||
327 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
328 | mov ip, #0 | ||
329 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
330 | add r0, r0, #CACHE_DLINESIZE | ||
331 | subs r1, r1, #CACHE_DLINESIZE | ||
332 | bhi 1b | ||
333 | #endif | ||
334 | mov pc, lr | ||
335 | |||
336 | /* =============================== PageTable ============================== */ | ||
337 | |||
338 | /* | ||
339 | * cpu_arm1020e_switch_mm(pgd) | ||
340 | * | ||
341 | * Set the translation base pointer to be as described by pgd. | ||
342 | * | ||
343 | * pgd: new page tables | ||
344 | */ | ||
345 | .align 5 | ||
346 | ENTRY(cpu_arm1020e_switch_mm) | ||
347 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
348 | mcr p15, 0, r3, c7, c10, 4 | ||
349 | mov r1, #0xF @ 16 segments | ||
350 | 1: mov r3, #0x3F @ 64 entries | ||
351 | 2: mov ip, r3, LSL #26 @ shift up entry | ||
352 | orr ip, ip, r1, LSL #5 @ shift in/up index | ||
353 | mcr p15, 0, ip, c7, c14, 2 @ Clean & Inval DCache entry | ||
354 | mov ip, #0 | ||
355 | subs r3, r3, #1 | ||
356 | cmp r3, #0 | ||
357 | bge 2b @ entries 3F to 0 | ||
358 | subs r1, r1, #1 | ||
359 | cmp r1, #0 | ||
360 | bge 1b @ segments 15 to 0 | ||
361 | |||
362 | #endif | ||
363 | mov r1, #0 | ||
364 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
365 | mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache | ||
366 | #endif | ||
367 | mcr p15, 0, r1, c7, c10, 4 @ drain WB | ||
368 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
369 | mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs | ||
370 | mov pc, lr | ||
371 | |||
372 | /* | ||
373 | * cpu_arm1020e_set_pte(ptep, pte) | ||
374 | * | ||
375 | * Set a PTE and flush it out | ||
376 | */ | ||
377 | .align 5 | ||
378 | ENTRY(cpu_arm1020e_set_pte) | ||
379 | str r1, [r0], #-2048 @ linux version | ||
380 | |||
381 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
382 | |||
383 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
384 | bic r2, r2, #PTE_TYPE_MASK | ||
385 | orr r2, r2, #PTE_TYPE_SMALL | ||
386 | |||
387 | tst r1, #L_PTE_USER @ User? | ||
388 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
389 | |||
390 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
391 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
392 | |||
393 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
394 | movne r2, #0 | ||
395 | |||
396 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
397 | eor r3, r1, #0x0a @ C & small page? | ||
398 | tst r3, #0x0b | ||
399 | biceq r2, r2, #4 | ||
400 | #endif | ||
401 | str r2, [r0] @ hardware version | ||
402 | mov r0, r0 | ||
403 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
404 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
405 | #endif | ||
406 | mov pc, lr | ||
407 | |||
408 | __INIT | ||
409 | |||
410 | .type __arm1020e_setup, #function | ||
411 | __arm1020e_setup: | ||
412 | mov r0, #0 | ||
413 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
414 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
415 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
416 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
417 | ldr r5, arm1020e_cr1_clear | ||
418 | bic r0, r0, r5 | ||
419 | ldr r5, arm1020e_cr1_set | ||
420 | orr r0, r0, r5 | ||
421 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
422 | orr r0, r0, #0x4000 @ .R.. .... .... .... | ||
423 | #endif | ||
424 | mov pc, lr | ||
425 | .size __arm1020e_setup, . - __arm1020e_setup | ||
426 | |||
427 | /* | ||
428 | * R | ||
429 | * .RVI ZFRS BLDP WCAM | ||
430 | * .0.1 1001 ..11 0101 /* FIXME: why no V bit? */ | ||
431 | */ | ||
432 | .type arm1020e_cr1_clear, #object | ||
433 | .type arm1020e_cr1_set, #object | ||
434 | arm1020e_cr1_clear: | ||
435 | .word 0x5f3f | ||
436 | arm1020e_cr1_set: | ||
437 | .word 0x1935 | ||
438 | |||
439 | __INITDATA | ||
440 | |||
441 | /* | ||
442 | * Purpose : Function pointers used to access above functions - all calls | ||
443 | * come through these | ||
444 | */ | ||
445 | .type arm1020e_processor_functions, #object | ||
446 | arm1020e_processor_functions: | ||
447 | .word v4t_early_abort | ||
448 | .word cpu_arm1020e_proc_init | ||
449 | .word cpu_arm1020e_proc_fin | ||
450 | .word cpu_arm1020e_reset | ||
451 | .word cpu_arm1020e_do_idle | ||
452 | .word cpu_arm1020e_dcache_clean_area | ||
453 | .word cpu_arm1020e_switch_mm | ||
454 | .word cpu_arm1020e_set_pte | ||
455 | .size arm1020e_processor_functions, . - arm1020e_processor_functions | ||
456 | |||
457 | .section ".rodata" | ||
458 | |||
459 | .type cpu_arch_name, #object | ||
460 | cpu_arch_name: | ||
461 | .asciz "armv5te" | ||
462 | .size cpu_arch_name, . - cpu_arch_name | ||
463 | |||
464 | .type cpu_elf_name, #object | ||
465 | cpu_elf_name: | ||
466 | .asciz "v5" | ||
467 | .size cpu_elf_name, . - cpu_elf_name | ||
468 | |||
469 | .type cpu_arm1020e_name, #object | ||
470 | cpu_arm1020e_name: | ||
471 | .ascii "ARM1020E" | ||
472 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
473 | .ascii "i" | ||
474 | #endif | ||
475 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
476 | .ascii "d" | ||
477 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
478 | .ascii "(wt)" | ||
479 | #else | ||
480 | .ascii "(wb)" | ||
481 | #endif | ||
482 | #endif | ||
483 | #ifndef CONFIG_CPU_BPREDICT_DISABLE | ||
484 | .ascii "B" | ||
485 | #endif | ||
486 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
487 | .ascii "RR" | ||
488 | #endif | ||
489 | .ascii "\0" | ||
490 | .size cpu_arm1020e_name, . - cpu_arm1020e_name | ||
491 | |||
492 | .align | ||
493 | |||
494 | .section ".proc.info", #alloc, #execinstr | ||
495 | |||
496 | .type __arm1020e_proc_info,#object | ||
497 | __arm1020e_proc_info: | ||
498 | .long 0x4105a200 @ ARM 1020TE (Architecture v5TE) | ||
499 | .long 0xff0ffff0 | ||
500 | .long PMD_TYPE_SECT | \ | ||
501 | PMD_BIT4 | \ | ||
502 | PMD_SECT_AP_WRITE | \ | ||
503 | PMD_SECT_AP_READ | ||
504 | b __arm1020e_setup | ||
505 | .long cpu_arch_name | ||
506 | .long cpu_elf_name | ||
507 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_EDSP | ||
508 | .long cpu_arm1020e_name | ||
509 | .long arm1020e_processor_functions | ||
510 | .long v4wbi_tlb_fns | ||
511 | .long v4wb_user_fns | ||
512 | .long arm1020e_cache_fns | ||
513 | .size __arm1020e_proc_info, . - __arm1020e_proc_info | ||
diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S new file mode 100644 index 000000000000..747ed963e1df --- /dev/null +++ b/arch/arm/mm/proc-arm1022.S | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm1022.S: MMU functions for ARM1022E | ||
3 | * | ||
4 | * Copyright (C) 2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * | ||
13 | * These are the low level assembler for performing cache and TLB | ||
14 | * functions on the ARM1022E. | ||
15 | */ | ||
16 | #include <linux/linkage.h> | ||
17 | #include <linux/config.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <asm/assembler.h> | ||
20 | #include <asm/constants.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/procinfo.h> | ||
23 | #include <asm/ptrace.h> | ||
24 | |||
25 | /* | ||
26 | * This is the maximum size of an area which will be invalidated | ||
27 | * using the single invalidate entry instructions. Anything larger | ||
28 | * than this, and we go for the whole cache. | ||
29 | * | ||
30 | * This value should be chosen such that we choose the cheapest | ||
31 | * alternative. | ||
32 | */ | ||
33 | #define MAX_AREA_SIZE 32768 | ||
34 | |||
35 | /* | ||
36 | * The size of one data cache line. | ||
37 | */ | ||
38 | #define CACHE_DLINESIZE 32 | ||
39 | |||
40 | /* | ||
41 | * The number of data cache segments. | ||
42 | */ | ||
43 | #define CACHE_DSEGMENTS 16 | ||
44 | |||
45 | /* | ||
46 | * The number of lines in a cache segment. | ||
47 | */ | ||
48 | #define CACHE_DENTRIES 64 | ||
49 | |||
50 | /* | ||
51 | * This is the size at which it becomes more efficient to | ||
52 | * clean the whole cache, rather than using the individual | ||
53 | * cache line maintainence instructions. | ||
54 | */ | ||
55 | #define CACHE_DLIMIT 32768 | ||
56 | |||
57 | .text | ||
58 | /* | ||
59 | * cpu_arm1022_proc_init() | ||
60 | */ | ||
61 | ENTRY(cpu_arm1022_proc_init) | ||
62 | mov pc, lr | ||
63 | |||
64 | /* | ||
65 | * cpu_arm1022_proc_fin() | ||
66 | */ | ||
67 | ENTRY(cpu_arm1022_proc_fin) | ||
68 | stmfd sp!, {lr} | ||
69 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
70 | msr cpsr_c, ip | ||
71 | bl arm1022_flush_kern_cache_all | ||
72 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
73 | bic r0, r0, #0x1000 @ ...i............ | ||
74 | bic r0, r0, #0x000e @ ............wca. | ||
75 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
76 | ldmfd sp!, {pc} | ||
77 | |||
78 | /* | ||
79 | * cpu_arm1022_reset(loc) | ||
80 | * | ||
81 | * Perform a soft reset of the system. Put the CPU into the | ||
82 | * same state as it would be if it had been reset, and branch | ||
83 | * to what would be the reset vector. | ||
84 | * | ||
85 | * loc: location to jump to for soft reset | ||
86 | */ | ||
87 | .align 5 | ||
88 | ENTRY(cpu_arm1022_reset) | ||
89 | mov ip, #0 | ||
90 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
91 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
92 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
93 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
94 | bic ip, ip, #0x000f @ ............wcam | ||
95 | bic ip, ip, #0x1100 @ ...i...s........ | ||
96 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
97 | mov pc, r0 | ||
98 | |||
99 | /* | ||
100 | * cpu_arm1022_do_idle() | ||
101 | */ | ||
102 | .align 5 | ||
103 | ENTRY(cpu_arm1022_do_idle) | ||
104 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
105 | mov pc, lr | ||
106 | |||
107 | /* ================================= CACHE ================================ */ | ||
108 | |||
109 | .align 5 | ||
110 | /* | ||
111 | * flush_user_cache_all() | ||
112 | * | ||
113 | * Invalidate all cache entries in a particular address | ||
114 | * space. | ||
115 | */ | ||
116 | ENTRY(arm1022_flush_user_cache_all) | ||
117 | /* FALLTHROUGH */ | ||
118 | /* | ||
119 | * flush_kern_cache_all() | ||
120 | * | ||
121 | * Clean and invalidate the entire cache. | ||
122 | */ | ||
123 | ENTRY(arm1022_flush_kern_cache_all) | ||
124 | mov r2, #VM_EXEC | ||
125 | mov ip, #0 | ||
126 | __flush_whole_cache: | ||
127 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
128 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 16 segments | ||
129 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
130 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
131 | subs r3, r3, #1 << 26 | ||
132 | bcs 2b @ entries 63 to 0 | ||
133 | subs r1, r1, #1 << 5 | ||
134 | bcs 1b @ segments 15 to 0 | ||
135 | #endif | ||
136 | tst r2, #VM_EXEC | ||
137 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
138 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
139 | #endif | ||
140 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
141 | mov pc, lr | ||
142 | |||
143 | /* | ||
144 | * flush_user_cache_range(start, end, flags) | ||
145 | * | ||
146 | * Invalidate a range of cache entries in the specified | ||
147 | * address space. | ||
148 | * | ||
149 | * - start - start address (inclusive) | ||
150 | * - end - end address (exclusive) | ||
151 | * - flags - vm_flags for this space | ||
152 | */ | ||
153 | ENTRY(arm1022_flush_user_cache_range) | ||
154 | mov ip, #0 | ||
155 | sub r3, r1, r0 @ calculate total size | ||
156 | cmp r3, #CACHE_DLIMIT | ||
157 | bhs __flush_whole_cache | ||
158 | |||
159 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
160 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
161 | add r0, r0, #CACHE_DLINESIZE | ||
162 | cmp r0, r1 | ||
163 | blo 1b | ||
164 | #endif | ||
165 | tst r2, #VM_EXEC | ||
166 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
167 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
168 | #endif | ||
169 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
170 | mov pc, lr | ||
171 | |||
172 | /* | ||
173 | * coherent_kern_range(start, end) | ||
174 | * | ||
175 | * Ensure coherency between the Icache and the Dcache in the | ||
176 | * region described by start. If you have non-snooping | ||
177 | * Harvard caches, you need to implement this function. | ||
178 | * | ||
179 | * - start - virtual start address | ||
180 | * - end - virtual end address | ||
181 | */ | ||
182 | ENTRY(arm1022_coherent_kern_range) | ||
183 | /* FALLTHROUGH */ | ||
184 | |||
185 | /* | ||
186 | * coherent_user_range(start, end) | ||
187 | * | ||
188 | * Ensure coherency between the Icache and the Dcache in the | ||
189 | * region described by start. If you have non-snooping | ||
190 | * Harvard caches, you need to implement this function. | ||
191 | * | ||
192 | * - start - virtual start address | ||
193 | * - end - virtual end address | ||
194 | */ | ||
195 | ENTRY(arm1022_coherent_user_range) | ||
196 | mov ip, #0 | ||
197 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
198 | 1: | ||
199 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
200 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
201 | #endif | ||
202 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
203 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
204 | #endif | ||
205 | add r0, r0, #CACHE_DLINESIZE | ||
206 | cmp r0, r1 | ||
207 | blo 1b | ||
208 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
209 | mov pc, lr | ||
210 | |||
211 | /* | ||
212 | * flush_kern_dcache_page(void *page) | ||
213 | * | ||
214 | * Ensure no D cache aliasing occurs, either with itself or | ||
215 | * the I cache | ||
216 | * | ||
217 | * - page - page aligned address | ||
218 | */ | ||
219 | ENTRY(arm1022_flush_kern_dcache_page) | ||
220 | mov ip, #0 | ||
221 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
222 | add r1, r0, #PAGE_SZ | ||
223 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
224 | add r0, r0, #CACHE_DLINESIZE | ||
225 | cmp r0, r1 | ||
226 | blo 1b | ||
227 | #endif | ||
228 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
229 | mov pc, lr | ||
230 | |||
231 | /* | ||
232 | * dma_inv_range(start, end) | ||
233 | * | ||
234 | * Invalidate (discard) the specified virtual address range. | ||
235 | * May not write back any entries. If 'start' or 'end' | ||
236 | * are not cache line aligned, those lines must be written | ||
237 | * back. | ||
238 | * | ||
239 | * - start - virtual start address | ||
240 | * - end - virtual end address | ||
241 | * | ||
242 | * (same as v4wb) | ||
243 | */ | ||
244 | ENTRY(arm1022_dma_inv_range) | ||
245 | mov ip, #0 | ||
246 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
247 | tst r0, #CACHE_DLINESIZE - 1 | ||
248 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
249 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
250 | tst r1, #CACHE_DLINESIZE - 1 | ||
251 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
252 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
253 | add r0, r0, #CACHE_DLINESIZE | ||
254 | cmp r0, r1 | ||
255 | blo 1b | ||
256 | #endif | ||
257 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
258 | mov pc, lr | ||
259 | |||
260 | /* | ||
261 | * dma_clean_range(start, end) | ||
262 | * | ||
263 | * Clean the specified virtual address range. | ||
264 | * | ||
265 | * - start - virtual start address | ||
266 | * - end - virtual end address | ||
267 | * | ||
268 | * (same as v4wb) | ||
269 | */ | ||
270 | ENTRY(arm1022_dma_clean_range) | ||
271 | mov ip, #0 | ||
272 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
273 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
274 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
275 | add r0, r0, #CACHE_DLINESIZE | ||
276 | cmp r0, r1 | ||
277 | blo 1b | ||
278 | #endif | ||
279 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
280 | mov pc, lr | ||
281 | |||
282 | /* | ||
283 | * dma_flush_range(start, end) | ||
284 | * | ||
285 | * Clean and invalidate the specified virtual address range. | ||
286 | * | ||
287 | * - start - virtual start address | ||
288 | * - end - virtual end address | ||
289 | */ | ||
290 | ENTRY(arm1022_dma_flush_range) | ||
291 | mov ip, #0 | ||
292 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
293 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
294 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
295 | add r0, r0, #CACHE_DLINESIZE | ||
296 | cmp r0, r1 | ||
297 | blo 1b | ||
298 | #endif | ||
299 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
300 | mov pc, lr | ||
301 | |||
302 | ENTRY(arm1022_cache_fns) | ||
303 | .long arm1022_flush_kern_cache_all | ||
304 | .long arm1022_flush_user_cache_all | ||
305 | .long arm1022_flush_user_cache_range | ||
306 | .long arm1022_coherent_kern_range | ||
307 | .long arm1022_coherent_user_range | ||
308 | .long arm1022_flush_kern_dcache_page | ||
309 | .long arm1022_dma_inv_range | ||
310 | .long arm1022_dma_clean_range | ||
311 | .long arm1022_dma_flush_range | ||
312 | |||
313 | .align 5 | ||
314 | ENTRY(cpu_arm1022_dcache_clean_area) | ||
315 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
316 | mov ip, #0 | ||
317 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
318 | add r0, r0, #CACHE_DLINESIZE | ||
319 | subs r1, r1, #CACHE_DLINESIZE | ||
320 | bhi 1b | ||
321 | #endif | ||
322 | mov pc, lr | ||
323 | |||
324 | /* =============================== PageTable ============================== */ | ||
325 | |||
326 | /* | ||
327 | * cpu_arm1022_switch_mm(pgd) | ||
328 | * | ||
329 | * Set the translation base pointer to be as described by pgd. | ||
330 | * | ||
331 | * pgd: new page tables | ||
332 | */ | ||
333 | .align 5 | ||
334 | ENTRY(cpu_arm1022_switch_mm) | ||
335 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
336 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 16 segments | ||
337 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
338 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
339 | subs r3, r3, #1 << 26 | ||
340 | bcs 2b @ entries 63 to 0 | ||
341 | subs r1, r1, #1 << 5 | ||
342 | bcs 1b @ segments 15 to 0 | ||
343 | #endif | ||
344 | mov r1, #0 | ||
345 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
346 | mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache | ||
347 | #endif | ||
348 | mcr p15, 0, r1, c7, c10, 4 @ drain WB | ||
349 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
350 | mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs | ||
351 | mov pc, lr | ||
352 | |||
353 | /* | ||
354 | * cpu_arm1022_set_pte(ptep, pte) | ||
355 | * | ||
356 | * Set a PTE and flush it out | ||
357 | */ | ||
358 | .align 5 | ||
359 | ENTRY(cpu_arm1022_set_pte) | ||
360 | str r1, [r0], #-2048 @ linux version | ||
361 | |||
362 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
363 | |||
364 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
365 | bic r2, r2, #PTE_TYPE_MASK | ||
366 | orr r2, r2, #PTE_TYPE_SMALL | ||
367 | |||
368 | tst r1, #L_PTE_USER @ User? | ||
369 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
370 | |||
371 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
372 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
373 | |||
374 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
375 | movne r2, #0 | ||
376 | |||
377 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
378 | eor r3, r1, #0x0a @ C & small page? | ||
379 | tst r3, #0x0b | ||
380 | biceq r2, r2, #4 | ||
381 | #endif | ||
382 | str r2, [r0] @ hardware version | ||
383 | mov r0, r0 | ||
384 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
385 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
386 | #endif | ||
387 | mov pc, lr | ||
388 | |||
389 | __INIT | ||
390 | |||
391 | .type __arm1022_setup, #function | ||
392 | __arm1022_setup: | ||
393 | mov r0, #0 | ||
394 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
395 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
396 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
397 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
398 | ldr r5, arm1022_cr1_clear | ||
399 | bic r0, r0, r5 | ||
400 | ldr r5, arm1022_cr1_set | ||
401 | orr r0, r0, r5 | ||
402 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
403 | orr r0, r0, #0x4000 @ .R.............. | ||
404 | #endif | ||
405 | mov pc, lr | ||
406 | .size __arm1022_setup, . - __arm1022_setup | ||
407 | |||
408 | /* | ||
409 | * R | ||
410 | * .RVI ZFRS BLDP WCAM | ||
411 | * .011 1001 ..11 0101 | ||
412 | * | ||
413 | */ | ||
414 | .type arm1022_cr1_clear, #object | ||
415 | .type arm1022_cr1_set, #object | ||
416 | arm1022_cr1_clear: | ||
417 | .word 0x7f3f | ||
418 | arm1022_cr1_set: | ||
419 | .word 0x3935 | ||
420 | |||
421 | __INITDATA | ||
422 | |||
423 | /* | ||
424 | * Purpose : Function pointers used to access above functions - all calls | ||
425 | * come through these | ||
426 | */ | ||
427 | .type arm1022_processor_functions, #object | ||
428 | arm1022_processor_functions: | ||
429 | .word v4t_early_abort | ||
430 | .word cpu_arm1022_proc_init | ||
431 | .word cpu_arm1022_proc_fin | ||
432 | .word cpu_arm1022_reset | ||
433 | .word cpu_arm1022_do_idle | ||
434 | .word cpu_arm1022_dcache_clean_area | ||
435 | .word cpu_arm1022_switch_mm | ||
436 | .word cpu_arm1022_set_pte | ||
437 | .size arm1022_processor_functions, . - arm1022_processor_functions | ||
438 | |||
439 | .section ".rodata" | ||
440 | |||
441 | .type cpu_arch_name, #object | ||
442 | cpu_arch_name: | ||
443 | .asciz "armv5te" | ||
444 | .size cpu_arch_name, . - cpu_arch_name | ||
445 | |||
446 | .type cpu_elf_name, #object | ||
447 | cpu_elf_name: | ||
448 | .asciz "v5" | ||
449 | .size cpu_elf_name, . - cpu_elf_name | ||
450 | |||
451 | .type cpu_arm1022_name, #object | ||
452 | cpu_arm1022_name: | ||
453 | .ascii "arm1022" | ||
454 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
455 | .ascii "i" | ||
456 | #endif | ||
457 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
458 | .ascii "d" | ||
459 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
460 | .ascii "(wt)" | ||
461 | #else | ||
462 | .ascii "(wb)" | ||
463 | #endif | ||
464 | #endif | ||
465 | #ifndef CONFIG_CPU_BPREDICT_DISABLE | ||
466 | .ascii "B" | ||
467 | #endif | ||
468 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
469 | .ascii "RR" | ||
470 | #endif | ||
471 | .ascii "\0" | ||
472 | .size cpu_arm1022_name, . - cpu_arm1022_name | ||
473 | |||
474 | .align | ||
475 | |||
476 | .section ".proc.info", #alloc, #execinstr | ||
477 | |||
478 | .type __arm1022_proc_info,#object | ||
479 | __arm1022_proc_info: | ||
480 | .long 0x4105a220 @ ARM 1022E (v5TE) | ||
481 | .long 0xff0ffff0 | ||
482 | .long PMD_TYPE_SECT | \ | ||
483 | PMD_BIT4 | \ | ||
484 | PMD_SECT_AP_WRITE | \ | ||
485 | PMD_SECT_AP_READ | ||
486 | b __arm1022_setup | ||
487 | .long cpu_arch_name | ||
488 | .long cpu_elf_name | ||
489 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_EDSP | ||
490 | .long cpu_arm1022_name | ||
491 | .long arm1022_processor_functions | ||
492 | .long v4wbi_tlb_fns | ||
493 | .long v4wb_user_fns | ||
494 | .long arm1022_cache_fns | ||
495 | .size __arm1022_proc_info, . - __arm1022_proc_info | ||
diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S new file mode 100644 index 000000000000..248110c9cf13 --- /dev/null +++ b/arch/arm/mm/proc-arm1026.S | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm1026.S: MMU functions for ARM1026EJ-S | ||
3 | * | ||
4 | * Copyright (C) 2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * | ||
13 | * These are the low level assembler for performing cache and TLB | ||
14 | * functions on the ARM1026EJ-S. | ||
15 | */ | ||
16 | #include <linux/linkage.h> | ||
17 | #include <linux/config.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <asm/assembler.h> | ||
20 | #include <asm/constants.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/procinfo.h> | ||
23 | #include <asm/ptrace.h> | ||
24 | |||
25 | /* | ||
26 | * This is the maximum size of an area which will be invalidated | ||
27 | * using the single invalidate entry instructions. Anything larger | ||
28 | * than this, and we go for the whole cache. | ||
29 | * | ||
30 | * This value should be chosen such that we choose the cheapest | ||
31 | * alternative. | ||
32 | */ | ||
33 | #define MAX_AREA_SIZE 32768 | ||
34 | |||
35 | /* | ||
36 | * The size of one data cache line. | ||
37 | */ | ||
38 | #define CACHE_DLINESIZE 32 | ||
39 | |||
40 | /* | ||
41 | * The number of data cache segments. | ||
42 | */ | ||
43 | #define CACHE_DSEGMENTS 16 | ||
44 | |||
45 | /* | ||
46 | * The number of lines in a cache segment. | ||
47 | */ | ||
48 | #define CACHE_DENTRIES 64 | ||
49 | |||
50 | /* | ||
51 | * This is the size at which it becomes more efficient to | ||
52 | * clean the whole cache, rather than using the individual | ||
53 | * cache line maintainence instructions. | ||
54 | */ | ||
55 | #define CACHE_DLIMIT 32768 | ||
56 | |||
57 | .text | ||
58 | /* | ||
59 | * cpu_arm1026_proc_init() | ||
60 | */ | ||
61 | ENTRY(cpu_arm1026_proc_init) | ||
62 | mov pc, lr | ||
63 | |||
64 | /* | ||
65 | * cpu_arm1026_proc_fin() | ||
66 | */ | ||
67 | ENTRY(cpu_arm1026_proc_fin) | ||
68 | stmfd sp!, {lr} | ||
69 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
70 | msr cpsr_c, ip | ||
71 | bl arm1026_flush_kern_cache_all | ||
72 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
73 | bic r0, r0, #0x1000 @ ...i............ | ||
74 | bic r0, r0, #0x000e @ ............wca. | ||
75 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
76 | ldmfd sp!, {pc} | ||
77 | |||
78 | /* | ||
79 | * cpu_arm1026_reset(loc) | ||
80 | * | ||
81 | * Perform a soft reset of the system. Put the CPU into the | ||
82 | * same state as it would be if it had been reset, and branch | ||
83 | * to what would be the reset vector. | ||
84 | * | ||
85 | * loc: location to jump to for soft reset | ||
86 | */ | ||
87 | .align 5 | ||
88 | ENTRY(cpu_arm1026_reset) | ||
89 | mov ip, #0 | ||
90 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
91 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
92 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
93 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
94 | bic ip, ip, #0x000f @ ............wcam | ||
95 | bic ip, ip, #0x1100 @ ...i...s........ | ||
96 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
97 | mov pc, r0 | ||
98 | |||
99 | /* | ||
100 | * cpu_arm1026_do_idle() | ||
101 | */ | ||
102 | .align 5 | ||
103 | ENTRY(cpu_arm1026_do_idle) | ||
104 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
105 | mov pc, lr | ||
106 | |||
107 | /* ================================= CACHE ================================ */ | ||
108 | |||
109 | .align 5 | ||
110 | /* | ||
111 | * flush_user_cache_all() | ||
112 | * | ||
113 | * Invalidate all cache entries in a particular address | ||
114 | * space. | ||
115 | */ | ||
116 | ENTRY(arm1026_flush_user_cache_all) | ||
117 | /* FALLTHROUGH */ | ||
118 | /* | ||
119 | * flush_kern_cache_all() | ||
120 | * | ||
121 | * Clean and invalidate the entire cache. | ||
122 | */ | ||
123 | ENTRY(arm1026_flush_kern_cache_all) | ||
124 | mov r2, #VM_EXEC | ||
125 | mov ip, #0 | ||
126 | __flush_whole_cache: | ||
127 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
128 | 1: mrc p15, 0, r15, c7, c14, 3 @ test, clean, invalidate | ||
129 | bne 1b | ||
130 | #endif | ||
131 | tst r2, #VM_EXEC | ||
132 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
133 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
134 | #endif | ||
135 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
136 | mov pc, lr | ||
137 | |||
138 | /* | ||
139 | * flush_user_cache_range(start, end, flags) | ||
140 | * | ||
141 | * Invalidate a range of cache entries in the specified | ||
142 | * address space. | ||
143 | * | ||
144 | * - start - start address (inclusive) | ||
145 | * - end - end address (exclusive) | ||
146 | * - flags - vm_flags for this space | ||
147 | */ | ||
148 | ENTRY(arm1026_flush_user_cache_range) | ||
149 | mov ip, #0 | ||
150 | sub r3, r1, r0 @ calculate total size | ||
151 | cmp r3, #CACHE_DLIMIT | ||
152 | bhs __flush_whole_cache | ||
153 | |||
154 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
155 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
156 | add r0, r0, #CACHE_DLINESIZE | ||
157 | cmp r0, r1 | ||
158 | blo 1b | ||
159 | #endif | ||
160 | tst r2, #VM_EXEC | ||
161 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
162 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
163 | #endif | ||
164 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
165 | mov pc, lr | ||
166 | |||
167 | /* | ||
168 | * coherent_kern_range(start, end) | ||
169 | * | ||
170 | * Ensure coherency between the Icache and the Dcache in the | ||
171 | * region described by start. If you have non-snooping | ||
172 | * Harvard caches, you need to implement this function. | ||
173 | * | ||
174 | * - start - virtual start address | ||
175 | * - end - virtual end address | ||
176 | */ | ||
177 | ENTRY(arm1026_coherent_kern_range) | ||
178 | /* FALLTHROUGH */ | ||
179 | /* | ||
180 | * coherent_user_range(start, end) | ||
181 | * | ||
182 | * Ensure coherency between the Icache and the Dcache in the | ||
183 | * region described by start. If you have non-snooping | ||
184 | * Harvard caches, you need to implement this function. | ||
185 | * | ||
186 | * - start - virtual start address | ||
187 | * - end - virtual end address | ||
188 | */ | ||
189 | ENTRY(arm1026_coherent_user_range) | ||
190 | mov ip, #0 | ||
191 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
192 | 1: | ||
193 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
194 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
195 | #endif | ||
196 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
197 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
198 | #endif | ||
199 | add r0, r0, #CACHE_DLINESIZE | ||
200 | cmp r0, r1 | ||
201 | blo 1b | ||
202 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
203 | mov pc, lr | ||
204 | |||
205 | /* | ||
206 | * flush_kern_dcache_page(void *page) | ||
207 | * | ||
208 | * Ensure no D cache aliasing occurs, either with itself or | ||
209 | * the I cache | ||
210 | * | ||
211 | * - page - page aligned address | ||
212 | */ | ||
213 | ENTRY(arm1026_flush_kern_dcache_page) | ||
214 | mov ip, #0 | ||
215 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
216 | add r1, r0, #PAGE_SZ | ||
217 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
218 | add r0, r0, #CACHE_DLINESIZE | ||
219 | cmp r0, r1 | ||
220 | blo 1b | ||
221 | #endif | ||
222 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
223 | mov pc, lr | ||
224 | |||
225 | /* | ||
226 | * dma_inv_range(start, end) | ||
227 | * | ||
228 | * Invalidate (discard) the specified virtual address range. | ||
229 | * May not write back any entries. If 'start' or 'end' | ||
230 | * are not cache line aligned, those lines must be written | ||
231 | * back. | ||
232 | * | ||
233 | * - start - virtual start address | ||
234 | * - end - virtual end address | ||
235 | * | ||
236 | * (same as v4wb) | ||
237 | */ | ||
238 | ENTRY(arm1026_dma_inv_range) | ||
239 | mov ip, #0 | ||
240 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
241 | tst r0, #CACHE_DLINESIZE - 1 | ||
242 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
243 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
244 | tst r1, #CACHE_DLINESIZE - 1 | ||
245 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
246 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
247 | add r0, r0, #CACHE_DLINESIZE | ||
248 | cmp r0, r1 | ||
249 | blo 1b | ||
250 | #endif | ||
251 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
252 | mov pc, lr | ||
253 | |||
254 | /* | ||
255 | * dma_clean_range(start, end) | ||
256 | * | ||
257 | * Clean the specified virtual address range. | ||
258 | * | ||
259 | * - start - virtual start address | ||
260 | * - end - virtual end address | ||
261 | * | ||
262 | * (same as v4wb) | ||
263 | */ | ||
264 | ENTRY(arm1026_dma_clean_range) | ||
265 | mov ip, #0 | ||
266 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
267 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
268 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
269 | add r0, r0, #CACHE_DLINESIZE | ||
270 | cmp r0, r1 | ||
271 | blo 1b | ||
272 | #endif | ||
273 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
274 | mov pc, lr | ||
275 | |||
276 | /* | ||
277 | * dma_flush_range(start, end) | ||
278 | * | ||
279 | * Clean and invalidate the specified virtual address range. | ||
280 | * | ||
281 | * - start - virtual start address | ||
282 | * - end - virtual end address | ||
283 | */ | ||
284 | ENTRY(arm1026_dma_flush_range) | ||
285 | mov ip, #0 | ||
286 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
287 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
288 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
289 | add r0, r0, #CACHE_DLINESIZE | ||
290 | cmp r0, r1 | ||
291 | blo 1b | ||
292 | #endif | ||
293 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
294 | mov pc, lr | ||
295 | |||
296 | ENTRY(arm1026_cache_fns) | ||
297 | .long arm1026_flush_kern_cache_all | ||
298 | .long arm1026_flush_user_cache_all | ||
299 | .long arm1026_flush_user_cache_range | ||
300 | .long arm1026_coherent_kern_range | ||
301 | .long arm1026_coherent_user_range | ||
302 | .long arm1026_flush_kern_dcache_page | ||
303 | .long arm1026_dma_inv_range | ||
304 | .long arm1026_dma_clean_range | ||
305 | .long arm1026_dma_flush_range | ||
306 | |||
307 | .align 5 | ||
308 | ENTRY(cpu_arm1026_dcache_clean_area) | ||
309 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
310 | mov ip, #0 | ||
311 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
312 | add r0, r0, #CACHE_DLINESIZE | ||
313 | subs r1, r1, #CACHE_DLINESIZE | ||
314 | bhi 1b | ||
315 | #endif | ||
316 | mov pc, lr | ||
317 | |||
318 | /* =============================== PageTable ============================== */ | ||
319 | |||
320 | /* | ||
321 | * cpu_arm1026_switch_mm(pgd) | ||
322 | * | ||
323 | * Set the translation base pointer to be as described by pgd. | ||
324 | * | ||
325 | * pgd: new page tables | ||
326 | */ | ||
327 | .align 5 | ||
328 | ENTRY(cpu_arm1026_switch_mm) | ||
329 | mov r1, #0 | ||
330 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
331 | 1: mrc p15, 0, r15, c7, c14, 3 @ test, clean, invalidate | ||
332 | bne 1b | ||
333 | #endif | ||
334 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
335 | mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache | ||
336 | #endif | ||
337 | mcr p15, 0, r1, c7, c10, 4 @ drain WB | ||
338 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
339 | mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs | ||
340 | mov pc, lr | ||
341 | |||
342 | /* | ||
343 | * cpu_arm1026_set_pte(ptep, pte) | ||
344 | * | ||
345 | * Set a PTE and flush it out | ||
346 | */ | ||
347 | .align 5 | ||
348 | ENTRY(cpu_arm1026_set_pte) | ||
349 | str r1, [r0], #-2048 @ linux version | ||
350 | |||
351 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
352 | |||
353 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
354 | bic r2, r2, #PTE_TYPE_MASK | ||
355 | orr r2, r2, #PTE_TYPE_SMALL | ||
356 | |||
357 | tst r1, #L_PTE_USER @ User? | ||
358 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
359 | |||
360 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
361 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
362 | |||
363 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
364 | movne r2, #0 | ||
365 | |||
366 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
367 | eor r3, r1, #0x0a @ C & small page? | ||
368 | tst r3, #0x0b | ||
369 | biceq r2, r2, #4 | ||
370 | #endif | ||
371 | str r2, [r0] @ hardware version | ||
372 | mov r0, r0 | ||
373 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
374 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
375 | #endif | ||
376 | mov pc, lr | ||
377 | |||
378 | |||
379 | __INIT | ||
380 | |||
381 | .type __arm1026_setup, #function | ||
382 | __arm1026_setup: | ||
383 | mov r0, #0 | ||
384 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
385 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
386 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
387 | mcr p15, 0, r4, c2, c0 @ load page table pointer | ||
388 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
389 | mov r0, #4 @ explicitly disable writeback | ||
390 | mcr p15, 7, r0, c15, c0, 0 | ||
391 | #endif | ||
392 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
393 | ldr r5, arm1026_cr1_clear | ||
394 | bic r0, r0, r5 | ||
395 | ldr r5, arm1026_cr1_set | ||
396 | orr r0, r0, r5 | ||
397 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
398 | orr r0, r0, #0x4000 @ .R.. .... .... .... | ||
399 | #endif | ||
400 | mov pc, lr | ||
401 | .size __arm1026_setup, . - __arm1026_setup | ||
402 | |||
403 | /* | ||
404 | * R | ||
405 | * .RVI ZFRS BLDP WCAM | ||
406 | * .011 1001 ..11 0101 | ||
407 | * | ||
408 | */ | ||
409 | .type arm1026_cr1_clear, #object | ||
410 | .type arm1026_cr1_set, #object | ||
411 | arm1026_cr1_clear: | ||
412 | .word 0x7f3f | ||
413 | arm1026_cr1_set: | ||
414 | .word 0x3935 | ||
415 | |||
416 | __INITDATA | ||
417 | |||
418 | /* | ||
419 | * Purpose : Function pointers used to access above functions - all calls | ||
420 | * come through these | ||
421 | */ | ||
422 | .type arm1026_processor_functions, #object | ||
423 | arm1026_processor_functions: | ||
424 | .word v5t_early_abort | ||
425 | .word cpu_arm1026_proc_init | ||
426 | .word cpu_arm1026_proc_fin | ||
427 | .word cpu_arm1026_reset | ||
428 | .word cpu_arm1026_do_idle | ||
429 | .word cpu_arm1026_dcache_clean_area | ||
430 | .word cpu_arm1026_switch_mm | ||
431 | .word cpu_arm1026_set_pte | ||
432 | .size arm1026_processor_functions, . - arm1026_processor_functions | ||
433 | |||
434 | .section .rodata | ||
435 | |||
436 | .type cpu_arch_name, #object | ||
437 | cpu_arch_name: | ||
438 | .asciz "armv5tej" | ||
439 | .size cpu_arch_name, . - cpu_arch_name | ||
440 | |||
441 | .type cpu_elf_name, #object | ||
442 | cpu_elf_name: | ||
443 | .asciz "v5" | ||
444 | .size cpu_elf_name, . - cpu_elf_name | ||
445 | .align | ||
446 | |||
447 | .type cpu_arm1026_name, #object | ||
448 | cpu_arm1026_name: | ||
449 | .ascii "ARM1026EJ-S" | ||
450 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
451 | .ascii "i" | ||
452 | #endif | ||
453 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
454 | .ascii "d" | ||
455 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
456 | .ascii "(wt)" | ||
457 | #else | ||
458 | .ascii "(wb)" | ||
459 | #endif | ||
460 | #endif | ||
461 | #ifndef CONFIG_CPU_BPREDICT_DISABLE | ||
462 | .ascii "B" | ||
463 | #endif | ||
464 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
465 | .ascii "RR" | ||
466 | #endif | ||
467 | .ascii "\0" | ||
468 | .size cpu_arm1026_name, . - cpu_arm1026_name | ||
469 | |||
470 | .align | ||
471 | |||
472 | .section ".proc.info", #alloc, #execinstr | ||
473 | |||
474 | .type __arm1026_proc_info,#object | ||
475 | __arm1026_proc_info: | ||
476 | .long 0x4106a260 @ ARM 1026EJ-S (v5TEJ) | ||
477 | .long 0xff0ffff0 | ||
478 | .long PMD_TYPE_SECT | \ | ||
479 | PMD_BIT4 | \ | ||
480 | PMD_SECT_AP_WRITE | \ | ||
481 | PMD_SECT_AP_READ | ||
482 | b __arm1026_setup | ||
483 | .long cpu_arch_name | ||
484 | .long cpu_elf_name | ||
485 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA | ||
486 | .long cpu_arm1026_name | ||
487 | .long arm1026_processor_functions | ||
488 | .long v4wbi_tlb_fns | ||
489 | .long v4wb_user_fns | ||
490 | .long arm1026_cache_fns | ||
491 | .size __arm1026_proc_info, . - __arm1026_proc_info | ||
diff --git a/arch/arm/mm/proc-arm6_7.S b/arch/arm/mm/proc-arm6_7.S new file mode 100644 index 000000000000..0ee214b824ff --- /dev/null +++ b/arch/arm/mm/proc-arm6_7.S | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm6,7.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2000 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * These are the low level assembler for performing cache and TLB | ||
11 | * functions on the ARM610 & ARM710. | ||
12 | */ | ||
13 | #include <linux/linkage.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <asm/assembler.h> | ||
16 | #include <asm/constants.h> | ||
17 | #include <asm/pgtable.h> | ||
18 | #include <asm/procinfo.h> | ||
19 | #include <asm/ptrace.h> | ||
20 | |||
21 | ENTRY(cpu_arm6_dcache_clean_area) | ||
22 | ENTRY(cpu_arm7_dcache_clean_area) | ||
23 | mov pc, lr | ||
24 | |||
25 | /* | ||
26 | * Function: arm6_7_data_abort () | ||
27 | * | ||
28 | * Params : r2 = address of aborted instruction | ||
29 | * : sp = pointer to registers | ||
30 | * | ||
31 | * Purpose : obtain information about current aborted instruction | ||
32 | * | ||
33 | * Returns : r0 = address of abort | ||
34 | * : r1 = FSR | ||
35 | */ | ||
36 | |||
37 | ENTRY(cpu_arm7_data_abort) | ||
38 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
39 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
40 | ldr r8, [r0] @ read arm instruction | ||
41 | tst r8, #1 << 20 @ L = 1 -> write? | ||
42 | orreq r1, r1, #1 << 8 @ yes. | ||
43 | and r7, r8, #15 << 24 | ||
44 | add pc, pc, r7, lsr #22 @ Now branch to the relevant processing routine | ||
45 | nop | ||
46 | |||
47 | /* 0 */ b .data_unknown | ||
48 | /* 1 */ mov pc, lr @ swp | ||
49 | /* 2 */ b .data_unknown | ||
50 | /* 3 */ b .data_unknown | ||
51 | /* 4 */ b .data_arm_lateldrpostconst @ ldr rd, [rn], #m | ||
52 | /* 5 */ b .data_arm_lateldrpreconst @ ldr rd, [rn, #m] | ||
53 | /* 6 */ b .data_arm_lateldrpostreg @ ldr rd, [rn], rm | ||
54 | /* 7 */ b .data_arm_lateldrprereg @ ldr rd, [rn, rm] | ||
55 | /* 8 */ b .data_arm_ldmstm @ ldm*a rn, <rlist> | ||
56 | /* 9 */ b .data_arm_ldmstm @ ldm*b rn, <rlist> | ||
57 | /* a */ b .data_unknown | ||
58 | /* b */ b .data_unknown | ||
59 | /* c */ mov pc, lr @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m | ||
60 | /* d */ mov pc, lr @ ldc rd, [rn, #m] | ||
61 | /* e */ b .data_unknown | ||
62 | /* f */ | ||
63 | .data_unknown: @ Part of jumptable | ||
64 | mov r0, r2 | ||
65 | mov r1, r8 | ||
66 | mov r2, sp | ||
67 | bl baddataabort | ||
68 | b ret_from_exception | ||
69 | |||
70 | ENTRY(cpu_arm6_data_abort) | ||
71 | mrc p15, 0, r1, c5, c0, 0 @ get FSR | ||
72 | mrc p15, 0, r0, c6, c0, 0 @ get FAR | ||
73 | ldr r8, [r2] @ read arm instruction | ||
74 | tst r8, #1 << 20 @ L = 1 -> write? | ||
75 | orreq r1, r1, #1 << 8 @ yes. | ||
76 | and r7, r8, #14 << 24 | ||
77 | teq r7, #8 << 24 @ was it ldm/stm | ||
78 | movne pc, lr | ||
79 | |||
80 | .data_arm_ldmstm: | ||
81 | tst r8, #1 << 21 @ check writeback bit | ||
82 | moveq pc, lr @ no writeback -> no fixup | ||
83 | mov r7, #0x11 | ||
84 | orr r7, r7, #0x1100 | ||
85 | and r6, r8, r7 | ||
86 | and r2, r8, r7, lsl #1 | ||
87 | add r6, r6, r2, lsr #1 | ||
88 | and r2, r8, r7, lsl #2 | ||
89 | add r6, r6, r2, lsr #2 | ||
90 | and r2, r8, r7, lsl #3 | ||
91 | add r6, r6, r2, lsr #3 | ||
92 | add r6, r6, r6, lsr #8 | ||
93 | add r6, r6, r6, lsr #4 | ||
94 | and r6, r6, #15 @ r6 = no. of registers to transfer. | ||
95 | and r5, r8, #15 << 16 @ Extract 'n' from instruction | ||
96 | ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' | ||
97 | tst r8, #1 << 23 @ Check U bit | ||
98 | subne r7, r7, r6, lsl #2 @ Undo increment | ||
99 | addeq r7, r7, r6, lsl #2 @ Undo decrement | ||
100 | str r7, [sp, r5, lsr #14] @ Put register 'Rn' | ||
101 | mov pc, lr | ||
102 | |||
103 | .data_arm_apply_r6_and_rn: | ||
104 | and r5, r8, #15 << 16 @ Extract 'n' from instruction | ||
105 | ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' | ||
106 | tst r8, #1 << 23 @ Check U bit | ||
107 | subne r7, r7, r6 @ Undo incrmenet | ||
108 | addeq r7, r7, r6 @ Undo decrement | ||
109 | str r7, [sp, r5, lsr #14] @ Put register 'Rn' | ||
110 | mov pc, lr | ||
111 | |||
112 | .data_arm_lateldrpreconst: | ||
113 | tst r8, #1 << 21 @ check writeback bit | ||
114 | moveq pc, lr @ no writeback -> no fixup | ||
115 | .data_arm_lateldrpostconst: | ||
116 | movs r2, r8, lsl #20 @ Get offset | ||
117 | moveq pc, lr @ zero -> no fixup | ||
118 | and r5, r8, #15 << 16 @ Extract 'n' from instruction | ||
119 | ldr r7, [sp, r5, lsr #14] @ Get register 'Rn' | ||
120 | tst r8, #1 << 23 @ Check U bit | ||
121 | subne r7, r7, r2, lsr #20 @ Undo increment | ||
122 | addeq r7, r7, r2, lsr #20 @ Undo decrement | ||
123 | str r7, [sp, r5, lsr #14] @ Put register 'Rn' | ||
124 | mov pc, lr | ||
125 | |||
126 | .data_arm_lateldrprereg: | ||
127 | tst r8, #1 << 21 @ check writeback bit | ||
128 | moveq pc, lr @ no writeback -> no fixup | ||
129 | .data_arm_lateldrpostreg: | ||
130 | and r7, r8, #15 @ Extract 'm' from instruction | ||
131 | ldr r6, [sp, r7, lsl #2] @ Get register 'Rm' | ||
132 | mov r5, r8, lsr #7 @ get shift count | ||
133 | ands r5, r5, #31 | ||
134 | and r7, r8, #0x70 @ get shift type | ||
135 | orreq r7, r7, #8 @ shift count = 0 | ||
136 | add pc, pc, r7 | ||
137 | nop | ||
138 | |||
139 | mov r6, r6, lsl r5 @ 0: LSL #!0 | ||
140 | b .data_arm_apply_r6_and_rn | ||
141 | b .data_arm_apply_r6_and_rn @ 1: LSL #0 | ||
142 | nop | ||
143 | b .data_unknown @ 2: MUL? | ||
144 | nop | ||
145 | b .data_unknown @ 3: MUL? | ||
146 | nop | ||
147 | mov r6, r6, lsr r5 @ 4: LSR #!0 | ||
148 | b .data_arm_apply_r6_and_rn | ||
149 | mov r6, r6, lsr #32 @ 5: LSR #32 | ||
150 | b .data_arm_apply_r6_and_rn | ||
151 | b .data_unknown @ 6: MUL? | ||
152 | nop | ||
153 | b .data_unknown @ 7: MUL? | ||
154 | nop | ||
155 | mov r6, r6, asr r5 @ 8: ASR #!0 | ||
156 | b .data_arm_apply_r6_and_rn | ||
157 | mov r6, r6, asr #32 @ 9: ASR #32 | ||
158 | b .data_arm_apply_r6_and_rn | ||
159 | b .data_unknown @ A: MUL? | ||
160 | nop | ||
161 | b .data_unknown @ B: MUL? | ||
162 | nop | ||
163 | mov r6, r6, ror r5 @ C: ROR #!0 | ||
164 | b .data_arm_apply_r6_and_rn | ||
165 | mov r6, r6, rrx @ D: RRX | ||
166 | b .data_arm_apply_r6_and_rn | ||
167 | b .data_unknown @ E: MUL? | ||
168 | nop | ||
169 | b .data_unknown @ F: MUL? | ||
170 | |||
171 | /* | ||
172 | * Function: arm6_7_proc_init (void) | ||
173 | * : arm6_7_proc_fin (void) | ||
174 | * | ||
175 | * Notes : This processor does not require these | ||
176 | */ | ||
177 | ENTRY(cpu_arm6_proc_init) | ||
178 | ENTRY(cpu_arm7_proc_init) | ||
179 | mov pc, lr | ||
180 | |||
181 | ENTRY(cpu_arm6_proc_fin) | ||
182 | ENTRY(cpu_arm7_proc_fin) | ||
183 | mov r0, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
184 | msr cpsr_c, r0 | ||
185 | mov r0, #0x31 @ ....S..DP...M | ||
186 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
187 | mov pc, lr | ||
188 | |||
189 | ENTRY(cpu_arm6_do_idle) | ||
190 | ENTRY(cpu_arm7_do_idle) | ||
191 | mov pc, lr | ||
192 | |||
193 | /* | ||
194 | * Function: arm6_7_switch_mm(unsigned long pgd_phys) | ||
195 | * Params : pgd_phys Physical address of page table | ||
196 | * Purpose : Perform a task switch, saving the old processes state, and restoring | ||
197 | * the new. | ||
198 | */ | ||
199 | ENTRY(cpu_arm6_switch_mm) | ||
200 | ENTRY(cpu_arm7_switch_mm) | ||
201 | mov r1, #0 | ||
202 | mcr p15, 0, r1, c7, c0, 0 @ flush cache | ||
203 | mcr p15, 0, r0, c2, c0, 0 @ update page table ptr | ||
204 | mcr p15, 0, r1, c5, c0, 0 @ flush TLBs | ||
205 | mov pc, lr | ||
206 | |||
207 | /* | ||
208 | * Function: arm6_7_set_pte(pte_t *ptep, pte_t pte) | ||
209 | * Params : r0 = Address to set | ||
210 | * : r1 = value to set | ||
211 | * Purpose : Set a PTE and flush it out of any WB cache | ||
212 | */ | ||
213 | .align 5 | ||
214 | ENTRY(cpu_arm6_set_pte) | ||
215 | ENTRY(cpu_arm7_set_pte) | ||
216 | str r1, [r0], #-2048 @ linux version | ||
217 | |||
218 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
219 | |||
220 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
221 | bic r2, r2, #PTE_TYPE_MASK | ||
222 | orr r2, r2, #PTE_TYPE_SMALL | ||
223 | |||
224 | tst r1, #L_PTE_USER @ User? | ||
225 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
226 | |||
227 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
228 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
229 | |||
230 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young | ||
231 | movne r2, #0 | ||
232 | |||
233 | str r2, [r0] @ hardware version | ||
234 | mov pc, lr | ||
235 | |||
236 | /* | ||
237 | * Function: _arm6_7_reset | ||
238 | * Params : r0 = address to jump to | ||
239 | * Notes : This sets up everything for a reset | ||
240 | */ | ||
241 | ENTRY(cpu_arm6_reset) | ||
242 | ENTRY(cpu_arm7_reset) | ||
243 | mov r1, #0 | ||
244 | mcr p15, 0, r1, c7, c0, 0 @ flush cache | ||
245 | mcr p15, 0, r1, c5, c0, 0 @ flush TLB | ||
246 | mov r1, #0x30 | ||
247 | mcr p15, 0, r1, c1, c0, 0 @ turn off MMU etc | ||
248 | mov pc, r0 | ||
249 | |||
250 | __INIT | ||
251 | |||
252 | .type __arm6_setup, #function | ||
253 | __arm6_setup: mov r0, #0 | ||
254 | mcr p15, 0, r0, c7, c0 @ flush caches on v3 | ||
255 | mcr p15, 0, r0, c5, c0 @ flush TLBs on v3 | ||
256 | mov r0, #0x3d @ . ..RS BLDP WCAM | ||
257 | orr r0, r0, #0x100 @ . ..01 0011 1101 | ||
258 | mov pc, lr | ||
259 | .size __arm6_setup, . - __arm6_setup | ||
260 | |||
261 | .type __arm7_setup, #function | ||
262 | __arm7_setup: mov r0, #0 | ||
263 | mcr p15, 0, r0, c7, c0 @ flush caches on v3 | ||
264 | mcr p15, 0, r0, c5, c0 @ flush TLBs on v3 | ||
265 | mcr p15, 0, r0, c3, c0 @ load domain access register | ||
266 | mov r0, #0x7d @ . ..RS BLDP WCAM | ||
267 | orr r0, r0, #0x100 @ . ..01 0111 1101 | ||
268 | mov pc, lr | ||
269 | .size __arm7_setup, . - __arm7_setup | ||
270 | |||
271 | __INITDATA | ||
272 | |||
273 | /* | ||
274 | * Purpose : Function pointers used to access above functions - all calls | ||
275 | * come through these | ||
276 | */ | ||
277 | .type arm6_processor_functions, #object | ||
278 | ENTRY(arm6_processor_functions) | ||
279 | .word cpu_arm6_data_abort | ||
280 | .word cpu_arm6_proc_init | ||
281 | .word cpu_arm6_proc_fin | ||
282 | .word cpu_arm6_reset | ||
283 | .word cpu_arm6_do_idle | ||
284 | .word cpu_arm6_dcache_clean_area | ||
285 | .word cpu_arm6_switch_mm | ||
286 | .word cpu_arm6_set_pte | ||
287 | .size arm6_processor_functions, . - arm6_processor_functions | ||
288 | |||
289 | /* | ||
290 | * Purpose : Function pointers used to access above functions - all calls | ||
291 | * come through these | ||
292 | */ | ||
293 | .type arm7_processor_functions, #object | ||
294 | ENTRY(arm7_processor_functions) | ||
295 | .word cpu_arm7_data_abort | ||
296 | .word cpu_arm7_proc_init | ||
297 | .word cpu_arm7_proc_fin | ||
298 | .word cpu_arm7_reset | ||
299 | .word cpu_arm7_do_idle | ||
300 | .word cpu_arm7_dcache_clean_area | ||
301 | .word cpu_arm7_switch_mm | ||
302 | .word cpu_arm7_set_pte | ||
303 | .size arm7_processor_functions, . - arm7_processor_functions | ||
304 | |||
305 | .section ".rodata" | ||
306 | |||
307 | .type cpu_arch_name, #object | ||
308 | cpu_arch_name: .asciz "armv3" | ||
309 | .size cpu_arch_name, . - cpu_arch_name | ||
310 | |||
311 | .type cpu_elf_name, #object | ||
312 | cpu_elf_name: .asciz "v3" | ||
313 | .size cpu_elf_name, . - cpu_elf_name | ||
314 | |||
315 | .type cpu_arm6_name, #object | ||
316 | cpu_arm6_name: .asciz "ARM6" | ||
317 | .size cpu_arm6_name, . - cpu_arm6_name | ||
318 | |||
319 | .type cpu_arm610_name, #object | ||
320 | cpu_arm610_name: | ||
321 | .asciz "ARM610" | ||
322 | .size cpu_arm610_name, . - cpu_arm610_name | ||
323 | |||
324 | .type cpu_arm7_name, #object | ||
325 | cpu_arm7_name: .asciz "ARM7" | ||
326 | .size cpu_arm7_name, . - cpu_arm7_name | ||
327 | |||
328 | .type cpu_arm710_name, #object | ||
329 | cpu_arm710_name: | ||
330 | .asciz "ARM710" | ||
331 | .size cpu_arm710_name, . - cpu_arm710_name | ||
332 | |||
333 | .align | ||
334 | |||
335 | .section ".proc.info", #alloc, #execinstr | ||
336 | |||
337 | .type __arm6_proc_info, #object | ||
338 | __arm6_proc_info: | ||
339 | .long 0x41560600 | ||
340 | .long 0xfffffff0 | ||
341 | .long 0x00000c1e | ||
342 | b __arm6_setup | ||
343 | .long cpu_arch_name | ||
344 | .long cpu_elf_name | ||
345 | .long HWCAP_SWP | HWCAP_26BIT | ||
346 | .long cpu_arm6_name | ||
347 | .long arm6_processor_functions | ||
348 | .long v3_tlb_fns | ||
349 | .long v3_user_fns | ||
350 | .long v3_cache_fns | ||
351 | .size __arm6_proc_info, . - __arm6_proc_info | ||
352 | |||
353 | .type __arm610_proc_info, #object | ||
354 | __arm610_proc_info: | ||
355 | .long 0x41560610 | ||
356 | .long 0xfffffff0 | ||
357 | .long 0x00000c1e | ||
358 | b __arm6_setup | ||
359 | .long cpu_arch_name | ||
360 | .long cpu_elf_name | ||
361 | .long HWCAP_SWP | HWCAP_26BIT | ||
362 | .long cpu_arm610_name | ||
363 | .long arm6_processor_functions | ||
364 | .long v3_tlb_fns | ||
365 | .long v3_user_fns | ||
366 | .long v3_cache_fns | ||
367 | .size __arm610_proc_info, . - __arm610_proc_info | ||
368 | |||
369 | .type __arm7_proc_info, #object | ||
370 | __arm7_proc_info: | ||
371 | .long 0x41007000 | ||
372 | .long 0xffffff00 | ||
373 | .long 0x00000c1e | ||
374 | b __arm7_setup | ||
375 | .long cpu_arch_name | ||
376 | .long cpu_elf_name | ||
377 | .long HWCAP_SWP | HWCAP_26BIT | ||
378 | .long cpu_arm7_name | ||
379 | .long arm7_processor_functions | ||
380 | .long v3_tlb_fns | ||
381 | .long v3_user_fns | ||
382 | .long v3_cache_fns | ||
383 | .size __arm7_proc_info, . - __arm7_proc_info | ||
384 | |||
385 | .type __arm710_proc_info, #object | ||
386 | __arm710_proc_info: | ||
387 | .long 0x41007100 | ||
388 | .long 0xfff8ff00 | ||
389 | .long PMD_TYPE_SECT | \ | ||
390 | PMD_SECT_BUFFERABLE | \ | ||
391 | PMD_SECT_CACHEABLE | \ | ||
392 | PMD_BIT4 | \ | ||
393 | PMD_SECT_AP_WRITE | \ | ||
394 | PMD_SECT_AP_READ | ||
395 | b __arm7_setup | ||
396 | .long cpu_arch_name | ||
397 | .long cpu_elf_name | ||
398 | .long HWCAP_SWP | HWCAP_26BIT | ||
399 | .long cpu_arm710_name | ||
400 | .long arm7_processor_functions | ||
401 | .long v3_tlb_fns | ||
402 | .long v3_user_fns | ||
403 | .long v3_cache_fns | ||
404 | .size __arm710_proc_info, . - __arm710_proc_info | ||
diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S new file mode 100644 index 000000000000..57cfa6a2f54f --- /dev/null +++ b/arch/arm/mm/proc-arm720.S | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm720.S: MMU functions for ARM720 | ||
3 | * | ||
4 | * Copyright (C) 2000 Steve Hill (sjhill@cotw.com) | ||
5 | * Rob Scott (rscott@mtrob.fdns.net) | ||
6 | * Copyright (C) 2000 ARM Limited, Deep Blue Solutions Ltd. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * | ||
23 | * These are the low level assembler for performing cache and TLB | ||
24 | * functions on the ARM720T. The ARM720T has a writethrough IDC | ||
25 | * cache, so we don't need to clean it. | ||
26 | * | ||
27 | * Changelog: | ||
28 | * 05-09-2000 SJH Created by moving 720 specific functions | ||
29 | * out of 'proc-arm6,7.S' per RMK discussion | ||
30 | * 07-25-2000 SJH Added idle function. | ||
31 | * 08-25-2000 DBS Updated for integration of ARM Ltd version. | ||
32 | */ | ||
33 | #include <linux/linkage.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <asm/assembler.h> | ||
36 | #include <asm/constants.h> | ||
37 | #include <asm/pgtable.h> | ||
38 | #include <asm/procinfo.h> | ||
39 | #include <asm/ptrace.h> | ||
40 | #include <asm/hardware.h> | ||
41 | |||
42 | /* | ||
43 | * Function: arm720_proc_init (void) | ||
44 | * : arm720_proc_fin (void) | ||
45 | * | ||
46 | * Notes : This processor does not require these | ||
47 | */ | ||
48 | ENTRY(cpu_arm720_dcache_clean_area) | ||
49 | ENTRY(cpu_arm720_proc_init) | ||
50 | mov pc, lr | ||
51 | |||
52 | ENTRY(cpu_arm720_proc_fin) | ||
53 | stmfd sp!, {lr} | ||
54 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
55 | msr cpsr_c, ip | ||
56 | mrc p15, 0, r0, c1, c0, 0 | ||
57 | bic r0, r0, #0x1000 @ ...i............ | ||
58 | bic r0, r0, #0x000e @ ............wca. | ||
59 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
60 | mcr p15, 0, r1, c7, c7, 0 @ invalidate cache | ||
61 | ldmfd sp!, {pc} | ||
62 | |||
63 | /* | ||
64 | * Function: arm720_proc_do_idle(void) | ||
65 | * Params : r0 = unused | ||
66 | * Purpose : put the processer in proper idle mode | ||
67 | */ | ||
68 | ENTRY(cpu_arm720_do_idle) | ||
69 | mov pc, lr | ||
70 | |||
71 | /* | ||
72 | * Function: arm720_switch_mm(unsigned long pgd_phys) | ||
73 | * Params : pgd_phys Physical address of page table | ||
74 | * Purpose : Perform a task switch, saving the old process' state and restoring | ||
75 | * the new. | ||
76 | */ | ||
77 | ENTRY(cpu_arm720_switch_mm) | ||
78 | mov r1, #0 | ||
79 | mcr p15, 0, r1, c7, c7, 0 @ invalidate cache | ||
80 | mcr p15, 0, r0, c2, c0, 0 @ update page table ptr | ||
81 | mcr p15, 0, r1, c8, c7, 0 @ flush TLB (v4) | ||
82 | mov pc, lr | ||
83 | |||
84 | /* | ||
85 | * Function: arm720_set_pte(pte_t *ptep, pte_t pte) | ||
86 | * Params : r0 = Address to set | ||
87 | * : r1 = value to set | ||
88 | * Purpose : Set a PTE and flush it out of any WB cache | ||
89 | */ | ||
90 | .align 5 | ||
91 | ENTRY(cpu_arm720_set_pte) | ||
92 | str r1, [r0], #-2048 @ linux version | ||
93 | |||
94 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
95 | |||
96 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
97 | bic r2, r2, #PTE_TYPE_MASK | ||
98 | orr r2, r2, #PTE_TYPE_SMALL | ||
99 | |||
100 | tst r1, #L_PTE_USER @ User? | ||
101 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
102 | |||
103 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
104 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
105 | |||
106 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young | ||
107 | movne r2, #0 | ||
108 | |||
109 | str r2, [r0] @ hardware version | ||
110 | mov pc, lr | ||
111 | |||
112 | /* | ||
113 | * Function: arm720_reset | ||
114 | * Params : r0 = address to jump to | ||
115 | * Notes : This sets up everything for a reset | ||
116 | */ | ||
117 | ENTRY(cpu_arm720_reset) | ||
118 | mov ip, #0 | ||
119 | mcr p15, 0, ip, c7, c7, 0 @ invalidate cache | ||
120 | mcr p15, 0, ip, c8, c7, 0 @ flush TLB (v4) | ||
121 | mrc p15, 0, ip, c1, c0, 0 @ get ctrl register | ||
122 | bic ip, ip, #0x000f @ ............wcam | ||
123 | bic ip, ip, #0x2100 @ ..v....s........ | ||
124 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
125 | mov pc, r0 | ||
126 | |||
127 | __INIT | ||
128 | |||
129 | .type __arm710_setup, #function | ||
130 | __arm710_setup: | ||
131 | mov r0, #0 | ||
132 | mcr p15, 0, r0, c7, c7, 0 @ invalidate caches | ||
133 | mcr p15, 0, r0, c8, c7, 0 @ flush TLB (v4) | ||
134 | mrc p15, 0, r0, c1, c0 @ get control register | ||
135 | ldr r5, arm710_cr1_clear | ||
136 | bic r0, r0, r5 | ||
137 | ldr r5, arm710_cr1_set | ||
138 | orr r0, r0, r5 | ||
139 | mov pc, lr @ __ret (head.S) | ||
140 | .size __arm710_setup, . - __arm710_setup | ||
141 | |||
142 | /* | ||
143 | * R | ||
144 | * .RVI ZFRS BLDP WCAM | ||
145 | * .... 0001 ..11 1101 | ||
146 | * | ||
147 | */ | ||
148 | .type arm710_cr1_clear, #object | ||
149 | .type arm710_cr1_set, #object | ||
150 | arm710_cr1_clear: | ||
151 | .word 0x0f3f | ||
152 | arm710_cr1_set: | ||
153 | .word 0x013d | ||
154 | |||
155 | .type __arm720_setup, #function | ||
156 | __arm720_setup: | ||
157 | mov r0, #0 | ||
158 | mcr p15, 0, r0, c7, c7, 0 @ invalidate caches | ||
159 | mcr p15, 0, r0, c8, c7, 0 @ flush TLB (v4) | ||
160 | mrc p15, 0, r0, c1, c0 @ get control register | ||
161 | ldr r5, arm720_cr1_clear | ||
162 | bic r0, r0, r5 | ||
163 | ldr r5, arm720_cr1_set | ||
164 | orr r0, r0, r5 | ||
165 | mov pc, lr @ __ret (head.S) | ||
166 | .size __arm720_setup, . - __arm720_setup | ||
167 | |||
168 | /* | ||
169 | * R | ||
170 | * .RVI ZFRS BLDP WCAM | ||
171 | * ..1. 1001 ..11 1101 | ||
172 | * | ||
173 | */ | ||
174 | .type arm720_cr1_clear, #object | ||
175 | .type arm720_cr1_set, #object | ||
176 | arm720_cr1_clear: | ||
177 | .word 0x2f3f | ||
178 | arm720_cr1_set: | ||
179 | .word 0x213d | ||
180 | |||
181 | __INITDATA | ||
182 | |||
183 | /* | ||
184 | * Purpose : Function pointers used to access above functions - all calls | ||
185 | * come through these | ||
186 | */ | ||
187 | .type arm720_processor_functions, #object | ||
188 | ENTRY(arm720_processor_functions) | ||
189 | .word v4t_late_abort | ||
190 | .word cpu_arm720_proc_init | ||
191 | .word cpu_arm720_proc_fin | ||
192 | .word cpu_arm720_reset | ||
193 | .word cpu_arm720_do_idle | ||
194 | .word cpu_arm720_dcache_clean_area | ||
195 | .word cpu_arm720_switch_mm | ||
196 | .word cpu_arm720_set_pte | ||
197 | .size arm720_processor_functions, . - arm720_processor_functions | ||
198 | |||
199 | .section ".rodata" | ||
200 | |||
201 | .type cpu_arch_name, #object | ||
202 | cpu_arch_name: .asciz "armv4t" | ||
203 | .size cpu_arch_name, . - cpu_arch_name | ||
204 | |||
205 | .type cpu_elf_name, #object | ||
206 | cpu_elf_name: .asciz "v4" | ||
207 | .size cpu_elf_name, . - cpu_elf_name | ||
208 | |||
209 | .type cpu_arm710_name, #object | ||
210 | cpu_arm710_name: | ||
211 | .asciz "ARM710T" | ||
212 | .size cpu_arm710_name, . - cpu_arm710_name | ||
213 | |||
214 | .type cpu_arm720_name, #object | ||
215 | cpu_arm720_name: | ||
216 | .asciz "ARM720T" | ||
217 | .size cpu_arm720_name, . - cpu_arm720_name | ||
218 | |||
219 | .align | ||
220 | |||
221 | /* | ||
222 | * See linux/include/asm-arm/procinfo.h for a definition of this structure. | ||
223 | */ | ||
224 | |||
225 | .section ".proc.info", #alloc, #execinstr | ||
226 | |||
227 | .type __arm710_proc_info, #object | ||
228 | __arm710_proc_info: | ||
229 | .long 0x41807100 @ cpu_val | ||
230 | .long 0xffffff00 @ cpu_mask | ||
231 | .long PMD_TYPE_SECT | \ | ||
232 | PMD_SECT_BUFFERABLE | \ | ||
233 | PMD_SECT_CACHEABLE | \ | ||
234 | PMD_BIT4 | \ | ||
235 | PMD_SECT_AP_WRITE | \ | ||
236 | PMD_SECT_AP_READ | ||
237 | b __arm710_setup @ cpu_flush | ||
238 | .long cpu_arch_name @ arch_name | ||
239 | .long cpu_elf_name @ elf_name | ||
240 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB @ elf_hwcap | ||
241 | .long cpu_arm710_name @ name | ||
242 | .long arm720_processor_functions | ||
243 | .long v4_tlb_fns | ||
244 | .long v4wt_user_fns | ||
245 | .long v4_cache_fns | ||
246 | .size __arm710_proc_info, . - __arm710_proc_info | ||
247 | |||
248 | .type __arm720_proc_info, #object | ||
249 | __arm720_proc_info: | ||
250 | .long 0x41807200 @ cpu_val | ||
251 | .long 0xffffff00 @ cpu_mask | ||
252 | .long PMD_TYPE_SECT | \ | ||
253 | PMD_SECT_BUFFERABLE | \ | ||
254 | PMD_SECT_CACHEABLE | \ | ||
255 | PMD_BIT4 | \ | ||
256 | PMD_SECT_AP_WRITE | \ | ||
257 | PMD_SECT_AP_READ | ||
258 | b __arm720_setup @ cpu_flush | ||
259 | .long cpu_arch_name @ arch_name | ||
260 | .long cpu_elf_name @ elf_name | ||
261 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB @ elf_hwcap | ||
262 | .long cpu_arm720_name @ name | ||
263 | .long arm720_processor_functions | ||
264 | .long v4_tlb_fns | ||
265 | .long v4wt_user_fns | ||
266 | .long v4_cache_fns | ||
267 | .size __arm720_proc_info, . - __arm720_proc_info | ||
diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S new file mode 100644 index 000000000000..0f490a0fcb71 --- /dev/null +++ b/arch/arm/mm/proc-arm920.S | |||
@@ -0,0 +1,480 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm920.S: MMU functions for ARM920 | ||
3 | * | ||
4 | * Copyright (C) 1999,2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * These are the low level assembler for performing cache and TLB | ||
23 | * functions on the arm920. | ||
24 | * | ||
25 | * CONFIG_CPU_ARM920_CPU_IDLE -> nohlt | ||
26 | */ | ||
27 | #include <linux/linkage.h> | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <asm/assembler.h> | ||
31 | #include <asm/pgtable.h> | ||
32 | #include <asm/procinfo.h> | ||
33 | #include <asm/hardware.h> | ||
34 | #include <asm/page.h> | ||
35 | #include <asm/ptrace.h> | ||
36 | #include "proc-macros.S" | ||
37 | |||
38 | /* | ||
39 | * The size of one data cache line. | ||
40 | */ | ||
41 | #define CACHE_DLINESIZE 32 | ||
42 | |||
43 | /* | ||
44 | * The number of data cache segments. | ||
45 | */ | ||
46 | #define CACHE_DSEGMENTS 8 | ||
47 | |||
48 | /* | ||
49 | * The number of lines in a cache segment. | ||
50 | */ | ||
51 | #define CACHE_DENTRIES 64 | ||
52 | |||
53 | /* | ||
54 | * This is the size at which it becomes more efficient to | ||
55 | * clean the whole cache, rather than using the individual | ||
56 | * cache line maintainence instructions. | ||
57 | */ | ||
58 | #define CACHE_DLIMIT 65536 | ||
59 | |||
60 | |||
61 | .text | ||
62 | /* | ||
63 | * cpu_arm920_proc_init() | ||
64 | */ | ||
65 | ENTRY(cpu_arm920_proc_init) | ||
66 | mov pc, lr | ||
67 | |||
68 | /* | ||
69 | * cpu_arm920_proc_fin() | ||
70 | */ | ||
71 | ENTRY(cpu_arm920_proc_fin) | ||
72 | stmfd sp!, {lr} | ||
73 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
74 | msr cpsr_c, ip | ||
75 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
76 | bl arm920_flush_kern_cache_all | ||
77 | #else | ||
78 | bl v4wt_flush_kern_cache_all | ||
79 | #endif | ||
80 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
81 | bic r0, r0, #0x1000 @ ...i............ | ||
82 | bic r0, r0, #0x000e @ ............wca. | ||
83 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
84 | ldmfd sp!, {pc} | ||
85 | |||
86 | /* | ||
87 | * cpu_arm920_reset(loc) | ||
88 | * | ||
89 | * Perform a soft reset of the system. Put the CPU into the | ||
90 | * same state as it would be if it had been reset, and branch | ||
91 | * to what would be the reset vector. | ||
92 | * | ||
93 | * loc: location to jump to for soft reset | ||
94 | */ | ||
95 | .align 5 | ||
96 | ENTRY(cpu_arm920_reset) | ||
97 | mov ip, #0 | ||
98 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
99 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
100 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
101 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
102 | bic ip, ip, #0x000f @ ............wcam | ||
103 | bic ip, ip, #0x1100 @ ...i...s........ | ||
104 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
105 | mov pc, r0 | ||
106 | |||
107 | /* | ||
108 | * cpu_arm920_do_idle() | ||
109 | */ | ||
110 | .align 5 | ||
111 | ENTRY(cpu_arm920_do_idle) | ||
112 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
113 | mov pc, lr | ||
114 | |||
115 | |||
116 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
117 | |||
118 | /* | ||
119 | * flush_user_cache_all() | ||
120 | * | ||
121 | * Invalidate all cache entries in a particular address | ||
122 | * space. | ||
123 | */ | ||
124 | ENTRY(arm920_flush_user_cache_all) | ||
125 | /* FALLTHROUGH */ | ||
126 | |||
127 | /* | ||
128 | * flush_kern_cache_all() | ||
129 | * | ||
130 | * Clean and invalidate the entire cache. | ||
131 | */ | ||
132 | ENTRY(arm920_flush_kern_cache_all) | ||
133 | mov r2, #VM_EXEC | ||
134 | mov ip, #0 | ||
135 | __flush_whole_cache: | ||
136 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 8 segments | ||
137 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
138 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
139 | subs r3, r3, #1 << 26 | ||
140 | bcs 2b @ entries 63 to 0 | ||
141 | subs r1, r1, #1 << 5 | ||
142 | bcs 1b @ segments 7 to 0 | ||
143 | tst r2, #VM_EXEC | ||
144 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
145 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
146 | mov pc, lr | ||
147 | |||
148 | /* | ||
149 | * flush_user_cache_range(start, end, flags) | ||
150 | * | ||
151 | * Invalidate a range of cache entries in the specified | ||
152 | * address space. | ||
153 | * | ||
154 | * - start - start address (inclusive) | ||
155 | * - end - end address (exclusive) | ||
156 | * - flags - vm_flags for address space | ||
157 | */ | ||
158 | ENTRY(arm920_flush_user_cache_range) | ||
159 | mov ip, #0 | ||
160 | sub r3, r1, r0 @ calculate total size | ||
161 | cmp r3, #CACHE_DLIMIT | ||
162 | bhs __flush_whole_cache | ||
163 | |||
164 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
165 | tst r2, #VM_EXEC | ||
166 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
167 | add r0, r0, #CACHE_DLINESIZE | ||
168 | cmp r0, r1 | ||
169 | blo 1b | ||
170 | tst r2, #VM_EXEC | ||
171 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
172 | mov pc, lr | ||
173 | |||
174 | /* | ||
175 | * coherent_kern_range(start, end) | ||
176 | * | ||
177 | * Ensure coherency between the Icache and the Dcache in the | ||
178 | * region described by start, end. If you have non-snooping | ||
179 | * Harvard caches, you need to implement this function. | ||
180 | * | ||
181 | * - start - virtual start address | ||
182 | * - end - virtual end address | ||
183 | */ | ||
184 | ENTRY(arm920_coherent_kern_range) | ||
185 | /* FALLTHROUGH */ | ||
186 | |||
187 | /* | ||
188 | * coherent_user_range(start, end) | ||
189 | * | ||
190 | * Ensure coherency between the Icache and the Dcache in the | ||
191 | * region described by start, end. If you have non-snooping | ||
192 | * Harvard caches, you need to implement this function. | ||
193 | * | ||
194 | * - start - virtual start address | ||
195 | * - end - virtual end address | ||
196 | */ | ||
197 | ENTRY(arm920_coherent_user_range) | ||
198 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
199 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
200 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
201 | add r0, r0, #CACHE_DLINESIZE | ||
202 | cmp r0, r1 | ||
203 | blo 1b | ||
204 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
205 | mov pc, lr | ||
206 | |||
207 | /* | ||
208 | * flush_kern_dcache_page(void *page) | ||
209 | * | ||
210 | * Ensure no D cache aliasing occurs, either with itself or | ||
211 | * the I cache | ||
212 | * | ||
213 | * - addr - page aligned address | ||
214 | */ | ||
215 | ENTRY(arm920_flush_kern_dcache_page) | ||
216 | add r1, r0, #PAGE_SZ | ||
217 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
218 | add r0, r0, #CACHE_DLINESIZE | ||
219 | cmp r0, r1 | ||
220 | blo 1b | ||
221 | mov r0, #0 | ||
222 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
223 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
224 | mov pc, lr | ||
225 | |||
226 | /* | ||
227 | * dma_inv_range(start, end) | ||
228 | * | ||
229 | * Invalidate (discard) the specified virtual address range. | ||
230 | * May not write back any entries. If 'start' or 'end' | ||
231 | * are not cache line aligned, those lines must be written | ||
232 | * back. | ||
233 | * | ||
234 | * - start - virtual start address | ||
235 | * - end - virtual end address | ||
236 | * | ||
237 | * (same as v4wb) | ||
238 | */ | ||
239 | ENTRY(arm920_dma_inv_range) | ||
240 | tst r0, #CACHE_DLINESIZE - 1 | ||
241 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
242 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
243 | tst r1, #CACHE_DLINESIZE - 1 | ||
244 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
245 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
246 | add r0, r0, #CACHE_DLINESIZE | ||
247 | cmp r0, r1 | ||
248 | blo 1b | ||
249 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
250 | mov pc, lr | ||
251 | |||
252 | /* | ||
253 | * dma_clean_range(start, end) | ||
254 | * | ||
255 | * Clean the specified virtual address range. | ||
256 | * | ||
257 | * - start - virtual start address | ||
258 | * - end - virtual end address | ||
259 | * | ||
260 | * (same as v4wb) | ||
261 | */ | ||
262 | ENTRY(arm920_dma_clean_range) | ||
263 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
264 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
265 | add r0, r0, #CACHE_DLINESIZE | ||
266 | cmp r0, r1 | ||
267 | blo 1b | ||
268 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
269 | mov pc, lr | ||
270 | |||
271 | /* | ||
272 | * dma_flush_range(start, end) | ||
273 | * | ||
274 | * Clean and invalidate the specified virtual address range. | ||
275 | * | ||
276 | * - start - virtual start address | ||
277 | * - end - virtual end address | ||
278 | */ | ||
279 | ENTRY(arm920_dma_flush_range) | ||
280 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
281 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
282 | add r0, r0, #CACHE_DLINESIZE | ||
283 | cmp r0, r1 | ||
284 | blo 1b | ||
285 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
286 | mov pc, lr | ||
287 | |||
288 | ENTRY(arm920_cache_fns) | ||
289 | .long arm920_flush_kern_cache_all | ||
290 | .long arm920_flush_user_cache_all | ||
291 | .long arm920_flush_user_cache_range | ||
292 | .long arm920_coherent_kern_range | ||
293 | .long arm920_coherent_user_range | ||
294 | .long arm920_flush_kern_dcache_page | ||
295 | .long arm920_dma_inv_range | ||
296 | .long arm920_dma_clean_range | ||
297 | .long arm920_dma_flush_range | ||
298 | |||
299 | #endif | ||
300 | |||
301 | |||
302 | ENTRY(cpu_arm920_dcache_clean_area) | ||
303 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
304 | add r0, r0, #CACHE_DLINESIZE | ||
305 | subs r1, r1, #CACHE_DLINESIZE | ||
306 | bhi 1b | ||
307 | mov pc, lr | ||
308 | |||
309 | /* =============================== PageTable ============================== */ | ||
310 | |||
311 | /* | ||
312 | * cpu_arm920_switch_mm(pgd) | ||
313 | * | ||
314 | * Set the translation base pointer to be as described by pgd. | ||
315 | * | ||
316 | * pgd: new page tables | ||
317 | */ | ||
318 | .align 5 | ||
319 | ENTRY(cpu_arm920_switch_mm) | ||
320 | mov ip, #0 | ||
321 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
322 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
323 | #else | ||
324 | @ && 'Clean & Invalidate whole DCache' | ||
325 | @ && Re-written to use Index Ops. | ||
326 | @ && Uses registers r1, r3 and ip | ||
327 | |||
328 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 8 segments | ||
329 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
330 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean & invalidate D index | ||
331 | subs r3, r3, #1 << 26 | ||
332 | bcs 2b @ entries 63 to 0 | ||
333 | subs r1, r1, #1 << 5 | ||
334 | bcs 1b @ segments 7 to 0 | ||
335 | #endif | ||
336 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
337 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
338 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
339 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
340 | mov pc, lr | ||
341 | |||
342 | /* | ||
343 | * cpu_arm920_set_pte(ptep, pte) | ||
344 | * | ||
345 | * Set a PTE and flush it out | ||
346 | */ | ||
347 | .align 5 | ||
348 | ENTRY(cpu_arm920_set_pte) | ||
349 | str r1, [r0], #-2048 @ linux version | ||
350 | |||
351 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
352 | |||
353 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
354 | bic r2, r2, #PTE_TYPE_MASK | ||
355 | orr r2, r2, #PTE_TYPE_SMALL | ||
356 | |||
357 | tst r1, #L_PTE_USER @ User? | ||
358 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
359 | |||
360 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
361 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
362 | |||
363 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
364 | movne r2, #0 | ||
365 | |||
366 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
367 | eor r3, r2, #0x0a @ C & small page? | ||
368 | tst r3, #0x0b | ||
369 | biceq r2, r2, #4 | ||
370 | #endif | ||
371 | str r2, [r0] @ hardware version | ||
372 | mov r0, r0 | ||
373 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
374 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
375 | mov pc, lr | ||
376 | |||
377 | __INIT | ||
378 | |||
379 | .type __arm920_setup, #function | ||
380 | __arm920_setup: | ||
381 | mov r0, #0 | ||
382 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
383 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
384 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
385 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
386 | ldr r5, arm920_cr1_clear | ||
387 | bic r0, r0, r5 | ||
388 | ldr r5, arm920_cr1_set | ||
389 | orr r0, r0, r5 | ||
390 | mov pc, lr | ||
391 | .size __arm920_setup, . - __arm920_setup | ||
392 | |||
393 | /* | ||
394 | * R | ||
395 | * .RVI ZFRS BLDP WCAM | ||
396 | * ..11 0001 ..11 0101 | ||
397 | * | ||
398 | */ | ||
399 | .type arm920_cr1_clear, #object | ||
400 | .type arm920_cr1_set, #object | ||
401 | arm920_cr1_clear: | ||
402 | .word 0x3f3f | ||
403 | arm920_cr1_set: | ||
404 | .word 0x3135 | ||
405 | |||
406 | __INITDATA | ||
407 | |||
408 | /* | ||
409 | * Purpose : Function pointers used to access above functions - all calls | ||
410 | * come through these | ||
411 | */ | ||
412 | .type arm920_processor_functions, #object | ||
413 | arm920_processor_functions: | ||
414 | .word v4t_early_abort | ||
415 | .word cpu_arm920_proc_init | ||
416 | .word cpu_arm920_proc_fin | ||
417 | .word cpu_arm920_reset | ||
418 | .word cpu_arm920_do_idle | ||
419 | .word cpu_arm920_dcache_clean_area | ||
420 | .word cpu_arm920_switch_mm | ||
421 | .word cpu_arm920_set_pte | ||
422 | .size arm920_processor_functions, . - arm920_processor_functions | ||
423 | |||
424 | .section ".rodata" | ||
425 | |||
426 | .type cpu_arch_name, #object | ||
427 | cpu_arch_name: | ||
428 | .asciz "armv4t" | ||
429 | .size cpu_arch_name, . - cpu_arch_name | ||
430 | |||
431 | .type cpu_elf_name, #object | ||
432 | cpu_elf_name: | ||
433 | .asciz "v4" | ||
434 | .size cpu_elf_name, . - cpu_elf_name | ||
435 | |||
436 | .type cpu_arm920_name, #object | ||
437 | cpu_arm920_name: | ||
438 | .ascii "ARM920T" | ||
439 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
440 | .ascii "i" | ||
441 | #endif | ||
442 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
443 | .ascii "d" | ||
444 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
445 | .ascii "(wt)" | ||
446 | #else | ||
447 | .ascii "(wb)" | ||
448 | #endif | ||
449 | #endif | ||
450 | .ascii "\0" | ||
451 | .size cpu_arm920_name, . - cpu_arm920_name | ||
452 | |||
453 | .align | ||
454 | |||
455 | .section ".proc.info", #alloc, #execinstr | ||
456 | |||
457 | .type __arm920_proc_info,#object | ||
458 | __arm920_proc_info: | ||
459 | .long 0x41009200 | ||
460 | .long 0xff00fff0 | ||
461 | .long PMD_TYPE_SECT | \ | ||
462 | PMD_SECT_BUFFERABLE | \ | ||
463 | PMD_SECT_CACHEABLE | \ | ||
464 | PMD_BIT4 | \ | ||
465 | PMD_SECT_AP_WRITE | \ | ||
466 | PMD_SECT_AP_READ | ||
467 | b __arm920_setup | ||
468 | .long cpu_arch_name | ||
469 | .long cpu_elf_name | ||
470 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | ||
471 | .long cpu_arm920_name | ||
472 | .long arm920_processor_functions | ||
473 | .long v4wbi_tlb_fns | ||
474 | .long v4wb_user_fns | ||
475 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
476 | .long arm920_cache_fns | ||
477 | #else | ||
478 | .long v4wt_cache_fns | ||
479 | #endif | ||
480 | .size __arm920_proc_info, . - __arm920_proc_info | ||
diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S new file mode 100644 index 000000000000..62bc34a139ee --- /dev/null +++ b/arch/arm/mm/proc-arm922.S | |||
@@ -0,0 +1,484 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm922.S: MMU functions for ARM922 | ||
3 | * | ||
4 | * Copyright (C) 1999,2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * Copyright (C) 2001 Altera Corporation | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * | ||
23 | * These are the low level assembler for performing cache and TLB | ||
24 | * functions on the arm922. | ||
25 | * | ||
26 | * CONFIG_CPU_ARM922_CPU_IDLE -> nohlt | ||
27 | */ | ||
28 | #include <linux/linkage.h> | ||
29 | #include <linux/config.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <asm/assembler.h> | ||
32 | #include <asm/pgtable.h> | ||
33 | #include <asm/procinfo.h> | ||
34 | #include <asm/hardware.h> | ||
35 | #include <asm/page.h> | ||
36 | #include <asm/ptrace.h> | ||
37 | #include "proc-macros.S" | ||
38 | |||
39 | /* | ||
40 | * The size of one data cache line. | ||
41 | */ | ||
42 | #define CACHE_DLINESIZE 32 | ||
43 | |||
44 | /* | ||
45 | * The number of data cache segments. | ||
46 | */ | ||
47 | #define CACHE_DSEGMENTS 4 | ||
48 | |||
49 | /* | ||
50 | * The number of lines in a cache segment. | ||
51 | */ | ||
52 | #define CACHE_DENTRIES 64 | ||
53 | |||
54 | /* | ||
55 | * This is the size at which it becomes more efficient to | ||
56 | * clean the whole cache, rather than using the individual | ||
57 | * cache line maintainence instructions. (I think this should | ||
58 | * be 32768). | ||
59 | */ | ||
60 | #define CACHE_DLIMIT 8192 | ||
61 | |||
62 | |||
63 | .text | ||
64 | /* | ||
65 | * cpu_arm922_proc_init() | ||
66 | */ | ||
67 | ENTRY(cpu_arm922_proc_init) | ||
68 | mov pc, lr | ||
69 | |||
70 | /* | ||
71 | * cpu_arm922_proc_fin() | ||
72 | */ | ||
73 | ENTRY(cpu_arm922_proc_fin) | ||
74 | stmfd sp!, {lr} | ||
75 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
76 | msr cpsr_c, ip | ||
77 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
78 | bl arm922_flush_kern_cache_all | ||
79 | #else | ||
80 | bl v4wt_flush_kern_cache_all | ||
81 | #endif | ||
82 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
83 | bic r0, r0, #0x1000 @ ...i............ | ||
84 | bic r0, r0, #0x000e @ ............wca. | ||
85 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
86 | ldmfd sp!, {pc} | ||
87 | |||
88 | /* | ||
89 | * cpu_arm922_reset(loc) | ||
90 | * | ||
91 | * Perform a soft reset of the system. Put the CPU into the | ||
92 | * same state as it would be if it had been reset, and branch | ||
93 | * to what would be the reset vector. | ||
94 | * | ||
95 | * loc: location to jump to for soft reset | ||
96 | */ | ||
97 | .align 5 | ||
98 | ENTRY(cpu_arm922_reset) | ||
99 | mov ip, #0 | ||
100 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
101 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
102 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
103 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
104 | bic ip, ip, #0x000f @ ............wcam | ||
105 | bic ip, ip, #0x1100 @ ...i...s........ | ||
106 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
107 | mov pc, r0 | ||
108 | |||
109 | /* | ||
110 | * cpu_arm922_do_idle() | ||
111 | */ | ||
112 | .align 5 | ||
113 | ENTRY(cpu_arm922_do_idle) | ||
114 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
115 | mov pc, lr | ||
116 | |||
117 | |||
118 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
119 | |||
120 | /* | ||
121 | * flush_user_cache_all() | ||
122 | * | ||
123 | * Clean and invalidate all cache entries in a particular | ||
124 | * address space. | ||
125 | */ | ||
126 | ENTRY(arm922_flush_user_cache_all) | ||
127 | /* FALLTHROUGH */ | ||
128 | |||
129 | /* | ||
130 | * flush_kern_cache_all() | ||
131 | * | ||
132 | * Clean and invalidate the entire cache. | ||
133 | */ | ||
134 | ENTRY(arm922_flush_kern_cache_all) | ||
135 | mov r2, #VM_EXEC | ||
136 | mov ip, #0 | ||
137 | __flush_whole_cache: | ||
138 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 8 segments | ||
139 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
140 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
141 | subs r3, r3, #1 << 26 | ||
142 | bcs 2b @ entries 63 to 0 | ||
143 | subs r1, r1, #1 << 5 | ||
144 | bcs 1b @ segments 7 to 0 | ||
145 | tst r2, #VM_EXEC | ||
146 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
147 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
148 | mov pc, lr | ||
149 | |||
150 | /* | ||
151 | * flush_user_cache_range(start, end, flags) | ||
152 | * | ||
153 | * Clean and invalidate a range of cache entries in the | ||
154 | * specified address range. | ||
155 | * | ||
156 | * - start - start address (inclusive) | ||
157 | * - end - end address (exclusive) | ||
158 | * - flags - vm_flags describing address space | ||
159 | */ | ||
160 | ENTRY(arm922_flush_user_cache_range) | ||
161 | mov ip, #0 | ||
162 | sub r3, r1, r0 @ calculate total size | ||
163 | cmp r3, #CACHE_DLIMIT | ||
164 | bhs __flush_whole_cache | ||
165 | |||
166 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
167 | tst r2, #VM_EXEC | ||
168 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
169 | add r0, r0, #CACHE_DLINESIZE | ||
170 | cmp r0, r1 | ||
171 | blo 1b | ||
172 | tst r2, #VM_EXEC | ||
173 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
174 | mov pc, lr | ||
175 | |||
176 | /* | ||
177 | * coherent_kern_range(start, end) | ||
178 | * | ||
179 | * Ensure coherency between the Icache and the Dcache in the | ||
180 | * region described by start, end. If you have non-snooping | ||
181 | * Harvard caches, you need to implement this function. | ||
182 | * | ||
183 | * - start - virtual start address | ||
184 | * - end - virtual end address | ||
185 | */ | ||
186 | ENTRY(arm922_coherent_kern_range) | ||
187 | /* FALLTHROUGH */ | ||
188 | |||
189 | /* | ||
190 | * coherent_user_range(start, end) | ||
191 | * | ||
192 | * Ensure coherency between the Icache and the Dcache in the | ||
193 | * region described by start, end. If you have non-snooping | ||
194 | * Harvard caches, you need to implement this function. | ||
195 | * | ||
196 | * - start - virtual start address | ||
197 | * - end - virtual end address | ||
198 | */ | ||
199 | ENTRY(arm922_coherent_user_range) | ||
200 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
201 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
202 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
203 | add r0, r0, #CACHE_DLINESIZE | ||
204 | cmp r0, r1 | ||
205 | blo 1b | ||
206 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
207 | mov pc, lr | ||
208 | |||
209 | /* | ||
210 | * flush_kern_dcache_page(void *page) | ||
211 | * | ||
212 | * Ensure no D cache aliasing occurs, either with itself or | ||
213 | * the I cache | ||
214 | * | ||
215 | * - addr - page aligned address | ||
216 | */ | ||
217 | ENTRY(arm922_flush_kern_dcache_page) | ||
218 | add r1, r0, #PAGE_SZ | ||
219 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
220 | add r0, r0, #CACHE_DLINESIZE | ||
221 | cmp r0, r1 | ||
222 | blo 1b | ||
223 | mov r0, #0 | ||
224 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
225 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
226 | mov pc, lr | ||
227 | |||
228 | /* | ||
229 | * dma_inv_range(start, end) | ||
230 | * | ||
231 | * Invalidate (discard) the specified virtual address range. | ||
232 | * May not write back any entries. If 'start' or 'end' | ||
233 | * are not cache line aligned, those lines must be written | ||
234 | * back. | ||
235 | * | ||
236 | * - start - virtual start address | ||
237 | * - end - virtual end address | ||
238 | * | ||
239 | * (same as v4wb) | ||
240 | */ | ||
241 | ENTRY(arm922_dma_inv_range) | ||
242 | tst r0, #CACHE_DLINESIZE - 1 | ||
243 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
244 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
245 | tst r1, #CACHE_DLINESIZE - 1 | ||
246 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
247 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
248 | add r0, r0, #CACHE_DLINESIZE | ||
249 | cmp r0, r1 | ||
250 | blo 1b | ||
251 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
252 | mov pc, lr | ||
253 | |||
254 | /* | ||
255 | * dma_clean_range(start, end) | ||
256 | * | ||
257 | * Clean the specified virtual address range. | ||
258 | * | ||
259 | * - start - virtual start address | ||
260 | * - end - virtual end address | ||
261 | * | ||
262 | * (same as v4wb) | ||
263 | */ | ||
264 | ENTRY(arm922_dma_clean_range) | ||
265 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
266 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
267 | add r0, r0, #CACHE_DLINESIZE | ||
268 | cmp r0, r1 | ||
269 | blo 1b | ||
270 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
271 | mov pc, lr | ||
272 | |||
273 | /* | ||
274 | * dma_flush_range(start, end) | ||
275 | * | ||
276 | * Clean and invalidate the specified virtual address range. | ||
277 | * | ||
278 | * - start - virtual start address | ||
279 | * - end - virtual end address | ||
280 | */ | ||
281 | ENTRY(arm922_dma_flush_range) | ||
282 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
283 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
284 | add r0, r0, #CACHE_DLINESIZE | ||
285 | cmp r0, r1 | ||
286 | blo 1b | ||
287 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
288 | mov pc, lr | ||
289 | |||
290 | ENTRY(arm922_cache_fns) | ||
291 | .long arm922_flush_kern_cache_all | ||
292 | .long arm922_flush_user_cache_all | ||
293 | .long arm922_flush_user_cache_range | ||
294 | .long arm922_coherent_kern_range | ||
295 | .long arm922_coherent_user_range | ||
296 | .long arm922_flush_kern_dcache_page | ||
297 | .long arm922_dma_inv_range | ||
298 | .long arm922_dma_clean_range | ||
299 | .long arm922_dma_flush_range | ||
300 | |||
301 | #endif | ||
302 | |||
303 | |||
304 | ENTRY(cpu_arm922_dcache_clean_area) | ||
305 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
306 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
307 | add r0, r0, #CACHE_DLINESIZE | ||
308 | subs r1, r1, #CACHE_DLINESIZE | ||
309 | bhi 1b | ||
310 | #endif | ||
311 | mov pc, lr | ||
312 | |||
313 | /* =============================== PageTable ============================== */ | ||
314 | |||
315 | /* | ||
316 | * cpu_arm922_switch_mm(pgd) | ||
317 | * | ||
318 | * Set the translation base pointer to be as described by pgd. | ||
319 | * | ||
320 | * pgd: new page tables | ||
321 | */ | ||
322 | .align 5 | ||
323 | ENTRY(cpu_arm922_switch_mm) | ||
324 | mov ip, #0 | ||
325 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
326 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
327 | #else | ||
328 | @ && 'Clean & Invalidate whole DCache' | ||
329 | @ && Re-written to use Index Ops. | ||
330 | @ && Uses registers r1, r3 and ip | ||
331 | |||
332 | mov r1, #(CACHE_DSEGMENTS - 1) << 5 @ 4 segments | ||
333 | 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries | ||
334 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean & invalidate D index | ||
335 | subs r3, r3, #1 << 26 | ||
336 | bcs 2b @ entries 63 to 0 | ||
337 | subs r1, r1, #1 << 5 | ||
338 | bcs 1b @ segments 7 to 0 | ||
339 | #endif | ||
340 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
341 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
342 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
343 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
344 | mov pc, lr | ||
345 | |||
346 | /* | ||
347 | * cpu_arm922_set_pte(ptep, pte) | ||
348 | * | ||
349 | * Set a PTE and flush it out | ||
350 | */ | ||
351 | .align 5 | ||
352 | ENTRY(cpu_arm922_set_pte) | ||
353 | str r1, [r0], #-2048 @ linux version | ||
354 | |||
355 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
356 | |||
357 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
358 | bic r2, r2, #PTE_TYPE_MASK | ||
359 | orr r2, r2, #PTE_TYPE_SMALL | ||
360 | |||
361 | tst r1, #L_PTE_USER @ User? | ||
362 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
363 | |||
364 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
365 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
366 | |||
367 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
368 | movne r2, #0 | ||
369 | |||
370 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
371 | eor r3, r2, #0x0a @ C & small page? | ||
372 | tst r3, #0x0b | ||
373 | biceq r2, r2, #4 | ||
374 | #endif | ||
375 | str r2, [r0] @ hardware version | ||
376 | mov r0, r0 | ||
377 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
378 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
379 | mov pc, lr | ||
380 | |||
381 | __INIT | ||
382 | |||
383 | .type __arm922_setup, #function | ||
384 | __arm922_setup: | ||
385 | mov r0, #0 | ||
386 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
387 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
388 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
389 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
390 | ldr r5, arm922_cr1_clear | ||
391 | bic r0, r0, r5 | ||
392 | ldr r5, arm922_cr1_set | ||
393 | orr r0, r0, r5 | ||
394 | mov pc, lr | ||
395 | .size __arm922_setup, . - __arm922_setup | ||
396 | |||
397 | /* | ||
398 | * R | ||
399 | * .RVI ZFRS BLDP WCAM | ||
400 | * ..11 0001 ..11 0101 | ||
401 | * | ||
402 | */ | ||
403 | .type arm922_cr1_clear, #object | ||
404 | .type arm922_cr1_set, #object | ||
405 | arm922_cr1_clear: | ||
406 | .word 0x3f3f | ||
407 | arm922_cr1_set: | ||
408 | .word 0x3135 | ||
409 | |||
410 | __INITDATA | ||
411 | |||
412 | /* | ||
413 | * Purpose : Function pointers used to access above functions - all calls | ||
414 | * come through these | ||
415 | */ | ||
416 | .type arm922_processor_functions, #object | ||
417 | arm922_processor_functions: | ||
418 | .word v4t_early_abort | ||
419 | .word cpu_arm922_proc_init | ||
420 | .word cpu_arm922_proc_fin | ||
421 | .word cpu_arm922_reset | ||
422 | .word cpu_arm922_do_idle | ||
423 | .word cpu_arm922_dcache_clean_area | ||
424 | .word cpu_arm922_switch_mm | ||
425 | .word cpu_arm922_set_pte | ||
426 | .size arm922_processor_functions, . - arm922_processor_functions | ||
427 | |||
428 | .section ".rodata" | ||
429 | |||
430 | .type cpu_arch_name, #object | ||
431 | cpu_arch_name: | ||
432 | .asciz "armv4t" | ||
433 | .size cpu_arch_name, . - cpu_arch_name | ||
434 | |||
435 | .type cpu_elf_name, #object | ||
436 | cpu_elf_name: | ||
437 | .asciz "v4" | ||
438 | .size cpu_elf_name, . - cpu_elf_name | ||
439 | |||
440 | .type cpu_arm922_name, #object | ||
441 | cpu_arm922_name: | ||
442 | .ascii "ARM922T" | ||
443 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
444 | .ascii "i" | ||
445 | #endif | ||
446 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
447 | .ascii "d" | ||
448 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
449 | .ascii "(wt)" | ||
450 | #else | ||
451 | .ascii "(wb)" | ||
452 | #endif | ||
453 | #endif | ||
454 | .ascii "\0" | ||
455 | .size cpu_arm922_name, . - cpu_arm922_name | ||
456 | |||
457 | .align | ||
458 | |||
459 | .section ".proc.info", #alloc, #execinstr | ||
460 | |||
461 | .type __arm922_proc_info,#object | ||
462 | __arm922_proc_info: | ||
463 | .long 0x41009220 | ||
464 | .long 0xff00fff0 | ||
465 | .long PMD_TYPE_SECT | \ | ||
466 | PMD_SECT_BUFFERABLE | \ | ||
467 | PMD_SECT_CACHEABLE | \ | ||
468 | PMD_BIT4 | \ | ||
469 | PMD_SECT_AP_WRITE | \ | ||
470 | PMD_SECT_AP_READ | ||
471 | b __arm922_setup | ||
472 | .long cpu_arch_name | ||
473 | .long cpu_elf_name | ||
474 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | ||
475 | .long cpu_arm922_name | ||
476 | .long arm922_processor_functions | ||
477 | .long v4wbi_tlb_fns | ||
478 | .long v4wb_user_fns | ||
479 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
480 | .long arm922_cache_fns | ||
481 | #else | ||
482 | .long v4wt_cache_fns | ||
483 | #endif | ||
484 | .size __arm922_proc_info, . - __arm922_proc_info | ||
diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S new file mode 100644 index 000000000000..ee49aa2ca781 --- /dev/null +++ b/arch/arm/mm/proc-arm925.S | |||
@@ -0,0 +1,562 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/arm925.S: MMU functions for ARM925 | ||
3 | * | ||
4 | * Copyright (C) 1999,2000 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * Copyright (C) 2002 RidgeRun, Inc. | ||
7 | * Copyright (C) 2002-2003 MontaVista Software, Inc. | ||
8 | * | ||
9 | * Update for Linux-2.6 and cache flush improvements | ||
10 | * Copyright (C) 2004 Nokia Corporation by Tony Lindgren <tony@atomide.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | * | ||
26 | * | ||
27 | * These are the low level assembler for performing cache and TLB | ||
28 | * functions on the arm925. | ||
29 | * | ||
30 | * CONFIG_CPU_ARM925_CPU_IDLE -> nohlt | ||
31 | * | ||
32 | * Some additional notes based on deciphering the TI TRM on OMAP-5910: | ||
33 | * | ||
34 | * NOTE1: The TI925T Configuration Register bit "D-cache clean and flush | ||
35 | * entry mode" must be 0 to flush the entries in both segments | ||
36 | * at once. This is the default value. See TRM 2-20 and 2-24 for | ||
37 | * more information. | ||
38 | * | ||
39 | * NOTE2: Default is the "D-cache clean and flush entry mode". It looks | ||
40 | * like the "Transparent mode" must be on for partial cache flushes | ||
41 | * to work in this mode. This mode only works with 16-bit external | ||
42 | * memory. See TRM 2-24 for more information. | ||
43 | * | ||
44 | * NOTE3: Write-back cache flushing seems to be flakey with devices using | ||
45 | * direct memory access, such as USB OHCI. The workaround is to use | ||
46 | * write-through cache with CONFIG_CPU_DCACHE_WRITETHROUGH (this is | ||
47 | * the default for OMAP-1510). | ||
48 | */ | ||
49 | |||
50 | #include <linux/linkage.h> | ||
51 | #include <linux/config.h> | ||
52 | #include <linux/init.h> | ||
53 | #include <asm/assembler.h> | ||
54 | #include <asm/pgtable.h> | ||
55 | #include <asm/procinfo.h> | ||
56 | #include <asm/hardware.h> | ||
57 | #include <asm/page.h> | ||
58 | #include <asm/ptrace.h> | ||
59 | #include "proc-macros.S" | ||
60 | |||
61 | /* | ||
62 | * The size of one data cache line. | ||
63 | */ | ||
64 | #define CACHE_DLINESIZE 16 | ||
65 | |||
66 | /* | ||
67 | * The number of data cache segments. | ||
68 | */ | ||
69 | #define CACHE_DSEGMENTS 2 | ||
70 | |||
71 | /* | ||
72 | * The number of lines in a cache segment. | ||
73 | */ | ||
74 | #define CACHE_DENTRIES 256 | ||
75 | |||
76 | /* | ||
77 | * This is the size at which it becomes more efficient to | ||
78 | * clean the whole cache, rather than using the individual | ||
79 | * cache line maintainence instructions. | ||
80 | */ | ||
81 | #define CACHE_DLIMIT 8192 | ||
82 | |||
83 | .text | ||
84 | /* | ||
85 | * cpu_arm925_proc_init() | ||
86 | */ | ||
87 | ENTRY(cpu_arm925_proc_init) | ||
88 | mov pc, lr | ||
89 | |||
90 | /* | ||
91 | * cpu_arm925_proc_fin() | ||
92 | */ | ||
93 | ENTRY(cpu_arm925_proc_fin) | ||
94 | stmfd sp!, {lr} | ||
95 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
96 | msr cpsr_c, ip | ||
97 | bl arm925_flush_kern_cache_all | ||
98 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
99 | bic r0, r0, #0x1000 @ ...i............ | ||
100 | bic r0, r0, #0x000e @ ............wca. | ||
101 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
102 | ldmfd sp!, {pc} | ||
103 | |||
104 | /* | ||
105 | * cpu_arm925_reset(loc) | ||
106 | * | ||
107 | * Perform a soft reset of the system. Put the CPU into the | ||
108 | * same state as it would be if it had been reset, and branch | ||
109 | * to what would be the reset vector. | ||
110 | * | ||
111 | * loc: location to jump to for soft reset | ||
112 | */ | ||
113 | .align 5 | ||
114 | ENTRY(cpu_arm925_reset) | ||
115 | /* Send software reset to MPU and DSP */ | ||
116 | mov ip, #0xff000000 | ||
117 | orr ip, ip, #0x00fe0000 | ||
118 | orr ip, ip, #0x0000ce00 | ||
119 | mov r4, #1 | ||
120 | strh r4, [ip, #0x10] | ||
121 | |||
122 | mov ip, #0 | ||
123 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
124 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
125 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
126 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
127 | bic ip, ip, #0x000f @ ............wcam | ||
128 | bic ip, ip, #0x1100 @ ...i...s........ | ||
129 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
130 | mov pc, r0 | ||
131 | |||
132 | /* | ||
133 | * cpu_arm925_do_idle() | ||
134 | * | ||
135 | * Called with IRQs disabled | ||
136 | */ | ||
137 | .align 10 | ||
138 | ENTRY(cpu_arm925_do_idle) | ||
139 | mov r0, #0 | ||
140 | mrc p15, 0, r1, c1, c0, 0 @ Read control register | ||
141 | mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer | ||
142 | bic r2, r1, #1 << 12 | ||
143 | mcr p15, 0, r2, c1, c0, 0 @ Disable I cache | ||
144 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
145 | mcr p15, 0, r1, c1, c0, 0 @ Restore ICache enable | ||
146 | mov pc, lr | ||
147 | |||
148 | /* | ||
149 | * flush_user_cache_all() | ||
150 | * | ||
151 | * Clean and invalidate all cache entries in a particular | ||
152 | * address space. | ||
153 | */ | ||
154 | ENTRY(arm925_flush_user_cache_all) | ||
155 | /* FALLTHROUGH */ | ||
156 | |||
157 | /* | ||
158 | * flush_kern_cache_all() | ||
159 | * | ||
160 | * Clean and invalidate the entire cache. | ||
161 | */ | ||
162 | ENTRY(arm925_flush_kern_cache_all) | ||
163 | mov r2, #VM_EXEC | ||
164 | mov ip, #0 | ||
165 | __flush_whole_cache: | ||
166 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
167 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
168 | #else | ||
169 | /* Flush entries in both segments at once, see NOTE1 above */ | ||
170 | mov r3, #(CACHE_DENTRIES - 1) << 4 @ 256 entries in segment | ||
171 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean+invalidate D index | ||
172 | subs r3, r3, #1 << 4 | ||
173 | bcs 2b @ entries 255 to 0 | ||
174 | #endif | ||
175 | tst r2, #VM_EXEC | ||
176 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
177 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
178 | mov pc, lr | ||
179 | |||
180 | /* | ||
181 | * flush_user_cache_range(start, end, flags) | ||
182 | * | ||
183 | * Clean and invalidate a range of cache entries in the | ||
184 | * specified address range. | ||
185 | * | ||
186 | * - start - start address (inclusive) | ||
187 | * - end - end address (exclusive) | ||
188 | * - flags - vm_flags describing address space | ||
189 | */ | ||
190 | ENTRY(arm925_flush_user_cache_range) | ||
191 | mov ip, #0 | ||
192 | sub r3, r1, r0 @ calculate total size | ||
193 | cmp r3, #CACHE_DLIMIT | ||
194 | bgt __flush_whole_cache | ||
195 | 1: tst r2, #VM_EXEC | ||
196 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
197 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
198 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
199 | add r0, r0, #CACHE_DLINESIZE | ||
200 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
201 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
202 | add r0, r0, #CACHE_DLINESIZE | ||
203 | #else | ||
204 | mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry | ||
205 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
206 | add r0, r0, #CACHE_DLINESIZE | ||
207 | mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry | ||
208 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
209 | add r0, r0, #CACHE_DLINESIZE | ||
210 | #endif | ||
211 | cmp r0, r1 | ||
212 | blo 1b | ||
213 | tst r2, #VM_EXEC | ||
214 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
215 | mov pc, lr | ||
216 | |||
217 | /* | ||
218 | * coherent_kern_range(start, end) | ||
219 | * | ||
220 | * Ensure coherency between the Icache and the Dcache in the | ||
221 | * region described by start, end. If you have non-snooping | ||
222 | * Harvard caches, you need to implement this function. | ||
223 | * | ||
224 | * - start - virtual start address | ||
225 | * - end - virtual end address | ||
226 | */ | ||
227 | ENTRY(arm925_coherent_kern_range) | ||
228 | /* FALLTHROUGH */ | ||
229 | |||
230 | /* | ||
231 | * coherent_user_range(start, end) | ||
232 | * | ||
233 | * Ensure coherency between the Icache and the Dcache in the | ||
234 | * region described by start, end. If you have non-snooping | ||
235 | * Harvard caches, you need to implement this function. | ||
236 | * | ||
237 | * - start - virtual start address | ||
238 | * - end - virtual end address | ||
239 | */ | ||
240 | ENTRY(arm925_coherent_user_range) | ||
241 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
242 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
243 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
244 | add r0, r0, #CACHE_DLINESIZE | ||
245 | cmp r0, r1 | ||
246 | blo 1b | ||
247 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
248 | mov pc, lr | ||
249 | |||
250 | /* | ||
251 | * flush_kern_dcache_page(void *page) | ||
252 | * | ||
253 | * Ensure no D cache aliasing occurs, either with itself or | ||
254 | * the I cache | ||
255 | * | ||
256 | * - addr - page aligned address | ||
257 | */ | ||
258 | ENTRY(arm925_flush_kern_dcache_page) | ||
259 | add r1, r0, #PAGE_SZ | ||
260 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
261 | add r0, r0, #CACHE_DLINESIZE | ||
262 | cmp r0, r1 | ||
263 | blo 1b | ||
264 | mov r0, #0 | ||
265 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
266 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
267 | mov pc, lr | ||
268 | |||
269 | /* | ||
270 | * dma_inv_range(start, end) | ||
271 | * | ||
272 | * Invalidate (discard) the specified virtual address range. | ||
273 | * May not write back any entries. If 'start' or 'end' | ||
274 | * are not cache line aligned, those lines must be written | ||
275 | * back. | ||
276 | * | ||
277 | * - start - virtual start address | ||
278 | * - end - virtual end address | ||
279 | * | ||
280 | * (same as v4wb) | ||
281 | */ | ||
282 | ENTRY(arm925_dma_inv_range) | ||
283 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
284 | tst r0, #CACHE_DLINESIZE - 1 | ||
285 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
286 | tst r1, #CACHE_DLINESIZE - 1 | ||
287 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
288 | #endif | ||
289 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
290 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
291 | add r0, r0, #CACHE_DLINESIZE | ||
292 | cmp r0, r1 | ||
293 | blo 1b | ||
294 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
295 | mov pc, lr | ||
296 | |||
297 | /* | ||
298 | * dma_clean_range(start, end) | ||
299 | * | ||
300 | * Clean the specified virtual address range. | ||
301 | * | ||
302 | * - start - virtual start address | ||
303 | * - end - virtual end address | ||
304 | * | ||
305 | * (same as v4wb) | ||
306 | */ | ||
307 | ENTRY(arm925_dma_clean_range) | ||
308 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
309 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
310 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
311 | add r0, r0, #CACHE_DLINESIZE | ||
312 | cmp r0, r1 | ||
313 | blo 1b | ||
314 | #endif | ||
315 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
316 | mov pc, lr | ||
317 | |||
318 | /* | ||
319 | * dma_flush_range(start, end) | ||
320 | * | ||
321 | * Clean and invalidate the specified virtual address range. | ||
322 | * | ||
323 | * - start - virtual start address | ||
324 | * - end - virtual end address | ||
325 | */ | ||
326 | ENTRY(arm925_dma_flush_range) | ||
327 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
328 | 1: | ||
329 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
330 | mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
331 | #else | ||
332 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
333 | #endif | ||
334 | add r0, r0, #CACHE_DLINESIZE | ||
335 | cmp r0, r1 | ||
336 | blo 1b | ||
337 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
338 | mov pc, lr | ||
339 | |||
340 | ENTRY(arm925_cache_fns) | ||
341 | .long arm925_flush_kern_cache_all | ||
342 | .long arm925_flush_user_cache_all | ||
343 | .long arm925_flush_user_cache_range | ||
344 | .long arm925_coherent_kern_range | ||
345 | .long arm925_coherent_user_range | ||
346 | .long arm925_flush_kern_dcache_page | ||
347 | .long arm925_dma_inv_range | ||
348 | .long arm925_dma_clean_range | ||
349 | .long arm925_dma_flush_range | ||
350 | |||
351 | ENTRY(cpu_arm925_dcache_clean_area) | ||
352 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
353 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
354 | add r0, r0, #CACHE_DLINESIZE | ||
355 | subs r1, r1, #CACHE_DLINESIZE | ||
356 | bhi 1b | ||
357 | #endif | ||
358 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
359 | mov pc, lr | ||
360 | |||
361 | /* =============================== PageTable ============================== */ | ||
362 | |||
363 | /* | ||
364 | * cpu_arm925_switch_mm(pgd) | ||
365 | * | ||
366 | * Set the translation base pointer to be as described by pgd. | ||
367 | * | ||
368 | * pgd: new page tables | ||
369 | */ | ||
370 | .align 5 | ||
371 | ENTRY(cpu_arm925_switch_mm) | ||
372 | mov ip, #0 | ||
373 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
374 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
375 | #else | ||
376 | /* Flush entries in bothe segments at once, see NOTE1 above */ | ||
377 | mov r3, #(CACHE_DENTRIES - 1) << 4 @ 256 entries in segment | ||
378 | 2: mcr p15, 0, r3, c7, c14, 2 @ clean & invalidate D index | ||
379 | subs r3, r3, #1 << 4 | ||
380 | bcs 2b @ entries 255 to 0 | ||
381 | #endif | ||
382 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
383 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
384 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
385 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
386 | mov pc, lr | ||
387 | |||
388 | /* | ||
389 | * cpu_arm925_set_pte(ptep, pte) | ||
390 | * | ||
391 | * Set a PTE and flush it out | ||
392 | */ | ||
393 | .align 5 | ||
394 | ENTRY(cpu_arm925_set_pte) | ||
395 | str r1, [r0], #-2048 @ linux version | ||
396 | |||
397 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
398 | |||
399 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
400 | bic r2, r2, #PTE_TYPE_MASK | ||
401 | orr r2, r2, #PTE_TYPE_SMALL | ||
402 | |||
403 | tst r1, #L_PTE_USER @ User? | ||
404 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
405 | |||
406 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
407 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
408 | |||
409 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
410 | movne r2, #0 | ||
411 | |||
412 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
413 | eor r3, r2, #0x0a @ C & small page? | ||
414 | tst r3, #0x0b | ||
415 | biceq r2, r2, #4 | ||
416 | #endif | ||
417 | str r2, [r0] @ hardware version | ||
418 | mov r0, r0 | ||
419 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
420 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
421 | #endif | ||
422 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
423 | mov pc, lr | ||
424 | |||
425 | __INIT | ||
426 | |||
427 | .type __arm925_setup, #function | ||
428 | __arm925_setup: | ||
429 | mov r0, #0 | ||
430 | #if defined(CONFIG_CPU_ICACHE_STREAMING_DISABLE) | ||
431 | orr r0,r0,#1 << 7 | ||
432 | #endif | ||
433 | |||
434 | /* Transparent on, D-cache clean & flush mode. See NOTE2 above */ | ||
435 | orr r0,r0,#1 << 1 @ transparent mode on | ||
436 | mcr p15, 0, r0, c15, c1, 0 @ write TI config register | ||
437 | |||
438 | mov r0, #0 | ||
439 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
440 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
441 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
442 | |||
443 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
444 | mov r0, #4 @ disable write-back on caches explicitly | ||
445 | mcr p15, 7, r0, c15, c0, 0 | ||
446 | #endif | ||
447 | |||
448 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
449 | ldr r5, arm925_cr1_clear | ||
450 | bic r0, r0, r5 | ||
451 | ldr r5, arm925_cr1_set | ||
452 | orr r0, r0, r5 | ||
453 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
454 | orr r0, r0, #0x4000 @ .1.. .... .... .... | ||
455 | #endif | ||
456 | mov pc, lr | ||
457 | .size __arm925_setup, . - __arm925_setup | ||
458 | |||
459 | /* | ||
460 | * R | ||
461 | * .RVI ZFRS BLDP WCAM | ||
462 | * .011 0001 ..11 1101 | ||
463 | * | ||
464 | */ | ||
465 | .type arm925_cr1_clear, #object | ||
466 | .type arm925_cr1_set, #object | ||
467 | arm925_cr1_clear: | ||
468 | .word 0x7f3f | ||
469 | arm925_cr1_set: | ||
470 | .word 0x313d | ||
471 | |||
472 | __INITDATA | ||
473 | |||
474 | /* | ||
475 | * Purpose : Function pointers used to access above functions - all calls | ||
476 | * come through these | ||
477 | */ | ||
478 | .type arm925_processor_functions, #object | ||
479 | arm925_processor_functions: | ||
480 | .word v4t_early_abort | ||
481 | .word cpu_arm925_proc_init | ||
482 | .word cpu_arm925_proc_fin | ||
483 | .word cpu_arm925_reset | ||
484 | .word cpu_arm925_do_idle | ||
485 | .word cpu_arm925_dcache_clean_area | ||
486 | .word cpu_arm925_switch_mm | ||
487 | .word cpu_arm925_set_pte | ||
488 | .size arm925_processor_functions, . - arm925_processor_functions | ||
489 | |||
490 | .section ".rodata" | ||
491 | |||
492 | .type cpu_arch_name, #object | ||
493 | cpu_arch_name: | ||
494 | .asciz "armv4t" | ||
495 | .size cpu_arch_name, . - cpu_arch_name | ||
496 | |||
497 | .type cpu_elf_name, #object | ||
498 | cpu_elf_name: | ||
499 | .asciz "v4" | ||
500 | .size cpu_elf_name, . - cpu_elf_name | ||
501 | |||
502 | .type cpu_arm925_name, #object | ||
503 | cpu_arm925_name: | ||
504 | .ascii "ARM925T" | ||
505 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
506 | .ascii "i" | ||
507 | #endif | ||
508 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
509 | .ascii "d" | ||
510 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
511 | .ascii "(wt)" | ||
512 | #else | ||
513 | .ascii "(wb)" | ||
514 | #endif | ||
515 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
516 | .ascii "RR" | ||
517 | #endif | ||
518 | #endif | ||
519 | .ascii "\0" | ||
520 | .size cpu_arm925_name, . - cpu_arm925_name | ||
521 | |||
522 | .align | ||
523 | |||
524 | .section ".proc.info", #alloc, #execinstr | ||
525 | |||
526 | .type __arm925_proc_info,#object | ||
527 | __arm925_proc_info: | ||
528 | .long 0x54029250 | ||
529 | .long 0xfffffff0 | ||
530 | .long PMD_TYPE_SECT | \ | ||
531 | PMD_BIT4 | \ | ||
532 | PMD_SECT_AP_WRITE | \ | ||
533 | PMD_SECT_AP_READ | ||
534 | b __arm925_setup | ||
535 | .long cpu_arch_name | ||
536 | .long cpu_elf_name | ||
537 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | ||
538 | .long cpu_arm925_name | ||
539 | .long arm925_processor_functions | ||
540 | .long v4wbi_tlb_fns | ||
541 | .long v4wb_user_fns | ||
542 | .long arm925_cache_fns | ||
543 | .size __arm925_proc_info, . - __arm925_proc_info | ||
544 | |||
545 | .type __arm915_proc_info,#object | ||
546 | __arm915_proc_info: | ||
547 | .long 0x54029150 | ||
548 | .long 0xfffffff0 | ||
549 | .long PMD_TYPE_SECT | \ | ||
550 | PMD_BIT4 | \ | ||
551 | PMD_SECT_AP_WRITE | \ | ||
552 | PMD_SECT_AP_READ | ||
553 | b __arm925_setup | ||
554 | .long cpu_arch_name | ||
555 | .long cpu_elf_name | ||
556 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | ||
557 | .long cpu_arm925_name | ||
558 | .long arm925_processor_functions | ||
559 | .long v4wbi_tlb_fns | ||
560 | .long v4wb_user_fns | ||
561 | .long arm925_cache_fns | ||
562 | .size __arm925_proc_info, . - __arm925_proc_info | ||
diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S new file mode 100644 index 000000000000..bb95cc9fed03 --- /dev/null +++ b/arch/arm/mm/proc-arm926.S | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-arm926.S: MMU functions for ARM926EJ-S | ||
3 | * | ||
4 | * Copyright (C) 1999-2001 ARM Limited | ||
5 | * Copyright (C) 2000 Deep Blue Solutions Ltd. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * | ||
22 | * These are the low level assembler for performing cache and TLB | ||
23 | * functions on the arm926. | ||
24 | * | ||
25 | * CONFIG_CPU_ARM926_CPU_IDLE -> nohlt | ||
26 | */ | ||
27 | #include <linux/linkage.h> | ||
28 | #include <linux/config.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <asm/assembler.h> | ||
31 | #include <asm/pgtable.h> | ||
32 | #include <asm/procinfo.h> | ||
33 | #include <asm/hardware.h> | ||
34 | #include <asm/page.h> | ||
35 | #include <asm/ptrace.h> | ||
36 | #include "proc-macros.S" | ||
37 | |||
38 | /* | ||
39 | * This is the maximum size of an area which will be invalidated | ||
40 | * using the single invalidate entry instructions. Anything larger | ||
41 | * than this, and we go for the whole cache. | ||
42 | * | ||
43 | * This value should be chosen such that we choose the cheapest | ||
44 | * alternative. | ||
45 | */ | ||
46 | #define CACHE_DLIMIT 16384 | ||
47 | |||
48 | /* | ||
49 | * the cache line size of the I and D cache | ||
50 | */ | ||
51 | #define CACHE_DLINESIZE 32 | ||
52 | |||
53 | .text | ||
54 | /* | ||
55 | * cpu_arm926_proc_init() | ||
56 | */ | ||
57 | ENTRY(cpu_arm926_proc_init) | ||
58 | mov pc, lr | ||
59 | |||
60 | /* | ||
61 | * cpu_arm926_proc_fin() | ||
62 | */ | ||
63 | ENTRY(cpu_arm926_proc_fin) | ||
64 | stmfd sp!, {lr} | ||
65 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
66 | msr cpsr_c, ip | ||
67 | bl arm926_flush_kern_cache_all | ||
68 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
69 | bic r0, r0, #0x1000 @ ...i............ | ||
70 | bic r0, r0, #0x000e @ ............wca. | ||
71 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
72 | ldmfd sp!, {pc} | ||
73 | |||
74 | /* | ||
75 | * cpu_arm926_reset(loc) | ||
76 | * | ||
77 | * Perform a soft reset of the system. Put the CPU into the | ||
78 | * same state as it would be if it had been reset, and branch | ||
79 | * to what would be the reset vector. | ||
80 | * | ||
81 | * loc: location to jump to for soft reset | ||
82 | */ | ||
83 | .align 5 | ||
84 | ENTRY(cpu_arm926_reset) | ||
85 | mov ip, #0 | ||
86 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
87 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
88 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
89 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
90 | bic ip, ip, #0x000f @ ............wcam | ||
91 | bic ip, ip, #0x1100 @ ...i...s........ | ||
92 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
93 | mov pc, r0 | ||
94 | |||
95 | /* | ||
96 | * cpu_arm926_do_idle() | ||
97 | * | ||
98 | * Called with IRQs disabled | ||
99 | */ | ||
100 | .align 10 | ||
101 | ENTRY(cpu_arm926_do_idle) | ||
102 | mov r0, #0 | ||
103 | mrc p15, 0, r1, c1, c0, 0 @ Read control register | ||
104 | mcr p15, 0, r0, c7, c10, 4 @ Drain write buffer | ||
105 | bic r2, r1, #1 << 12 | ||
106 | mcr p15, 0, r2, c1, c0, 0 @ Disable I cache | ||
107 | mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt | ||
108 | mcr p15, 0, r1, c1, c0, 0 @ Restore ICache enable | ||
109 | mov pc, lr | ||
110 | |||
111 | /* | ||
112 | * flush_user_cache_all() | ||
113 | * | ||
114 | * Clean and invalidate all cache entries in a particular | ||
115 | * address space. | ||
116 | */ | ||
117 | ENTRY(arm926_flush_user_cache_all) | ||
118 | /* FALLTHROUGH */ | ||
119 | |||
120 | /* | ||
121 | * flush_kern_cache_all() | ||
122 | * | ||
123 | * Clean and invalidate the entire cache. | ||
124 | */ | ||
125 | ENTRY(arm926_flush_kern_cache_all) | ||
126 | mov r2, #VM_EXEC | ||
127 | mov ip, #0 | ||
128 | __flush_whole_cache: | ||
129 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
130 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
131 | #else | ||
132 | 1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate | ||
133 | bne 1b | ||
134 | #endif | ||
135 | tst r2, #VM_EXEC | ||
136 | mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
137 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
138 | mov pc, lr | ||
139 | |||
140 | /* | ||
141 | * flush_user_cache_range(start, end, flags) | ||
142 | * | ||
143 | * Clean and invalidate a range of cache entries in the | ||
144 | * specified address range. | ||
145 | * | ||
146 | * - start - start address (inclusive) | ||
147 | * - end - end address (exclusive) | ||
148 | * - flags - vm_flags describing address space | ||
149 | */ | ||
150 | ENTRY(arm926_flush_user_cache_range) | ||
151 | mov ip, #0 | ||
152 | sub r3, r1, r0 @ calculate total size | ||
153 | cmp r3, #CACHE_DLIMIT | ||
154 | bgt __flush_whole_cache | ||
155 | 1: tst r2, #VM_EXEC | ||
156 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
157 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
158 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
159 | add r0, r0, #CACHE_DLINESIZE | ||
160 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
161 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
162 | add r0, r0, #CACHE_DLINESIZE | ||
163 | #else | ||
164 | mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry | ||
165 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
166 | add r0, r0, #CACHE_DLINESIZE | ||
167 | mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry | ||
168 | mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
169 | add r0, r0, #CACHE_DLINESIZE | ||
170 | #endif | ||
171 | cmp r0, r1 | ||
172 | blo 1b | ||
173 | tst r2, #VM_EXEC | ||
174 | mcrne p15, 0, ip, c7, c10, 4 @ drain WB | ||
175 | mov pc, lr | ||
176 | |||
177 | /* | ||
178 | * coherent_kern_range(start, end) | ||
179 | * | ||
180 | * Ensure coherency between the Icache and the Dcache in the | ||
181 | * region described by start, end. If you have non-snooping | ||
182 | * Harvard caches, you need to implement this function. | ||
183 | * | ||
184 | * - start - virtual start address | ||
185 | * - end - virtual end address | ||
186 | */ | ||
187 | ENTRY(arm926_coherent_kern_range) | ||
188 | /* FALLTHROUGH */ | ||
189 | |||
190 | /* | ||
191 | * coherent_user_range(start, end) | ||
192 | * | ||
193 | * Ensure coherency between the Icache and the Dcache in the | ||
194 | * region described by start, end. If you have non-snooping | ||
195 | * Harvard caches, you need to implement this function. | ||
196 | * | ||
197 | * - start - virtual start address | ||
198 | * - end - virtual end address | ||
199 | */ | ||
200 | ENTRY(arm926_coherent_user_range) | ||
201 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
202 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
203 | mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry | ||
204 | add r0, r0, #CACHE_DLINESIZE | ||
205 | cmp r0, r1 | ||
206 | blo 1b | ||
207 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
208 | mov pc, lr | ||
209 | |||
210 | /* | ||
211 | * flush_kern_dcache_page(void *page) | ||
212 | * | ||
213 | * Ensure no D cache aliasing occurs, either with itself or | ||
214 | * the I cache | ||
215 | * | ||
216 | * - addr - page aligned address | ||
217 | */ | ||
218 | ENTRY(arm926_flush_kern_dcache_page) | ||
219 | add r1, r0, #PAGE_SZ | ||
220 | 1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
221 | add r0, r0, #CACHE_DLINESIZE | ||
222 | cmp r0, r1 | ||
223 | blo 1b | ||
224 | mov r0, #0 | ||
225 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
226 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
227 | mov pc, lr | ||
228 | |||
229 | /* | ||
230 | * dma_inv_range(start, end) | ||
231 | * | ||
232 | * Invalidate (discard) the specified virtual address range. | ||
233 | * May not write back any entries. If 'start' or 'end' | ||
234 | * are not cache line aligned, those lines must be written | ||
235 | * back. | ||
236 | * | ||
237 | * - start - virtual start address | ||
238 | * - end - virtual end address | ||
239 | * | ||
240 | * (same as v4wb) | ||
241 | */ | ||
242 | ENTRY(arm926_dma_inv_range) | ||
243 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
244 | tst r0, #CACHE_DLINESIZE - 1 | ||
245 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
246 | tst r1, #CACHE_DLINESIZE - 1 | ||
247 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
248 | #endif | ||
249 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
250 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
251 | add r0, r0, #CACHE_DLINESIZE | ||
252 | cmp r0, r1 | ||
253 | blo 1b | ||
254 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
255 | mov pc, lr | ||
256 | |||
257 | /* | ||
258 | * dma_clean_range(start, end) | ||
259 | * | ||
260 | * Clean the specified virtual address range. | ||
261 | * | ||
262 | * - start - virtual start address | ||
263 | * - end - virtual end address | ||
264 | * | ||
265 | * (same as v4wb) | ||
266 | */ | ||
267 | ENTRY(arm926_dma_clean_range) | ||
268 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
269 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
270 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
271 | add r0, r0, #CACHE_DLINESIZE | ||
272 | cmp r0, r1 | ||
273 | blo 1b | ||
274 | #endif | ||
275 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
276 | mov pc, lr | ||
277 | |||
278 | /* | ||
279 | * dma_flush_range(start, end) | ||
280 | * | ||
281 | * Clean and invalidate the specified virtual address range. | ||
282 | * | ||
283 | * - start - virtual start address | ||
284 | * - end - virtual end address | ||
285 | */ | ||
286 | ENTRY(arm926_dma_flush_range) | ||
287 | bic r0, r0, #CACHE_DLINESIZE - 1 | ||
288 | 1: | ||
289 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
290 | mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry | ||
291 | #else | ||
292 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
293 | #endif | ||
294 | add r0, r0, #CACHE_DLINESIZE | ||
295 | cmp r0, r1 | ||
296 | blo 1b | ||
297 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
298 | mov pc, lr | ||
299 | |||
300 | ENTRY(arm926_cache_fns) | ||
301 | .long arm926_flush_kern_cache_all | ||
302 | .long arm926_flush_user_cache_all | ||
303 | .long arm926_flush_user_cache_range | ||
304 | .long arm926_coherent_kern_range | ||
305 | .long arm926_coherent_user_range | ||
306 | .long arm926_flush_kern_dcache_page | ||
307 | .long arm926_dma_inv_range | ||
308 | .long arm926_dma_clean_range | ||
309 | .long arm926_dma_flush_range | ||
310 | |||
311 | ENTRY(cpu_arm926_dcache_clean_area) | ||
312 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
313 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
314 | add r0, r0, #CACHE_DLINESIZE | ||
315 | subs r1, r1, #CACHE_DLINESIZE | ||
316 | bhi 1b | ||
317 | #endif | ||
318 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
319 | mov pc, lr | ||
320 | |||
321 | /* =============================== PageTable ============================== */ | ||
322 | |||
323 | /* | ||
324 | * cpu_arm926_switch_mm(pgd) | ||
325 | * | ||
326 | * Set the translation base pointer to be as described by pgd. | ||
327 | * | ||
328 | * pgd: new page tables | ||
329 | */ | ||
330 | .align 5 | ||
331 | ENTRY(cpu_arm926_switch_mm) | ||
332 | mov ip, #0 | ||
333 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
334 | mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache | ||
335 | #else | ||
336 | @ && 'Clean & Invalidate whole DCache' | ||
337 | 1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate | ||
338 | bne 1b | ||
339 | #endif | ||
340 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
341 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
342 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
343 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
344 | mov pc, lr | ||
345 | |||
346 | /* | ||
347 | * cpu_arm926_set_pte(ptep, pte) | ||
348 | * | ||
349 | * Set a PTE and flush it out | ||
350 | */ | ||
351 | .align 5 | ||
352 | ENTRY(cpu_arm926_set_pte) | ||
353 | str r1, [r0], #-2048 @ linux version | ||
354 | |||
355 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
356 | |||
357 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
358 | bic r2, r2, #PTE_TYPE_MASK | ||
359 | orr r2, r2, #PTE_TYPE_SMALL | ||
360 | |||
361 | tst r1, #L_PTE_USER @ User? | ||
362 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
363 | |||
364 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
365 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
366 | |||
367 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
368 | movne r2, #0 | ||
369 | |||
370 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
371 | eor r3, r2, #0x0a @ C & small page? | ||
372 | tst r3, #0x0b | ||
373 | biceq r2, r2, #4 | ||
374 | #endif | ||
375 | str r2, [r0] @ hardware version | ||
376 | mov r0, r0 | ||
377 | #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
378 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
379 | #endif | ||
380 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
381 | mov pc, lr | ||
382 | |||
383 | __INIT | ||
384 | |||
385 | .type __arm926_setup, #function | ||
386 | __arm926_setup: | ||
387 | mov r0, #0 | ||
388 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
389 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
390 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
391 | |||
392 | |||
393 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
394 | mov r0, #4 @ disable write-back on caches explicitly | ||
395 | mcr p15, 7, r0, c15, c0, 0 | ||
396 | #endif | ||
397 | |||
398 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
399 | ldr r5, arm926_cr1_clear | ||
400 | bic r0, r0, r5 | ||
401 | ldr r5, arm926_cr1_set | ||
402 | orr r0, r0, r5 | ||
403 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
404 | orr r0, r0, #0x4000 @ .1.. .... .... .... | ||
405 | #endif | ||
406 | mov pc, lr | ||
407 | .size __arm926_setup, . - __arm926_setup | ||
408 | |||
409 | /* | ||
410 | * R | ||
411 | * .RVI ZFRS BLDP WCAM | ||
412 | * .011 0001 ..11 0101 | ||
413 | * | ||
414 | */ | ||
415 | .type arm926_cr1_clear, #object | ||
416 | .type arm926_cr1_set, #object | ||
417 | arm926_cr1_clear: | ||
418 | .word 0x7f3f | ||
419 | arm926_cr1_set: | ||
420 | .word 0x3135 | ||
421 | |||
422 | __INITDATA | ||
423 | |||
424 | /* | ||
425 | * Purpose : Function pointers used to access above functions - all calls | ||
426 | * come through these | ||
427 | */ | ||
428 | .type arm926_processor_functions, #object | ||
429 | arm926_processor_functions: | ||
430 | .word v5tj_early_abort | ||
431 | .word cpu_arm926_proc_init | ||
432 | .word cpu_arm926_proc_fin | ||
433 | .word cpu_arm926_reset | ||
434 | .word cpu_arm926_do_idle | ||
435 | .word cpu_arm926_dcache_clean_area | ||
436 | .word cpu_arm926_switch_mm | ||
437 | .word cpu_arm926_set_pte | ||
438 | .size arm926_processor_functions, . - arm926_processor_functions | ||
439 | |||
440 | .section ".rodata" | ||
441 | |||
442 | .type cpu_arch_name, #object | ||
443 | cpu_arch_name: | ||
444 | .asciz "armv5tej" | ||
445 | .size cpu_arch_name, . - cpu_arch_name | ||
446 | |||
447 | .type cpu_elf_name, #object | ||
448 | cpu_elf_name: | ||
449 | .asciz "v5" | ||
450 | .size cpu_elf_name, . - cpu_elf_name | ||
451 | |||
452 | .type cpu_arm926_name, #object | ||
453 | cpu_arm926_name: | ||
454 | .ascii "ARM926EJ-S" | ||
455 | #ifndef CONFIG_CPU_ICACHE_DISABLE | ||
456 | .ascii "i" | ||
457 | #endif | ||
458 | #ifndef CONFIG_CPU_DCACHE_DISABLE | ||
459 | .ascii "d" | ||
460 | #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH | ||
461 | .ascii "(wt)" | ||
462 | #else | ||
463 | .ascii "(wb)" | ||
464 | #endif | ||
465 | #ifdef CONFIG_CPU_CACHE_ROUND_ROBIN | ||
466 | .ascii "RR" | ||
467 | #endif | ||
468 | #endif | ||
469 | .ascii "\0" | ||
470 | .size cpu_arm926_name, . - cpu_arm926_name | ||
471 | |||
472 | .align | ||
473 | |||
474 | .section ".proc.info", #alloc, #execinstr | ||
475 | |||
476 | .type __arm926_proc_info,#object | ||
477 | __arm926_proc_info: | ||
478 | .long 0x41069260 @ ARM926EJ-S (v5TEJ) | ||
479 | .long 0xff0ffff0 | ||
480 | .long PMD_TYPE_SECT | \ | ||
481 | PMD_SECT_BUFFERABLE | \ | ||
482 | PMD_SECT_CACHEABLE | \ | ||
483 | PMD_BIT4 | \ | ||
484 | PMD_SECT_AP_WRITE | \ | ||
485 | PMD_SECT_AP_READ | ||
486 | b __arm926_setup | ||
487 | .long cpu_arch_name | ||
488 | .long cpu_elf_name | ||
489 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA | ||
490 | .long cpu_arm926_name | ||
491 | .long arm926_processor_functions | ||
492 | .long v4wbi_tlb_fns | ||
493 | .long v4wb_user_fns | ||
494 | .long arm926_cache_fns | ||
495 | .size __arm926_proc_info, . - __arm926_proc_info | ||
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S new file mode 100644 index 000000000000..9137fe563599 --- /dev/null +++ b/arch/arm/mm/proc-macros.S | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * We need constants.h for: | ||
3 | * VMA_VM_MM | ||
4 | * VMA_VM_FLAGS | ||
5 | * VM_EXEC | ||
6 | */ | ||
7 | #include <asm/constants.h> | ||
8 | #include <asm/thread_info.h> | ||
9 | |||
10 | /* | ||
11 | * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm) | ||
12 | */ | ||
13 | .macro vma_vm_mm, rd, rn | ||
14 | ldr \rd, [\rn, #VMA_VM_MM] | ||
15 | .endm | ||
16 | |||
17 | /* | ||
18 | * vma_vm_flags - get vma->vm_flags | ||
19 | */ | ||
20 | .macro vma_vm_flags, rd, rn | ||
21 | ldr \rd, [\rn, #VMA_VM_FLAGS] | ||
22 | .endm | ||
23 | |||
24 | .macro tsk_mm, rd, rn | ||
25 | ldr \rd, [\rn, #TI_TASK] | ||
26 | ldr \rd, [\rd, #TSK_ACTIVE_MM] | ||
27 | .endm | ||
28 | |||
29 | /* | ||
30 | * act_mm - get current->active_mm | ||
31 | */ | ||
32 | .macro act_mm, rd | ||
33 | bic \rd, sp, #8128 | ||
34 | bic \rd, \rd, #63 | ||
35 | ldr \rd, [\rd, #TI_TASK] | ||
36 | ldr \rd, [\rd, #TSK_ACTIVE_MM] | ||
37 | .endm | ||
38 | |||
39 | /* | ||
40 | * mmid - get context id from mm pointer (mm->context.id) | ||
41 | */ | ||
42 | .macro mmid, rd, rn | ||
43 | ldr \rd, [\rn, #MM_CONTEXT_ID] | ||
44 | .endm | ||
45 | |||
46 | /* | ||
47 | * mask_asid - mask the ASID from the context ID | ||
48 | */ | ||
49 | .macro asid, rd, rn | ||
50 | and \rd, \rn, #255 | ||
51 | .endm | ||
diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S new file mode 100644 index 000000000000..360cae905692 --- /dev/null +++ b/arch/arm/mm/proc-sa110.S | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-sa110.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * MMU functions for SA110 | ||
11 | * | ||
12 | * These are the low level assembler for performing cache and TLB | ||
13 | * functions on the StrongARM-110. | ||
14 | */ | ||
15 | #include <linux/linkage.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <asm/assembler.h> | ||
18 | #include <asm/constants.h> | ||
19 | #include <asm/procinfo.h> | ||
20 | #include <asm/hardware.h> | ||
21 | #include <asm/pgtable.h> | ||
22 | #include <asm/ptrace.h> | ||
23 | |||
24 | /* | ||
25 | * the cache line size of the I and D cache | ||
26 | */ | ||
27 | #define DCACHELINESIZE 32 | ||
28 | #define FLUSH_OFFSET 32768 | ||
29 | |||
30 | .macro flush_110_dcache rd, ra, re | ||
31 | ldr \rd, =flush_base | ||
32 | ldr \ra, [\rd] | ||
33 | eor \ra, \ra, #FLUSH_OFFSET | ||
34 | str \ra, [\rd] | ||
35 | add \re, \ra, #16384 @ only necessary for 16k | ||
36 | 1001: ldr \rd, [\ra], #DCACHELINESIZE | ||
37 | teq \re, \ra | ||
38 | bne 1001b | ||
39 | .endm | ||
40 | |||
41 | .data | ||
42 | flush_base: | ||
43 | .long FLUSH_BASE | ||
44 | .text | ||
45 | |||
46 | /* | ||
47 | * cpu_sa110_proc_init() | ||
48 | */ | ||
49 | ENTRY(cpu_sa110_proc_init) | ||
50 | mov r0, #0 | ||
51 | mcr p15, 0, r0, c15, c1, 2 @ Enable clock switching | ||
52 | mov pc, lr | ||
53 | |||
54 | /* | ||
55 | * cpu_sa110_proc_fin() | ||
56 | */ | ||
57 | ENTRY(cpu_sa110_proc_fin) | ||
58 | stmfd sp!, {lr} | ||
59 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
60 | msr cpsr_c, ip | ||
61 | bl v4wb_flush_kern_cache_all @ clean caches | ||
62 | 1: mov r0, #0 | ||
63 | mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching | ||
64 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
65 | bic r0, r0, #0x1000 @ ...i............ | ||
66 | bic r0, r0, #0x000e @ ............wca. | ||
67 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
68 | ldmfd sp!, {pc} | ||
69 | |||
70 | /* | ||
71 | * cpu_sa110_reset(loc) | ||
72 | * | ||
73 | * Perform a soft reset of the system. Put the CPU into the | ||
74 | * same state as it would be if it had been reset, and branch | ||
75 | * to what would be the reset vector. | ||
76 | * | ||
77 | * loc: location to jump to for soft reset | ||
78 | */ | ||
79 | .align 5 | ||
80 | ENTRY(cpu_sa110_reset) | ||
81 | mov ip, #0 | ||
82 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
83 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
84 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
85 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
86 | bic ip, ip, #0x000f @ ............wcam | ||
87 | bic ip, ip, #0x1100 @ ...i...s........ | ||
88 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
89 | mov pc, r0 | ||
90 | |||
91 | /* | ||
92 | * cpu_sa110_do_idle(type) | ||
93 | * | ||
94 | * Cause the processor to idle | ||
95 | * | ||
96 | * type: call type: | ||
97 | * 0 = slow idle | ||
98 | * 1 = fast idle | ||
99 | * 2 = switch to slow processor clock | ||
100 | * 3 = switch to fast processor clock | ||
101 | */ | ||
102 | .align 5 | ||
103 | |||
104 | ENTRY(cpu_sa110_do_idle) | ||
105 | mcr p15, 0, ip, c15, c2, 2 @ disable clock switching | ||
106 | ldr r1, =UNCACHEABLE_ADDR @ load from uncacheable loc | ||
107 | ldr r1, [r1, #0] @ force switch to MCLK | ||
108 | mov r0, r0 @ safety | ||
109 | mov r0, r0 @ safety | ||
110 | mov r0, r0 @ safety | ||
111 | mcr p15, 0, r0, c15, c8, 2 @ Wait for interrupt, cache aligned | ||
112 | mov r0, r0 @ safety | ||
113 | mov r0, r0 @ safety | ||
114 | mov r0, r0 @ safety | ||
115 | mcr p15, 0, r0, c15, c1, 2 @ enable clock switching | ||
116 | mov pc, lr | ||
117 | |||
118 | /* ================================= CACHE ================================ */ | ||
119 | |||
120 | /* | ||
121 | * cpu_sa110_dcache_clean_area(addr,sz) | ||
122 | * | ||
123 | * Clean the specified entry of any caches such that the MMU | ||
124 | * translation fetches will obtain correct data. | ||
125 | * | ||
126 | * addr: cache-unaligned virtual address | ||
127 | */ | ||
128 | .align 5 | ||
129 | ENTRY(cpu_sa110_dcache_clean_area) | ||
130 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
131 | add r0, r0, #DCACHELINESIZE | ||
132 | subs r1, r1, #DCACHELINESIZE | ||
133 | bhi 1b | ||
134 | mov pc, lr | ||
135 | |||
136 | /* =============================== PageTable ============================== */ | ||
137 | |||
138 | /* | ||
139 | * cpu_sa110_switch_mm(pgd) | ||
140 | * | ||
141 | * Set the translation base pointer to be as described by pgd. | ||
142 | * | ||
143 | * pgd: new page tables | ||
144 | */ | ||
145 | .align 5 | ||
146 | ENTRY(cpu_sa110_switch_mm) | ||
147 | flush_110_dcache r3, ip, r1 | ||
148 | mov r1, #0 | ||
149 | mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache | ||
150 | mcr p15, 0, r1, c7, c10, 4 @ drain WB | ||
151 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
152 | mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs | ||
153 | mov pc, lr | ||
154 | |||
155 | /* | ||
156 | * cpu_sa110_set_pte(ptep, pte) | ||
157 | * | ||
158 | * Set a PTE and flush it out | ||
159 | */ | ||
160 | .align 5 | ||
161 | ENTRY(cpu_sa110_set_pte) | ||
162 | str r1, [r0], #-2048 @ linux version | ||
163 | |||
164 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
165 | |||
166 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
167 | bic r2, r2, #PTE_TYPE_MASK | ||
168 | orr r2, r2, #PTE_TYPE_SMALL | ||
169 | |||
170 | tst r1, #L_PTE_USER @ User? | ||
171 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
172 | |||
173 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
174 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
175 | |||
176 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
177 | movne r2, #0 | ||
178 | |||
179 | str r2, [r0] @ hardware version | ||
180 | mov r0, r0 | ||
181 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
182 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
183 | mov pc, lr | ||
184 | |||
185 | __INIT | ||
186 | |||
187 | .type __sa110_setup, #function | ||
188 | __sa110_setup: | ||
189 | mov r10, #0 | ||
190 | mcr p15, 0, r10, c7, c7 @ invalidate I,D caches on v4 | ||
191 | mcr p15, 0, r10, c7, c10, 4 @ drain write buffer on v4 | ||
192 | mcr p15, 0, r10, c8, c7 @ invalidate I,D TLBs on v4 | ||
193 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
194 | ldr r5, sa110_cr1_clear | ||
195 | bic r0, r0, r5 | ||
196 | ldr r5, sa110_cr1_set | ||
197 | orr r0, r0, r5 | ||
198 | mov pc, lr | ||
199 | .size __sa110_setup, . - __sa110_setup | ||
200 | |||
201 | /* | ||
202 | * R | ||
203 | * .RVI ZFRS BLDP WCAM | ||
204 | * ..01 0001 ..11 1101 | ||
205 | * | ||
206 | */ | ||
207 | .type sa110_cr1_clear, #object | ||
208 | .type sa110_cr1_set, #object | ||
209 | sa110_cr1_clear: | ||
210 | .word 0x3f3f | ||
211 | sa110_cr1_set: | ||
212 | .word 0x113d | ||
213 | |||
214 | __INITDATA | ||
215 | |||
216 | /* | ||
217 | * Purpose : Function pointers used to access above functions - all calls | ||
218 | * come through these | ||
219 | */ | ||
220 | |||
221 | .type sa110_processor_functions, #object | ||
222 | ENTRY(sa110_processor_functions) | ||
223 | .word v4_early_abort | ||
224 | .word cpu_sa110_proc_init | ||
225 | .word cpu_sa110_proc_fin | ||
226 | .word cpu_sa110_reset | ||
227 | .word cpu_sa110_do_idle | ||
228 | .word cpu_sa110_dcache_clean_area | ||
229 | .word cpu_sa110_switch_mm | ||
230 | .word cpu_sa110_set_pte | ||
231 | .size sa110_processor_functions, . - sa110_processor_functions | ||
232 | |||
233 | .section ".rodata" | ||
234 | |||
235 | .type cpu_arch_name, #object | ||
236 | cpu_arch_name: | ||
237 | .asciz "armv4" | ||
238 | .size cpu_arch_name, . - cpu_arch_name | ||
239 | |||
240 | .type cpu_elf_name, #object | ||
241 | cpu_elf_name: | ||
242 | .asciz "v4" | ||
243 | .size cpu_elf_name, . - cpu_elf_name | ||
244 | |||
245 | .type cpu_sa110_name, #object | ||
246 | cpu_sa110_name: | ||
247 | .asciz "StrongARM-110" | ||
248 | .size cpu_sa110_name, . - cpu_sa110_name | ||
249 | |||
250 | .align | ||
251 | |||
252 | .section ".proc.info", #alloc, #execinstr | ||
253 | |||
254 | .type __sa110_proc_info,#object | ||
255 | __sa110_proc_info: | ||
256 | .long 0x4401a100 | ||
257 | .long 0xfffffff0 | ||
258 | .long PMD_TYPE_SECT | \ | ||
259 | PMD_SECT_BUFFERABLE | \ | ||
260 | PMD_SECT_CACHEABLE | \ | ||
261 | PMD_SECT_AP_WRITE | \ | ||
262 | PMD_SECT_AP_READ | ||
263 | b __sa110_setup | ||
264 | .long cpu_arch_name | ||
265 | .long cpu_elf_name | ||
266 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT | ||
267 | .long cpu_sa110_name | ||
268 | .long sa110_processor_functions | ||
269 | .long v4wb_tlb_fns | ||
270 | .long v4wb_user_fns | ||
271 | .long v4wb_cache_fns | ||
272 | .size __sa110_proc_info, . - __sa110_proc_info | ||
diff --git a/arch/arm/mm/proc-sa1100.S b/arch/arm/mm/proc-sa1100.S new file mode 100644 index 000000000000..d447cd5f3dd9 --- /dev/null +++ b/arch/arm/mm/proc-sa1100.S | |||
@@ -0,0 +1,323 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-sa1100.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * MMU functions for SA110 | ||
11 | * | ||
12 | * These are the low level assembler for performing cache and TLB | ||
13 | * functions on the StrongARM-1100 and StrongARM-1110. | ||
14 | * | ||
15 | * Note that SA1100 and SA1110 share everything but their name and CPU ID. | ||
16 | * | ||
17 | * 12-jun-2000, Erik Mouw (J.A.K.Mouw@its.tudelft.nl): | ||
18 | * Flush the read buffer at context switches | ||
19 | */ | ||
20 | #include <linux/linkage.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <asm/assembler.h> | ||
23 | #include <asm/constants.h> | ||
24 | #include <asm/procinfo.h> | ||
25 | #include <asm/hardware.h> | ||
26 | #include <asm/pgtable.h> | ||
27 | |||
28 | /* | ||
29 | * the cache line size of the I and D cache | ||
30 | */ | ||
31 | #define DCACHELINESIZE 32 | ||
32 | #define FLUSH_OFFSET 32768 | ||
33 | |||
34 | .macro flush_1100_dcache rd, ra, re | ||
35 | ldr \rd, =flush_base | ||
36 | ldr \ra, [\rd] | ||
37 | eor \ra, \ra, #FLUSH_OFFSET | ||
38 | str \ra, [\rd] | ||
39 | add \re, \ra, #8192 @ only necessary for 8k | ||
40 | 1001: ldr \rd, [\ra], #DCACHELINESIZE | ||
41 | teq \re, \ra | ||
42 | bne 1001b | ||
43 | #ifdef FLUSH_BASE_MINICACHE | ||
44 | add \ra, \ra, #FLUSH_BASE_MINICACHE - FLUSH_BASE | ||
45 | add \re, \ra, #512 @ only 512 bytes | ||
46 | 1002: ldr \rd, [\ra], #DCACHELINESIZE | ||
47 | teq \re, \ra | ||
48 | bne 1002b | ||
49 | #endif | ||
50 | .endm | ||
51 | |||
52 | .data | ||
53 | flush_base: | ||
54 | .long FLUSH_BASE | ||
55 | .text | ||
56 | |||
57 | __INIT | ||
58 | |||
59 | /* | ||
60 | * cpu_sa1100_proc_init() | ||
61 | */ | ||
62 | ENTRY(cpu_sa1100_proc_init) | ||
63 | mov r0, #0 | ||
64 | mcr p15, 0, r0, c15, c1, 2 @ Enable clock switching | ||
65 | mcr p15, 0, r0, c9, c0, 5 @ Allow read-buffer operations from userland | ||
66 | mov pc, lr | ||
67 | |||
68 | .previous | ||
69 | |||
70 | /* | ||
71 | * cpu_sa1100_proc_fin() | ||
72 | * | ||
73 | * Prepare the CPU for reset: | ||
74 | * - Disable interrupts | ||
75 | * - Clean and turn off caches. | ||
76 | */ | ||
77 | ENTRY(cpu_sa1100_proc_fin) | ||
78 | stmfd sp!, {lr} | ||
79 | mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE | ||
80 | msr cpsr_c, ip | ||
81 | flush_1100_dcache r0, r1, r2 @ clean caches | ||
82 | mov r0, #0 | ||
83 | mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching | ||
84 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
85 | bic r0, r0, #0x1000 @ ...i............ | ||
86 | bic r0, r0, #0x000e @ ............wca. | ||
87 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
88 | ldmfd sp!, {pc} | ||
89 | |||
90 | /* | ||
91 | * cpu_sa1100_reset(loc) | ||
92 | * | ||
93 | * Perform a soft reset of the system. Put the CPU into the | ||
94 | * same state as it would be if it had been reset, and branch | ||
95 | * to what would be the reset vector. | ||
96 | * | ||
97 | * loc: location to jump to for soft reset | ||
98 | */ | ||
99 | .align 5 | ||
100 | ENTRY(cpu_sa1100_reset) | ||
101 | mov ip, #0 | ||
102 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches | ||
103 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
104 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
105 | mrc p15, 0, ip, c1, c0, 0 @ ctrl register | ||
106 | bic ip, ip, #0x000f @ ............wcam | ||
107 | bic ip, ip, #0x1100 @ ...i...s........ | ||
108 | mcr p15, 0, ip, c1, c0, 0 @ ctrl register | ||
109 | mov pc, r0 | ||
110 | |||
111 | /* | ||
112 | * cpu_sa1100_do_idle(type) | ||
113 | * | ||
114 | * Cause the processor to idle | ||
115 | * | ||
116 | * type: call type: | ||
117 | * 0 = slow idle | ||
118 | * 1 = fast idle | ||
119 | * 2 = switch to slow processor clock | ||
120 | * 3 = switch to fast processor clock | ||
121 | */ | ||
122 | .align 5 | ||
123 | ENTRY(cpu_sa1100_do_idle) | ||
124 | mov r0, r0 @ 4 nop padding | ||
125 | mov r0, r0 | ||
126 | mov r0, r0 | ||
127 | mov r0, r0 @ 4 nop padding | ||
128 | mov r0, r0 | ||
129 | mov r0, r0 | ||
130 | mov r0, #0 | ||
131 | ldr r1, =UNCACHEABLE_ADDR @ ptr to uncacheable address | ||
132 | @ --- aligned to a cache line | ||
133 | mcr p15, 0, r0, c15, c2, 2 @ disable clock switching | ||
134 | ldr r1, [r1, #0] @ force switch to MCLK | ||
135 | mcr p15, 0, r0, c15, c8, 2 @ wait for interrupt | ||
136 | mov r0, r0 @ safety | ||
137 | mcr p15, 0, r0, c15, c1, 2 @ enable clock switching | ||
138 | mov pc, lr | ||
139 | |||
140 | /* ================================= CACHE ================================ */ | ||
141 | |||
142 | /* | ||
143 | * cpu_sa1100_dcache_clean_area(addr,sz) | ||
144 | * | ||
145 | * Clean the specified entry of any caches such that the MMU | ||
146 | * translation fetches will obtain correct data. | ||
147 | * | ||
148 | * addr: cache-unaligned virtual address | ||
149 | */ | ||
150 | .align 5 | ||
151 | ENTRY(cpu_sa1100_dcache_clean_area) | ||
152 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
153 | add r0, r0, #DCACHELINESIZE | ||
154 | subs r1, r1, #DCACHELINESIZE | ||
155 | bhi 1b | ||
156 | mov pc, lr | ||
157 | |||
158 | /* =============================== PageTable ============================== */ | ||
159 | |||
160 | /* | ||
161 | * cpu_sa1100_switch_mm(pgd) | ||
162 | * | ||
163 | * Set the translation base pointer to be as described by pgd. | ||
164 | * | ||
165 | * pgd: new page tables | ||
166 | */ | ||
167 | .align 5 | ||
168 | ENTRY(cpu_sa1100_switch_mm) | ||
169 | flush_1100_dcache r3, ip, r1 | ||
170 | mov ip, #0 | ||
171 | mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache | ||
172 | mcr p15, 0, ip, c9, c0, 0 @ invalidate RB | ||
173 | mcr p15, 0, ip, c7, c10, 4 @ drain WB | ||
174 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
175 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
176 | mov pc, lr | ||
177 | |||
178 | /* | ||
179 | * cpu_sa1100_set_pte(ptep, pte) | ||
180 | * | ||
181 | * Set a PTE and flush it out | ||
182 | */ | ||
183 | .align 5 | ||
184 | ENTRY(cpu_sa1100_set_pte) | ||
185 | str r1, [r0], #-2048 @ linux version | ||
186 | |||
187 | eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
188 | |||
189 | bic r2, r1, #PTE_SMALL_AP_MASK | ||
190 | bic r2, r2, #PTE_TYPE_MASK | ||
191 | orr r2, r2, #PTE_TYPE_SMALL | ||
192 | |||
193 | tst r1, #L_PTE_USER @ User? | ||
194 | orrne r2, r2, #PTE_SMALL_AP_URO_SRW | ||
195 | |||
196 | tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
197 | orreq r2, r2, #PTE_SMALL_AP_UNO_SRW | ||
198 | |||
199 | tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
200 | movne r2, #0 | ||
201 | |||
202 | str r2, [r0] @ hardware version | ||
203 | mov r0, r0 | ||
204 | mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
205 | mcr p15, 0, r0, c7, c10, 4 @ drain WB | ||
206 | mov pc, lr | ||
207 | |||
208 | __INIT | ||
209 | |||
210 | .type __sa1100_setup, #function | ||
211 | __sa1100_setup: | ||
212 | mov r0, #0 | ||
213 | mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 | ||
214 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 | ||
215 | mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 | ||
216 | mrc p15, 0, r0, c1, c0 @ get control register v4 | ||
217 | ldr r5, sa1100_cr1_clear | ||
218 | bic r0, r0, r5 | ||
219 | ldr r5, sa1100_cr1_set | ||
220 | orr r0, r0, r5 | ||
221 | mov pc, lr | ||
222 | .size __sa1100_setup, . - __sa1100_setup | ||
223 | |||
224 | /* | ||
225 | * R | ||
226 | * .RVI ZFRS BLDP WCAM | ||
227 | * ..11 0001 ..11 1101 | ||
228 | * | ||
229 | */ | ||
230 | .type sa1100_cr1_clear, #object | ||
231 | .type sa1100_cr1_set, #object | ||
232 | sa1100_cr1_clear: | ||
233 | .word 0x3f3f | ||
234 | sa1100_cr1_set: | ||
235 | .word 0x313d | ||
236 | |||
237 | __INITDATA | ||
238 | |||
239 | /* | ||
240 | * Purpose : Function pointers used to access above functions - all calls | ||
241 | * come through these | ||
242 | */ | ||
243 | |||
244 | /* | ||
245 | * SA1100 and SA1110 share the same function calls | ||
246 | */ | ||
247 | .type sa1100_processor_functions, #object | ||
248 | ENTRY(sa1100_processor_functions) | ||
249 | .word v4_early_abort | ||
250 | .word cpu_sa1100_proc_init | ||
251 | .word cpu_sa1100_proc_fin | ||
252 | .word cpu_sa1100_reset | ||
253 | .word cpu_sa1100_do_idle | ||
254 | .word cpu_sa1100_dcache_clean_area | ||
255 | .word cpu_sa1100_switch_mm | ||
256 | .word cpu_sa1100_set_pte | ||
257 | .size sa1100_processor_functions, . - sa1100_processor_functions | ||
258 | |||
259 | .section ".rodata" | ||
260 | |||
261 | .type cpu_arch_name, #object | ||
262 | cpu_arch_name: | ||
263 | .asciz "armv4" | ||
264 | .size cpu_arch_name, . - cpu_arch_name | ||
265 | |||
266 | .type cpu_elf_name, #object | ||
267 | cpu_elf_name: | ||
268 | .asciz "v4" | ||
269 | .size cpu_elf_name, . - cpu_elf_name | ||
270 | |||
271 | .type cpu_sa1100_name, #object | ||
272 | cpu_sa1100_name: | ||
273 | .asciz "StrongARM-1100" | ||
274 | .size cpu_sa1100_name, . - cpu_sa1100_name | ||
275 | |||
276 | .type cpu_sa1110_name, #object | ||
277 | cpu_sa1110_name: | ||
278 | .asciz "StrongARM-1110" | ||
279 | .size cpu_sa1110_name, . - cpu_sa1110_name | ||
280 | |||
281 | .align | ||
282 | |||
283 | .section ".proc.info", #alloc, #execinstr | ||
284 | |||
285 | .type __sa1100_proc_info,#object | ||
286 | __sa1100_proc_info: | ||
287 | .long 0x4401a110 | ||
288 | .long 0xfffffff0 | ||
289 | .long PMD_TYPE_SECT | \ | ||
290 | PMD_SECT_BUFFERABLE | \ | ||
291 | PMD_SECT_CACHEABLE | \ | ||
292 | PMD_SECT_AP_WRITE | \ | ||
293 | PMD_SECT_AP_READ | ||
294 | b __sa1100_setup | ||
295 | .long cpu_arch_name | ||
296 | .long cpu_elf_name | ||
297 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT | ||
298 | .long cpu_sa1100_name | ||
299 | .long sa1100_processor_functions | ||
300 | .long v4wb_tlb_fns | ||
301 | .long v4_mc_user_fns | ||
302 | .long v4wb_cache_fns | ||
303 | .size __sa1100_proc_info, . - __sa1100_proc_info | ||
304 | |||
305 | .type __sa1110_proc_info,#object | ||
306 | __sa1110_proc_info: | ||
307 | .long 0x6901b110 | ||
308 | .long 0xfffffff0 | ||
309 | .long PMD_TYPE_SECT | \ | ||
310 | PMD_SECT_BUFFERABLE | \ | ||
311 | PMD_SECT_CACHEABLE | \ | ||
312 | PMD_SECT_AP_WRITE | \ | ||
313 | PMD_SECT_AP_READ | ||
314 | b __sa1100_setup | ||
315 | .long cpu_arch_name | ||
316 | .long cpu_elf_name | ||
317 | .long HWCAP_SWP | HWCAP_HALF | HWCAP_26BIT | HWCAP_FAST_MULT | ||
318 | .long cpu_sa1110_name | ||
319 | .long sa1100_processor_functions | ||
320 | .long v4wb_tlb_fns | ||
321 | .long v4_mc_user_fns | ||
322 | .long v4wb_cache_fns | ||
323 | .size __sa1110_proc_info, . - __sa1110_proc_info | ||
diff --git a/arch/arm/mm/proc-syms.c b/arch/arm/mm/proc-syms.c new file mode 100644 index 000000000000..6c5f0fe578a5 --- /dev/null +++ b/arch/arm/mm/proc-syms.c | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-syms.c | ||
3 | * | ||
4 | * Copyright (C) 2000-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/mm.h> | ||
12 | |||
13 | #include <asm/cacheflush.h> | ||
14 | #include <asm/proc-fns.h> | ||
15 | #include <asm/tlbflush.h> | ||
16 | |||
17 | #ifndef MULTI_CPU | ||
18 | EXPORT_SYMBOL(cpu_dcache_clean_area); | ||
19 | EXPORT_SYMBOL(cpu_set_pte); | ||
20 | #else | ||
21 | EXPORT_SYMBOL(processor); | ||
22 | #endif | ||
23 | |||
24 | #ifndef MULTI_CACHE | ||
25 | EXPORT_SYMBOL(__cpuc_flush_kern_all); | ||
26 | EXPORT_SYMBOL(__cpuc_flush_user_all); | ||
27 | EXPORT_SYMBOL(__cpuc_flush_user_range); | ||
28 | EXPORT_SYMBOL(__cpuc_coherent_kern_range); | ||
29 | #else | ||
30 | EXPORT_SYMBOL(cpu_cache); | ||
31 | #endif | ||
32 | |||
33 | /* | ||
34 | * No module should need to touch the TLB (and currently | ||
35 | * no modules do. We export this for "loadkernel" support | ||
36 | * (booting a new kernel from within a running kernel.) | ||
37 | */ | ||
38 | #ifdef MULTI_TLB | ||
39 | EXPORT_SYMBOL(cpu_tlb); | ||
40 | #endif | ||
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S new file mode 100644 index 000000000000..0aa73d414783 --- /dev/null +++ b/arch/arm/mm/proc-v6.S | |||
@@ -0,0 +1,272 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-v6.S | ||
3 | * | ||
4 | * Copyright (C) 2001 Deep Blue Solutions Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This is the "shell" of the ARMv6 processor support. | ||
11 | */ | ||
12 | #include <linux/linkage.h> | ||
13 | #include <asm/assembler.h> | ||
14 | #include <asm/constants.h> | ||
15 | #include <asm/procinfo.h> | ||
16 | #include <asm/pgtable.h> | ||
17 | |||
18 | #include "proc-macros.S" | ||
19 | |||
20 | #define D_CACHE_LINE_SIZE 32 | ||
21 | |||
22 | .macro cpsie, flags | ||
23 | .ifc \flags, f | ||
24 | .long 0xf1080040 | ||
25 | .exitm | ||
26 | .endif | ||
27 | .ifc \flags, i | ||
28 | .long 0xf1080080 | ||
29 | .exitm | ||
30 | .endif | ||
31 | .ifc \flags, if | ||
32 | .long 0xf10800c0 | ||
33 | .exitm | ||
34 | .endif | ||
35 | .err | ||
36 | .endm | ||
37 | |||
38 | .macro cpsid, flags | ||
39 | .ifc \flags, f | ||
40 | .long 0xf10c0040 | ||
41 | .exitm | ||
42 | .endif | ||
43 | .ifc \flags, i | ||
44 | .long 0xf10c0080 | ||
45 | .exitm | ||
46 | .endif | ||
47 | .ifc \flags, if | ||
48 | .long 0xf10c00c0 | ||
49 | .exitm | ||
50 | .endif | ||
51 | .err | ||
52 | .endm | ||
53 | |||
54 | ENTRY(cpu_v6_proc_init) | ||
55 | mov pc, lr | ||
56 | |||
57 | ENTRY(cpu_v6_proc_fin) | ||
58 | mov pc, lr | ||
59 | |||
60 | /* | ||
61 | * cpu_v6_reset(loc) | ||
62 | * | ||
63 | * Perform a soft reset of the system. Put the CPU into the | ||
64 | * same state as it would be if it had been reset, and branch | ||
65 | * to what would be the reset vector. | ||
66 | * | ||
67 | * - loc - location to jump to for soft reset | ||
68 | * | ||
69 | * It is assumed that: | ||
70 | */ | ||
71 | .align 5 | ||
72 | ENTRY(cpu_v6_reset) | ||
73 | mov pc, r0 | ||
74 | |||
75 | /* | ||
76 | * cpu_v6_do_idle() | ||
77 | * | ||
78 | * Idle the processor (eg, wait for interrupt). | ||
79 | * | ||
80 | * IRQs are already disabled. | ||
81 | */ | ||
82 | ENTRY(cpu_v6_do_idle) | ||
83 | mcr p15, 0, r1, c7, c0, 4 @ wait for interrupt | ||
84 | mov pc, lr | ||
85 | |||
86 | ENTRY(cpu_v6_dcache_clean_area) | ||
87 | #ifndef TLB_CAN_READ_FROM_L1_CACHE | ||
88 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
89 | add r0, r0, #D_CACHE_LINE_SIZE | ||
90 | subs r1, r1, #D_CACHE_LINE_SIZE | ||
91 | bhi 1b | ||
92 | #endif | ||
93 | mov pc, lr | ||
94 | |||
95 | /* | ||
96 | * cpu_arm926_switch_mm(pgd_phys, tsk) | ||
97 | * | ||
98 | * Set the translation table base pointer to be pgd_phys | ||
99 | * | ||
100 | * - pgd_phys - physical address of new TTB | ||
101 | * | ||
102 | * It is assumed that: | ||
103 | * - we are not using split page tables | ||
104 | */ | ||
105 | ENTRY(cpu_v6_switch_mm) | ||
106 | mov r2, #0 | ||
107 | ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id | ||
108 | mcr p15, 0, r2, c7, c5, 6 @ flush BTAC/BTB | ||
109 | mcr p15, 0, r2, c7, c10, 4 @ drain write buffer | ||
110 | mcr p15, 0, r0, c2, c0, 0 @ set TTB 0 | ||
111 | mcr p15, 0, r1, c13, c0, 1 @ set context ID | ||
112 | mov pc, lr | ||
113 | |||
114 | #define nG (1 << 11) | ||
115 | #define APX (1 << 9) | ||
116 | #define AP1 (1 << 5) | ||
117 | #define AP0 (1 << 4) | ||
118 | #define XN (1 << 0) | ||
119 | |||
120 | /* | ||
121 | * cpu_v6_set_pte(ptep, pte) | ||
122 | * | ||
123 | * Set a level 2 translation table entry. | ||
124 | * | ||
125 | * - ptep - pointer to level 2 translation table entry | ||
126 | * (hardware version is stored at -1024 bytes) | ||
127 | * - pte - PTE value to store | ||
128 | * | ||
129 | * Permissions: | ||
130 | * YUWD APX AP1 AP0 SVC User | ||
131 | * 0xxx 0 0 0 no acc no acc | ||
132 | * 100x 1 0 1 r/o no acc | ||
133 | * 10x0 1 0 1 r/o no acc | ||
134 | * 1011 0 0 1 r/w no acc | ||
135 | * 110x 1 1 0 r/o r/o | ||
136 | * 11x0 1 1 0 r/o r/o | ||
137 | * 1111 0 1 1 r/w r/w | ||
138 | */ | ||
139 | ENTRY(cpu_v6_set_pte) | ||
140 | str r1, [r0], #-2048 @ linux version | ||
141 | |||
142 | bic r2, r1, #0x00000ff0 | ||
143 | bic r2, r2, #0x00000003 | ||
144 | orr r2, r2, #AP0 | 2 | ||
145 | |||
146 | tst r1, #L_PTE_WRITE | ||
147 | tstne r1, #L_PTE_DIRTY | ||
148 | orreq r2, r2, #APX | ||
149 | |||
150 | tst r1, #L_PTE_USER | ||
151 | orrne r2, r2, #AP1 | nG | ||
152 | tstne r2, #APX | ||
153 | eorne r2, r2, #AP0 | ||
154 | |||
155 | tst r1, #L_PTE_YOUNG | ||
156 | biceq r2, r2, #APX | AP1 | AP0 | ||
157 | |||
158 | @ tst r1, #L_PTE_EXEC | ||
159 | @ orreq r2, r2, #XN | ||
160 | |||
161 | tst r1, #L_PTE_PRESENT | ||
162 | moveq r2, #0 | ||
163 | |||
164 | str r2, [r0] | ||
165 | mcr p15, 0, r0, c7, c10, 1 @ flush_pte | ||
166 | mov pc, lr | ||
167 | |||
168 | |||
169 | |||
170 | |||
171 | cpu_v6_name: | ||
172 | .asciz "Some Random V6 Processor" | ||
173 | .align | ||
174 | |||
175 | .section ".text.init", #alloc, #execinstr | ||
176 | |||
177 | /* | ||
178 | * __v6_setup | ||
179 | * | ||
180 | * Initialise TLB, Caches, and MMU state ready to switch the MMU | ||
181 | * on. Return in r0 the new CP15 C1 control register setting. | ||
182 | * | ||
183 | * We automatically detect if we have a Harvard cache, and use the | ||
184 | * Harvard cache control instructions insead of the unified cache | ||
185 | * control instructions. | ||
186 | * | ||
187 | * This should be able to cover all ARMv6 cores. | ||
188 | * | ||
189 | * It is assumed that: | ||
190 | * - cache type register is implemented | ||
191 | */ | ||
192 | __v6_setup: | ||
193 | mov r0, #0 | ||
194 | mcr p15, 0, r0, c7, c14, 0 @ clean+invalidate D cache | ||
195 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
196 | mcr p15, 0, r0, c7, c15, 0 @ clean+invalidate cache | ||
197 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
198 | mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs | ||
199 | mcr p15, 0, r0, c2, c0, 2 @ TTB control register | ||
200 | mcr p15, 0, r4, c2, c0, 1 @ load TTB1 | ||
201 | #ifdef CONFIG_VFP | ||
202 | mrc p15, 0, r0, c1, c0, 2 | ||
203 | orr r0, r0, #(3 << 20) | ||
204 | mcr p15, 0, r0, c1, c0, 2 @ Enable full access to VFP | ||
205 | #endif | ||
206 | mrc p15, 0, r0, c1, c0, 0 @ read control register | ||
207 | ldr r5, v6_cr1_clear @ get mask for bits to clear | ||
208 | bic r0, r0, r5 @ clear bits them | ||
209 | ldr r5, v6_cr1_set @ get mask for bits to set | ||
210 | orr r0, r0, r5 @ set them | ||
211 | mov pc, lr @ return to head.S:__ret | ||
212 | |||
213 | /* | ||
214 | * V X F I D LR | ||
215 | * .... ...E PUI. .T.T 4RVI ZFRS BLDP WCAM | ||
216 | * rrrr rrrx xxx0 0101 xxxx xxxx x111 xxxx < forced | ||
217 | * 0 110 0011 1.00 .111 1101 < we want | ||
218 | */ | ||
219 | .type v6_cr1_clear, #object | ||
220 | .type v6_cr1_set, #object | ||
221 | v6_cr1_clear: | ||
222 | .word 0x01e0fb7f | ||
223 | v6_cr1_set: | ||
224 | .word 0x00c0387d | ||
225 | |||
226 | .type v6_processor_functions, #object | ||
227 | ENTRY(v6_processor_functions) | ||
228 | .word v6_early_abort | ||
229 | .word cpu_v6_proc_init | ||
230 | .word cpu_v6_proc_fin | ||
231 | .word cpu_v6_reset | ||
232 | .word cpu_v6_do_idle | ||
233 | .word cpu_v6_dcache_clean_area | ||
234 | .word cpu_v6_switch_mm | ||
235 | .word cpu_v6_set_pte | ||
236 | .size v6_processor_functions, . - v6_processor_functions | ||
237 | |||
238 | .type cpu_arch_name, #object | ||
239 | cpu_arch_name: | ||
240 | .asciz "armv6" | ||
241 | .size cpu_arch_name, . - cpu_arch_name | ||
242 | |||
243 | .type cpu_elf_name, #object | ||
244 | cpu_elf_name: | ||
245 | .asciz "v6" | ||
246 | .size cpu_elf_name, . - cpu_elf_name | ||
247 | .align | ||
248 | |||
249 | .section ".proc.info", #alloc, #execinstr | ||
250 | |||
251 | /* | ||
252 | * Match any ARMv6 processor core. | ||
253 | */ | ||
254 | .type __v6_proc_info, #object | ||
255 | __v6_proc_info: | ||
256 | .long 0x0007b000 | ||
257 | .long 0x0007f000 | ||
258 | .long PMD_TYPE_SECT | \ | ||
259 | PMD_SECT_BUFFERABLE | \ | ||
260 | PMD_SECT_CACHEABLE | \ | ||
261 | PMD_SECT_AP_WRITE | \ | ||
262 | PMD_SECT_AP_READ | ||
263 | b __v6_setup | ||
264 | .long cpu_arch_name | ||
265 | .long cpu_elf_name | ||
266 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_VFP|HWCAP_EDSP|HWCAP_JAVA | ||
267 | .long cpu_v6_name | ||
268 | .long v6_processor_functions | ||
269 | .long v6wbi_tlb_fns | ||
270 | .long v6_user_fns | ||
271 | .long v6_cache_fns | ||
272 | .size __v6_proc_info, . - __v6_proc_info | ||
diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S new file mode 100644 index 000000000000..2d977b4eeeab --- /dev/null +++ b/arch/arm/mm/proc-xscale.S | |||
@@ -0,0 +1,934 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/proc-xscale.S | ||
3 | * | ||
4 | * Author: Nicolas Pitre | ||
5 | * Created: November 2000 | ||
6 | * Copyright: (C) 2000, 2001 MontaVista Software Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | * MMU functions for the Intel XScale CPUs | ||
13 | * | ||
14 | * 2001 Aug 21: | ||
15 | * some contributions by Brett Gaines <brett.w.gaines@intel.com> | ||
16 | * Copyright 2001 by Intel Corp. | ||
17 | * | ||
18 | * 2001 Sep 08: | ||
19 | * Completely revisited, many important fixes | ||
20 | * Nicolas Pitre <nico@cam.org> | ||
21 | */ | ||
22 | |||
23 | #include <linux/linkage.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <asm/assembler.h> | ||
26 | #include <asm/procinfo.h> | ||
27 | #include <asm/hardware.h> | ||
28 | #include <asm/pgtable.h> | ||
29 | #include <asm/page.h> | ||
30 | #include <asm/ptrace.h> | ||
31 | #include "proc-macros.S" | ||
32 | |||
33 | /* | ||
34 | * This is the maximum size of an area which will be flushed. If the area | ||
35 | * is larger than this, then we flush the whole cache | ||
36 | */ | ||
37 | #define MAX_AREA_SIZE 32768 | ||
38 | |||
39 | /* | ||
40 | * the cache line size of the I and D cache | ||
41 | */ | ||
42 | #define CACHELINESIZE 32 | ||
43 | |||
44 | /* | ||
45 | * the size of the data cache | ||
46 | */ | ||
47 | #define CACHESIZE 32768 | ||
48 | |||
49 | /* | ||
50 | * Virtual address used to allocate the cache when flushed | ||
51 | * | ||
52 | * This must be an address range which is _never_ used. It should | ||
53 | * apparently have a mapping in the corresponding page table for | ||
54 | * compatibility with future CPUs that _could_ require it. For instance we | ||
55 | * don't care. | ||
56 | * | ||
57 | * This must be aligned on a 2*CACHESIZE boundary. The code selects one of | ||
58 | * the 2 areas in alternance each time the clean_d_cache macro is used. | ||
59 | * Without this the XScale core exhibits cache eviction problems and no one | ||
60 | * knows why. | ||
61 | * | ||
62 | * Reminder: the vector table is located at 0xffff0000-0xffff0fff. | ||
63 | */ | ||
64 | #define CLEAN_ADDR 0xfffe0000 | ||
65 | |||
66 | /* | ||
67 | * This macro is used to wait for a CP15 write and is needed | ||
68 | * when we have to ensure that the last operation to the co-pro | ||
69 | * was completed before continuing with operation. | ||
70 | */ | ||
71 | .macro cpwait, rd | ||
72 | mrc p15, 0, \rd, c2, c0, 0 @ arbitrary read of cp15 | ||
73 | mov \rd, \rd @ wait for completion | ||
74 | sub pc, pc, #4 @ flush instruction pipeline | ||
75 | .endm | ||
76 | |||
77 | .macro cpwait_ret, lr, rd | ||
78 | mrc p15, 0, \rd, c2, c0, 0 @ arbitrary read of cp15 | ||
79 | sub pc, \lr, \rd, LSR #32 @ wait for completion and | ||
80 | @ flush instruction pipeline | ||
81 | .endm | ||
82 | |||
83 | /* | ||
84 | * This macro cleans the entire dcache using line allocate. | ||
85 | * The main loop has been unrolled to reduce loop overhead. | ||
86 | * rd and rs are two scratch registers. | ||
87 | */ | ||
88 | .macro clean_d_cache, rd, rs | ||
89 | ldr \rs, =clean_addr | ||
90 | ldr \rd, [\rs] | ||
91 | eor \rd, \rd, #CACHESIZE | ||
92 | str \rd, [\rs] | ||
93 | add \rs, \rd, #CACHESIZE | ||
94 | 1: mcr p15, 0, \rd, c7, c2, 5 @ allocate D cache line | ||
95 | add \rd, \rd, #CACHELINESIZE | ||
96 | mcr p15, 0, \rd, c7, c2, 5 @ allocate D cache line | ||
97 | add \rd, \rd, #CACHELINESIZE | ||
98 | mcr p15, 0, \rd, c7, c2, 5 @ allocate D cache line | ||
99 | add \rd, \rd, #CACHELINESIZE | ||
100 | mcr p15, 0, \rd, c7, c2, 5 @ allocate D cache line | ||
101 | add \rd, \rd, #CACHELINESIZE | ||
102 | teq \rd, \rs | ||
103 | bne 1b | ||
104 | .endm | ||
105 | |||
106 | .data | ||
107 | clean_addr: .word CLEAN_ADDR | ||
108 | |||
109 | .text | ||
110 | |||
111 | /* | ||
112 | * cpu_xscale_proc_init() | ||
113 | * | ||
114 | * Nothing too exciting at the moment | ||
115 | */ | ||
116 | ENTRY(cpu_xscale_proc_init) | ||
117 | mov pc, lr | ||
118 | |||
119 | /* | ||
120 | * cpu_xscale_proc_fin() | ||
121 | */ | ||
122 | ENTRY(cpu_xscale_proc_fin) | ||
123 | str lr, [sp, #-4]! | ||
124 | mov r0, #PSR_F_BIT|PSR_I_BIT|SVC_MODE | ||
125 | msr cpsr_c, r0 | ||
126 | bl xscale_flush_kern_cache_all @ clean caches | ||
127 | mrc p15, 0, r0, c1, c0, 0 @ ctrl register | ||
128 | bic r0, r0, #0x1800 @ ...IZ........... | ||
129 | bic r0, r0, #0x0006 @ .............CA. | ||
130 | mcr p15, 0, r0, c1, c0, 0 @ disable caches | ||
131 | ldr pc, [sp], #4 | ||
132 | |||
133 | /* | ||
134 | * cpu_xscale_reset(loc) | ||
135 | * | ||
136 | * Perform a soft reset of the system. Put the CPU into the | ||
137 | * same state as it would be if it had been reset, and branch | ||
138 | * to what would be the reset vector. | ||
139 | * | ||
140 | * loc: location to jump to for soft reset | ||
141 | */ | ||
142 | .align 5 | ||
143 | ENTRY(cpu_xscale_reset) | ||
144 | mov r1, #PSR_F_BIT|PSR_I_BIT|SVC_MODE | ||
145 | msr cpsr_c, r1 @ reset CPSR | ||
146 | mrc p15, 0, r1, c1, c0, 0 @ ctrl register | ||
147 | bic r1, r1, #0x0086 @ ........B....CA. | ||
148 | bic r1, r1, #0x3900 @ ..VIZ..S........ | ||
149 | mcr p15, 0, r1, c1, c0, 0 @ ctrl register | ||
150 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches & BTB | ||
151 | bic r1, r1, #0x0001 @ ...............M | ||
152 | mcr p15, 0, r1, c1, c0, 0 @ ctrl register | ||
153 | @ CAUTION: MMU turned off from this point. We count on the pipeline | ||
154 | @ already containing those two last instructions to survive. | ||
155 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
156 | mov pc, r0 | ||
157 | |||
158 | /* | ||
159 | * cpu_xscale_do_idle() | ||
160 | * | ||
161 | * Cause the processor to idle | ||
162 | * | ||
163 | * For now we do nothing but go to idle mode for every case | ||
164 | * | ||
165 | * XScale supports clock switching, but using idle mode support | ||
166 | * allows external hardware to react to system state changes. | ||
167 | */ | ||
168 | .align 5 | ||
169 | |||
170 | ENTRY(cpu_xscale_do_idle) | ||
171 | mov r0, #1 | ||
172 | mcr p14, 0, r0, c7, c0, 0 @ Go to IDLE | ||
173 | mov pc, lr | ||
174 | |||
175 | /* ================================= CACHE ================================ */ | ||
176 | |||
177 | /* | ||
178 | * flush_user_cache_all() | ||
179 | * | ||
180 | * Invalidate all cache entries in a particular address | ||
181 | * space. | ||
182 | */ | ||
183 | ENTRY(xscale_flush_user_cache_all) | ||
184 | /* FALLTHROUGH */ | ||
185 | |||
186 | /* | ||
187 | * flush_kern_cache_all() | ||
188 | * | ||
189 | * Clean and invalidate the entire cache. | ||
190 | */ | ||
191 | ENTRY(xscale_flush_kern_cache_all) | ||
192 | mov r2, #VM_EXEC | ||
193 | mov ip, #0 | ||
194 | __flush_whole_cache: | ||
195 | clean_d_cache r0, r1 | ||
196 | tst r2, #VM_EXEC | ||
197 | mcrne p15, 0, ip, c7, c5, 0 @ Invalidate I cache & BTB | ||
198 | mcrne p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
199 | mov pc, lr | ||
200 | |||
201 | /* | ||
202 | * flush_user_cache_range(start, end, vm_flags) | ||
203 | * | ||
204 | * Invalidate a range of cache entries in the specified | ||
205 | * address space. | ||
206 | * | ||
207 | * - start - start address (may not be aligned) | ||
208 | * - end - end address (exclusive, may not be aligned) | ||
209 | * - vma - vma_area_struct describing address space | ||
210 | */ | ||
211 | .align 5 | ||
212 | ENTRY(xscale_flush_user_cache_range) | ||
213 | mov ip, #0 | ||
214 | sub r3, r1, r0 @ calculate total size | ||
215 | cmp r3, #MAX_AREA_SIZE | ||
216 | bhs __flush_whole_cache | ||
217 | |||
218 | 1: tst r2, #VM_EXEC | ||
219 | mcrne p15, 0, r0, c7, c5, 1 @ Invalidate I cache line | ||
220 | mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line | ||
221 | mcr p15, 0, r0, c7, c6, 1 @ Invalidate D cache line | ||
222 | add r0, r0, #CACHELINESIZE | ||
223 | cmp r0, r1 | ||
224 | blo 1b | ||
225 | tst r2, #VM_EXEC | ||
226 | mcrne p15, 0, ip, c7, c5, 6 @ Invalidate BTB | ||
227 | mcrne p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
228 | mov pc, lr | ||
229 | |||
230 | /* | ||
231 | * coherent_kern_range(start, end) | ||
232 | * | ||
233 | * Ensure coherency between the Icache and the Dcache in the | ||
234 | * region described by start. If you have non-snooping | ||
235 | * Harvard caches, you need to implement this function. | ||
236 | * | ||
237 | * - start - virtual start address | ||
238 | * - end - virtual end address | ||
239 | * | ||
240 | * Note: single I-cache line invalidation isn't used here since | ||
241 | * it also trashes the mini I-cache used by JTAG debuggers. | ||
242 | */ | ||
243 | ENTRY(xscale_coherent_kern_range) | ||
244 | /* FALLTHROUGH */ | ||
245 | |||
246 | /* | ||
247 | * coherent_user_range(start, end) | ||
248 | * | ||
249 | * Ensure coherency between the Icache and the Dcache in the | ||
250 | * region described by start. If you have non-snooping | ||
251 | * Harvard caches, you need to implement this function. | ||
252 | * | ||
253 | * - start - virtual start address | ||
254 | * - end - virtual end address | ||
255 | * | ||
256 | * Note: single I-cache line invalidation isn't used here since | ||
257 | * it also trashes the mini I-cache used by JTAG debuggers. | ||
258 | */ | ||
259 | ENTRY(xscale_coherent_user_range) | ||
260 | bic r0, r0, #CACHELINESIZE - 1 | ||
261 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
262 | add r0, r0, #CACHELINESIZE | ||
263 | cmp r0, r1 | ||
264 | blo 1b | ||
265 | mov r0, #0 | ||
266 | mcr p15, 0, r0, c7, c5, 0 @ Invalidate I cache & BTB | ||
267 | mcr p15, 0, r0, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
268 | mov pc, lr | ||
269 | |||
270 | /* | ||
271 | * flush_kern_dcache_page(void *page) | ||
272 | * | ||
273 | * Ensure no D cache aliasing occurs, either with itself or | ||
274 | * the I cache | ||
275 | * | ||
276 | * - addr - page aligned address | ||
277 | */ | ||
278 | ENTRY(xscale_flush_kern_dcache_page) | ||
279 | add r1, r0, #PAGE_SZ | ||
280 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
281 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
282 | add r0, r0, #CACHELINESIZE | ||
283 | cmp r0, r1 | ||
284 | blo 1b | ||
285 | mov r0, #0 | ||
286 | mcr p15, 0, r0, c7, c5, 0 @ Invalidate I cache & BTB | ||
287 | mcr p15, 0, r0, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
288 | mov pc, lr | ||
289 | |||
290 | /* | ||
291 | * dma_inv_range(start, end) | ||
292 | * | ||
293 | * Invalidate (discard) the specified virtual address range. | ||
294 | * May not write back any entries. If 'start' or 'end' | ||
295 | * are not cache line aligned, those lines must be written | ||
296 | * back. | ||
297 | * | ||
298 | * - start - virtual start address | ||
299 | * - end - virtual end address | ||
300 | */ | ||
301 | ENTRY(xscale_dma_inv_range) | ||
302 | mrc p15, 0, r2, c0, c0, 0 @ read ID | ||
303 | eor r2, r2, #0x69000000 | ||
304 | eor r2, r2, #0x00052000 | ||
305 | bics r2, r2, #1 | ||
306 | beq xscale_dma_flush_range | ||
307 | |||
308 | tst r0, #CACHELINESIZE - 1 | ||
309 | bic r0, r0, #CACHELINESIZE - 1 | ||
310 | mcrne p15, 0, r0, c7, c10, 1 @ clean D entry | ||
311 | tst r1, #CACHELINESIZE - 1 | ||
312 | mcrne p15, 0, r1, c7, c10, 1 @ clean D entry | ||
313 | 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
314 | add r0, r0, #CACHELINESIZE | ||
315 | cmp r0, r1 | ||
316 | blo 1b | ||
317 | mcr p15, 0, r0, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
318 | mov pc, lr | ||
319 | |||
320 | /* | ||
321 | * dma_clean_range(start, end) | ||
322 | * | ||
323 | * Clean the specified virtual address range. | ||
324 | * | ||
325 | * - start - virtual start address | ||
326 | * - end - virtual end address | ||
327 | */ | ||
328 | ENTRY(xscale_dma_clean_range) | ||
329 | bic r0, r0, #CACHELINESIZE - 1 | ||
330 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
331 | add r0, r0, #CACHELINESIZE | ||
332 | cmp r0, r1 | ||
333 | blo 1b | ||
334 | mcr p15, 0, r0, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
335 | mov pc, lr | ||
336 | |||
337 | /* | ||
338 | * dma_flush_range(start, end) | ||
339 | * | ||
340 | * Clean and invalidate the specified virtual address range. | ||
341 | * | ||
342 | * - start - virtual start address | ||
343 | * - end - virtual end address | ||
344 | */ | ||
345 | ENTRY(xscale_dma_flush_range) | ||
346 | bic r0, r0, #CACHELINESIZE - 1 | ||
347 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
348 | mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry | ||
349 | add r0, r0, #CACHELINESIZE | ||
350 | cmp r0, r1 | ||
351 | blo 1b | ||
352 | mcr p15, 0, r0, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
353 | mov pc, lr | ||
354 | |||
355 | ENTRY(xscale_cache_fns) | ||
356 | .long xscale_flush_kern_cache_all | ||
357 | .long xscale_flush_user_cache_all | ||
358 | .long xscale_flush_user_cache_range | ||
359 | .long xscale_coherent_kern_range | ||
360 | .long xscale_coherent_user_range | ||
361 | .long xscale_flush_kern_dcache_page | ||
362 | .long xscale_dma_inv_range | ||
363 | .long xscale_dma_clean_range | ||
364 | .long xscale_dma_flush_range | ||
365 | |||
366 | ENTRY(cpu_xscale_dcache_clean_area) | ||
367 | 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry | ||
368 | add r0, r0, #CACHELINESIZE | ||
369 | subs r1, r1, #CACHELINESIZE | ||
370 | bhi 1b | ||
371 | mov pc, lr | ||
372 | |||
373 | /* ================================ CACHE LOCKING============================ | ||
374 | * | ||
375 | * The XScale MicroArchitecture implements support for locking entries into | ||
376 | * the data and instruction cache. The following functions implement the core | ||
377 | * low level instructions needed to accomplish the locking. The developer's | ||
378 | * manual states that the code that performs the locking must be in non-cached | ||
379 | * memory. To accomplish this, the code in xscale-cache-lock.c copies the | ||
380 | * following functions from the cache into a non-cached memory region that | ||
381 | * is allocated through consistent_alloc(). | ||
382 | * | ||
383 | */ | ||
384 | .align 5 | ||
385 | /* | ||
386 | * xscale_icache_lock | ||
387 | * | ||
388 | * r0: starting address to lock | ||
389 | * r1: end address to lock | ||
390 | */ | ||
391 | ENTRY(xscale_icache_lock) | ||
392 | |||
393 | iLockLoop: | ||
394 | bic r0, r0, #CACHELINESIZE - 1 | ||
395 | mcr p15, 0, r0, c9, c1, 0 @ lock into cache | ||
396 | cmp r0, r1 @ are we done? | ||
397 | add r0, r0, #CACHELINESIZE @ advance to next cache line | ||
398 | bls iLockLoop | ||
399 | mov pc, lr | ||
400 | |||
401 | /* | ||
402 | * xscale_icache_unlock | ||
403 | */ | ||
404 | ENTRY(xscale_icache_unlock) | ||
405 | mcr p15, 0, r0, c9, c1, 1 @ Unlock icache | ||
406 | mov pc, lr | ||
407 | |||
408 | /* | ||
409 | * xscale_dcache_lock | ||
410 | * | ||
411 | * r0: starting address to lock | ||
412 | * r1: end address to lock | ||
413 | */ | ||
414 | ENTRY(xscale_dcache_lock) | ||
415 | mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
416 | mov r2, #1 | ||
417 | mcr p15, 0, r2, c9, c2, 0 @ Put dcache in lock mode | ||
418 | cpwait ip @ Wait for completion | ||
419 | |||
420 | mrs r2, cpsr | ||
421 | orr r3, r2, #PSR_F_BIT | PSR_I_BIT | ||
422 | dLockLoop: | ||
423 | msr cpsr_c, r3 | ||
424 | mcr p15, 0, r0, c7, c10, 1 @ Write back line if it is dirty | ||
425 | mcr p15, 0, r0, c7, c6, 1 @ Flush/invalidate line | ||
426 | msr cpsr_c, r2 | ||
427 | ldr ip, [r0], #CACHELINESIZE @ Preload 32 bytes into cache from | ||
428 | @ location [r0]. Post-increment | ||
429 | @ r3 to next cache line | ||
430 | cmp r0, r1 @ Are we done? | ||
431 | bls dLockLoop | ||
432 | |||
433 | mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
434 | mov r2, #0 | ||
435 | mcr p15, 0, r2, c9, c2, 0 @ Get out of lock mode | ||
436 | cpwait_ret lr, ip | ||
437 | |||
438 | /* | ||
439 | * xscale_dcache_unlock | ||
440 | */ | ||
441 | ENTRY(xscale_dcache_unlock) | ||
442 | mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
443 | mcr p15, 0, ip, c9, c2, 1 @ Unlock cache | ||
444 | mov pc, lr | ||
445 | |||
446 | /* | ||
447 | * Needed to determine the length of the code that needs to be copied. | ||
448 | */ | ||
449 | .align 5 | ||
450 | ENTRY(xscale_cache_dummy) | ||
451 | mov pc, lr | ||
452 | |||
453 | /* ================================ TLB LOCKING============================== | ||
454 | * | ||
455 | * The XScale MicroArchitecture implements support for locking entries into | ||
456 | * the Instruction and Data TLBs. The following functions provide the | ||
457 | * low level support for supporting these under Linux. xscale-lock.c | ||
458 | * implements some higher level management code. Most of the following | ||
459 | * is taken straight out of the Developer's Manual. | ||
460 | */ | ||
461 | |||
462 | /* | ||
463 | * Lock I-TLB entry | ||
464 | * | ||
465 | * r0: Virtual address to translate and lock | ||
466 | */ | ||
467 | .align 5 | ||
468 | ENTRY(xscale_itlb_lock) | ||
469 | mrs r2, cpsr | ||
470 | orr r3, r2, #PSR_F_BIT | PSR_I_BIT | ||
471 | msr cpsr_c, r3 @ Disable interrupts | ||
472 | mcr p15, 0, r0, c8, c5, 1 @ Invalidate I-TLB entry | ||
473 | mcr p15, 0, r0, c10, c4, 0 @ Translate and lock | ||
474 | msr cpsr_c, r2 @ Restore interrupts | ||
475 | cpwait_ret lr, ip | ||
476 | |||
477 | /* | ||
478 | * Lock D-TLB entry | ||
479 | * | ||
480 | * r0: Virtual address to translate and lock | ||
481 | */ | ||
482 | .align 5 | ||
483 | ENTRY(xscale_dtlb_lock) | ||
484 | mrs r2, cpsr | ||
485 | orr r3, r2, #PSR_F_BIT | PSR_I_BIT | ||
486 | msr cpsr_c, r3 @ Disable interrupts | ||
487 | mcr p15, 0, r0, c8, c6, 1 @ Invalidate D-TLB entry | ||
488 | mcr p15, 0, r0, c10, c8, 0 @ Translate and lock | ||
489 | msr cpsr_c, r2 @ Restore interrupts | ||
490 | cpwait_ret lr, ip | ||
491 | |||
492 | /* | ||
493 | * Unlock all I-TLB entries | ||
494 | */ | ||
495 | .align 5 | ||
496 | ENTRY(xscale_itlb_unlock) | ||
497 | mcr p15, 0, ip, c10, c4, 1 @ Unlock I-TLB | ||
498 | mcr p15, 0, ip, c8, c5, 0 @ Invalidate I-TLB | ||
499 | cpwait_ret lr, ip | ||
500 | |||
501 | /* | ||
502 | * Unlock all D-TLB entries | ||
503 | */ | ||
504 | ENTRY(xscale_dtlb_unlock) | ||
505 | mcr p15, 0, ip, c10, c8, 1 @ Unlock D-TBL | ||
506 | mcr p15, 0, ip, c8, c6, 0 @ Invalidate D-TLB | ||
507 | cpwait_ret lr, ip | ||
508 | |||
509 | /* =============================== PageTable ============================== */ | ||
510 | |||
511 | #define PTE_CACHE_WRITE_ALLOCATE 0 | ||
512 | |||
513 | /* | ||
514 | * cpu_xscale_switch_mm(pgd) | ||
515 | * | ||
516 | * Set the translation base pointer to be as described by pgd. | ||
517 | * | ||
518 | * pgd: new page tables | ||
519 | */ | ||
520 | .align 5 | ||
521 | ENTRY(cpu_xscale_switch_mm) | ||
522 | clean_d_cache r1, r2 | ||
523 | mcr p15, 0, ip, c7, c5, 0 @ Invalidate I cache & BTB | ||
524 | mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
525 | mcr p15, 0, r0, c2, c0, 0 @ load page table pointer | ||
526 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs | ||
527 | cpwait_ret lr, ip | ||
528 | |||
529 | /* | ||
530 | * cpu_xscale_set_pte(ptep, pte) | ||
531 | * | ||
532 | * Set a PTE and flush it out | ||
533 | * | ||
534 | * Errata 40: must set memory to write-through for user read-only pages. | ||
535 | */ | ||
536 | .align 5 | ||
537 | ENTRY(cpu_xscale_set_pte) | ||
538 | str r1, [r0], #-2048 @ linux version | ||
539 | |||
540 | bic r2, r1, #0xff0 | ||
541 | orr r2, r2, #PTE_TYPE_EXT @ extended page | ||
542 | |||
543 | eor r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY | ||
544 | |||
545 | tst r3, #L_PTE_USER @ User? | ||
546 | orrne r2, r2, #PTE_EXT_AP_URO_SRW @ yes -> user r/o, system r/w | ||
547 | |||
548 | tst r3, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? | ||
549 | orreq r2, r2, #PTE_EXT_AP_UNO_SRW @ yes -> user n/a, system r/w | ||
550 | @ combined with user -> user r/w | ||
551 | |||
552 | @ | ||
553 | @ Handle the X bit. We want to set this bit for the minicache | ||
554 | @ (U = E = B = W = 0, C = 1) or when write allocate is enabled, | ||
555 | @ and we have a writeable, cacheable region. If we ignore the | ||
556 | @ U and E bits, we can allow user space to use the minicache as | ||
557 | @ well. | ||
558 | @ | ||
559 | @ X = (C & ~W & ~B) | (C & W & B & write_allocate) | ||
560 | @ | ||
561 | eor ip, r1, #L_PTE_CACHEABLE | ||
562 | tst ip, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE | ||
563 | #if PTE_CACHE_WRITE_ALLOCATE | ||
564 | eorne ip, r1, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE | ||
565 | tstne ip, #L_PTE_CACHEABLE | L_PTE_WRITE | L_PTE_BUFFERABLE | ||
566 | #endif | ||
567 | orreq r2, r2, #PTE_EXT_TEX(1) | ||
568 | |||
569 | @ | ||
570 | @ Erratum 40: The B bit must be cleared for a user read-only | ||
571 | @ cacheable page. | ||
572 | @ | ||
573 | @ B = B & ~(U & C & ~W) | ||
574 | @ | ||
575 | and ip, r1, #L_PTE_USER | L_PTE_WRITE | L_PTE_CACHEABLE | ||
576 | teq ip, #L_PTE_USER | L_PTE_CACHEABLE | ||
577 | biceq r2, r2, #PTE_BUFFERABLE | ||
578 | |||
579 | tst r3, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? | ||
580 | movne r2, #0 @ no -> fault | ||
581 | |||
582 | str r2, [r0] @ hardware version | ||
583 | mov ip, #0 | ||
584 | mcr p15, 0, r0, c7, c10, 1 @ Clean D cache line | ||
585 | mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
586 | mov pc, lr | ||
587 | |||
588 | |||
589 | .ltorg | ||
590 | |||
591 | .align | ||
592 | |||
593 | __INIT | ||
594 | |||
595 | .type __xscale_setup, #function | ||
596 | __xscale_setup: | ||
597 | mcr p15, 0, ip, c7, c7, 0 @ invalidate I, D caches & BTB | ||
598 | mcr p15, 0, ip, c7, c10, 4 @ Drain Write (& Fill) Buffer | ||
599 | mcr p15, 0, ip, c8, c7, 0 @ invalidate I, D TLBs | ||
600 | #ifdef CONFIG_IWMMXT | ||
601 | mov r0, #0 @ initially disallow access to CP0/CP1 | ||
602 | #else | ||
603 | mov r0, #1 @ Allow access to CP0 | ||
604 | #endif | ||
605 | orr r0, r0, #1 << 6 @ cp6 for IOP3xx and Bulverde | ||
606 | orr r0, r0, #1 << 13 @ Its undefined whether this | ||
607 | mcr p15, 0, r0, c15, c1, 0 @ affects USR or SVC modes | ||
608 | mrc p15, 0, r0, c1, c0, 0 @ get control register | ||
609 | ldr r5, xscale_cr1_clear | ||
610 | bic r0, r0, r5 | ||
611 | ldr r5, xscale_cr1_set | ||
612 | orr r0, r0, r5 | ||
613 | mov pc, lr | ||
614 | .size __xscale_setup, . - __xscale_setup | ||
615 | |||
616 | /* | ||
617 | * R | ||
618 | * .RVI ZFRS BLDP WCAM | ||
619 | * ..11 1.01 .... .101 | ||
620 | * | ||
621 | */ | ||
622 | .type xscale_cr1_clear, #object | ||
623 | .type xscale_cr1_set, #object | ||
624 | xscale_cr1_clear: | ||
625 | .word 0x3b07 | ||
626 | xscale_cr1_set: | ||
627 | .word 0x3905 | ||
628 | |||
629 | __INITDATA | ||
630 | |||
631 | /* | ||
632 | * Purpose : Function pointers used to access above functions - all calls | ||
633 | * come through these | ||
634 | */ | ||
635 | |||
636 | .type xscale_processor_functions, #object | ||
637 | ENTRY(xscale_processor_functions) | ||
638 | .word v5t_early_abort | ||
639 | .word cpu_xscale_proc_init | ||
640 | .word cpu_xscale_proc_fin | ||
641 | .word cpu_xscale_reset | ||
642 | .word cpu_xscale_do_idle | ||
643 | .word cpu_xscale_dcache_clean_area | ||
644 | .word cpu_xscale_switch_mm | ||
645 | .word cpu_xscale_set_pte | ||
646 | .size xscale_processor_functions, . - xscale_processor_functions | ||
647 | |||
648 | .section ".rodata" | ||
649 | |||
650 | .type cpu_arch_name, #object | ||
651 | cpu_arch_name: | ||
652 | .asciz "armv5te" | ||
653 | .size cpu_arch_name, . - cpu_arch_name | ||
654 | |||
655 | .type cpu_elf_name, #object | ||
656 | cpu_elf_name: | ||
657 | .asciz "v5" | ||
658 | .size cpu_elf_name, . - cpu_elf_name | ||
659 | |||
660 | .type cpu_80200_name, #object | ||
661 | cpu_80200_name: | ||
662 | .asciz "XScale-80200" | ||
663 | .size cpu_80200_name, . - cpu_80200_name | ||
664 | |||
665 | .type cpu_8032x_name, #object | ||
666 | cpu_8032x_name: | ||
667 | .asciz "XScale-IOP8032x Family" | ||
668 | .size cpu_8032x_name, . - cpu_8032x_name | ||
669 | |||
670 | .type cpu_8033x_name, #object | ||
671 | cpu_8033x_name: | ||
672 | .asciz "XScale-IOP8033x Family" | ||
673 | .size cpu_8033x_name, . - cpu_8033x_name | ||
674 | |||
675 | .type cpu_pxa250_name, #object | ||
676 | cpu_pxa250_name: | ||
677 | .asciz "XScale-PXA250" | ||
678 | .size cpu_pxa250_name, . - cpu_pxa250_name | ||
679 | |||
680 | .type cpu_pxa210_name, #object | ||
681 | cpu_pxa210_name: | ||
682 | .asciz "XScale-PXA210" | ||
683 | .size cpu_pxa210_name, . - cpu_pxa210_name | ||
684 | |||
685 | .type cpu_ixp42x_name, #object | ||
686 | cpu_ixp42x_name: | ||
687 | .asciz "XScale-IXP42x Family" | ||
688 | .size cpu_ixp42x_name, . - cpu_ixp42x_name | ||
689 | |||
690 | .type cpu_ixp46x_name, #object | ||
691 | cpu_ixp46x_name: | ||
692 | .asciz "XScale-IXP46x Family" | ||
693 | .size cpu_ixp46x_name, . - cpu_ixp46x_name | ||
694 | |||
695 | .type cpu_ixp2400_name, #object | ||
696 | cpu_ixp2400_name: | ||
697 | .asciz "XScale-IXP2400" | ||
698 | .size cpu_ixp2400_name, . - cpu_ixp2400_name | ||
699 | |||
700 | .type cpu_ixp2800_name, #object | ||
701 | cpu_ixp2800_name: | ||
702 | .asciz "XScale-IXP2800" | ||
703 | .size cpu_ixp2800_name, . - cpu_ixp2800_name | ||
704 | |||
705 | .type cpu_pxa255_name, #object | ||
706 | cpu_pxa255_name: | ||
707 | .asciz "XScale-PXA255" | ||
708 | .size cpu_pxa255_name, . - cpu_pxa255_name | ||
709 | |||
710 | .type cpu_pxa270_name, #object | ||
711 | cpu_pxa270_name: | ||
712 | .asciz "XScale-PXA270" | ||
713 | .size cpu_pxa270_name, . - cpu_pxa270_name | ||
714 | |||
715 | .align | ||
716 | |||
717 | .section ".proc.info", #alloc, #execinstr | ||
718 | |||
719 | .type __80200_proc_info,#object | ||
720 | __80200_proc_info: | ||
721 | .long 0x69052000 | ||
722 | .long 0xfffffff0 | ||
723 | .long PMD_TYPE_SECT | \ | ||
724 | PMD_SECT_BUFFERABLE | \ | ||
725 | PMD_SECT_CACHEABLE | \ | ||
726 | PMD_SECT_AP_WRITE | \ | ||
727 | PMD_SECT_AP_READ | ||
728 | b __xscale_setup | ||
729 | .long cpu_arch_name | ||
730 | .long cpu_elf_name | ||
731 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
732 | .long cpu_80200_name | ||
733 | .long xscale_processor_functions | ||
734 | .long v4wbi_tlb_fns | ||
735 | .long xscale_mc_user_fns | ||
736 | .long xscale_cache_fns | ||
737 | .size __80200_proc_info, . - __80200_proc_info | ||
738 | |||
739 | .type __8032x_proc_info,#object | ||
740 | __8032x_proc_info: | ||
741 | .long 0x69052420 | ||
742 | .long 0xfffff5e0 @ mask should accomodate IOP80219 also | ||
743 | .long PMD_TYPE_SECT | \ | ||
744 | PMD_SECT_BUFFERABLE | \ | ||
745 | PMD_SECT_CACHEABLE | \ | ||
746 | PMD_SECT_AP_WRITE | \ | ||
747 | PMD_SECT_AP_READ | ||
748 | b __xscale_setup | ||
749 | .long cpu_arch_name | ||
750 | .long cpu_elf_name | ||
751 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
752 | .long cpu_8032x_name | ||
753 | .long xscale_processor_functions | ||
754 | .long v4wbi_tlb_fns | ||
755 | .long xscale_mc_user_fns | ||
756 | .long xscale_cache_fns | ||
757 | .size __8032x_proc_info, . - __8032x_proc_info | ||
758 | |||
759 | .type __8033x_proc_info,#object | ||
760 | __8033x_proc_info: | ||
761 | .long 0x69054010 | ||
762 | .long 0xffffff30 | ||
763 | .long PMD_TYPE_SECT | \ | ||
764 | PMD_SECT_BUFFERABLE | \ | ||
765 | PMD_SECT_CACHEABLE | \ | ||
766 | PMD_SECT_AP_WRITE | \ | ||
767 | PMD_SECT_AP_READ | ||
768 | b __xscale_setup | ||
769 | .long cpu_arch_name | ||
770 | .long cpu_elf_name | ||
771 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
772 | .long cpu_8033x_name | ||
773 | .long xscale_processor_functions | ||
774 | .long v4wbi_tlb_fns | ||
775 | .long xscale_mc_user_fns | ||
776 | .long xscale_cache_fns | ||
777 | .size __8033x_proc_info, . - __8033x_proc_info | ||
778 | |||
779 | .type __pxa250_proc_info,#object | ||
780 | __pxa250_proc_info: | ||
781 | .long 0x69052100 | ||
782 | .long 0xfffff7f0 | ||
783 | .long PMD_TYPE_SECT | \ | ||
784 | PMD_SECT_BUFFERABLE | \ | ||
785 | PMD_SECT_CACHEABLE | \ | ||
786 | PMD_SECT_AP_WRITE | \ | ||
787 | PMD_SECT_AP_READ | ||
788 | b __xscale_setup | ||
789 | .long cpu_arch_name | ||
790 | .long cpu_elf_name | ||
791 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
792 | .long cpu_pxa250_name | ||
793 | .long xscale_processor_functions | ||
794 | .long v4wbi_tlb_fns | ||
795 | .long xscale_mc_user_fns | ||
796 | .long xscale_cache_fns | ||
797 | .size __pxa250_proc_info, . - __pxa250_proc_info | ||
798 | |||
799 | .type __pxa210_proc_info,#object | ||
800 | __pxa210_proc_info: | ||
801 | .long 0x69052120 | ||
802 | .long 0xfffff3f0 | ||
803 | .long PMD_TYPE_SECT | \ | ||
804 | PMD_SECT_BUFFERABLE | \ | ||
805 | PMD_SECT_CACHEABLE | \ | ||
806 | PMD_SECT_AP_WRITE | \ | ||
807 | PMD_SECT_AP_READ | ||
808 | b __xscale_setup | ||
809 | .long cpu_arch_name | ||
810 | .long cpu_elf_name | ||
811 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
812 | .long cpu_pxa210_name | ||
813 | .long xscale_processor_functions | ||
814 | .long v4wbi_tlb_fns | ||
815 | .long xscale_mc_user_fns | ||
816 | .long xscale_cache_fns | ||
817 | .size __pxa210_proc_info, . - __pxa210_proc_info | ||
818 | |||
819 | .type __ixp2400_proc_info, #object | ||
820 | __ixp2400_proc_info: | ||
821 | .long 0x69054190 | ||
822 | .long 0xfffffff0 | ||
823 | .long PMD_TYPE_SECT | \ | ||
824 | PMD_SECT_BUFFERABLE | \ | ||
825 | PMD_SECT_CACHEABLE | \ | ||
826 | PMD_SECT_AP_WRITE | \ | ||
827 | PMD_SECT_AP_READ | ||
828 | b __xscale_setup | ||
829 | .long cpu_arch_name | ||
830 | .long cpu_elf_name | ||
831 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
832 | .long cpu_ixp2400_name | ||
833 | .long xscale_processor_functions | ||
834 | .long v4wbi_tlb_fns | ||
835 | .long xscale_mc_user_fns | ||
836 | .long xscale_cache_fns | ||
837 | .size __ixp2400_proc_info, . - __ixp2400_proc_info | ||
838 | |||
839 | .type __ixp2800_proc_info, #object | ||
840 | __ixp2800_proc_info: | ||
841 | .long 0x690541a0 | ||
842 | .long 0xfffffff0 | ||
843 | .long PMD_TYPE_SECT | \ | ||
844 | PMD_SECT_BUFFERABLE | \ | ||
845 | PMD_SECT_CACHEABLE | \ | ||
846 | PMD_SECT_AP_WRITE | \ | ||
847 | PMD_SECT_AP_READ | ||
848 | b __xscale_setup | ||
849 | .long cpu_arch_name | ||
850 | .long cpu_elf_name | ||
851 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
852 | .long cpu_ixp2800_name | ||
853 | .long xscale_processor_functions | ||
854 | .long v4wbi_tlb_fns | ||
855 | .long xscale_mc_user_fns | ||
856 | .long xscale_cache_fns | ||
857 | .size __ixp2800_proc_info, . - __ixp2800_proc_info | ||
858 | |||
859 | .type __ixp42x_proc_info, #object | ||
860 | __ixp42x_proc_info: | ||
861 | .long 0x690541c0 | ||
862 | .long 0xffffffc0 | ||
863 | .long PMD_TYPE_SECT | \ | ||
864 | PMD_SECT_BUFFERABLE | \ | ||
865 | PMD_SECT_CACHEABLE | \ | ||
866 | PMD_SECT_AP_WRITE | \ | ||
867 | PMD_SECT_AP_READ | ||
868 | b __xscale_setup | ||
869 | .long cpu_arch_name | ||
870 | .long cpu_elf_name | ||
871 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
872 | .long cpu_ixp42x_name | ||
873 | .long xscale_processor_functions | ||
874 | .long v4wbi_tlb_fns | ||
875 | .long xscale_mc_user_fns | ||
876 | .long xscale_cache_fns | ||
877 | .size __ixp42x_proc_info, . - __ixp42x_proc_info | ||
878 | |||
879 | .type __ixp46x_proc_info, #object | ||
880 | __ixp46x_proc_info: | ||
881 | .long 0x69054200 | ||
882 | .long 0xffffff00 | ||
883 | .long 0x00000c0e | ||
884 | b __xscale_setup | ||
885 | .long cpu_arch_name | ||
886 | .long cpu_elf_name | ||
887 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
888 | .long cpu_ixp46x_name | ||
889 | .long xscale_processor_functions | ||
890 | .long v4wbi_tlb_fns | ||
891 | .long xscale_mc_user_fns | ||
892 | .long xscale_cache_fns | ||
893 | .size __ixp46x_proc_info, . - __ixp46x_proc_info | ||
894 | |||
895 | .type __pxa255_proc_info,#object | ||
896 | __pxa255_proc_info: | ||
897 | .long 0x69052d00 | ||
898 | .long 0xfffffff0 | ||
899 | .long PMD_TYPE_SECT | \ | ||
900 | PMD_SECT_BUFFERABLE | \ | ||
901 | PMD_SECT_CACHEABLE | \ | ||
902 | PMD_SECT_AP_WRITE | \ | ||
903 | PMD_SECT_AP_READ | ||
904 | b __xscale_setup | ||
905 | .long cpu_arch_name | ||
906 | .long cpu_elf_name | ||
907 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
908 | .long cpu_pxa255_name | ||
909 | .long xscale_processor_functions | ||
910 | .long v4wbi_tlb_fns | ||
911 | .long xscale_mc_user_fns | ||
912 | .long xscale_cache_fns | ||
913 | .size __pxa255_proc_info, . - __pxa255_proc_info | ||
914 | |||
915 | .type __pxa270_proc_info,#object | ||
916 | __pxa270_proc_info: | ||
917 | .long 0x69054110 | ||
918 | .long 0xfffffff0 | ||
919 | .long PMD_TYPE_SECT | \ | ||
920 | PMD_SECT_BUFFERABLE | \ | ||
921 | PMD_SECT_CACHEABLE | \ | ||
922 | PMD_SECT_AP_WRITE | \ | ||
923 | PMD_SECT_AP_READ | ||
924 | b __xscale_setup | ||
925 | .long cpu_arch_name | ||
926 | .long cpu_elf_name | ||
927 | .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP | ||
928 | .long cpu_pxa270_name | ||
929 | .long xscale_processor_functions | ||
930 | .long v4wbi_tlb_fns | ||
931 | .long xscale_mc_user_fns | ||
932 | .long xscale_cache_fns | ||
933 | .size __pxa270_proc_info, . - __pxa270_proc_info | ||
934 | |||
diff --git a/arch/arm/mm/tlb-v3.S b/arch/arm/mm/tlb-v3.S new file mode 100644 index 000000000000..44b0daeaff9b --- /dev/null +++ b/arch/arm/mm/tlb-v3.S | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/tlbv3.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARM architecture version 3 TLB handling functions. | ||
11 | * | ||
12 | * Processors: ARM610, ARM710. | ||
13 | */ | ||
14 | #include <linux/linkage.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <asm/constants.h> | ||
17 | #include <asm/tlbflush.h> | ||
18 | #include "proc-macros.S" | ||
19 | |||
20 | .align 5 | ||
21 | /* | ||
22 | * v3_flush_user_tlb_range(start, end, mm) | ||
23 | * | ||
24 | * Invalidate a range of TLB entries in the specified address space. | ||
25 | * | ||
26 | * - start - range start address | ||
27 | * - end - range end address | ||
28 | * - mm - mm_struct describing address space | ||
29 | */ | ||
30 | .align 5 | ||
31 | ENTRY(v3_flush_user_tlb_range) | ||
32 | vma_vm_mm r2, r2 | ||
33 | act_mm r3 @ get current->active_mm | ||
34 | teq r2, r3 @ == mm ? | ||
35 | movne pc, lr @ no, we dont do anything | ||
36 | ENTRY(v3_flush_kern_tlb_range) | ||
37 | bic r0, r0, #0x0ff | ||
38 | bic r0, r0, #0xf00 | ||
39 | 1: mcr p15, 0, r0, c6, c0, 0 @ invalidate TLB entry | ||
40 | add r0, r0, #PAGE_SZ | ||
41 | cmp r0, r1 | ||
42 | blo 1b | ||
43 | mov pc, lr | ||
44 | |||
45 | __INITDATA | ||
46 | |||
47 | .type v3_tlb_fns, #object | ||
48 | ENTRY(v3_tlb_fns) | ||
49 | .long v3_flush_user_tlb_range | ||
50 | .long v3_flush_kern_tlb_range | ||
51 | .long v3_tlb_flags | ||
52 | .size v3_tlb_fns, . - v3_tlb_fns | ||
diff --git a/arch/arm/mm/tlb-v4.S b/arch/arm/mm/tlb-v4.S new file mode 100644 index 000000000000..db82ee468248 --- /dev/null +++ b/arch/arm/mm/tlb-v4.S | |||
@@ -0,0 +1,65 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/tlbv4.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARM architecture version 4 TLB handling functions. | ||
11 | * These assume a split I/D TLBs, and no write buffer. | ||
12 | * | ||
13 | * Processors: ARM720T | ||
14 | */ | ||
15 | #include <linux/linkage.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <asm/constants.h> | ||
18 | #include <asm/tlbflush.h> | ||
19 | #include "proc-macros.S" | ||
20 | |||
21 | .align 5 | ||
22 | /* | ||
23 | * v4_flush_user_tlb_range(start, end, mm) | ||
24 | * | ||
25 | * Invalidate a range of TLB entries in the specified user address space. | ||
26 | * | ||
27 | * - start - range start address | ||
28 | * - end - range end address | ||
29 | * - mm - mm_struct describing address space | ||
30 | */ | ||
31 | .align 5 | ||
32 | ENTRY(v4_flush_user_tlb_range) | ||
33 | vma_vm_mm ip, r2 | ||
34 | act_mm r3 @ get current->active_mm | ||
35 | eors r3, ip, r3 @ == mm ? | ||
36 | movne pc, lr @ no, we dont do anything | ||
37 | .v4_flush_kern_tlb_range: | ||
38 | bic r0, r0, #0x0ff | ||
39 | bic r0, r0, #0xf00 | ||
40 | 1: mcr p15, 0, r0, c8, c7, 1 @ invalidate TLB entry | ||
41 | add r0, r0, #PAGE_SZ | ||
42 | cmp r0, r1 | ||
43 | blo 1b | ||
44 | mov pc, lr | ||
45 | |||
46 | /* | ||
47 | * v4_flush_kern_tlb_range(start, end) | ||
48 | * | ||
49 | * Invalidate a range of TLB entries in the specified kernel | ||
50 | * address range. | ||
51 | * | ||
52 | * - start - virtual address (may not be aligned) | ||
53 | * - end - virtual address (may not be aligned) | ||
54 | */ | ||
55 | .globl v4_flush_kern_tlb_range | ||
56 | .equ v4_flush_kern_tlb_range, .v4_flush_kern_tlb_range | ||
57 | |||
58 | __INITDATA | ||
59 | |||
60 | .type v4_tlb_fns, #object | ||
61 | ENTRY(v4_tlb_fns) | ||
62 | .long v4_flush_user_tlb_range | ||
63 | .long v4_flush_kern_tlb_range | ||
64 | .long v4_tlb_flags | ||
65 | .size v4_tlb_fns, . - v4_tlb_fns | ||
diff --git a/arch/arm/mm/tlb-v4wb.S b/arch/arm/mm/tlb-v4wb.S new file mode 100644 index 000000000000..7908d5f1f130 --- /dev/null +++ b/arch/arm/mm/tlb-v4wb.S | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/tlbv4wb.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARM architecture version 4 TLB handling functions. | ||
11 | * These assume a split I/D TLBs w/o I TLB entry, with a write buffer. | ||
12 | * | ||
13 | * Processors: SA110 SA1100 SA1110 | ||
14 | */ | ||
15 | #include <linux/linkage.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <asm/constants.h> | ||
18 | #include <asm/tlbflush.h> | ||
19 | #include "proc-macros.S" | ||
20 | |||
21 | .align 5 | ||
22 | /* | ||
23 | * v4wb_flush_user_tlb_range(start, end, mm) | ||
24 | * | ||
25 | * Invalidate a range of TLB entries in the specified address space. | ||
26 | * | ||
27 | * - start - range start address | ||
28 | * - end - range end address | ||
29 | * - mm - mm_struct describing address space | ||
30 | */ | ||
31 | .align 5 | ||
32 | ENTRY(v4wb_flush_user_tlb_range) | ||
33 | vma_vm_mm ip, r2 | ||
34 | act_mm r3 @ get current->active_mm | ||
35 | eors r3, ip, r3 @ == mm ? | ||
36 | movne pc, lr @ no, we dont do anything | ||
37 | vma_vm_flags r2, r2 | ||
38 | mcr p15, 0, r3, c7, c10, 4 @ drain WB | ||
39 | tst r2, #VM_EXEC | ||
40 | mcrne p15, 0, r3, c8, c5, 0 @ invalidate I TLB | ||
41 | bic r0, r0, #0x0ff | ||
42 | bic r0, r0, #0xf00 | ||
43 | 1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry | ||
44 | add r0, r0, #PAGE_SZ | ||
45 | cmp r0, r1 | ||
46 | blo 1b | ||
47 | mov pc, lr | ||
48 | |||
49 | /* | ||
50 | * v4_flush_kern_tlb_range(start, end) | ||
51 | * | ||
52 | * Invalidate a range of TLB entries in the specified kernel | ||
53 | * address range. | ||
54 | * | ||
55 | * - start - virtual address (may not be aligned) | ||
56 | * - end - virtual address (may not be aligned) | ||
57 | */ | ||
58 | ENTRY(v4wb_flush_kern_tlb_range) | ||
59 | mov r3, #0 | ||
60 | mcr p15, 0, r3, c7, c10, 4 @ drain WB | ||
61 | bic r0, r0, #0x0ff | ||
62 | bic r0, r0, #0xf00 | ||
63 | mcr p15, 0, r3, c8, c5, 0 @ invalidate I TLB | ||
64 | 1: mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry | ||
65 | add r0, r0, #PAGE_SZ | ||
66 | cmp r0, r1 | ||
67 | blo 1b | ||
68 | mov pc, lr | ||
69 | |||
70 | __INITDATA | ||
71 | |||
72 | .type v4wb_tlb_fns, #object | ||
73 | ENTRY(v4wb_tlb_fns) | ||
74 | .long v4wb_flush_user_tlb_range | ||
75 | .long v4wb_flush_kern_tlb_range | ||
76 | .long v4wb_tlb_flags | ||
77 | .size v4wb_tlb_fns, . - v4wb_tlb_fns | ||
diff --git a/arch/arm/mm/tlb-v4wbi.S b/arch/arm/mm/tlb-v4wbi.S new file mode 100644 index 000000000000..efbe94bbe1a7 --- /dev/null +++ b/arch/arm/mm/tlb-v4wbi.S | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/tlbv4wbi.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARM architecture version 4 and version 5 TLB handling functions. | ||
11 | * These assume a split I/D TLBs, with a write buffer. | ||
12 | * | ||
13 | * Processors: ARM920 ARM922 ARM925 ARM926 XScale | ||
14 | */ | ||
15 | #include <linux/linkage.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <asm/constants.h> | ||
18 | #include <asm/tlbflush.h> | ||
19 | #include "proc-macros.S" | ||
20 | |||
21 | /* | ||
22 | * v4wb_flush_user_tlb_range(start, end, mm) | ||
23 | * | ||
24 | * Invalidate a range of TLB entries in the specified address space. | ||
25 | * | ||
26 | * - start - range start address | ||
27 | * - end - range end address | ||
28 | * - mm - mm_struct describing address space | ||
29 | */ | ||
30 | .align 5 | ||
31 | ENTRY(v4wbi_flush_user_tlb_range) | ||
32 | vma_vm_mm ip, r2 | ||
33 | act_mm r3 @ get current->active_mm | ||
34 | eors r3, ip, r3 @ == mm ? | ||
35 | movne pc, lr @ no, we dont do anything | ||
36 | mov r3, #0 | ||
37 | mcr p15, 0, r3, c7, c10, 4 @ drain WB | ||
38 | vma_vm_flags r2, r2 | ||
39 | bic r0, r0, #0x0ff | ||
40 | bic r0, r0, #0xf00 | ||
41 | 1: tst r2, #VM_EXEC | ||
42 | mcrne p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry | ||
43 | mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry | ||
44 | add r0, r0, #PAGE_SZ | ||
45 | cmp r0, r1 | ||
46 | blo 1b | ||
47 | mov pc, lr | ||
48 | |||
49 | ENTRY(v4wbi_flush_kern_tlb_range) | ||
50 | mov r3, #0 | ||
51 | mcr p15, 0, r3, c7, c10, 4 @ drain WB | ||
52 | bic r0, r0, #0x0ff | ||
53 | bic r0, r0, #0xf00 | ||
54 | 1: mcr p15, 0, r0, c8, c5, 1 @ invalidate I TLB entry | ||
55 | mcr p15, 0, r0, c8, c6, 1 @ invalidate D TLB entry | ||
56 | add r0, r0, #PAGE_SZ | ||
57 | cmp r0, r1 | ||
58 | blo 1b | ||
59 | mov pc, lr | ||
60 | |||
61 | __INITDATA | ||
62 | |||
63 | .type v4wbi_tlb_fns, #object | ||
64 | ENTRY(v4wbi_tlb_fns) | ||
65 | .long v4wbi_flush_user_tlb_range | ||
66 | .long v4wbi_flush_kern_tlb_range | ||
67 | .long v4wbi_tlb_flags | ||
68 | .size v4wbi_tlb_fns, . - v4wbi_tlb_fns | ||
diff --git a/arch/arm/mm/tlb-v6.S b/arch/arm/mm/tlb-v6.S new file mode 100644 index 000000000000..99ed26e78adf --- /dev/null +++ b/arch/arm/mm/tlb-v6.S | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mm/tlb-v6.S | ||
3 | * | ||
4 | * Copyright (C) 1997-2002 Russell King | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * ARM architecture version 6 TLB handling functions. | ||
11 | * These assume a split I/D TLB. | ||
12 | */ | ||
13 | #include <linux/linkage.h> | ||
14 | #include <asm/constants.h> | ||
15 | #include <asm/page.h> | ||
16 | #include <asm/tlbflush.h> | ||
17 | #include "proc-macros.S" | ||
18 | |||
19 | #define HARVARD_TLB | ||
20 | |||
21 | /* | ||
22 | * v6wbi_flush_user_tlb_range(start, end, vma) | ||
23 | * | ||
24 | * Invalidate a range of TLB entries in the specified address space. | ||
25 | * | ||
26 | * - start - start address (may not be aligned) | ||
27 | * - end - end address (exclusive, may not be aligned) | ||
28 | * - vma - vma_struct describing address range | ||
29 | * | ||
30 | * It is assumed that: | ||
31 | * - the "Invalidate single entry" instruction will invalidate | ||
32 | * both the I and the D TLBs on Harvard-style TLBs | ||
33 | */ | ||
34 | ENTRY(v6wbi_flush_user_tlb_range) | ||
35 | vma_vm_mm r3, r2 @ get vma->vm_mm | ||
36 | mov ip, #0 | ||
37 | mmid r3, r3 @ get vm_mm->context.id | ||
38 | mcr p15, 0, ip, c7, c10, 4 @ drain write buffer | ||
39 | mov r0, r0, lsr #PAGE_SHIFT @ align address | ||
40 | mov r1, r1, lsr #PAGE_SHIFT | ||
41 | asid r3, r3 @ mask ASID | ||
42 | orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA | ||
43 | mov r1, r1, lsl #PAGE_SHIFT | ||
44 | vma_vm_flags r2, r2 @ get vma->vm_flags | ||
45 | 1: | ||
46 | #ifdef HARVARD_TLB | ||
47 | mcr p15, 0, r0, c8, c6, 1 @ TLB invalidate D MVA (was 1) | ||
48 | tst r2, #VM_EXEC @ Executable area ? | ||
49 | mcrne p15, 0, r0, c8, c5, 1 @ TLB invalidate I MVA (was 1) | ||
50 | #else | ||
51 | mcr p15, 0, r0, c8, c7, 1 @ TLB invalidate MVA (was 1) | ||
52 | #endif | ||
53 | add r0, r0, #PAGE_SZ | ||
54 | cmp r0, r1 | ||
55 | blo 1b | ||
56 | mov pc, lr | ||
57 | |||
58 | /* | ||
59 | * v6wbi_flush_kern_tlb_range(start,end) | ||
60 | * | ||
61 | * Invalidate a range of kernel TLB entries | ||
62 | * | ||
63 | * - start - start address (may not be aligned) | ||
64 | * - end - end address (exclusive, may not be aligned) | ||
65 | */ | ||
66 | ENTRY(v6wbi_flush_kern_tlb_range) | ||
67 | mov r2, #0 | ||
68 | mcr p15, 0, r2, c7, c10, 4 @ drain write buffer | ||
69 | mov r0, r0, lsr #PAGE_SHIFT @ align address | ||
70 | mov r1, r1, lsr #PAGE_SHIFT | ||
71 | mov r0, r0, lsl #PAGE_SHIFT | ||
72 | mov r1, r1, lsl #PAGE_SHIFT | ||
73 | 1: | ||
74 | #ifdef HARVARD_TLB | ||
75 | mcr p15, 0, r0, c8, c6, 1 @ TLB invalidate D MVA | ||
76 | mcr p15, 0, r0, c8, c5, 1 @ TLB invalidate I MVA | ||
77 | #else | ||
78 | mcr p15, 0, r0, c8, c7, 1 @ TLB invalidate MVA | ||
79 | #endif | ||
80 | add r0, r0, #PAGE_SZ | ||
81 | cmp r0, r1 | ||
82 | blo 1b | ||
83 | mov pc, lr | ||
84 | |||
85 | .section ".text.init", #alloc, #execinstr | ||
86 | |||
87 | .type v6wbi_tlb_fns, #object | ||
88 | ENTRY(v6wbi_tlb_fns) | ||
89 | .long v6wbi_flush_user_tlb_range | ||
90 | .long v6wbi_flush_kern_tlb_range | ||
91 | .long v6wbi_tlb_flags | ||
92 | .size v6wbi_tlb_fns, . - v6wbi_tlb_fns | ||