1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ARM cacheinfo support 4 * 5 * Copyright (C) 2023 Linaro Ltd. 6 * Copyright (C) 2015 ARM Ltd. 7 * All Rights Reserved 8 */ 9 10 #include <linux/bitfield.h> 11 #include <linux/cacheinfo.h> 12 #include <linux/of.h> 13 14 #include <asm/cachetype.h> 15 #include <asm/cputype.h> 16 #include <asm/system_info.h> 17 18 /* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */ 19 #define CLIDR_CTYPE_SHIFT(level) (3 * (level - 1)) 20 #define CLIDR_CTYPE_MASK(level) (7 << CLIDR_CTYPE_SHIFT(level)) 21 #define CLIDR_CTYPE(clidr, level) \ 22 (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level)) 23 24 #define MAX_CACHE_LEVEL 7 /* Max 7 level supported */ 25 26 #define CTR_FORMAT_MASK GENMASK(31, 29) 27 #define CTR_FORMAT_ARMV6 0 28 #define CTR_FORMAT_ARMV7 4 29 #define CTR_CWG_MASK GENMASK(27, 24) 30 #define CTR_DSIZE_LEN_MASK GENMASK(13, 12) 31 #define CTR_ISIZE_LEN_MASK GENMASK(1, 0) 32 33 /* Also valid for v7m */ 34 static inline int cache_line_size_cp15(void) 35 { 36 u32 ctr = read_cpuid_cachetype(); 37 u32 format = FIELD_GET(CTR_FORMAT_MASK, ctr); 38 39 if (format == CTR_FORMAT_ARMV7) { 40 u32 cwg = FIELD_GET(CTR_CWG_MASK, ctr); 41 42 return cwg ? 4 << cwg : ARCH_DMA_MINALIGN; 43 } else if (WARN_ON_ONCE(format != CTR_FORMAT_ARMV6)) { 44 return ARCH_DMA_MINALIGN; 45 } 46 47 return 8 << max(FIELD_GET(CTR_ISIZE_LEN_MASK, ctr), 48 FIELD_GET(CTR_DSIZE_LEN_MASK, ctr)); 49 } 50 51 int cache_line_size(void) 52 { 53 if (coherency_max_size != 0) 54 return coherency_max_size; 55 56 /* CP15 is optional / implementation defined before ARMv6 */ 57 if (cpu_architecture() < CPU_ARCH_ARMv6) 58 return ARCH_DMA_MINALIGN; 59 60 return cache_line_size_cp15(); 61 } 62 EXPORT_SYMBOL_GPL(cache_line_size); 63 64 static inline enum cache_type get_cache_type(int level) 65 { 66 u32 clidr; 67 68 if (level > MAX_CACHE_LEVEL) 69 return CACHE_TYPE_NOCACHE; 70 71 clidr = read_clidr(); 72 73 return CLIDR_CTYPE(clidr, level); 74 } 75 76 static void ci_leaf_init(struct cacheinfo *this_leaf, 77 enum cache_type type, unsigned int level) 78 { 79 this_leaf->level = level; 80 this_leaf->type = type; 81 } 82 83 static bool clidr_present(void) 84 { 85 /* CLIDR is not present before ARMv7/v7m */ 86 if (cpu_architecture() < CPU_ARCH_ARMv7) 87 return false; 88 89 /* Don't try reading CLIDR if CTR declares old format */ 90 return FIELD_GET(CTR_FORMAT_MASK, read_cpuid_cachetype()) == CTR_FORMAT_ARMV7; 91 } 92 93 static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p) 94 { 95 unsigned int ctype, level, leaves; 96 97 if (!clidr_present()) 98 return -EOPNOTSUPP; 99 100 for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) { 101 ctype = get_cache_type(level); 102 if (ctype == CACHE_TYPE_NOCACHE) { 103 level--; 104 break; 105 } 106 /* Separate instruction and data caches */ 107 leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; 108 } 109 110 *level_p = level; 111 *leaves_p = leaves; 112 113 return 0; 114 } 115 116 int init_cache_level(unsigned int cpu) 117 { 118 unsigned int level, leaves; 119 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 120 int fw_level; 121 int ret; 122 123 ret = detect_cache_level(&level, &leaves); 124 if (ret) 125 return ret; 126 127 fw_level = of_find_last_cache_level(cpu); 128 129 if (level < fw_level) { 130 /* 131 * some external caches not specified in CLIDR_EL1 132 * the information may be available in the device tree 133 * only unified external caches are considered here 134 */ 135 leaves += (fw_level - level); 136 level = fw_level; 137 } 138 139 this_cpu_ci->num_levels = level; 140 this_cpu_ci->num_leaves = leaves; 141 return 0; 142 } 143 144 int early_cache_level(unsigned int cpu) 145 { 146 return init_cache_level(cpu); 147 } 148 149 int populate_cache_leaves(unsigned int cpu) 150 { 151 unsigned int level, idx; 152 enum cache_type type; 153 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 154 struct cacheinfo *infos = this_cpu_ci->info_list; 155 156 /* The device tree can describe caches CLIDR cannot fill in. */ 157 if (!clidr_present()) 158 return -ENOENT; 159 160 for (idx = 0, level = 1; level <= this_cpu_ci->num_levels && 161 idx < this_cpu_ci->num_leaves; level++) { 162 type = get_cache_type(level); 163 if (type == CACHE_TYPE_SEPARATE) { 164 if (idx + 1 >= this_cpu_ci->num_leaves) 165 break; 166 ci_leaf_init(&infos[idx++], CACHE_TYPE_DATA, level); 167 ci_leaf_init(&infos[idx++], CACHE_TYPE_INST, level); 168 } else { 169 ci_leaf_init(&infos[idx++], type, level); 170 } 171 } 172 173 return 0; 174 } 175