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 <linux/highmem.h>
17 #include <asm/cacheflush.h>
18 #include <plat/cache-feroceon-l2.h>
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 
l2_get_va(unsigned long paddr)38 static inline unsigned long l2_get_va(unsigned long paddr)
39 {
40 #ifdef CONFIG_HIGHMEM
41 	/*
42 	 * Because range ops can't be done on physical addresses,
43 	 * we simply install a virtual mapping for it only for the
44 	 * TLB lookup to occur, hence no need to flush the untouched
45 	 * memory mapping afterwards (note: a cache flush may happen
46 	 * in some circumstances depending on the path taken in kunmap_atomic).
47 	 */
48 	void *vaddr = kmap_atomic_pfn(paddr >> PAGE_SHIFT);
49 	return (unsigned long)vaddr + (paddr & ~PAGE_MASK);
50 #else
51 	return __phys_to_virt(paddr);
52 #endif
53 }
54 
l2_put_va(unsigned long vaddr)55 static inline void l2_put_va(unsigned long vaddr)
56 {
57 #ifdef CONFIG_HIGHMEM
58 	kunmap_atomic((void *)vaddr);
59 #endif
60 }
61 
l2_clean_pa(unsigned long addr)62 static inline void l2_clean_pa(unsigned long addr)
63 {
64 	__asm__("mcr p15, 1, %0, c15, c9, 3" : : "r" (addr));
65 }
66 
l2_clean_pa_range(unsigned long start,unsigned long end)67 static inline void l2_clean_pa_range(unsigned long start, unsigned long end)
68 {
69 	unsigned long va_start, va_end, flags;
70 
71 	/*
72 	 * Make sure 'start' and 'end' reference the same page, as
73 	 * L2 is PIPT and range operations only do a TLB lookup on
74 	 * the start address.
75 	 */
76 	BUG_ON((start ^ end) >> PAGE_SHIFT);
77 
78 	va_start = l2_get_va(start);
79 	va_end = va_start + (end - start);
80 	raw_local_irq_save(flags);
81 	__asm__("mcr p15, 1, %0, c15, c9, 4\n\t"
82 		"mcr p15, 1, %1, c15, c9, 5"
83 		: : "r" (va_start), "r" (va_end));
84 	raw_local_irq_restore(flags);
85 	l2_put_va(va_start);
86 }
87 
l2_clean_inv_pa(unsigned long addr)88 static inline void l2_clean_inv_pa(unsigned long addr)
89 {
90 	__asm__("mcr p15, 1, %0, c15, c10, 3" : : "r" (addr));
91 }
92 
l2_inv_pa(unsigned long addr)93 static inline void l2_inv_pa(unsigned long addr)
94 {
95 	__asm__("mcr p15, 1, %0, c15, c11, 3" : : "r" (addr));
96 }
97 
l2_inv_pa_range(unsigned long start,unsigned long end)98 static inline void l2_inv_pa_range(unsigned long start, unsigned long end)
99 {
100 	unsigned long va_start, va_end, flags;
101 
102 	/*
103 	 * Make sure 'start' and 'end' reference the same page, as
104 	 * L2 is PIPT and range operations only do a TLB lookup on
105 	 * the start address.
106 	 */
107 	BUG_ON((start ^ end) >> PAGE_SHIFT);
108 
109 	va_start = l2_get_va(start);
110 	va_end = va_start + (end - start);
111 	raw_local_irq_save(flags);
112 	__asm__("mcr p15, 1, %0, c15, c11, 4\n\t"
113 		"mcr p15, 1, %1, c15, c11, 5"
114 		: : "r" (va_start), "r" (va_end));
115 	raw_local_irq_restore(flags);
116 	l2_put_va(va_start);
117 }
118 
l2_inv_all(void)119 static inline void l2_inv_all(void)
120 {
121 	__asm__("mcr p15, 1, %0, c15, c11, 0" : : "r" (0));
122 }
123 
124 /*
125  * Linux primitives.
126  *
127  * Note that the end addresses passed to Linux primitives are
128  * noninclusive, while the hardware cache range operations use
129  * inclusive start and end addresses.
130  */
131 #define CACHE_LINE_SIZE		32
132 #define MAX_RANGE_SIZE		1024
133 
134 static int l2_wt_override;
135 
calc_range_end(unsigned long start,unsigned long end)136 static unsigned long calc_range_end(unsigned long start, unsigned long end)
137 {
138 	unsigned long range_end;
139 
140 	BUG_ON(start & (CACHE_LINE_SIZE - 1));
141 	BUG_ON(end & (CACHE_LINE_SIZE - 1));
142 
143 	/*
144 	 * Try to process all cache lines between 'start' and 'end'.
145 	 */
146 	range_end = end;
147 
148 	/*
149 	 * Limit the number of cache lines processed at once,
150 	 * since cache range operations stall the CPU pipeline
151 	 * until completion.
152 	 */
153 	if (range_end > start + MAX_RANGE_SIZE)
154 		range_end = start + MAX_RANGE_SIZE;
155 
156 	/*
157 	 * Cache range operations can't straddle a page boundary.
158 	 */
159 	if (range_end > (start | (PAGE_SIZE - 1)) + 1)
160 		range_end = (start | (PAGE_SIZE - 1)) + 1;
161 
162 	return range_end;
163 }
164 
feroceon_l2_inv_range(unsigned long start,unsigned long end)165 static void feroceon_l2_inv_range(unsigned long start, unsigned long end)
166 {
167 	/*
168 	 * Clean and invalidate partial first cache line.
169 	 */
170 	if (start & (CACHE_LINE_SIZE - 1)) {
171 		l2_clean_inv_pa(start & ~(CACHE_LINE_SIZE - 1));
172 		start = (start | (CACHE_LINE_SIZE - 1)) + 1;
173 	}
174 
175 	/*
176 	 * Clean and invalidate partial last cache line.
177 	 */
178 	if (start < end && end & (CACHE_LINE_SIZE - 1)) {
179 		l2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1));
180 		end &= ~(CACHE_LINE_SIZE - 1);
181 	}
182 
183 	/*
184 	 * Invalidate all full cache lines between 'start' and 'end'.
185 	 */
186 	while (start < end) {
187 		unsigned long range_end = calc_range_end(start, end);
188 		l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE);
189 		start = range_end;
190 	}
191 
192 	dsb();
193 }
194 
feroceon_l2_clean_range(unsigned long start,unsigned long end)195 static void feroceon_l2_clean_range(unsigned long start, unsigned long end)
196 {
197 	/*
198 	 * If L2 is forced to WT, the L2 will always be clean and we
199 	 * don't need to do anything here.
200 	 */
201 	if (!l2_wt_override) {
202 		start &= ~(CACHE_LINE_SIZE - 1);
203 		end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
204 		while (start != end) {
205 			unsigned long range_end = calc_range_end(start, end);
206 			l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE);
207 			start = range_end;
208 		}
209 	}
210 
211 	dsb();
212 }
213 
feroceon_l2_flush_range(unsigned long start,unsigned long end)214 static void feroceon_l2_flush_range(unsigned long start, unsigned long end)
215 {
216 	start &= ~(CACHE_LINE_SIZE - 1);
217 	end = (end + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
218 	while (start != end) {
219 		unsigned long range_end = calc_range_end(start, end);
220 		if (!l2_wt_override)
221 			l2_clean_pa_range(start, range_end - CACHE_LINE_SIZE);
222 		l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE);
223 		start = range_end;
224 	}
225 
226 	dsb();
227 }
228 
229 
230 /*
231  * Routines to disable and re-enable the D-cache and I-cache at run
232  * time.  These are necessary because the L2 cache can only be enabled
233  * or disabled while the L1 Dcache and Icache are both disabled.
234  */
flush_and_disable_dcache(void)235 static int __init flush_and_disable_dcache(void)
236 {
237 	u32 cr;
238 
239 	cr = get_cr();
240 	if (cr & CR_C) {
241 		unsigned long flags;
242 
243 		raw_local_irq_save(flags);
244 		flush_cache_all();
245 		set_cr(cr & ~CR_C);
246 		raw_local_irq_restore(flags);
247 		return 1;
248 	}
249 	return 0;
250 }
251 
enable_dcache(void)252 static void __init enable_dcache(void)
253 {
254 	u32 cr;
255 
256 	cr = get_cr();
257 	set_cr(cr | CR_C);
258 }
259 
__invalidate_icache(void)260 static void __init __invalidate_icache(void)
261 {
262 	__asm__("mcr p15, 0, %0, c7, c5, 0" : : "r" (0));
263 }
264 
invalidate_and_disable_icache(void)265 static int __init invalidate_and_disable_icache(void)
266 {
267 	u32 cr;
268 
269 	cr = get_cr();
270 	if (cr & CR_I) {
271 		set_cr(cr & ~CR_I);
272 		__invalidate_icache();
273 		return 1;
274 	}
275 	return 0;
276 }
277 
enable_icache(void)278 static void __init enable_icache(void)
279 {
280 	u32 cr;
281 
282 	cr = get_cr();
283 	set_cr(cr | CR_I);
284 }
285 
read_extra_features(void)286 static inline u32 read_extra_features(void)
287 {
288 	u32 u;
289 
290 	__asm__("mrc p15, 1, %0, c15, c1, 0" : "=r" (u));
291 
292 	return u;
293 }
294 
write_extra_features(u32 u)295 static inline void write_extra_features(u32 u)
296 {
297 	__asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u));
298 }
299 
disable_l2_prefetch(void)300 static void __init disable_l2_prefetch(void)
301 {
302 	u32 u;
303 
304 	/*
305 	 * Read the CPU Extra Features register and verify that the
306 	 * Disable L2 Prefetch bit is set.
307 	 */
308 	u = read_extra_features();
309 	if (!(u & 0x01000000)) {
310 		printk(KERN_INFO "Feroceon L2: Disabling L2 prefetch.\n");
311 		write_extra_features(u | 0x01000000);
312 	}
313 }
314 
enable_l2(void)315 static void __init enable_l2(void)
316 {
317 	u32 u;
318 
319 	u = read_extra_features();
320 	if (!(u & 0x00400000)) {
321 		int i, d;
322 
323 		printk(KERN_INFO "Feroceon L2: Enabling L2\n");
324 
325 		d = flush_and_disable_dcache();
326 		i = invalidate_and_disable_icache();
327 		l2_inv_all();
328 		write_extra_features(u | 0x00400000);
329 		if (i)
330 			enable_icache();
331 		if (d)
332 			enable_dcache();
333 	}
334 }
335 
feroceon_l2_init(int __l2_wt_override)336 void __init feroceon_l2_init(int __l2_wt_override)
337 {
338 	l2_wt_override = __l2_wt_override;
339 
340 	disable_l2_prefetch();
341 
342 	outer_cache.inv_range = feroceon_l2_inv_range;
343 	outer_cache.clean_range = feroceon_l2_clean_range;
344 	outer_cache.flush_range = feroceon_l2_flush_range;
345 
346 	enable_l2();
347 
348 	printk(KERN_INFO "Feroceon L2: Cache support initialised%s.\n",
349 			 l2_wt_override ? ", in WT override mode" : "");
350 }
351