12c451f78SAneesh V /* 22c451f78SAneesh V * (C) Copyright 2010 32c451f78SAneesh V * Texas Instruments, <www.ti.com> 42c451f78SAneesh V * Aneesh V <aneesh@ti.com> 52c451f78SAneesh V * 62c451f78SAneesh V * See file CREDITS for list of people who contributed to this 72c451f78SAneesh V * project. 82c451f78SAneesh V * 92c451f78SAneesh V * This program is free software; you can redistribute it and/or 102c451f78SAneesh V * modify it under the terms of the GNU General Public License as 112c451f78SAneesh V * published by the Free Software Foundation; either version 2 of 122c451f78SAneesh V * the License, or (at your option) any later version. 132c451f78SAneesh V * 142c451f78SAneesh V * This program is distributed in the hope that it will be useful, 152c451f78SAneesh V * but WITHOUT ANY WARRANTY; without even the implied warranty of 162c451f78SAneesh V * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 172c451f78SAneesh V * GNU General Public License for more details. 182c451f78SAneesh V * 192c451f78SAneesh V * You should have received a copy of the GNU General Public License 202c451f78SAneesh V * along with this program; if not, write to the Free Software 212c451f78SAneesh V * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 222c451f78SAneesh V * MA 02111-1307 USA 232c451f78SAneesh V */ 242c451f78SAneesh V #include <linux/types.h> 252c451f78SAneesh V #include <common.h> 262c451f78SAneesh V #include <asm/armv7.h> 272c451f78SAneesh V #include <asm/utils.h> 282c451f78SAneesh V 292c451f78SAneesh V #define ARMV7_DCACHE_INVAL_ALL 1 302c451f78SAneesh V #define ARMV7_DCACHE_CLEAN_INVAL_ALL 2 312c451f78SAneesh V #define ARMV7_DCACHE_INVAL_RANGE 3 322c451f78SAneesh V #define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4 332c451f78SAneesh V 342c451f78SAneesh V #ifndef CONFIG_SYS_DCACHE_OFF 352c451f78SAneesh V /* 362c451f78SAneesh V * Write the level and type you want to Cache Size Selection Register(CSSELR) 372c451f78SAneesh V * to get size details from Current Cache Size ID Register(CCSIDR) 382c451f78SAneesh V */ 392c451f78SAneesh V static void set_csselr(u32 level, u32 type) 402c451f78SAneesh V { u32 csselr = level << 1 | type; 412c451f78SAneesh V 422c451f78SAneesh V /* Write to Cache Size Selection Register(CSSELR) */ 432c451f78SAneesh V asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr)); 442c451f78SAneesh V } 452c451f78SAneesh V 462c451f78SAneesh V static u32 get_ccsidr(void) 472c451f78SAneesh V { 482c451f78SAneesh V u32 ccsidr; 492c451f78SAneesh V 502c451f78SAneesh V /* Read current CP15 Cache Size ID Register */ 512c451f78SAneesh V asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr)); 522c451f78SAneesh V return ccsidr; 532c451f78SAneesh V } 542c451f78SAneesh V 552c451f78SAneesh V static u32 get_clidr(void) 562c451f78SAneesh V { 572c451f78SAneesh V u32 clidr; 582c451f78SAneesh V 592c451f78SAneesh V /* Read current CP15 Cache Level ID Register */ 602c451f78SAneesh V asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr)); 612c451f78SAneesh V return clidr; 622c451f78SAneesh V } 632c451f78SAneesh V 642c451f78SAneesh V static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, 652c451f78SAneesh V u32 num_ways, u32 way_shift, 662c451f78SAneesh V u32 log2_line_len) 672c451f78SAneesh V { 682c451f78SAneesh V int way, set, setway; 692c451f78SAneesh V 702c451f78SAneesh V /* 712c451f78SAneesh V * For optimal assembly code: 722c451f78SAneesh V * a. count down 732c451f78SAneesh V * b. have bigger loop inside 742c451f78SAneesh V */ 752c451f78SAneesh V for (way = num_ways - 1; way >= 0 ; way--) { 762c451f78SAneesh V for (set = num_sets - 1; set >= 0; set--) { 772c451f78SAneesh V setway = (level << 1) | (set << log2_line_len) | 782c451f78SAneesh V (way << way_shift); 792c451f78SAneesh V /* Invalidate data/unified cache line by set/way */ 802c451f78SAneesh V asm volatile (" mcr p15, 0, %0, c7, c6, 2" 812c451f78SAneesh V : : "r" (setway)); 822c451f78SAneesh V } 832c451f78SAneesh V } 84882f80b9SAneesh V /* DSB to make sure the operation is complete */ 85882f80b9SAneesh V CP15DSB; 862c451f78SAneesh V } 872c451f78SAneesh V 882c451f78SAneesh V static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, 892c451f78SAneesh V u32 num_ways, u32 way_shift, 902c451f78SAneesh V u32 log2_line_len) 912c451f78SAneesh V { 922c451f78SAneesh V int way, set, setway; 932c451f78SAneesh V 942c451f78SAneesh V /* 952c451f78SAneesh V * For optimal assembly code: 962c451f78SAneesh V * a. count down 972c451f78SAneesh V * b. have bigger loop inside 982c451f78SAneesh V */ 992c451f78SAneesh V for (way = num_ways - 1; way >= 0 ; way--) { 1002c451f78SAneesh V for (set = num_sets - 1; set >= 0; set--) { 1012c451f78SAneesh V setway = (level << 1) | (set << log2_line_len) | 1022c451f78SAneesh V (way << way_shift); 1032c451f78SAneesh V /* 1042c451f78SAneesh V * Clean & Invalidate data/unified 1052c451f78SAneesh V * cache line by set/way 1062c451f78SAneesh V */ 1072c451f78SAneesh V asm volatile (" mcr p15, 0, %0, c7, c14, 2" 1082c451f78SAneesh V : : "r" (setway)); 1092c451f78SAneesh V } 1102c451f78SAneesh V } 111882f80b9SAneesh V /* DSB to make sure the operation is complete */ 112882f80b9SAneesh V CP15DSB; 1132c451f78SAneesh V } 1142c451f78SAneesh V 1152c451f78SAneesh V static void v7_maint_dcache_level_setway(u32 level, u32 operation) 1162c451f78SAneesh V { 1172c451f78SAneesh V u32 ccsidr; 1182c451f78SAneesh V u32 num_sets, num_ways, log2_line_len, log2_num_ways; 1192c451f78SAneesh V u32 way_shift; 1202c451f78SAneesh V 1212c451f78SAneesh V set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED); 1222c451f78SAneesh V 1232c451f78SAneesh V ccsidr = get_ccsidr(); 1242c451f78SAneesh V 1252c451f78SAneesh V log2_line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> 1262c451f78SAneesh V CCSIDR_LINE_SIZE_OFFSET) + 2; 1272c451f78SAneesh V /* Converting from words to bytes */ 1282c451f78SAneesh V log2_line_len += 2; 1292c451f78SAneesh V 1302c451f78SAneesh V num_ways = ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >> 1312c451f78SAneesh V CCSIDR_ASSOCIATIVITY_OFFSET) + 1; 1322c451f78SAneesh V num_sets = ((ccsidr & CCSIDR_NUM_SETS_MASK) >> 1332c451f78SAneesh V CCSIDR_NUM_SETS_OFFSET) + 1; 1342c451f78SAneesh V /* 1352c451f78SAneesh V * According to ARMv7 ARM number of sets and number of ways need 1362c451f78SAneesh V * not be a power of 2 1372c451f78SAneesh V */ 1382c451f78SAneesh V log2_num_ways = log_2_n_round_up(num_ways); 1392c451f78SAneesh V 1402c451f78SAneesh V way_shift = (32 - log2_num_ways); 1412c451f78SAneesh V if (operation == ARMV7_DCACHE_INVAL_ALL) { 1422c451f78SAneesh V v7_inval_dcache_level_setway(level, num_sets, num_ways, 1432c451f78SAneesh V way_shift, log2_line_len); 1442c451f78SAneesh V } else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL) { 1452c451f78SAneesh V v7_clean_inval_dcache_level_setway(level, num_sets, num_ways, 1462c451f78SAneesh V way_shift, log2_line_len); 1472c451f78SAneesh V } 1482c451f78SAneesh V } 1492c451f78SAneesh V 1502c451f78SAneesh V static void v7_maint_dcache_all(u32 operation) 1512c451f78SAneesh V { 1522c451f78SAneesh V u32 level, cache_type, level_start_bit = 0; 1532c451f78SAneesh V 1542c451f78SAneesh V u32 clidr = get_clidr(); 1552c451f78SAneesh V 1562c451f78SAneesh V for (level = 0; level < 7; level++) { 1572c451f78SAneesh V cache_type = (clidr >> level_start_bit) & 0x7; 1582c451f78SAneesh V if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) || 1592c451f78SAneesh V (cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) || 1602c451f78SAneesh V (cache_type == ARMV7_CLIDR_CTYPE_UNIFIED)) 1612c451f78SAneesh V v7_maint_dcache_level_setway(level, operation); 1622c451f78SAneesh V level_start_bit += 3; 1632c451f78SAneesh V } 1642c451f78SAneesh V } 1652c451f78SAneesh V 1662c451f78SAneesh V static void v7_dcache_clean_inval_range(u32 start, 1672c451f78SAneesh V u32 stop, u32 line_len) 1682c451f78SAneesh V { 1692c451f78SAneesh V u32 mva; 1702c451f78SAneesh V 1712c451f78SAneesh V /* Align start to cache line boundary */ 1722c451f78SAneesh V start &= ~(line_len - 1); 1732c451f78SAneesh V for (mva = start; mva < stop; mva = mva + line_len) { 1742c451f78SAneesh V /* DCCIMVAC - Clean & Invalidate data cache by MVA to PoC */ 1752c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva)); 1762c451f78SAneesh V } 1772c451f78SAneesh V } 1782c451f78SAneesh V 1792c451f78SAneesh V static void v7_dcache_inval_range(u32 start, u32 stop, u32 line_len) 1802c451f78SAneesh V { 1812c451f78SAneesh V u32 mva; 1822c451f78SAneesh V 1832c451f78SAneesh V /* 184cabe2878SAneesh V * If start address is not aligned to cache-line do not 185cabe2878SAneesh V * invalidate the first cache-line 1862c451f78SAneesh V */ 1872c451f78SAneesh V if (start & (line_len - 1)) { 188cabe2878SAneesh V printf("ERROR: %s - start address is not aligned - 0x%08x\n", 189cabe2878SAneesh V __func__, start); 1902c451f78SAneesh V /* move to next cache line */ 1912c451f78SAneesh V start = (start + line_len - 1) & ~(line_len - 1); 1922c451f78SAneesh V } 1932c451f78SAneesh V 1942c451f78SAneesh V /* 195cabe2878SAneesh V * If stop address is not aligned to cache-line do not 196cabe2878SAneesh V * invalidate the last cache-line 1972c451f78SAneesh V */ 1982c451f78SAneesh V if (stop & (line_len - 1)) { 199cabe2878SAneesh V printf("ERROR: %s - stop address is not aligned - 0x%08x\n", 200cabe2878SAneesh V __func__, stop); 2012c451f78SAneesh V /* align to the beginning of this cache line */ 2022c451f78SAneesh V stop &= ~(line_len - 1); 2032c451f78SAneesh V } 2042c451f78SAneesh V 2052c451f78SAneesh V for (mva = start; mva < stop; mva = mva + line_len) { 2062c451f78SAneesh V /* DCIMVAC - Invalidate data cache by MVA to PoC */ 2072c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva)); 2082c451f78SAneesh V } 2092c451f78SAneesh V } 2102c451f78SAneesh V 2112c451f78SAneesh V static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op) 2122c451f78SAneesh V { 2132c451f78SAneesh V u32 line_len, ccsidr; 2142c451f78SAneesh V 2152c451f78SAneesh V ccsidr = get_ccsidr(); 2162c451f78SAneesh V line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> 2172c451f78SAneesh V CCSIDR_LINE_SIZE_OFFSET) + 2; 2182c451f78SAneesh V /* Converting from words to bytes */ 2192c451f78SAneesh V line_len += 2; 2202c451f78SAneesh V /* converting from log2(linelen) to linelen */ 2212c451f78SAneesh V line_len = 1 << line_len; 2222c451f78SAneesh V 2232c451f78SAneesh V switch (range_op) { 2242c451f78SAneesh V case ARMV7_DCACHE_CLEAN_INVAL_RANGE: 2252c451f78SAneesh V v7_dcache_clean_inval_range(start, stop, line_len); 2262c451f78SAneesh V break; 2272c451f78SAneesh V case ARMV7_DCACHE_INVAL_RANGE: 2282c451f78SAneesh V v7_dcache_inval_range(start, stop, line_len); 2292c451f78SAneesh V break; 2302c451f78SAneesh V } 2312c451f78SAneesh V 232882f80b9SAneesh V /* DSB to make sure the operation is complete */ 233882f80b9SAneesh V CP15DSB; 2342c451f78SAneesh V } 2352c451f78SAneesh V 2362c451f78SAneesh V /* Invalidate TLB */ 2372c451f78SAneesh V static void v7_inval_tlb(void) 2382c451f78SAneesh V { 2392c451f78SAneesh V /* Invalidate entire unified TLB */ 2402c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)); 2412c451f78SAneesh V /* Invalidate entire data TLB */ 2422c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0)); 2432c451f78SAneesh V /* Invalidate entire instruction TLB */ 2442c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0)); 2452c451f78SAneesh V /* Full system DSB - make sure that the invalidation is complete */ 2462c451f78SAneesh V CP15DSB; 2472c451f78SAneesh V /* Full system ISB - make sure the instruction stream sees it */ 2482c451f78SAneesh V CP15ISB; 2492c451f78SAneesh V } 2502c451f78SAneesh V 2512c451f78SAneesh V void invalidate_dcache_all(void) 2522c451f78SAneesh V { 2532c451f78SAneesh V v7_maint_dcache_all(ARMV7_DCACHE_INVAL_ALL); 2542c451f78SAneesh V 2552c451f78SAneesh V v7_outer_cache_inval_all(); 2562c451f78SAneesh V } 2572c451f78SAneesh V 2582c451f78SAneesh V /* 2592c451f78SAneesh V * Performs a clean & invalidation of the entire data cache 2602c451f78SAneesh V * at all levels 2612c451f78SAneesh V */ 2622c451f78SAneesh V void flush_dcache_all(void) 2632c451f78SAneesh V { 2642c451f78SAneesh V v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL); 2652c451f78SAneesh V 2662c451f78SAneesh V v7_outer_cache_flush_all(); 2672c451f78SAneesh V } 2682c451f78SAneesh V 2692c451f78SAneesh V /* 2702c451f78SAneesh V * Invalidates range in all levels of D-cache/unified cache used: 2712c451f78SAneesh V * Affects the range [start, stop - 1] 2722c451f78SAneesh V */ 2732c451f78SAneesh V void invalidate_dcache_range(unsigned long start, unsigned long stop) 2742c451f78SAneesh V { 2752c451f78SAneesh V 2762c451f78SAneesh V v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE); 2772c451f78SAneesh V 2782c451f78SAneesh V v7_outer_cache_inval_range(start, stop); 2792c451f78SAneesh V } 2802c451f78SAneesh V 2812c451f78SAneesh V /* 2822c451f78SAneesh V * Flush range(clean & invalidate) from all levels of D-cache/unified 2832c451f78SAneesh V * cache used: 2842c451f78SAneesh V * Affects the range [start, stop - 1] 2852c451f78SAneesh V */ 2862c451f78SAneesh V void flush_dcache_range(unsigned long start, unsigned long stop) 2872c451f78SAneesh V { 2882c451f78SAneesh V v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE); 2892c451f78SAneesh V 2902c451f78SAneesh V v7_outer_cache_flush_range(start, stop); 2912c451f78SAneesh V } 2922c451f78SAneesh V 2932c451f78SAneesh V void arm_init_before_mmu(void) 2942c451f78SAneesh V { 2952c451f78SAneesh V v7_outer_cache_enable(); 2962c451f78SAneesh V invalidate_dcache_all(); 2972c451f78SAneesh V v7_inval_tlb(); 2982c451f78SAneesh V } 2992c451f78SAneesh V 300*0dde7f53SSimon Glass void mmu_page_table_flush(unsigned long start, unsigned long stop) 301*0dde7f53SSimon Glass { 302*0dde7f53SSimon Glass flush_dcache_range(start, stop); 303*0dde7f53SSimon Glass v7_inval_tlb(); 304*0dde7f53SSimon Glass } 305*0dde7f53SSimon Glass 3062c451f78SAneesh V /* 3072c451f78SAneesh V * Flush range from all levels of d-cache/unified-cache used: 3082c451f78SAneesh V * Affects the range [start, start + size - 1] 3092c451f78SAneesh V */ 3102c451f78SAneesh V void flush_cache(unsigned long start, unsigned long size) 3112c451f78SAneesh V { 3122c451f78SAneesh V flush_dcache_range(start, start + size); 3132c451f78SAneesh V } 3142c451f78SAneesh V #else /* #ifndef CONFIG_SYS_DCACHE_OFF */ 3152c451f78SAneesh V void invalidate_dcache_all(void) 3162c451f78SAneesh V { 3172c451f78SAneesh V } 3182c451f78SAneesh V 3192c451f78SAneesh V void flush_dcache_all(void) 3202c451f78SAneesh V { 3212c451f78SAneesh V } 3222c451f78SAneesh V 3232c451f78SAneesh V void invalidate_dcache_range(unsigned long start, unsigned long stop) 3242c451f78SAneesh V { 3252c451f78SAneesh V } 3262c451f78SAneesh V 3272c451f78SAneesh V void flush_dcache_range(unsigned long start, unsigned long stop) 3282c451f78SAneesh V { 3292c451f78SAneesh V } 3302c451f78SAneesh V 3312c451f78SAneesh V void arm_init_before_mmu(void) 3322c451f78SAneesh V { 3332c451f78SAneesh V } 3342c451f78SAneesh V 3352c451f78SAneesh V void flush_cache(unsigned long start, unsigned long size) 3362c451f78SAneesh V { 3372c451f78SAneesh V } 338*0dde7f53SSimon Glass 339*0dde7f53SSimon Glass void mmu_page_table_flush(unsigned long start, unsigned long stop) 340*0dde7f53SSimon Glass { 341*0dde7f53SSimon Glass } 342*0dde7f53SSimon Glass 3432c451f78SAneesh V #endif /* #ifndef CONFIG_SYS_DCACHE_OFF */ 3442c451f78SAneesh V 3452c451f78SAneesh V #ifndef CONFIG_SYS_ICACHE_OFF 3462c451f78SAneesh V /* Invalidate entire I-cache and branch predictor array */ 3472c451f78SAneesh V void invalidate_icache_all(void) 3482c451f78SAneesh V { 3492c451f78SAneesh V /* 3502c451f78SAneesh V * Invalidate all instruction caches to PoU. 3512c451f78SAneesh V * Also flushes branch target cache. 3522c451f78SAneesh V */ 3532c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); 3542c451f78SAneesh V 3552c451f78SAneesh V /* Invalidate entire branch predictor array */ 3562c451f78SAneesh V asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0)); 3572c451f78SAneesh V 3582c451f78SAneesh V /* Full system DSB - make sure that the invalidation is complete */ 3592c451f78SAneesh V CP15DSB; 3602c451f78SAneesh V 3612c451f78SAneesh V /* ISB - make sure the instruction stream sees it */ 3622c451f78SAneesh V CP15ISB; 3632c451f78SAneesh V } 3642c451f78SAneesh V #else 3652c451f78SAneesh V void invalidate_icache_all(void) 3662c451f78SAneesh V { 3672c451f78SAneesh V } 3682c451f78SAneesh V #endif 3692c451f78SAneesh V 3702c451f78SAneesh V /* 3712c451f78SAneesh V * Stub implementations for outer cache operations 3722c451f78SAneesh V */ 3732c451f78SAneesh V void __v7_outer_cache_enable(void) 3742c451f78SAneesh V { 3752c451f78SAneesh V } 3762c451f78SAneesh V void v7_outer_cache_enable(void) 3772c451f78SAneesh V __attribute__((weak, alias("__v7_outer_cache_enable"))); 3782c451f78SAneesh V 3792c451f78SAneesh V void __v7_outer_cache_disable(void) 3802c451f78SAneesh V { 3812c451f78SAneesh V } 3822c451f78SAneesh V void v7_outer_cache_disable(void) 3832c451f78SAneesh V __attribute__((weak, alias("__v7_outer_cache_disable"))); 3842c451f78SAneesh V 3852c451f78SAneesh V void __v7_outer_cache_flush_all(void) 3862c451f78SAneesh V { 3872c451f78SAneesh V } 3882c451f78SAneesh V void v7_outer_cache_flush_all(void) 3892c451f78SAneesh V __attribute__((weak, alias("__v7_outer_cache_flush_all"))); 3902c451f78SAneesh V 3912c451f78SAneesh V void __v7_outer_cache_inval_all(void) 3922c451f78SAneesh V { 3932c451f78SAneesh V } 3942c451f78SAneesh V void v7_outer_cache_inval_all(void) 3952c451f78SAneesh V __attribute__((weak, alias("__v7_outer_cache_inval_all"))); 3962c451f78SAneesh V 3972c451f78SAneesh V void __v7_outer_cache_flush_range(u32 start, u32 end) 3982c451f78SAneesh V { 3992c451f78SAneesh V } 4002c451f78SAneesh V void v7_outer_cache_flush_range(u32 start, u32 end) 4012c451f78SAneesh V __attribute__((weak, alias("__v7_outer_cache_flush_range"))); 4022c451f78SAneesh V 4032c451f78SAneesh V void __v7_outer_cache_inval_range(u32 start, u32 end) 4042c451f78SAneesh V { 4052c451f78SAneesh V } 4062c451f78SAneesh V void v7_outer_cache_inval_range(u32 start, u32 end) 4072c451f78SAneesh V __attribute__((weak, alias("__v7_outer_cache_inval_range"))); 408