Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers: base: cacheinfo: support DT overrides for cache properties

Few architectures like x86, ia64 and s390 derive the cache topology and
all the properties using a specific architected mechanism while some
other architectures like powerpc all those information id derived from
the device tree.

On ARM, both the mechanism is used. While all the cache properties can
be derived in a architected way, it needs to rely on device tree to get
the cache topology information.

However there are few platforms where this architected mechanism is
broken and the device tree properties can be used to override these
incorrect values.

This patch adds support for overriding the cache properties values to
the values specified in the device tree.

Cc: Alex Van Brunt <avanbrunt@nvidia.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Sudeep Holla and committed by
Greg Kroah-Hartman
dfea747d 8e1073b1

+121
+121
drivers/base/cacheinfo.c
··· 88 88 { 89 89 return sib_leaf->of_node == this_leaf->of_node; 90 90 } 91 + 92 + /* OF properties to query for a given cache type */ 93 + struct cache_type_info { 94 + const char *size_prop; 95 + const char *line_size_props[2]; 96 + const char *nr_sets_prop; 97 + }; 98 + 99 + static const struct cache_type_info cache_type_info[] = { 100 + { 101 + .size_prop = "cache-size", 102 + .line_size_props = { "cache-line-size", 103 + "cache-block-size", }, 104 + .nr_sets_prop = "cache-sets", 105 + }, { 106 + .size_prop = "i-cache-size", 107 + .line_size_props = { "i-cache-line-size", 108 + "i-cache-block-size", }, 109 + .nr_sets_prop = "i-cache-sets", 110 + }, { 111 + .size_prop = "d-cache-size", 112 + .line_size_props = { "d-cache-line-size", 113 + "d-cache-block-size", }, 114 + .nr_sets_prop = "d-cache-sets", 115 + }, 116 + }; 117 + 118 + static inline int get_cacheinfo_idx(enum cache_type type) 119 + { 120 + if (type == CACHE_TYPE_UNIFIED) 121 + return 0; 122 + return type; 123 + } 124 + 125 + static void cache_size(struct cacheinfo *this_leaf) 126 + { 127 + const char *propname; 128 + const __be32 *cache_size; 129 + int ct_idx; 130 + 131 + ct_idx = get_cacheinfo_idx(this_leaf->type); 132 + propname = cache_type_info[ct_idx].size_prop; 133 + 134 + cache_size = of_get_property(this_leaf->of_node, propname, NULL); 135 + if (cache_size) 136 + this_leaf->size = of_read_number(cache_size, 1); 137 + } 138 + 139 + /* not cache_line_size() because that's a macro in include/linux/cache.h */ 140 + static void cache_get_line_size(struct cacheinfo *this_leaf) 141 + { 142 + const __be32 *line_size; 143 + int i, lim, ct_idx; 144 + 145 + ct_idx = get_cacheinfo_idx(this_leaf->type); 146 + lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); 147 + 148 + for (i = 0; i < lim; i++) { 149 + const char *propname; 150 + 151 + propname = cache_type_info[ct_idx].line_size_props[i]; 152 + line_size = of_get_property(this_leaf->of_node, propname, NULL); 153 + if (line_size) 154 + break; 155 + } 156 + 157 + if (line_size) 158 + this_leaf->coherency_line_size = of_read_number(line_size, 1); 159 + } 160 + 161 + static void cache_nr_sets(struct cacheinfo *this_leaf) 162 + { 163 + const char *propname; 164 + const __be32 *nr_sets; 165 + int ct_idx; 166 + 167 + ct_idx = get_cacheinfo_idx(this_leaf->type); 168 + propname = cache_type_info[ct_idx].nr_sets_prop; 169 + 170 + nr_sets = of_get_property(this_leaf->of_node, propname, NULL); 171 + if (nr_sets) 172 + this_leaf->number_of_sets = of_read_number(nr_sets, 1); 173 + } 174 + 175 + static void cache_associativity(struct cacheinfo *this_leaf) 176 + { 177 + unsigned int line_size = this_leaf->coherency_line_size; 178 + unsigned int nr_sets = this_leaf->number_of_sets; 179 + unsigned int size = this_leaf->size; 180 + 181 + /* 182 + * If the cache is fully associative, there is no need to 183 + * check the other properties. 184 + */ 185 + if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) 186 + this_leaf->ways_of_associativity = (size / nr_sets) / line_size; 187 + } 188 + 189 + static void cache_of_override_properties(unsigned int cpu) 190 + { 191 + int index; 192 + struct cacheinfo *this_leaf; 193 + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 194 + 195 + for (index = 0; index < cache_leaves(cpu); index++) { 196 + this_leaf = this_cpu_ci->info_list + index; 197 + cache_size(this_leaf); 198 + cache_get_line_size(this_leaf); 199 + cache_nr_sets(this_leaf); 200 + cache_associativity(this_leaf); 201 + } 202 + } 91 203 #else 204 + static void cache_of_override_properties(unsigned int cpu) { } 92 205 static inline int cache_setup_of_node(unsigned int cpu) { return 0; } 93 206 static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, 94 207 struct cacheinfo *sib_leaf) ··· 284 171 } 285 172 } 286 173 174 + static void cache_override_properties(unsigned int cpu) 175 + { 176 + if (of_have_populated_dt()) 177 + return cache_of_override_properties(cpu); 178 + } 179 + 287 180 static void free_cache_attributes(unsigned int cpu) 288 181 { 289 182 if (!per_cpu_cacheinfo(cpu)) ··· 335 216 pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); 336 217 goto free_ci; 337 218 } 219 + 220 + cache_override_properties(cpu); 338 221 return 0; 339 222 340 223 free_ci: