aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2008-06-22 16:45:04 -0400
committerLennert Buytenhek <buytenh@marvell.com>2008-06-22 16:45:04 -0400
commit99c6dc117d27d6259214812bd0b113aaf467f600 (patch)
tree0baadd0827fc39f9d2b052305f9447e4b50c5162
parent836a8051d54525e0782f156dcfa3c13d30f22840 (diff)
[ARM] Feroceon: L2 cache support
This patch adds support for the unified Feroceon L2 cache controller as found in e.g. the Marvell Kirkwood and Marvell Discovery Duo families of ARM SoCs. Note that: - Page table walks are outer uncacheable on Kirkwood and Discovery Duo, since the ARMv5 spec provides no way to indicate outer cacheability of page table walks (specifying it in TTBR[4:3] is an ARMv6+ feature). This requires adding L2 cache clean instructions to proc-feroceon.S (dcache_clean_area(), set_pte()) as well as to tlbflush.h ({flush,clean}_pmd_entry()). The latter case is handled by defining a new TLB type (TLB_FEROCEON) which is almost identical to the v4wbi one but provides a TLB_L2CLEAN_FR flag. - The Feroceon L2 cache controller supports L2 range (i.e. 'clean L2 range by MVA' and 'invalidate L2 range by MVA') operations, and this patch uses those range operations for all Linux outer cache operations, as they are faster than the regular per-line operations. L2 range operations are not interruptible on this hardware, which avoids potential livelock issues, but can be bad for interrupt latency, so there is a compile-time tunable (MAX_RANGE_SIZE) which allows you to select the maximum range size to operate on at once. (Valid range is between one cache line and one 4KiB page, and must be a multiple of the line size.) Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
-rw-r--r--arch/arm/mm/Kconfig15
-rw-r--r--arch/arm/mm/Makefile2
-rw-r--r--arch/arm/mm/cache-feroceon-l2.c318
-rw-r--r--arch/arm/mm/proc-feroceon.S20
-rw-r--r--include/asm-arm/plat-orion/cache-feroceon-l2.h11
-rw-r--r--include/asm-arm/tlbflush.h30
6 files changed, 395 insertions, 1 deletions
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 08ca264459bd..3b90051c0e05 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -373,7 +373,7 @@ config CPU_FEROCEON
373 select CPU_CACHE_VIVT 373 select CPU_CACHE_VIVT
374 select CPU_CP15_MMU 374 select CPU_CP15_MMU
375 select CPU_COPY_FEROCEON if MMU 375 select CPU_COPY_FEROCEON if MMU
376 select CPU_TLB_V4WBI if MMU 376 select CPU_TLB_FEROCEON if MMU
377 377
378config CPU_FEROCEON_OLD_ID 378config CPU_FEROCEON_OLD_ID
379 bool "Accept early Feroceon cores with an ARM926 ID" 379 bool "Accept early Feroceon cores with an ARM926 ID"
@@ -551,6 +551,11 @@ config CPU_TLB_V4WBI
551 ARM Architecture Version 4 TLB with writeback cache and invalidate 551 ARM Architecture Version 4 TLB with writeback cache and invalidate
552 instruction cache entry. 552 instruction cache entry.
553 553
554config CPU_TLB_FEROCEON
555 bool
556 help
557 Feroceon TLB (v4wbi with non-outer-cachable page table walks).
558
554config CPU_TLB_V6 559config CPU_TLB_V6
555 bool 560 bool
556 561
@@ -709,6 +714,14 @@ config OUTER_CACHE
709 bool 714 bool
710 default n 715 default n
711 716
717config CACHE_FEROCEON_L2
718 bool "Enable the Feroceon L2 cache controller"
719 depends on FOOBAR
720 default y
721 select OUTER_CACHE
722 help
723 This option enables the Feroceon L2 cache controller.
724
712config CACHE_L2X0 725config CACHE_L2X0
713 bool "Enable the L2x0 outer cache controller" 726 bool "Enable the L2x0 outer cache controller"
714 depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 727 depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 32b2d2d213a6..f64b92557b11 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_CPU_TLB_V3) += tlb-v3.o
46obj-$(CONFIG_CPU_TLB_V4WT) += tlb-v4.o 46obj-$(CONFIG_CPU_TLB_V4WT) += tlb-v4.o
47obj-$(CONFIG_CPU_TLB_V4WB) += tlb-v4wb.o 47obj-$(CONFIG_CPU_TLB_V4WB) += tlb-v4wb.o
48obj-$(CONFIG_CPU_TLB_V4WBI) += tlb-v4wbi.o 48obj-$(CONFIG_CPU_TLB_V4WBI) += tlb-v4wbi.o
49obj-$(CONFIG_CPU_TLB_FEROCEON) += tlb-v4wbi.o # reuse v4wbi TLB functions
49obj-$(CONFIG_CPU_TLB_V6) += tlb-v6.o 50obj-$(CONFIG_CPU_TLB_V6) += tlb-v6.o
50obj-$(CONFIG_CPU_TLB_V7) += tlb-v7.o 51obj-$(CONFIG_CPU_TLB_V7) += tlb-v7.o
51 52
@@ -73,4 +74,5 @@ obj-$(CONFIG_CPU_FEROCEON) += proc-feroceon.o
73obj-$(CONFIG_CPU_V6) += proc-v6.o 74obj-$(CONFIG_CPU_V6) += proc-v6.o
74obj-$(CONFIG_CPU_V7) += proc-v7.o 75obj-$(CONFIG_CPU_V7) += proc-v7.o
75 76
77obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o
76obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o 78obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c
new file mode 100644
index 000000000000..20eec4ba173f
--- /dev/null
+++ b/arch/arm/mm/cache-feroceon-l2.c
@@ -0,0 +1,318 @@
1/*
2 * arch/arm/mm/cache-feroceon-l2.c - Feroceon L2 cache controller support
3 *
4 * Copyright (C) 2008 Marvell Semiconductor
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 *
10 * References:
11 * - Unified Layer 2 Cache for Feroceon CPU Cores,
12 * Document ID MV-S104858-00, Rev. A, October 23 2007.
13 */
14
15#include <linux/init.h>
16#include <asm/cacheflush.h>
17#include <asm/plat-orion/cache-feroceon-l2.h>
18
19
20/*
21 * Low-level cache maintenance operations.
22 *
23 * As well as the regular 'clean/invalidate/flush L2 cache line by
24 * MVA' instructions, the Feroceon L2 cache controller also features
25 * 'clean/invalidate L2 range by MVA' operations.
26 *
27 * Cache range operations are initiated by writing the start and
28 * end addresses to successive cp15 registers, and process every
29 * cache line whose first byte address lies in the inclusive range
30 * [start:end].
31 *
32 * The cache range operations stall the CPU pipeline until completion.
33 *
34 * The range operations require two successive cp15 writes, in
35 * between which we don't want to be preempted.
36 */
37static inline void l2_clean_pa(unsigned long addr)
38{
39 __asm__("mcr p15, 1, %0, c15, c9, 3" : : "r" (addr));
40}
41
42static inline void l2_clean_mva_range(unsigned long start, unsigned long end)
43{
44 unsigned long flags;
45
46 /*
47 * Make sure 'start' and 'end' reference the same page, as
48 * L2 is PIPT and range operations only do a TLB lookup on
49 * the start address.
50 */
51 BUG_ON((start ^ end) & ~(PAGE_SIZE - 1));
52
53 raw_local_irq_save(flags);
54 __asm__("mcr p15, 1, %0, c15, c9, 4" : : "r" (start));
55 __asm__("mcr p15, 1, %0, c15, c9, 5" : : "r" (end));
56 raw_local_irq_restore(flags);
57}
58
59static inline void l2_clean_pa_range(unsigned long start, unsigned long end)
60{
61 l2_clean_mva_range(__phys_to_virt(start), __phys_to_virt(end));
62}
63
64static inline void l2_clean_inv_pa(unsigned long addr)
65{
66 __asm__("mcr p15, 1, %0, c15, c10, 3" : : "r" (addr));
67}
68
69static inline void l2_inv_pa(unsigned long addr)
70{
71 __asm__("mcr p15, 1, %0, c15, c11, 3" : : "r" (addr));
72}
73
74static inline void l2_inv_mva_range(unsigned long start, unsigned long end)
75{
76 unsigned long flags;
77
78 /*
79 * Make sure 'start' and 'end' reference the same page, as
80 * L2 is PIPT and range operations only do a TLB lookup on
81 * the start address.
82 */
83 BUG_ON((start ^ end) & ~(PAGE_SIZE - 1));
84
85 raw_local_irq_save(flags);
86 __asm__("mcr p15, 1, %0, c15, c11, 4" : : "r" (start));
87 __asm__("mcr p15, 1, %0, c15, c11, 5" : : "r" (end));
88 raw_local_irq_restore(flags);
89}
90
91static inline void l2_inv_pa_range(unsigned long start, unsigned long end)
92{
93 l2_inv_mva_range(__phys_to_virt(start), __phys_to_virt(end));
94}
95
96
97/*
98 * Linux primitives.
99 *
100 * Note that the end addresses passed to Linux primitives are
101 * noninclusive, while the hardware cache range operations use
102 * inclusive start and end addresses.
103 */
104#define CACHE_LINE_SIZE 32
105#define MAX_RANGE_SIZE 1024
106
107static int l2_wt_override;
108
109static unsigned long calc_range_end(unsigned long start, unsigned long end)
110{
111 unsigned long range_end;
112
113 BUG_ON(start & (CACHE_LINE_SIZE - 1));
114 BUG_ON(end & (CACHE_LINE_SIZE - 1));
115
116 /*
117 * Try to process all cache lines between 'start' and 'end'.
118 */
119 range_end = end;
120
121 /*
122 * Limit the number of cache lines processed at once,
123 * since cache range operations stall the CPU pipeline
124 * until completion.
125 */
126 if (range_end > start + MAX_RANGE_SIZE)
127 range_end = start + MAX_RANGE_SIZE;
128
129 /*
130 * Cache range operations can't straddle a page boundary.
131 */
132 if (range_end > (start | (PAGE_SIZE - 1)) + 1)
133 range_end = (start | (PAGE_SIZE - 1)) + 1;
134
135 return range_end;
136}
137
138static void feroceon_l2_inv_range(unsigned long start, unsigned long end)
139{
140 /*
141 * Clean and invalidate partial first cache line.
142 */
143 if (start & (CACHE_LINE_SIZE - 1)) {
144 l2_clean_inv_pa(start & ~(CACHE_LINE_SIZE - 1));
145 start = (start | (CACHE_LINE_SIZE - 1)) + 1;
146 }
147
148 /*
149 * Clean and invalidate partial last cache line.
150 */
151 if (end & (CACHE_LINE_SIZE - 1)) {
152 l2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1));
153 end &= ~(CACHE_LINE_SIZE - 1);
154 }
155
156 /*
157 * Invalidate all full cache lines between 'start' and 'end'.
158 */
159 while (start != end) {
160 unsigned long range_end = calc_range_end(start, end);
161 l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE);
162 start = range_end;
163 }
164
165 dsb();
166}
167
168static void feroceon_l2_clean_range(unsigned long start, unsigned long end)
169{
170 /*
171 * If L2 is forced to WT, the L2 will always be clean and we
172 * don't need to do anything here.
173 */
174 if (!l2_wt_override) {
175 start &= ~(CACHE_LINE_SIZE - 1);
176 end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
177 while (start != end) {
178 unsigned long range_end = calc_range_end(start, end);
179 l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE);
180 start = range_end;
181 }
182 }
183
184 dsb();
185}
186
187static void feroceon_l2_flush_range(unsigned long start, unsigned long end)
188{
189 start &= ~(CACHE_LINE_SIZE - 1);
190 end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
191 while (start != end) {
192 unsigned long range_end = calc_range_end(start, end);
193 if (!l2_wt_override)
194 l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE);
195 l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE);
196 start = range_end;
197 }
198
199 dsb();
200}
201
202
203/*
204 * Routines to disable and re-enable the D-cache and I-cache at run
205 * time. These are necessary because the L2 cache can only be enabled
206 * or disabled while the L1 Dcache and Icache are both disabled.
207 */
208static void __init invalidate_and_disable_dcache(void)
209{
210 u32 cr;
211
212 cr = get_cr();
213 if (cr & CR_C) {
214 unsigned long flags;
215
216 raw_local_irq_save(flags);
217 flush_cache_all();
218 set_cr(cr & ~CR_C);
219 raw_local_irq_restore(flags);
220 }
221}
222
223static void __init enable_dcache(void)
224{
225 u32 cr;
226
227 cr = get_cr();
228 if (!(cr & CR_C))
229 set_cr(cr | CR_C);
230}
231
232static void __init __invalidate_icache(void)
233{
234 int dummy;
235
236 __asm__ __volatile__("mcr p15, 0, %0, c7, c5, 0\n" : "=r" (dummy));
237}
238
239static void __init invalidate_and_disable_icache(void)
240{
241 u32 cr;
242
243 cr = get_cr();
244 if (cr & CR_I) {
245 set_cr(cr & ~CR_I);
246 __invalidate_icache();
247 }
248}
249
250static void __init enable_icache(void)
251{
252 u32 cr;
253
254 cr = get_cr();
255 if (!(cr & CR_I))
256 set_cr(cr | CR_I);
257}
258
259static inline u32 read_extra_features(void)
260{
261 u32 u;
262
263 __asm__("mrc p15, 1, %0, c15, c1, 0" : "=r" (u));
264
265 return u;
266}
267
268static inline void write_extra_features(u32 u)
269{
270 __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u));
271}
272
273static void __init disable_l2_prefetch(void)
274{
275 u32 u;
276
277 /*
278 * Read the CPU Extra Features register and verify that the
279 * Disable L2 Prefetch bit is set.
280 */
281 u = read_extra_features();
282 if (!(u & 0x01000000)) {
283 printk(KERN_INFO "Feroceon L2: Disabling L2 prefetch.\n");
284 write_extra_features(u | 0x01000000);
285 }
286}
287
288static void __init enable_l2(void)
289{
290 u32 u;
291
292 u = read_extra_features();
293 if (!(u & 0x00400000)) {
294 printk(KERN_INFO "Feroceon L2: Enabling L2\n");
295
296 invalidate_and_disable_dcache();
297 invalidate_and_disable_icache();
298 write_extra_features(u | 0x00400000);
299 enable_icache();
300 enable_dcache();
301 }
302}
303
304void __init feroceon_l2_init(int __l2_wt_override)
305{
306 l2_wt_override = __l2_wt_override;
307
308 disable_l2_prefetch();
309
310 outer_cache.inv_range = feroceon_l2_inv_range;
311 outer_cache.clean_range = feroceon_l2_clean_range;
312 outer_cache.flush_range = feroceon_l2_flush_range;
313
314 enable_l2();
315
316 printk(KERN_INFO "Feroceon L2: Cache support initialised%s.\n",
317 l2_wt_override ? ", in WT override mode" : "");
318}
diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S
index c279652a98fd..cecf96ee07d5 100644
--- a/arch/arm/mm/proc-feroceon.S
+++ b/arch/arm/mm/proc-feroceon.S
@@ -79,6 +79,13 @@ ENTRY(cpu_feroceon_proc_fin)
79 mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE 79 mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
80 msr cpsr_c, ip 80 msr cpsr_c, ip
81 bl feroceon_flush_kern_cache_all 81 bl feroceon_flush_kern_cache_all
82
83#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH)
84 mov r0, #0
85 mcr p15, 1, r0, c15, c9, 0 @ clean L2
86 mcr p15, 0, r0, c7, c10, 4 @ drain WB
87#endif
88
82 mrc p15, 0, r0, c1, c0, 0 @ ctrl register 89 mrc p15, 0, r0, c1, c0, 0 @ ctrl register
83 bic r0, r0, #0x1000 @ ...i............ 90 bic r0, r0, #0x1000 @ ...i............
84 bic r0, r0, #0x000e @ ............wca. 91 bic r0, r0, #0x000e @ ............wca.
@@ -382,10 +389,20 @@ ENTRY(feroceon_range_cache_fns)
382 389
383 .align 5 390 .align 5
384ENTRY(cpu_feroceon_dcache_clean_area) 391ENTRY(cpu_feroceon_dcache_clean_area)
392#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH)
393 mov r2, r0
394 mov r3, r1
395#endif
3851: mcr p15, 0, r0, c7, c10, 1 @ clean D entry 3961: mcr p15, 0, r0, c7, c10, 1 @ clean D entry
386 add r0, r0, #CACHE_DLINESIZE 397 add r0, r0, #CACHE_DLINESIZE
387 subs r1, r1, #CACHE_DLINESIZE 398 subs r1, r1, #CACHE_DLINESIZE
388 bhi 1b 399 bhi 1b
400#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH)
4011: mcr p15, 1, r2, c15, c9, 1 @ clean L2 entry
402 add r2, r2, #CACHE_DLINESIZE
403 subs r3, r3, #CACHE_DLINESIZE
404 bhi 1b
405#endif
389 mcr p15, 0, r0, c7, c10, 4 @ drain WB 406 mcr p15, 0, r0, c7, c10, 4 @ drain WB
390 mov pc, lr 407 mov pc, lr
391 408
@@ -449,6 +466,9 @@ ENTRY(cpu_feroceon_set_pte_ext)
449 str r2, [r0] @ hardware version 466 str r2, [r0] @ hardware version
450 mov r0, r0 467 mov r0, r0
451 mcr p15, 0, r0, c7, c10, 1 @ clean D entry 468 mcr p15, 0, r0, c7, c10, 1 @ clean D entry
469#if defined(CONFIG_CACHE_FEROCEON_L2) && !defined(CONFIG_L2_CACHE_WRITETHROUGH)
470 mcr p15, 1, r0, c15, c9, 1 @ clean L2 entry
471#endif
452 mcr p15, 0, r0, c7, c10, 4 @ drain WB 472 mcr p15, 0, r0, c7, c10, 4 @ drain WB
453#endif 473#endif
454 mov pc, lr 474 mov pc, lr
diff --git a/include/asm-arm/plat-orion/cache-feroceon-l2.h b/include/asm-arm/plat-orion/cache-feroceon-l2.h
new file mode 100644
index 000000000000..ba4e016d3ec0
--- /dev/null
+++ b/include/asm-arm/plat-orion/cache-feroceon-l2.h
@@ -0,0 +1,11 @@
1/*
2 * include/asm-arm/plat-orion/cache-feroceon-l2.h
3 *
4 * Copyright (C) 2008 Marvell Semiconductor
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11extern void __init feroceon_l2_init(int l2_wt_override);
diff --git a/include/asm-arm/tlbflush.h b/include/asm-arm/tlbflush.h
index 8c6bc1bb9d1a..909656c747ef 100644
--- a/include/asm-arm/tlbflush.h
+++ b/include/asm-arm/tlbflush.h
@@ -39,6 +39,7 @@
39#define TLB_V6_D_ASID (1 << 17) 39#define TLB_V6_D_ASID (1 << 17)
40#define TLB_V6_I_ASID (1 << 18) 40#define TLB_V6_I_ASID (1 << 18)
41 41
42#define TLB_L2CLEAN_FR (1 << 29) /* Feroceon */
42#define TLB_DCLEAN (1 << 30) 43#define TLB_DCLEAN (1 << 30)
43#define TLB_WB (1 << 31) 44#define TLB_WB (1 << 31)
44 45
@@ -51,6 +52,7 @@
51 * v4 - ARMv4 without write buffer 52 * v4 - ARMv4 without write buffer
52 * v4wb - ARMv4 with write buffer without I TLB flush entry instruction 53 * v4wb - ARMv4 with write buffer without I TLB flush entry instruction
53 * v4wbi - ARMv4 with write buffer with I TLB flush entry instruction 54 * v4wbi - ARMv4 with write buffer with I TLB flush entry instruction
55 * fr - Feroceon (v4wbi with non-outer-cacheable page table walks)
54 * v6wbi - ARMv6 with write buffer with I TLB flush entry instruction 56 * v6wbi - ARMv6 with write buffer with I TLB flush entry instruction
55 */ 57 */
56#undef _TLB 58#undef _TLB
@@ -103,6 +105,23 @@
103# define v4wbi_always_flags (-1UL) 105# define v4wbi_always_flags (-1UL)
104#endif 106#endif
105 107
108#define fr_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_L2CLEAN_FR | \
109 TLB_V4_I_FULL | TLB_V4_D_FULL | \
110 TLB_V4_I_PAGE | TLB_V4_D_PAGE)
111
112#ifdef CONFIG_CPU_TLB_FEROCEON
113# define fr_possible_flags fr_tlb_flags
114# define fr_always_flags fr_tlb_flags
115# ifdef _TLB
116# define MULTI_TLB 1
117# else
118# define _TLB v4wbi
119# endif
120#else
121# define fr_possible_flags 0
122# define fr_always_flags (-1UL)
123#endif
124
106#define v4wb_tlb_flags (TLB_WB | TLB_DCLEAN | \ 125#define v4wb_tlb_flags (TLB_WB | TLB_DCLEAN | \
107 TLB_V4_I_FULL | TLB_V4_D_FULL | \ 126 TLB_V4_I_FULL | TLB_V4_D_FULL | \
108 TLB_V4_D_PAGE) 127 TLB_V4_D_PAGE)
@@ -245,12 +264,14 @@ extern struct cpu_tlb_fns cpu_tlb;
245#define possible_tlb_flags (v3_possible_flags | \ 264#define possible_tlb_flags (v3_possible_flags | \
246 v4_possible_flags | \ 265 v4_possible_flags | \
247 v4wbi_possible_flags | \ 266 v4wbi_possible_flags | \
267 fr_possible_flags | \
248 v4wb_possible_flags | \ 268 v4wb_possible_flags | \
249 v6wbi_possible_flags) 269 v6wbi_possible_flags)
250 270
251#define always_tlb_flags (v3_always_flags & \ 271#define always_tlb_flags (v3_always_flags & \
252 v4_always_flags & \ 272 v4_always_flags & \
253 v4wbi_always_flags & \ 273 v4wbi_always_flags & \
274 fr_always_flags & \
254 v4wb_always_flags & \ 275 v4wb_always_flags & \
255 v6wbi_always_flags) 276 v6wbi_always_flags)
256 277
@@ -417,6 +438,11 @@ static inline void flush_pmd_entry(pmd_t *pmd)
417 if (tlb_flag(TLB_DCLEAN)) 438 if (tlb_flag(TLB_DCLEAN))
418 asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd" 439 asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd"
419 : : "r" (pmd) : "cc"); 440 : : "r" (pmd) : "cc");
441
442 if (tlb_flag(TLB_L2CLEAN_FR))
443 asm("mcr p15, 1, %0, c15, c9, 1 @ L2 flush_pmd"
444 : : "r" (pmd) : "cc");
445
420 if (tlb_flag(TLB_WB)) 446 if (tlb_flag(TLB_WB))
421 dsb(); 447 dsb();
422} 448}
@@ -428,6 +454,10 @@ static inline void clean_pmd_entry(pmd_t *pmd)
428 if (tlb_flag(TLB_DCLEAN)) 454 if (tlb_flag(TLB_DCLEAN))
429 asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd" 455 asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pmd"
430 : : "r" (pmd) : "cc"); 456 : : "r" (pmd) : "cc");
457
458 if (tlb_flag(TLB_L2CLEAN_FR))
459 asm("mcr p15, 1, %0, c15, c9, 1 @ L2 flush_pmd"
460 : : "r" (pmd) : "cc");
431} 461}
432 462
433#undef tlb_flag 463#undef tlb_flag