1*5d425c18SSudeep Holla /* 2*5d425c18SSudeep Holla * ARM64 cacheinfo support 3*5d425c18SSudeep Holla * 4*5d425c18SSudeep Holla * Copyright (C) 2015 ARM Ltd. 5*5d425c18SSudeep Holla * All Rights Reserved 6*5d425c18SSudeep Holla * 7*5d425c18SSudeep Holla * This program is free software; you can redistribute it and/or modify 8*5d425c18SSudeep Holla * it under the terms of the GNU General Public License version 2 as 9*5d425c18SSudeep Holla * published by the Free Software Foundation. 10*5d425c18SSudeep Holla * 11*5d425c18SSudeep Holla * This program is distributed "as is" WITHOUT ANY WARRANTY of any 12*5d425c18SSudeep Holla * kind, whether express or implied; without even the implied warranty 13*5d425c18SSudeep Holla * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*5d425c18SSudeep Holla * GNU General Public License for more details. 15*5d425c18SSudeep Holla * 16*5d425c18SSudeep Holla * You should have received a copy of the GNU General Public License 17*5d425c18SSudeep Holla * along with this program. If not, see <http://www.gnu.org/licenses/>. 18*5d425c18SSudeep Holla */ 19*5d425c18SSudeep Holla 20*5d425c18SSudeep Holla #include <linux/bitops.h> 21*5d425c18SSudeep Holla #include <linux/cacheinfo.h> 22*5d425c18SSudeep Holla #include <linux/cpu.h> 23*5d425c18SSudeep Holla #include <linux/compiler.h> 24*5d425c18SSudeep Holla #include <linux/of.h> 25*5d425c18SSudeep Holla 26*5d425c18SSudeep Holla #include <asm/cachetype.h> 27*5d425c18SSudeep Holla #include <asm/processor.h> 28*5d425c18SSudeep Holla 29*5d425c18SSudeep Holla #define MAX_CACHE_LEVEL 7 /* Max 7 level supported */ 30*5d425c18SSudeep Holla /* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */ 31*5d425c18SSudeep Holla #define CLIDR_CTYPE_SHIFT(level) (3 * (level - 1)) 32*5d425c18SSudeep Holla #define CLIDR_CTYPE_MASK(level) (7 << CLIDR_CTYPE_SHIFT(level)) 33*5d425c18SSudeep Holla #define CLIDR_CTYPE(clidr, level) \ 34*5d425c18SSudeep Holla (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level)) 35*5d425c18SSudeep Holla 36*5d425c18SSudeep Holla static inline enum cache_type get_cache_type(int level) 37*5d425c18SSudeep Holla { 38*5d425c18SSudeep Holla u64 clidr; 39*5d425c18SSudeep Holla 40*5d425c18SSudeep Holla if (level > MAX_CACHE_LEVEL) 41*5d425c18SSudeep Holla return CACHE_TYPE_NOCACHE; 42*5d425c18SSudeep Holla asm volatile ("mrs %x0, clidr_el1" : "=r" (clidr)); 43*5d425c18SSudeep Holla return CLIDR_CTYPE(clidr, level); 44*5d425c18SSudeep Holla } 45*5d425c18SSudeep Holla 46*5d425c18SSudeep Holla /* 47*5d425c18SSudeep Holla * Cache Size Selection Register(CSSELR) selects which Cache Size ID 48*5d425c18SSudeep Holla * Register(CCSIDR) is accessible by specifying the required cache 49*5d425c18SSudeep Holla * level and the cache type. We need to ensure that no one else changes 50*5d425c18SSudeep Holla * CSSELR by calling this in non-preemtible context 51*5d425c18SSudeep Holla */ 52*5d425c18SSudeep Holla u64 __attribute_const__ cache_get_ccsidr(u64 csselr) 53*5d425c18SSudeep Holla { 54*5d425c18SSudeep Holla u64 ccsidr; 55*5d425c18SSudeep Holla 56*5d425c18SSudeep Holla WARN_ON(preemptible()); 57*5d425c18SSudeep Holla 58*5d425c18SSudeep Holla /* Put value into CSSELR */ 59*5d425c18SSudeep Holla asm volatile("msr csselr_el1, %x0" : : "r" (csselr)); 60*5d425c18SSudeep Holla isb(); 61*5d425c18SSudeep Holla /* Read result out of CCSIDR */ 62*5d425c18SSudeep Holla asm volatile("mrs %x0, ccsidr_el1" : "=r" (ccsidr)); 63*5d425c18SSudeep Holla 64*5d425c18SSudeep Holla return ccsidr; 65*5d425c18SSudeep Holla } 66*5d425c18SSudeep Holla 67*5d425c18SSudeep Holla static void ci_leaf_init(struct cacheinfo *this_leaf, 68*5d425c18SSudeep Holla enum cache_type type, unsigned int level) 69*5d425c18SSudeep Holla { 70*5d425c18SSudeep Holla bool is_icache = type & CACHE_TYPE_INST; 71*5d425c18SSudeep Holla u64 tmp = cache_get_ccsidr((level - 1) << 1 | is_icache); 72*5d425c18SSudeep Holla 73*5d425c18SSudeep Holla this_leaf->level = level; 74*5d425c18SSudeep Holla this_leaf->type = type; 75*5d425c18SSudeep Holla this_leaf->coherency_line_size = CACHE_LINESIZE(tmp); 76*5d425c18SSudeep Holla this_leaf->number_of_sets = CACHE_NUMSETS(tmp); 77*5d425c18SSudeep Holla this_leaf->ways_of_associativity = CACHE_ASSOCIATIVITY(tmp); 78*5d425c18SSudeep Holla this_leaf->size = this_leaf->number_of_sets * 79*5d425c18SSudeep Holla this_leaf->coherency_line_size * this_leaf->ways_of_associativity; 80*5d425c18SSudeep Holla this_leaf->attributes = 81*5d425c18SSudeep Holla ((tmp & CCSIDR_EL1_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) | 82*5d425c18SSudeep Holla ((tmp & CCSIDR_EL1_WRITE_BACK) ? CACHE_WRITE_BACK : 0) | 83*5d425c18SSudeep Holla ((tmp & CCSIDR_EL1_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) | 84*5d425c18SSudeep Holla ((tmp & CCSIDR_EL1_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0); 85*5d425c18SSudeep Holla } 86*5d425c18SSudeep Holla 87*5d425c18SSudeep Holla static int __init_cache_level(unsigned int cpu) 88*5d425c18SSudeep Holla { 89*5d425c18SSudeep Holla unsigned int ctype, level, leaves; 90*5d425c18SSudeep Holla struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 91*5d425c18SSudeep Holla 92*5d425c18SSudeep Holla for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) { 93*5d425c18SSudeep Holla ctype = get_cache_type(level); 94*5d425c18SSudeep Holla if (ctype == CACHE_TYPE_NOCACHE) { 95*5d425c18SSudeep Holla level--; 96*5d425c18SSudeep Holla break; 97*5d425c18SSudeep Holla } 98*5d425c18SSudeep Holla /* Separate instruction and data caches */ 99*5d425c18SSudeep Holla leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; 100*5d425c18SSudeep Holla } 101*5d425c18SSudeep Holla 102*5d425c18SSudeep Holla this_cpu_ci->num_levels = level; 103*5d425c18SSudeep Holla this_cpu_ci->num_leaves = leaves; 104*5d425c18SSudeep Holla return 0; 105*5d425c18SSudeep Holla } 106*5d425c18SSudeep Holla 107*5d425c18SSudeep Holla static int __populate_cache_leaves(unsigned int cpu) 108*5d425c18SSudeep Holla { 109*5d425c18SSudeep Holla unsigned int level, idx; 110*5d425c18SSudeep Holla enum cache_type type; 111*5d425c18SSudeep Holla struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 112*5d425c18SSudeep Holla struct cacheinfo *this_leaf = this_cpu_ci->info_list; 113*5d425c18SSudeep Holla 114*5d425c18SSudeep Holla for (idx = 0, level = 1; level <= this_cpu_ci->num_levels && 115*5d425c18SSudeep Holla idx < this_cpu_ci->num_leaves; idx++, level++) { 116*5d425c18SSudeep Holla type = get_cache_type(level); 117*5d425c18SSudeep Holla if (type == CACHE_TYPE_SEPARATE) { 118*5d425c18SSudeep Holla ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); 119*5d425c18SSudeep Holla ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level); 120*5d425c18SSudeep Holla } else { 121*5d425c18SSudeep Holla ci_leaf_init(this_leaf++, type, level); 122*5d425c18SSudeep Holla } 123*5d425c18SSudeep Holla } 124*5d425c18SSudeep Holla return 0; 125*5d425c18SSudeep Holla } 126*5d425c18SSudeep Holla 127*5d425c18SSudeep Holla DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level) 128*5d425c18SSudeep Holla DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves) 129