xref: /openbmc/linux/arch/arm/mm/cache-feroceon-l2.c (revision 384740dc)
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 <plat/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 }
319