162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cacheinfo support - processor cache information via sysfs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on arch/x86/kernel/cpu/intel_cacheinfo.c 662306a36Sopenharmony_ci * Author: Sudeep Holla <sudeep.holla@arm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/acpi.h> 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/cacheinfo.h> 1362306a36Sopenharmony_ci#include <linux/compiler.h> 1462306a36Sopenharmony_ci#include <linux/cpu.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/smp.h> 2162306a36Sopenharmony_ci#include <linux/sysfs.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* pointer to per cpu cacheinfo */ 2462306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo); 2562306a36Sopenharmony_ci#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu)) 2662306a36Sopenharmony_ci#define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves) 2762306a36Sopenharmony_ci#define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list) 2862306a36Sopenharmony_ci#define per_cpu_cacheinfo_idx(cpu, idx) \ 2962306a36Sopenharmony_ci (per_cpu_cacheinfo(cpu) + (idx)) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Set if no cache information is found in DT/ACPI. */ 3262306a36Sopenharmony_cistatic bool use_arch_info; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return ci_cacheinfo(cpu); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, 4062306a36Sopenharmony_ci struct cacheinfo *sib_leaf) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci /* 4362306a36Sopenharmony_ci * For non DT/ACPI systems, assume unique level 1 caches, 4462306a36Sopenharmony_ci * system-wide shared caches for all other levels. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci if (!(IS_ENABLED(CONFIG_OF) || IS_ENABLED(CONFIG_ACPI)) || 4762306a36Sopenharmony_ci use_arch_info) 4862306a36Sopenharmony_ci return (this_leaf->level != 1) && (sib_leaf->level != 1); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if ((sib_leaf->attributes & CACHE_ID) && 5162306a36Sopenharmony_ci (this_leaf->attributes & CACHE_ID)) 5262306a36Sopenharmony_ci return sib_leaf->id == this_leaf->id; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return sib_leaf->fw_token == this_leaf->fw_token; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cibool last_level_cache_is_valid(unsigned int cpu) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct cacheinfo *llc; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (!cache_leaves(cpu)) 6262306a36Sopenharmony_ci return false; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return (llc->attributes & CACHE_ID) || !!llc->fw_token; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cibool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct cacheinfo *llc_x, *llc_y; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!last_level_cache_is_valid(cpu_x) || 7562306a36Sopenharmony_ci !last_level_cache_is_valid(cpu_y)) 7662306a36Sopenharmony_ci return false; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci llc_x = per_cpu_cacheinfo_idx(cpu_x, cache_leaves(cpu_x) - 1); 7962306a36Sopenharmony_ci llc_y = per_cpu_cacheinfo_idx(cpu_y, cache_leaves(cpu_y) - 1); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return cache_leaves_are_shared(llc_x, llc_y); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#ifdef CONFIG_OF 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic bool of_check_cache_nodes(struct device_node *np); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* OF properties to query for a given cache type */ 8962306a36Sopenharmony_cistruct cache_type_info { 9062306a36Sopenharmony_ci const char *size_prop; 9162306a36Sopenharmony_ci const char *line_size_props[2]; 9262306a36Sopenharmony_ci const char *nr_sets_prop; 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const struct cache_type_info cache_type_info[] = { 9662306a36Sopenharmony_ci { 9762306a36Sopenharmony_ci .size_prop = "cache-size", 9862306a36Sopenharmony_ci .line_size_props = { "cache-line-size", 9962306a36Sopenharmony_ci "cache-block-size", }, 10062306a36Sopenharmony_ci .nr_sets_prop = "cache-sets", 10162306a36Sopenharmony_ci }, { 10262306a36Sopenharmony_ci .size_prop = "i-cache-size", 10362306a36Sopenharmony_ci .line_size_props = { "i-cache-line-size", 10462306a36Sopenharmony_ci "i-cache-block-size", }, 10562306a36Sopenharmony_ci .nr_sets_prop = "i-cache-sets", 10662306a36Sopenharmony_ci }, { 10762306a36Sopenharmony_ci .size_prop = "d-cache-size", 10862306a36Sopenharmony_ci .line_size_props = { "d-cache-line-size", 10962306a36Sopenharmony_ci "d-cache-block-size", }, 11062306a36Sopenharmony_ci .nr_sets_prop = "d-cache-sets", 11162306a36Sopenharmony_ci }, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic inline int get_cacheinfo_idx(enum cache_type type) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci if (type == CACHE_TYPE_UNIFIED) 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci return type; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void cache_size(struct cacheinfo *this_leaf, struct device_node *np) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci const char *propname; 12462306a36Sopenharmony_ci int ct_idx; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ct_idx = get_cacheinfo_idx(this_leaf->type); 12762306a36Sopenharmony_ci propname = cache_type_info[ct_idx].size_prop; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci of_property_read_u32(np, propname, &this_leaf->size); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* not cache_line_size() because that's a macro in include/linux/cache.h */ 13362306a36Sopenharmony_cistatic void cache_get_line_size(struct cacheinfo *this_leaf, 13462306a36Sopenharmony_ci struct device_node *np) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci int i, lim, ct_idx; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ct_idx = get_cacheinfo_idx(this_leaf->type); 13962306a36Sopenharmony_ci lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci for (i = 0; i < lim; i++) { 14262306a36Sopenharmony_ci int ret; 14362306a36Sopenharmony_ci u32 line_size; 14462306a36Sopenharmony_ci const char *propname; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci propname = cache_type_info[ct_idx].line_size_props[i]; 14762306a36Sopenharmony_ci ret = of_property_read_u32(np, propname, &line_size); 14862306a36Sopenharmony_ci if (!ret) { 14962306a36Sopenharmony_ci this_leaf->coherency_line_size = line_size; 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void cache_nr_sets(struct cacheinfo *this_leaf, struct device_node *np) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci const char *propname; 15862306a36Sopenharmony_ci int ct_idx; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ct_idx = get_cacheinfo_idx(this_leaf->type); 16162306a36Sopenharmony_ci propname = cache_type_info[ct_idx].nr_sets_prop; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci of_property_read_u32(np, propname, &this_leaf->number_of_sets); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void cache_associativity(struct cacheinfo *this_leaf) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci unsigned int line_size = this_leaf->coherency_line_size; 16962306a36Sopenharmony_ci unsigned int nr_sets = this_leaf->number_of_sets; 17062306a36Sopenharmony_ci unsigned int size = this_leaf->size; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * If the cache is fully associative, there is no need to 17462306a36Sopenharmony_ci * check the other properties. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) 17762306a36Sopenharmony_ci this_leaf->ways_of_associativity = (size / nr_sets) / line_size; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic bool cache_node_is_unified(struct cacheinfo *this_leaf, 18162306a36Sopenharmony_ci struct device_node *np) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci return of_property_read_bool(np, "cache-unified"); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void cache_of_set_props(struct cacheinfo *this_leaf, 18762306a36Sopenharmony_ci struct device_node *np) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci /* 19062306a36Sopenharmony_ci * init_cache_level must setup the cache level correctly 19162306a36Sopenharmony_ci * overriding the architecturally specified levels, so 19262306a36Sopenharmony_ci * if type is NONE at this stage, it should be unified 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci if (this_leaf->type == CACHE_TYPE_NOCACHE && 19562306a36Sopenharmony_ci cache_node_is_unified(this_leaf, np)) 19662306a36Sopenharmony_ci this_leaf->type = CACHE_TYPE_UNIFIED; 19762306a36Sopenharmony_ci cache_size(this_leaf, np); 19862306a36Sopenharmony_ci cache_get_line_size(this_leaf, np); 19962306a36Sopenharmony_ci cache_nr_sets(this_leaf, np); 20062306a36Sopenharmony_ci cache_associativity(this_leaf); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int cache_setup_of_node(unsigned int cpu) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct device_node *np, *prev; 20662306a36Sopenharmony_ci struct cacheinfo *this_leaf; 20762306a36Sopenharmony_ci unsigned int index = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci np = of_cpu_device_node_get(cpu); 21062306a36Sopenharmony_ci if (!np) { 21162306a36Sopenharmony_ci pr_err("Failed to find cpu%d device node\n", cpu); 21262306a36Sopenharmony_ci return -ENOENT; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!of_check_cache_nodes(np)) { 21662306a36Sopenharmony_ci of_node_put(np); 21762306a36Sopenharmony_ci return -ENOENT; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci prev = np; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci while (index < cache_leaves(cpu)) { 22362306a36Sopenharmony_ci this_leaf = per_cpu_cacheinfo_idx(cpu, index); 22462306a36Sopenharmony_ci if (this_leaf->level != 1) { 22562306a36Sopenharmony_ci np = of_find_next_cache_node(np); 22662306a36Sopenharmony_ci of_node_put(prev); 22762306a36Sopenharmony_ci prev = np; 22862306a36Sopenharmony_ci if (!np) 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci cache_of_set_props(this_leaf, np); 23262306a36Sopenharmony_ci this_leaf->fw_token = np; 23362306a36Sopenharmony_ci index++; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci of_node_put(np); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (index != cache_leaves(cpu)) /* not all OF nodes populated */ 23962306a36Sopenharmony_ci return -ENOENT; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic bool of_check_cache_nodes(struct device_node *np) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct device_node *next; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (of_property_present(np, "cache-size") || 24962306a36Sopenharmony_ci of_property_present(np, "i-cache-size") || 25062306a36Sopenharmony_ci of_property_present(np, "d-cache-size") || 25162306a36Sopenharmony_ci of_property_present(np, "cache-unified")) 25262306a36Sopenharmony_ci return true; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci next = of_find_next_cache_node(np); 25562306a36Sopenharmony_ci if (next) { 25662306a36Sopenharmony_ci of_node_put(next); 25762306a36Sopenharmony_ci return true; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return false; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int of_count_cache_leaves(struct device_node *np) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned int leaves = 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (of_property_read_bool(np, "cache-size")) 26862306a36Sopenharmony_ci ++leaves; 26962306a36Sopenharmony_ci if (of_property_read_bool(np, "i-cache-size")) 27062306a36Sopenharmony_ci ++leaves; 27162306a36Sopenharmony_ci if (of_property_read_bool(np, "d-cache-size")) 27262306a36Sopenharmony_ci ++leaves; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!leaves) { 27562306a36Sopenharmony_ci /* The '[i-|d-|]cache-size' property is required, but 27662306a36Sopenharmony_ci * if absent, fallback on the 'cache-unified' property. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (of_property_read_bool(np, "cache-unified")) 27962306a36Sopenharmony_ci return 1; 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci return 2; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return leaves; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciint init_of_cache_level(unsigned int cpu) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 29062306a36Sopenharmony_ci struct device_node *np = of_cpu_device_node_get(cpu); 29162306a36Sopenharmony_ci struct device_node *prev = NULL; 29262306a36Sopenharmony_ci unsigned int levels = 0, leaves, level; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!of_check_cache_nodes(np)) { 29562306a36Sopenharmony_ci of_node_put(np); 29662306a36Sopenharmony_ci return -ENOENT; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci leaves = of_count_cache_leaves(np); 30062306a36Sopenharmony_ci if (leaves > 0) 30162306a36Sopenharmony_ci levels = 1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci prev = np; 30462306a36Sopenharmony_ci while ((np = of_find_next_cache_node(np))) { 30562306a36Sopenharmony_ci of_node_put(prev); 30662306a36Sopenharmony_ci prev = np; 30762306a36Sopenharmony_ci if (!of_device_is_compatible(np, "cache")) 30862306a36Sopenharmony_ci goto err_out; 30962306a36Sopenharmony_ci if (of_property_read_u32(np, "cache-level", &level)) 31062306a36Sopenharmony_ci goto err_out; 31162306a36Sopenharmony_ci if (level <= levels) 31262306a36Sopenharmony_ci goto err_out; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci leaves += of_count_cache_leaves(np); 31562306a36Sopenharmony_ci levels = level; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci of_node_put(np); 31962306a36Sopenharmony_ci this_cpu_ci->num_levels = levels; 32062306a36Sopenharmony_ci this_cpu_ci->num_leaves = leaves; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cierr_out: 32562306a36Sopenharmony_ci of_node_put(np); 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci#else 33062306a36Sopenharmony_cistatic inline int cache_setup_of_node(unsigned int cpu) { return 0; } 33162306a36Sopenharmony_ciint init_of_cache_level(unsigned int cpu) { return 0; } 33262306a36Sopenharmony_ci#endif 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ciint __weak cache_setup_acpi(unsigned int cpu) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci return -ENOTSUPP; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ciunsigned int coherency_max_size; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int cache_setup_properties(unsigned int cpu) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int ret = 0; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (of_have_populated_dt()) 34662306a36Sopenharmony_ci ret = cache_setup_of_node(cpu); 34762306a36Sopenharmony_ci else if (!acpi_disabled) 34862306a36Sopenharmony_ci ret = cache_setup_acpi(cpu); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci // Assume there is no cache information available in DT/ACPI from now. 35162306a36Sopenharmony_ci if (ret && use_arch_cache_info()) 35262306a36Sopenharmony_ci use_arch_info = true; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int cache_shared_cpu_map_setup(unsigned int cpu) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 36062306a36Sopenharmony_ci struct cacheinfo *this_leaf, *sib_leaf; 36162306a36Sopenharmony_ci unsigned int index, sib_index; 36262306a36Sopenharmony_ci int ret = 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (this_cpu_ci->cpu_map_populated) 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * skip setting up cache properties if LLC is valid, just need 36962306a36Sopenharmony_ci * to update the shared cpu_map if the cache attributes were 37062306a36Sopenharmony_ci * populated early before all the cpus are brought online 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci if (!last_level_cache_is_valid(cpu) && !use_arch_info) { 37362306a36Sopenharmony_ci ret = cache_setup_properties(cpu); 37462306a36Sopenharmony_ci if (ret) 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci for (index = 0; index < cache_leaves(cpu); index++) { 37962306a36Sopenharmony_ci unsigned int i; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci this_leaf = per_cpu_cacheinfo_idx(cpu, index); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); 38462306a36Sopenharmony_ci for_each_online_cpu(i) { 38562306a36Sopenharmony_ci struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (i == cpu || !sib_cpu_ci->info_list) 38862306a36Sopenharmony_ci continue;/* skip if itself or no cacheinfo */ 38962306a36Sopenharmony_ci for (sib_index = 0; sib_index < cache_leaves(i); sib_index++) { 39062306a36Sopenharmony_ci sib_leaf = per_cpu_cacheinfo_idx(i, sib_index); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * Comparing cache IDs only makes sense if the leaves 39462306a36Sopenharmony_ci * belong to the same cache level of same type. Skip 39562306a36Sopenharmony_ci * the check if level and type do not match. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci if (sib_leaf->level != this_leaf->level || 39862306a36Sopenharmony_ci sib_leaf->type != this_leaf->type) 39962306a36Sopenharmony_ci continue; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (cache_leaves_are_shared(this_leaf, sib_leaf)) { 40262306a36Sopenharmony_ci cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); 40362306a36Sopenharmony_ci cpumask_set_cpu(i, &this_leaf->shared_cpu_map); 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci /* record the maximum cache line size */ 40962306a36Sopenharmony_ci if (this_leaf->coherency_line_size > coherency_max_size) 41062306a36Sopenharmony_ci coherency_max_size = this_leaf->coherency_line_size; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* shared_cpu_map is now populated for the cpu */ 41462306a36Sopenharmony_ci this_cpu_ci->cpu_map_populated = true; 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void cache_shared_cpu_map_remove(unsigned int cpu) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 42162306a36Sopenharmony_ci struct cacheinfo *this_leaf, *sib_leaf; 42262306a36Sopenharmony_ci unsigned int sibling, index, sib_index; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci for (index = 0; index < cache_leaves(cpu); index++) { 42562306a36Sopenharmony_ci this_leaf = per_cpu_cacheinfo_idx(cpu, index); 42662306a36Sopenharmony_ci for_each_cpu(sibling, &this_leaf->shared_cpu_map) { 42762306a36Sopenharmony_ci struct cpu_cacheinfo *sib_cpu_ci = 42862306a36Sopenharmony_ci get_cpu_cacheinfo(sibling); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (sibling == cpu || !sib_cpu_ci->info_list) 43162306a36Sopenharmony_ci continue;/* skip if itself or no cacheinfo */ 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (sib_index = 0; sib_index < cache_leaves(sibling); sib_index++) { 43462306a36Sopenharmony_ci sib_leaf = per_cpu_cacheinfo_idx(sibling, sib_index); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * Comparing cache IDs only makes sense if the leaves 43862306a36Sopenharmony_ci * belong to the same cache level of same type. Skip 43962306a36Sopenharmony_ci * the check if level and type do not match. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci if (sib_leaf->level != this_leaf->level || 44262306a36Sopenharmony_ci sib_leaf->type != this_leaf->type) 44362306a36Sopenharmony_ci continue; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (cache_leaves_are_shared(this_leaf, sib_leaf)) { 44662306a36Sopenharmony_ci cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); 44762306a36Sopenharmony_ci cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* cpu is no longer populated in the shared map */ 45562306a36Sopenharmony_ci this_cpu_ci->cpu_map_populated = false; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void free_cache_attributes(unsigned int cpu) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci if (!per_cpu_cacheinfo(cpu)) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci cache_shared_cpu_map_remove(cpu); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciint __weak early_cache_level(unsigned int cpu) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci return -ENOENT; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciint __weak init_cache_level(unsigned int cpu) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci return -ENOENT; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciint __weak populate_cache_leaves(unsigned int cpu) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci return -ENOENT; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic inline 48262306a36Sopenharmony_ciint allocate_cache_info(int cpu) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), 48562306a36Sopenharmony_ci sizeof(struct cacheinfo), GFP_ATOMIC); 48662306a36Sopenharmony_ci if (!per_cpu_cacheinfo(cpu)) { 48762306a36Sopenharmony_ci cache_leaves(cpu) = 0; 48862306a36Sopenharmony_ci return -ENOMEM; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ciint fetch_cache_info(unsigned int cpu) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 49762306a36Sopenharmony_ci unsigned int levels = 0, split_levels = 0; 49862306a36Sopenharmony_ci int ret; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (acpi_disabled) { 50162306a36Sopenharmony_ci ret = init_of_cache_level(cpu); 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci ret = acpi_get_cache_info(cpu, &levels, &split_levels); 50462306a36Sopenharmony_ci if (!ret) { 50562306a36Sopenharmony_ci this_cpu_ci->num_levels = levels; 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * This assumes that: 50862306a36Sopenharmony_ci * - there cannot be any split caches (data/instruction) 50962306a36Sopenharmony_ci * above a unified cache 51062306a36Sopenharmony_ci * - data/instruction caches come by pair 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci this_cpu_ci->num_leaves = levels + split_levels; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (ret || !cache_leaves(cpu)) { 51762306a36Sopenharmony_ci ret = early_cache_level(cpu); 51862306a36Sopenharmony_ci if (ret) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (!cache_leaves(cpu)) 52262306a36Sopenharmony_ci return -ENOENT; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci this_cpu_ci->early_ci_levels = true; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return allocate_cache_info(cpu); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic inline int init_level_allocate_ci(unsigned int cpu) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci unsigned int early_leaves = cache_leaves(cpu); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Since early initialization/allocation of the cacheinfo is allowed 53562306a36Sopenharmony_ci * via fetch_cache_info() and this also gets called as CPU hotplug 53662306a36Sopenharmony_ci * callbacks via cacheinfo_cpu_online, the init/alloc can be skipped 53762306a36Sopenharmony_ci * as it will happen only once (the cacheinfo memory is never freed). 53862306a36Sopenharmony_ci * Just populate the cacheinfo. However, if the cacheinfo has been 53962306a36Sopenharmony_ci * allocated early through the arch-specific early_cache_level() call, 54062306a36Sopenharmony_ci * there is a chance the info is wrong (this can happen on arm64). In 54162306a36Sopenharmony_ci * that case, call init_cache_level() anyway to give the arch-specific 54262306a36Sopenharmony_ci * code a chance to make things right. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci if (per_cpu_cacheinfo(cpu) && !ci_cacheinfo(cpu)->early_ci_levels) 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (init_cache_level(cpu) || !cache_leaves(cpu)) 54862306a36Sopenharmony_ci return -ENOENT; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * Now that we have properly initialized the cache level info, make 55262306a36Sopenharmony_ci * sure we don't try to do that again the next time we are called 55362306a36Sopenharmony_ci * (e.g. as CPU hotplug callbacks). 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci ci_cacheinfo(cpu)->early_ci_levels = false; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (cache_leaves(cpu) <= early_leaves) 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci kfree(per_cpu_cacheinfo(cpu)); 56162306a36Sopenharmony_ci return allocate_cache_info(cpu); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ciint detect_cache_attributes(unsigned int cpu) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci int ret; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = init_level_allocate_ci(cpu); 56962306a36Sopenharmony_ci if (ret) 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * If LLC is valid the cache leaves were already populated so just go to 57462306a36Sopenharmony_ci * update the cpu map. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci if (!last_level_cache_is_valid(cpu)) { 57762306a36Sopenharmony_ci /* 57862306a36Sopenharmony_ci * populate_cache_leaves() may completely setup the cache leaves and 57962306a36Sopenharmony_ci * shared_cpu_map or it may leave it partially setup. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci ret = populate_cache_leaves(cpu); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci goto free_ci; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* 58762306a36Sopenharmony_ci * For systems using DT for cache hierarchy, fw_token 58862306a36Sopenharmony_ci * and shared_cpu_map will be set up here only if they are 58962306a36Sopenharmony_ci * not populated already 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci ret = cache_shared_cpu_map_setup(cpu); 59262306a36Sopenharmony_ci if (ret) { 59362306a36Sopenharmony_ci pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); 59462306a36Sopenharmony_ci goto free_ci; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cifree_ci: 60062306a36Sopenharmony_ci free_cache_attributes(cpu); 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/* pointer to cpuX/cache device */ 60562306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct device *, ci_cache_dev); 60662306a36Sopenharmony_ci#define per_cpu_cache_dev(cpu) (per_cpu(ci_cache_dev, cpu)) 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic cpumask_t cache_dev_map; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/* pointer to array of devices for cpuX/cache/indexY */ 61162306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct device **, ci_index_dev); 61262306a36Sopenharmony_ci#define per_cpu_index_dev(cpu) (per_cpu(ci_index_dev, cpu)) 61362306a36Sopenharmony_ci#define per_cache_index_dev(cpu, idx) ((per_cpu_index_dev(cpu))[idx]) 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#define show_one(file_name, object) \ 61662306a36Sopenharmony_cistatic ssize_t file_name##_show(struct device *dev, \ 61762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) \ 61862306a36Sopenharmony_ci{ \ 61962306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ 62062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", this_leaf->object); \ 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cishow_one(id, id); 62462306a36Sopenharmony_cishow_one(level, level); 62562306a36Sopenharmony_cishow_one(coherency_line_size, coherency_line_size); 62662306a36Sopenharmony_cishow_one(number_of_sets, number_of_sets); 62762306a36Sopenharmony_cishow_one(physical_line_partition, physical_line_partition); 62862306a36Sopenharmony_cishow_one(ways_of_associativity, ways_of_associativity); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic ssize_t size_show(struct device *dev, 63162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return sysfs_emit(buf, "%uK\n", this_leaf->size >> 10); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic ssize_t shared_cpu_map_show(struct device *dev, 63962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 64262306a36Sopenharmony_ci const struct cpumask *mask = &this_leaf->shared_cpu_map; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return sysfs_emit(buf, "%*pb\n", nr_cpu_ids, mask); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic ssize_t shared_cpu_list_show(struct device *dev, 64862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 65162306a36Sopenharmony_ci const struct cpumask *mask = &this_leaf->shared_cpu_map; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return sysfs_emit(buf, "%*pbl\n", nr_cpu_ids, mask); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic ssize_t type_show(struct device *dev, 65762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 66062306a36Sopenharmony_ci const char *output; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci switch (this_leaf->type) { 66362306a36Sopenharmony_ci case CACHE_TYPE_DATA: 66462306a36Sopenharmony_ci output = "Data"; 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case CACHE_TYPE_INST: 66762306a36Sopenharmony_ci output = "Instruction"; 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci case CACHE_TYPE_UNIFIED: 67062306a36Sopenharmony_ci output = "Unified"; 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci default: 67362306a36Sopenharmony_ci return -EINVAL; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", output); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic ssize_t allocation_policy_show(struct device *dev, 68062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 68362306a36Sopenharmony_ci unsigned int ci_attr = this_leaf->attributes; 68462306a36Sopenharmony_ci const char *output; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if ((ci_attr & CACHE_READ_ALLOCATE) && (ci_attr & CACHE_WRITE_ALLOCATE)) 68762306a36Sopenharmony_ci output = "ReadWriteAllocate"; 68862306a36Sopenharmony_ci else if (ci_attr & CACHE_READ_ALLOCATE) 68962306a36Sopenharmony_ci output = "ReadAllocate"; 69062306a36Sopenharmony_ci else if (ci_attr & CACHE_WRITE_ALLOCATE) 69162306a36Sopenharmony_ci output = "WriteAllocate"; 69262306a36Sopenharmony_ci else 69362306a36Sopenharmony_ci return 0; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", output); 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic ssize_t write_policy_show(struct device *dev, 69962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 70262306a36Sopenharmony_ci unsigned int ci_attr = this_leaf->attributes; 70362306a36Sopenharmony_ci int n = 0; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (ci_attr & CACHE_WRITE_THROUGH) 70662306a36Sopenharmony_ci n = sysfs_emit(buf, "WriteThrough\n"); 70762306a36Sopenharmony_ci else if (ci_attr & CACHE_WRITE_BACK) 70862306a36Sopenharmony_ci n = sysfs_emit(buf, "WriteBack\n"); 70962306a36Sopenharmony_ci return n; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(id); 71362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(level); 71462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(type); 71562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(coherency_line_size); 71662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ways_of_associativity); 71762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(number_of_sets); 71862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(size); 71962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(allocation_policy); 72062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(write_policy); 72162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(shared_cpu_map); 72262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(shared_cpu_list); 72362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(physical_line_partition); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic struct attribute *cache_default_attrs[] = { 72662306a36Sopenharmony_ci &dev_attr_id.attr, 72762306a36Sopenharmony_ci &dev_attr_type.attr, 72862306a36Sopenharmony_ci &dev_attr_level.attr, 72962306a36Sopenharmony_ci &dev_attr_shared_cpu_map.attr, 73062306a36Sopenharmony_ci &dev_attr_shared_cpu_list.attr, 73162306a36Sopenharmony_ci &dev_attr_coherency_line_size.attr, 73262306a36Sopenharmony_ci &dev_attr_ways_of_associativity.attr, 73362306a36Sopenharmony_ci &dev_attr_number_of_sets.attr, 73462306a36Sopenharmony_ci &dev_attr_size.attr, 73562306a36Sopenharmony_ci &dev_attr_allocation_policy.attr, 73662306a36Sopenharmony_ci &dev_attr_write_policy.attr, 73762306a36Sopenharmony_ci &dev_attr_physical_line_partition.attr, 73862306a36Sopenharmony_ci NULL 73962306a36Sopenharmony_ci}; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic umode_t 74262306a36Sopenharmony_cicache_default_attrs_is_visible(struct kobject *kobj, 74362306a36Sopenharmony_ci struct attribute *attr, int unused) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 74662306a36Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 74762306a36Sopenharmony_ci const struct cpumask *mask = &this_leaf->shared_cpu_map; 74862306a36Sopenharmony_ci umode_t mode = attr->mode; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if ((attr == &dev_attr_id.attr) && (this_leaf->attributes & CACHE_ID)) 75162306a36Sopenharmony_ci return mode; 75262306a36Sopenharmony_ci if ((attr == &dev_attr_type.attr) && this_leaf->type) 75362306a36Sopenharmony_ci return mode; 75462306a36Sopenharmony_ci if ((attr == &dev_attr_level.attr) && this_leaf->level) 75562306a36Sopenharmony_ci return mode; 75662306a36Sopenharmony_ci if ((attr == &dev_attr_shared_cpu_map.attr) && !cpumask_empty(mask)) 75762306a36Sopenharmony_ci return mode; 75862306a36Sopenharmony_ci if ((attr == &dev_attr_shared_cpu_list.attr) && !cpumask_empty(mask)) 75962306a36Sopenharmony_ci return mode; 76062306a36Sopenharmony_ci if ((attr == &dev_attr_coherency_line_size.attr) && 76162306a36Sopenharmony_ci this_leaf->coherency_line_size) 76262306a36Sopenharmony_ci return mode; 76362306a36Sopenharmony_ci if ((attr == &dev_attr_ways_of_associativity.attr) && 76462306a36Sopenharmony_ci this_leaf->size) /* allow 0 = full associativity */ 76562306a36Sopenharmony_ci return mode; 76662306a36Sopenharmony_ci if ((attr == &dev_attr_number_of_sets.attr) && 76762306a36Sopenharmony_ci this_leaf->number_of_sets) 76862306a36Sopenharmony_ci return mode; 76962306a36Sopenharmony_ci if ((attr == &dev_attr_size.attr) && this_leaf->size) 77062306a36Sopenharmony_ci return mode; 77162306a36Sopenharmony_ci if ((attr == &dev_attr_write_policy.attr) && 77262306a36Sopenharmony_ci (this_leaf->attributes & CACHE_WRITE_POLICY_MASK)) 77362306a36Sopenharmony_ci return mode; 77462306a36Sopenharmony_ci if ((attr == &dev_attr_allocation_policy.attr) && 77562306a36Sopenharmony_ci (this_leaf->attributes & CACHE_ALLOCATE_POLICY_MASK)) 77662306a36Sopenharmony_ci return mode; 77762306a36Sopenharmony_ci if ((attr == &dev_attr_physical_line_partition.attr) && 77862306a36Sopenharmony_ci this_leaf->physical_line_partition) 77962306a36Sopenharmony_ci return mode; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_cistatic const struct attribute_group cache_default_group = { 78562306a36Sopenharmony_ci .attrs = cache_default_attrs, 78662306a36Sopenharmony_ci .is_visible = cache_default_attrs_is_visible, 78762306a36Sopenharmony_ci}; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic const struct attribute_group *cache_default_groups[] = { 79062306a36Sopenharmony_ci &cache_default_group, 79162306a36Sopenharmony_ci NULL, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct attribute_group *cache_private_groups[] = { 79562306a36Sopenharmony_ci &cache_default_group, 79662306a36Sopenharmony_ci NULL, /* Place holder for private group */ 79762306a36Sopenharmony_ci NULL, 79862306a36Sopenharmony_ci}; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ciconst struct attribute_group * 80162306a36Sopenharmony_ci__weak cache_get_priv_group(struct cacheinfo *this_leaf) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci return NULL; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic const struct attribute_group ** 80762306a36Sopenharmony_cicache_get_attribute_groups(struct cacheinfo *this_leaf) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci const struct attribute_group *priv_group = 81062306a36Sopenharmony_ci cache_get_priv_group(this_leaf); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (!priv_group) 81362306a36Sopenharmony_ci return cache_default_groups; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (!cache_private_groups[1]) 81662306a36Sopenharmony_ci cache_private_groups[1] = priv_group; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return cache_private_groups; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/* Add/Remove cache interface for CPU device */ 82262306a36Sopenharmony_cistatic void cpu_cache_sysfs_exit(unsigned int cpu) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci int i; 82562306a36Sopenharmony_ci struct device *ci_dev; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (per_cpu_index_dev(cpu)) { 82862306a36Sopenharmony_ci for (i = 0; i < cache_leaves(cpu); i++) { 82962306a36Sopenharmony_ci ci_dev = per_cache_index_dev(cpu, i); 83062306a36Sopenharmony_ci if (!ci_dev) 83162306a36Sopenharmony_ci continue; 83262306a36Sopenharmony_ci device_unregister(ci_dev); 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci kfree(per_cpu_index_dev(cpu)); 83562306a36Sopenharmony_ci per_cpu_index_dev(cpu) = NULL; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci device_unregister(per_cpu_cache_dev(cpu)); 83862306a36Sopenharmony_ci per_cpu_cache_dev(cpu) = NULL; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int cpu_cache_sysfs_init(unsigned int cpu) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct device *dev = get_cpu_device(cpu); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (per_cpu_cacheinfo(cpu) == NULL) 84662306a36Sopenharmony_ci return -ENOENT; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci per_cpu_cache_dev(cpu) = cpu_device_create(dev, NULL, NULL, "cache"); 84962306a36Sopenharmony_ci if (IS_ERR(per_cpu_cache_dev(cpu))) 85062306a36Sopenharmony_ci return PTR_ERR(per_cpu_cache_dev(cpu)); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Allocate all required memory */ 85362306a36Sopenharmony_ci per_cpu_index_dev(cpu) = kcalloc(cache_leaves(cpu), 85462306a36Sopenharmony_ci sizeof(struct device *), GFP_KERNEL); 85562306a36Sopenharmony_ci if (unlikely(per_cpu_index_dev(cpu) == NULL)) 85662306a36Sopenharmony_ci goto err_out; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return 0; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cierr_out: 86162306a36Sopenharmony_ci cpu_cache_sysfs_exit(cpu); 86262306a36Sopenharmony_ci return -ENOMEM; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int cache_add_dev(unsigned int cpu) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci unsigned int i; 86862306a36Sopenharmony_ci int rc; 86962306a36Sopenharmony_ci struct device *ci_dev, *parent; 87062306a36Sopenharmony_ci struct cacheinfo *this_leaf; 87162306a36Sopenharmony_ci const struct attribute_group **cache_groups; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci rc = cpu_cache_sysfs_init(cpu); 87462306a36Sopenharmony_ci if (unlikely(rc < 0)) 87562306a36Sopenharmony_ci return rc; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci parent = per_cpu_cache_dev(cpu); 87862306a36Sopenharmony_ci for (i = 0; i < cache_leaves(cpu); i++) { 87962306a36Sopenharmony_ci this_leaf = per_cpu_cacheinfo_idx(cpu, i); 88062306a36Sopenharmony_ci if (this_leaf->disable_sysfs) 88162306a36Sopenharmony_ci continue; 88262306a36Sopenharmony_ci if (this_leaf->type == CACHE_TYPE_NOCACHE) 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci cache_groups = cache_get_attribute_groups(this_leaf); 88562306a36Sopenharmony_ci ci_dev = cpu_device_create(parent, this_leaf, cache_groups, 88662306a36Sopenharmony_ci "index%1u", i); 88762306a36Sopenharmony_ci if (IS_ERR(ci_dev)) { 88862306a36Sopenharmony_ci rc = PTR_ERR(ci_dev); 88962306a36Sopenharmony_ci goto err; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci per_cache_index_dev(cpu, i) = ci_dev; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci cpumask_set_cpu(cpu, &cache_dev_map); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_cierr: 89762306a36Sopenharmony_ci cpu_cache_sysfs_exit(cpu); 89862306a36Sopenharmony_ci return rc; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic int cacheinfo_cpu_online(unsigned int cpu) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci int rc = detect_cache_attributes(cpu); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (rc) 90662306a36Sopenharmony_ci return rc; 90762306a36Sopenharmony_ci rc = cache_add_dev(cpu); 90862306a36Sopenharmony_ci if (rc) 90962306a36Sopenharmony_ci free_cache_attributes(cpu); 91062306a36Sopenharmony_ci return rc; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int cacheinfo_cpu_pre_down(unsigned int cpu) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) 91662306a36Sopenharmony_ci cpu_cache_sysfs_exit(cpu); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci free_cache_attributes(cpu); 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int __init cacheinfo_sysfs_init(void) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci return cpuhp_setup_state(CPUHP_AP_BASE_CACHEINFO_ONLINE, 92562306a36Sopenharmony_ci "base/cacheinfo:online", 92662306a36Sopenharmony_ci cacheinfo_cpu_online, cacheinfo_cpu_pre_down); 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_cidevice_initcall(cacheinfo_sysfs_init); 929