Merge branch 'for_linus' of git://git.infradead.org/~dedekind/ubifs-2.6
[pandora-kernel.git] / arch / arm / mm / cache-feroceon-l2.c
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  */
37 static inline void l2_clean_pa(unsigned long addr)
38 {
39         __asm__("mcr p15, 1, %0, c15, c9, 3" : : "r" (addr));
40 }
41
42 static 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
59 static 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
64 static inline void l2_clean_inv_pa(unsigned long addr)
65 {
66         __asm__("mcr p15, 1, %0, c15, c10, 3" : : "r" (addr));
67 }
68
69 static inline void l2_inv_pa(unsigned long addr)
70 {
71         __asm__("mcr p15, 1, %0, c15, c11, 3" : : "r" (addr));
72 }
73
74 static 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
91 static 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
107 static int l2_wt_override;
108
109 static 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
138 static 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
168 static 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
187 static 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  */
208 static 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
223 static 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
232 static 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
239 static 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
250 static 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
259 static 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
268 static inline void write_extra_features(u32 u)
269 {
270         __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u));
271 }
272
273 static 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
288 static 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
304 void __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 }