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 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 55 static inline void l2_put_va(unsigned long vaddr) 56 { 57 #ifdef CONFIG_HIGHMEM 58 kunmap_atomic((void *)vaddr); 59 #endif 60 } 61 62 static inline void l2_clean_pa(unsigned long addr) 63 { 64 __asm__("mcr p15, 1, %0, c15, c9, 3" : : "r" (addr)); 65 } 66 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 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 93 static inline void l2_inv_pa(unsigned long addr) 94 { 95 __asm__("mcr p15, 1, %0, c15, c11, 3" : : "r" (addr)); 96 } 97 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 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 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 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 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 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 */ 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 252 static void __init enable_dcache(void) 253 { 254 u32 cr; 255 256 cr = get_cr(); 257 set_cr(cr | CR_C); 258 } 259 260 static void __init __invalidate_icache(void) 261 { 262 __asm__("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); 263 } 264 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 278 static void __init enable_icache(void) 279 { 280 u32 cr; 281 282 cr = get_cr(); 283 set_cr(cr | CR_I); 284 } 285 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 295 static inline void write_extra_features(u32 u) 296 { 297 __asm__("mcr p15, 1, %0, c15, c1, 0" : : "r" (u)); 298 } 299 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 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 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