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