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