18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * cacheinfo support - processor cache information via sysfs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on arch/x86/kernel/cpu/intel_cacheinfo.c 68c2ecf20Sopenharmony_ci * Author: Sudeep Holla <sudeep.holla@arm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/cacheinfo.h> 138c2ecf20Sopenharmony_ci#include <linux/compiler.h> 148c2ecf20Sopenharmony_ci#include <linux/cpu.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/sched.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/smp.h> 218c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* pointer to per cpu cacheinfo */ 248c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo); 258c2ecf20Sopenharmony_ci#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu)) 268c2ecf20Sopenharmony_ci#define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves) 278c2ecf20Sopenharmony_ci#define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return ci_cacheinfo(cpu); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 358c2ecf20Sopenharmony_cistatic inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, 368c2ecf20Sopenharmony_ci struct cacheinfo *sib_leaf) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return sib_leaf->fw_token == this_leaf->fw_token; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* OF properties to query for a given cache type */ 428c2ecf20Sopenharmony_cistruct cache_type_info { 438c2ecf20Sopenharmony_ci const char *size_prop; 448c2ecf20Sopenharmony_ci const char *line_size_props[2]; 458c2ecf20Sopenharmony_ci const char *nr_sets_prop; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const struct cache_type_info cache_type_info[] = { 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci .size_prop = "cache-size", 518c2ecf20Sopenharmony_ci .line_size_props = { "cache-line-size", 528c2ecf20Sopenharmony_ci "cache-block-size", }, 538c2ecf20Sopenharmony_ci .nr_sets_prop = "cache-sets", 548c2ecf20Sopenharmony_ci }, { 558c2ecf20Sopenharmony_ci .size_prop = "i-cache-size", 568c2ecf20Sopenharmony_ci .line_size_props = { "i-cache-line-size", 578c2ecf20Sopenharmony_ci "i-cache-block-size", }, 588c2ecf20Sopenharmony_ci .nr_sets_prop = "i-cache-sets", 598c2ecf20Sopenharmony_ci }, { 608c2ecf20Sopenharmony_ci .size_prop = "d-cache-size", 618c2ecf20Sopenharmony_ci .line_size_props = { "d-cache-line-size", 628c2ecf20Sopenharmony_ci "d-cache-block-size", }, 638c2ecf20Sopenharmony_ci .nr_sets_prop = "d-cache-sets", 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline int get_cacheinfo_idx(enum cache_type type) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci if (type == CACHE_TYPE_UNIFIED) 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci return type; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void cache_size(struct cacheinfo *this_leaf, struct device_node *np) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci const char *propname; 778c2ecf20Sopenharmony_ci int ct_idx; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ct_idx = get_cacheinfo_idx(this_leaf->type); 808c2ecf20Sopenharmony_ci propname = cache_type_info[ct_idx].size_prop; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci of_property_read_u32(np, propname, &this_leaf->size); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* not cache_line_size() because that's a macro in include/linux/cache.h */ 868c2ecf20Sopenharmony_cistatic void cache_get_line_size(struct cacheinfo *this_leaf, 878c2ecf20Sopenharmony_ci struct device_node *np) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int i, lim, ct_idx; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ct_idx = get_cacheinfo_idx(this_leaf->type); 928c2ecf20Sopenharmony_ci lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci for (i = 0; i < lim; i++) { 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci u32 line_size; 978c2ecf20Sopenharmony_ci const char *propname; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci propname = cache_type_info[ct_idx].line_size_props[i]; 1008c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, propname, &line_size); 1018c2ecf20Sopenharmony_ci if (!ret) { 1028c2ecf20Sopenharmony_ci this_leaf->coherency_line_size = line_size; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void cache_nr_sets(struct cacheinfo *this_leaf, struct device_node *np) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci const char *propname; 1118c2ecf20Sopenharmony_ci int ct_idx; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ct_idx = get_cacheinfo_idx(this_leaf->type); 1148c2ecf20Sopenharmony_ci propname = cache_type_info[ct_idx].nr_sets_prop; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci of_property_read_u32(np, propname, &this_leaf->number_of_sets); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void cache_associativity(struct cacheinfo *this_leaf) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned int line_size = this_leaf->coherency_line_size; 1228c2ecf20Sopenharmony_ci unsigned int nr_sets = this_leaf->number_of_sets; 1238c2ecf20Sopenharmony_ci unsigned int size = this_leaf->size; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * If the cache is fully associative, there is no need to 1278c2ecf20Sopenharmony_ci * check the other properties. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) 1308c2ecf20Sopenharmony_ci this_leaf->ways_of_associativity = (size / nr_sets) / line_size; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic bool cache_node_is_unified(struct cacheinfo *this_leaf, 1348c2ecf20Sopenharmony_ci struct device_node *np) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return of_property_read_bool(np, "cache-unified"); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void cache_of_set_props(struct cacheinfo *this_leaf, 1408c2ecf20Sopenharmony_ci struct device_node *np) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * init_cache_level must setup the cache level correctly 1448c2ecf20Sopenharmony_ci * overriding the architecturally specified levels, so 1458c2ecf20Sopenharmony_ci * if type is NONE at this stage, it should be unified 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci if (this_leaf->type == CACHE_TYPE_NOCACHE && 1488c2ecf20Sopenharmony_ci cache_node_is_unified(this_leaf, np)) 1498c2ecf20Sopenharmony_ci this_leaf->type = CACHE_TYPE_UNIFIED; 1508c2ecf20Sopenharmony_ci cache_size(this_leaf, np); 1518c2ecf20Sopenharmony_ci cache_get_line_size(this_leaf, np); 1528c2ecf20Sopenharmony_ci cache_nr_sets(this_leaf, np); 1538c2ecf20Sopenharmony_ci cache_associativity(this_leaf); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int cache_setup_of_node(unsigned int cpu) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct device_node *np; 1598c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf; 1608c2ecf20Sopenharmony_ci struct device *cpu_dev = get_cpu_device(cpu); 1618c2ecf20Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 1628c2ecf20Sopenharmony_ci unsigned int index = 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* skip if fw_token is already populated */ 1658c2ecf20Sopenharmony_ci if (this_cpu_ci->info_list->fw_token) { 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!cpu_dev) { 1708c2ecf20Sopenharmony_ci pr_err("No cpu device for CPU %d\n", cpu); 1718c2ecf20Sopenharmony_ci return -ENODEV; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci np = cpu_dev->of_node; 1748c2ecf20Sopenharmony_ci if (!np) { 1758c2ecf20Sopenharmony_ci pr_err("Failed to find cpu%d device node\n", cpu); 1768c2ecf20Sopenharmony_ci return -ENOENT; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci while (index < cache_leaves(cpu)) { 1808c2ecf20Sopenharmony_ci this_leaf = this_cpu_ci->info_list + index; 1818c2ecf20Sopenharmony_ci if (this_leaf->level != 1) 1828c2ecf20Sopenharmony_ci np = of_find_next_cache_node(np); 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci np = of_node_get(np);/* cpu node itself */ 1858c2ecf20Sopenharmony_ci if (!np) 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci cache_of_set_props(this_leaf, np); 1888c2ecf20Sopenharmony_ci this_leaf->fw_token = np; 1898c2ecf20Sopenharmony_ci index++; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (index != cache_leaves(cpu)) /* not all OF nodes populated */ 1938c2ecf20Sopenharmony_ci return -ENOENT; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci#else 1988c2ecf20Sopenharmony_cistatic inline int cache_setup_of_node(unsigned int cpu) { return 0; } 1998c2ecf20Sopenharmony_cistatic inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, 2008c2ecf20Sopenharmony_ci struct cacheinfo *sib_leaf) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * For non-DT/ACPI systems, assume unique level 1 caches, system-wide 2048c2ecf20Sopenharmony_ci * shared caches for all other levels. This will be used only if 2058c2ecf20Sopenharmony_ci * arch specific code has not populated shared_cpu_map 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci return !(this_leaf->level == 1); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciint __weak cache_setup_acpi(unsigned int cpu) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return -ENOTSUPP; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciunsigned int coherency_max_size; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int cache_shared_cpu_map_setup(unsigned int cpu) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 2218c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf, *sib_leaf; 2228c2ecf20Sopenharmony_ci unsigned int index; 2238c2ecf20Sopenharmony_ci int ret = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (this_cpu_ci->cpu_map_populated) 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (of_have_populated_dt()) 2298c2ecf20Sopenharmony_ci ret = cache_setup_of_node(cpu); 2308c2ecf20Sopenharmony_ci else if (!acpi_disabled) 2318c2ecf20Sopenharmony_ci ret = cache_setup_acpi(cpu); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (ret) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci for (index = 0; index < cache_leaves(cpu); index++) { 2378c2ecf20Sopenharmony_ci unsigned int i; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci this_leaf = this_cpu_ci->info_list + index; 2408c2ecf20Sopenharmony_ci /* skip if shared_cpu_map is already populated */ 2418c2ecf20Sopenharmony_ci if (!cpumask_empty(&this_leaf->shared_cpu_map)) 2428c2ecf20Sopenharmony_ci continue; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); 2458c2ecf20Sopenharmony_ci for_each_online_cpu(i) { 2468c2ecf20Sopenharmony_ci struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (i == cpu || !sib_cpu_ci->info_list) 2498c2ecf20Sopenharmony_ci continue;/* skip if itself or no cacheinfo */ 2508c2ecf20Sopenharmony_ci sib_leaf = sib_cpu_ci->info_list + index; 2518c2ecf20Sopenharmony_ci if (cache_leaves_are_shared(this_leaf, sib_leaf)) { 2528c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); 2538c2ecf20Sopenharmony_ci cpumask_set_cpu(i, &this_leaf->shared_cpu_map); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci /* record the maximum cache line size */ 2578c2ecf20Sopenharmony_ci if (this_leaf->coherency_line_size > coherency_max_size) 2588c2ecf20Sopenharmony_ci coherency_max_size = this_leaf->coherency_line_size; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void cache_shared_cpu_map_remove(unsigned int cpu) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 2678c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf, *sib_leaf; 2688c2ecf20Sopenharmony_ci unsigned int sibling, index; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci for (index = 0; index < cache_leaves(cpu); index++) { 2718c2ecf20Sopenharmony_ci this_leaf = this_cpu_ci->info_list + index; 2728c2ecf20Sopenharmony_ci for_each_cpu(sibling, &this_leaf->shared_cpu_map) { 2738c2ecf20Sopenharmony_ci struct cpu_cacheinfo *sib_cpu_ci; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (sibling == cpu) /* skip itself */ 2768c2ecf20Sopenharmony_ci continue; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci sib_cpu_ci = get_cpu_cacheinfo(sibling); 2798c2ecf20Sopenharmony_ci if (!sib_cpu_ci->info_list) 2808c2ecf20Sopenharmony_ci continue; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci sib_leaf = sib_cpu_ci->info_list + index; 2838c2ecf20Sopenharmony_ci cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); 2848c2ecf20Sopenharmony_ci cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci if (of_have_populated_dt()) 2878c2ecf20Sopenharmony_ci of_node_put(this_leaf->fw_token); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic void free_cache_attributes(unsigned int cpu) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci if (!per_cpu_cacheinfo(cpu)) 2948c2ecf20Sopenharmony_ci return; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci cache_shared_cpu_map_remove(cpu); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci kfree(per_cpu_cacheinfo(cpu)); 2998c2ecf20Sopenharmony_ci per_cpu_cacheinfo(cpu) = NULL; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciint __weak init_cache_level(unsigned int cpu) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci return -ENOENT; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciint __weak populate_cache_leaves(unsigned int cpu) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci return -ENOENT; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int detect_cache_attributes(unsigned int cpu) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int ret; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (init_cache_level(cpu) || !cache_leaves(cpu)) 3178c2ecf20Sopenharmony_ci return -ENOENT; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), 3208c2ecf20Sopenharmony_ci sizeof(struct cacheinfo), GFP_KERNEL); 3218c2ecf20Sopenharmony_ci if (per_cpu_cacheinfo(cpu) == NULL) 3228c2ecf20Sopenharmony_ci return -ENOMEM; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * populate_cache_leaves() may completely setup the cache leaves and 3268c2ecf20Sopenharmony_ci * shared_cpu_map or it may leave it partially setup. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci ret = populate_cache_leaves(cpu); 3298c2ecf20Sopenharmony_ci if (ret) 3308c2ecf20Sopenharmony_ci goto free_ci; 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * For systems using DT for cache hierarchy, fw_token 3338c2ecf20Sopenharmony_ci * and shared_cpu_map will be set up here only if they are 3348c2ecf20Sopenharmony_ci * not populated already 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci ret = cache_shared_cpu_map_setup(cpu); 3378c2ecf20Sopenharmony_ci if (ret) { 3388c2ecf20Sopenharmony_ci pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); 3398c2ecf20Sopenharmony_ci goto free_ci; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cifree_ci: 3458c2ecf20Sopenharmony_ci free_cache_attributes(cpu); 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/* pointer to cpuX/cache device */ 3508c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct device *, ci_cache_dev); 3518c2ecf20Sopenharmony_ci#define per_cpu_cache_dev(cpu) (per_cpu(ci_cache_dev, cpu)) 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic cpumask_t cache_dev_map; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* pointer to array of devices for cpuX/cache/indexY */ 3568c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct device **, ci_index_dev); 3578c2ecf20Sopenharmony_ci#define per_cpu_index_dev(cpu) (per_cpu(ci_index_dev, cpu)) 3588c2ecf20Sopenharmony_ci#define per_cache_index_dev(cpu, idx) ((per_cpu_index_dev(cpu))[idx]) 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci#define show_one(file_name, object) \ 3618c2ecf20Sopenharmony_cistatic ssize_t file_name##_show(struct device *dev, \ 3628c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 3638c2ecf20Sopenharmony_ci{ \ 3648c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); \ 3658c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%u\n", this_leaf->object); \ 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cishow_one(id, id); 3698c2ecf20Sopenharmony_cishow_one(level, level); 3708c2ecf20Sopenharmony_cishow_one(coherency_line_size, coherency_line_size); 3718c2ecf20Sopenharmony_cishow_one(number_of_sets, number_of_sets); 3728c2ecf20Sopenharmony_cishow_one(physical_line_partition, physical_line_partition); 3738c2ecf20Sopenharmony_cishow_one(ways_of_associativity, ways_of_associativity); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic ssize_t size_show(struct device *dev, 3768c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%uK\n", this_leaf->size >> 10); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic ssize_t shared_cpu_map_show(struct device *dev, 3848c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 3878c2ecf20Sopenharmony_ci const struct cpumask *mask = &this_leaf->shared_cpu_map; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%*pb\n", nr_cpu_ids, mask); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic ssize_t shared_cpu_list_show(struct device *dev, 3938c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 3968c2ecf20Sopenharmony_ci const struct cpumask *mask = &this_leaf->shared_cpu_map; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%*pbl\n", nr_cpu_ids, mask); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic ssize_t type_show(struct device *dev, 4028c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 4058c2ecf20Sopenharmony_ci const char *output; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci switch (this_leaf->type) { 4088c2ecf20Sopenharmony_ci case CACHE_TYPE_DATA: 4098c2ecf20Sopenharmony_ci output = "Data"; 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci case CACHE_TYPE_INST: 4128c2ecf20Sopenharmony_ci output = "Instruction"; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci case CACHE_TYPE_UNIFIED: 4158c2ecf20Sopenharmony_ci output = "Unified"; 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci default: 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%s\n", output); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic ssize_t allocation_policy_show(struct device *dev, 4258c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 4288c2ecf20Sopenharmony_ci unsigned int ci_attr = this_leaf->attributes; 4298c2ecf20Sopenharmony_ci const char *output; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if ((ci_attr & CACHE_READ_ALLOCATE) && (ci_attr & CACHE_WRITE_ALLOCATE)) 4328c2ecf20Sopenharmony_ci output = "ReadWriteAllocate"; 4338c2ecf20Sopenharmony_ci else if (ci_attr & CACHE_READ_ALLOCATE) 4348c2ecf20Sopenharmony_ci output = "ReadAllocate"; 4358c2ecf20Sopenharmony_ci else if (ci_attr & CACHE_WRITE_ALLOCATE) 4368c2ecf20Sopenharmony_ci output = "WriteAllocate"; 4378c2ecf20Sopenharmony_ci else 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return sysfs_emit(buf, "%s\n", output); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic ssize_t write_policy_show(struct device *dev, 4448c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 4478c2ecf20Sopenharmony_ci unsigned int ci_attr = this_leaf->attributes; 4488c2ecf20Sopenharmony_ci int n = 0; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (ci_attr & CACHE_WRITE_THROUGH) 4518c2ecf20Sopenharmony_ci n = sysfs_emit(buf, "WriteThrough\n"); 4528c2ecf20Sopenharmony_ci else if (ci_attr & CACHE_WRITE_BACK) 4538c2ecf20Sopenharmony_ci n = sysfs_emit(buf, "WriteBack\n"); 4548c2ecf20Sopenharmony_ci return n; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id); 4588c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(level); 4598c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(type); 4608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(coherency_line_size); 4618c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ways_of_associativity); 4628c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(number_of_sets); 4638c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(size); 4648c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(allocation_policy); 4658c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(write_policy); 4668c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(shared_cpu_map); 4678c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(shared_cpu_list); 4688c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(physical_line_partition); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic struct attribute *cache_default_attrs[] = { 4718c2ecf20Sopenharmony_ci &dev_attr_id.attr, 4728c2ecf20Sopenharmony_ci &dev_attr_type.attr, 4738c2ecf20Sopenharmony_ci &dev_attr_level.attr, 4748c2ecf20Sopenharmony_ci &dev_attr_shared_cpu_map.attr, 4758c2ecf20Sopenharmony_ci &dev_attr_shared_cpu_list.attr, 4768c2ecf20Sopenharmony_ci &dev_attr_coherency_line_size.attr, 4778c2ecf20Sopenharmony_ci &dev_attr_ways_of_associativity.attr, 4788c2ecf20Sopenharmony_ci &dev_attr_number_of_sets.attr, 4798c2ecf20Sopenharmony_ci &dev_attr_size.attr, 4808c2ecf20Sopenharmony_ci &dev_attr_allocation_policy.attr, 4818c2ecf20Sopenharmony_ci &dev_attr_write_policy.attr, 4828c2ecf20Sopenharmony_ci &dev_attr_physical_line_partition.attr, 4838c2ecf20Sopenharmony_ci NULL 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic umode_t 4878c2ecf20Sopenharmony_cicache_default_attrs_is_visible(struct kobject *kobj, 4888c2ecf20Sopenharmony_ci struct attribute *attr, int unused) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 4918c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf = dev_get_drvdata(dev); 4928c2ecf20Sopenharmony_ci const struct cpumask *mask = &this_leaf->shared_cpu_map; 4938c2ecf20Sopenharmony_ci umode_t mode = attr->mode; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if ((attr == &dev_attr_id.attr) && (this_leaf->attributes & CACHE_ID)) 4968c2ecf20Sopenharmony_ci return mode; 4978c2ecf20Sopenharmony_ci if ((attr == &dev_attr_type.attr) && this_leaf->type) 4988c2ecf20Sopenharmony_ci return mode; 4998c2ecf20Sopenharmony_ci if ((attr == &dev_attr_level.attr) && this_leaf->level) 5008c2ecf20Sopenharmony_ci return mode; 5018c2ecf20Sopenharmony_ci if ((attr == &dev_attr_shared_cpu_map.attr) && !cpumask_empty(mask)) 5028c2ecf20Sopenharmony_ci return mode; 5038c2ecf20Sopenharmony_ci if ((attr == &dev_attr_shared_cpu_list.attr) && !cpumask_empty(mask)) 5048c2ecf20Sopenharmony_ci return mode; 5058c2ecf20Sopenharmony_ci if ((attr == &dev_attr_coherency_line_size.attr) && 5068c2ecf20Sopenharmony_ci this_leaf->coherency_line_size) 5078c2ecf20Sopenharmony_ci return mode; 5088c2ecf20Sopenharmony_ci if ((attr == &dev_attr_ways_of_associativity.attr) && 5098c2ecf20Sopenharmony_ci this_leaf->size) /* allow 0 = full associativity */ 5108c2ecf20Sopenharmony_ci return mode; 5118c2ecf20Sopenharmony_ci if ((attr == &dev_attr_number_of_sets.attr) && 5128c2ecf20Sopenharmony_ci this_leaf->number_of_sets) 5138c2ecf20Sopenharmony_ci return mode; 5148c2ecf20Sopenharmony_ci if ((attr == &dev_attr_size.attr) && this_leaf->size) 5158c2ecf20Sopenharmony_ci return mode; 5168c2ecf20Sopenharmony_ci if ((attr == &dev_attr_write_policy.attr) && 5178c2ecf20Sopenharmony_ci (this_leaf->attributes & CACHE_WRITE_POLICY_MASK)) 5188c2ecf20Sopenharmony_ci return mode; 5198c2ecf20Sopenharmony_ci if ((attr == &dev_attr_allocation_policy.attr) && 5208c2ecf20Sopenharmony_ci (this_leaf->attributes & CACHE_ALLOCATE_POLICY_MASK)) 5218c2ecf20Sopenharmony_ci return mode; 5228c2ecf20Sopenharmony_ci if ((attr == &dev_attr_physical_line_partition.attr) && 5238c2ecf20Sopenharmony_ci this_leaf->physical_line_partition) 5248c2ecf20Sopenharmony_ci return mode; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic const struct attribute_group cache_default_group = { 5308c2ecf20Sopenharmony_ci .attrs = cache_default_attrs, 5318c2ecf20Sopenharmony_ci .is_visible = cache_default_attrs_is_visible, 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct attribute_group *cache_default_groups[] = { 5358c2ecf20Sopenharmony_ci &cache_default_group, 5368c2ecf20Sopenharmony_ci NULL, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic const struct attribute_group *cache_private_groups[] = { 5408c2ecf20Sopenharmony_ci &cache_default_group, 5418c2ecf20Sopenharmony_ci NULL, /* Place holder for private group */ 5428c2ecf20Sopenharmony_ci NULL, 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ciconst struct attribute_group * 5468c2ecf20Sopenharmony_ci__weak cache_get_priv_group(struct cacheinfo *this_leaf) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci return NULL; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic const struct attribute_group ** 5528c2ecf20Sopenharmony_cicache_get_attribute_groups(struct cacheinfo *this_leaf) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci const struct attribute_group *priv_group = 5558c2ecf20Sopenharmony_ci cache_get_priv_group(this_leaf); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (!priv_group) 5588c2ecf20Sopenharmony_ci return cache_default_groups; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (!cache_private_groups[1]) 5618c2ecf20Sopenharmony_ci cache_private_groups[1] = priv_group; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci return cache_private_groups; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci/* Add/Remove cache interface for CPU device */ 5678c2ecf20Sopenharmony_cistatic void cpu_cache_sysfs_exit(unsigned int cpu) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci int i; 5708c2ecf20Sopenharmony_ci struct device *ci_dev; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (per_cpu_index_dev(cpu)) { 5738c2ecf20Sopenharmony_ci for (i = 0; i < cache_leaves(cpu); i++) { 5748c2ecf20Sopenharmony_ci ci_dev = per_cache_index_dev(cpu, i); 5758c2ecf20Sopenharmony_ci if (!ci_dev) 5768c2ecf20Sopenharmony_ci continue; 5778c2ecf20Sopenharmony_ci device_unregister(ci_dev); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci kfree(per_cpu_index_dev(cpu)); 5808c2ecf20Sopenharmony_ci per_cpu_index_dev(cpu) = NULL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci device_unregister(per_cpu_cache_dev(cpu)); 5838c2ecf20Sopenharmony_ci per_cpu_cache_dev(cpu) = NULL; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int cpu_cache_sysfs_init(unsigned int cpu) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct device *dev = get_cpu_device(cpu); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (per_cpu_cacheinfo(cpu) == NULL) 5918c2ecf20Sopenharmony_ci return -ENOENT; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci per_cpu_cache_dev(cpu) = cpu_device_create(dev, NULL, NULL, "cache"); 5948c2ecf20Sopenharmony_ci if (IS_ERR(per_cpu_cache_dev(cpu))) 5958c2ecf20Sopenharmony_ci return PTR_ERR(per_cpu_cache_dev(cpu)); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Allocate all required memory */ 5988c2ecf20Sopenharmony_ci per_cpu_index_dev(cpu) = kcalloc(cache_leaves(cpu), 5998c2ecf20Sopenharmony_ci sizeof(struct device *), GFP_KERNEL); 6008c2ecf20Sopenharmony_ci if (unlikely(per_cpu_index_dev(cpu) == NULL)) 6018c2ecf20Sopenharmony_ci goto err_out; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cierr_out: 6068c2ecf20Sopenharmony_ci cpu_cache_sysfs_exit(cpu); 6078c2ecf20Sopenharmony_ci return -ENOMEM; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int cache_add_dev(unsigned int cpu) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci unsigned int i; 6138c2ecf20Sopenharmony_ci int rc; 6148c2ecf20Sopenharmony_ci struct device *ci_dev, *parent; 6158c2ecf20Sopenharmony_ci struct cacheinfo *this_leaf; 6168c2ecf20Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 6178c2ecf20Sopenharmony_ci const struct attribute_group **cache_groups; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci rc = cpu_cache_sysfs_init(cpu); 6208c2ecf20Sopenharmony_ci if (unlikely(rc < 0)) 6218c2ecf20Sopenharmony_ci return rc; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci parent = per_cpu_cache_dev(cpu); 6248c2ecf20Sopenharmony_ci for (i = 0; i < cache_leaves(cpu); i++) { 6258c2ecf20Sopenharmony_ci this_leaf = this_cpu_ci->info_list + i; 6268c2ecf20Sopenharmony_ci if (this_leaf->disable_sysfs) 6278c2ecf20Sopenharmony_ci continue; 6288c2ecf20Sopenharmony_ci if (this_leaf->type == CACHE_TYPE_NOCACHE) 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci cache_groups = cache_get_attribute_groups(this_leaf); 6318c2ecf20Sopenharmony_ci ci_dev = cpu_device_create(parent, this_leaf, cache_groups, 6328c2ecf20Sopenharmony_ci "index%1u", i); 6338c2ecf20Sopenharmony_ci if (IS_ERR(ci_dev)) { 6348c2ecf20Sopenharmony_ci rc = PTR_ERR(ci_dev); 6358c2ecf20Sopenharmony_ci goto err; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci per_cache_index_dev(cpu, i) = ci_dev; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, &cache_dev_map); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_cierr: 6438c2ecf20Sopenharmony_ci cpu_cache_sysfs_exit(cpu); 6448c2ecf20Sopenharmony_ci return rc; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int cacheinfo_cpu_online(unsigned int cpu) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci int rc = detect_cache_attributes(cpu); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (rc) 6528c2ecf20Sopenharmony_ci return rc; 6538c2ecf20Sopenharmony_ci rc = cache_add_dev(cpu); 6548c2ecf20Sopenharmony_ci if (rc) 6558c2ecf20Sopenharmony_ci free_cache_attributes(cpu); 6568c2ecf20Sopenharmony_ci return rc; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic int cacheinfo_cpu_pre_down(unsigned int cpu) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) 6628c2ecf20Sopenharmony_ci cpu_cache_sysfs_exit(cpu); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci free_cache_attributes(cpu); 6658c2ecf20Sopenharmony_ci return 0; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int __init cacheinfo_sysfs_init(void) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci return cpuhp_setup_state(CPUHP_AP_BASE_CACHEINFO_ONLINE, 6718c2ecf20Sopenharmony_ci "base/cacheinfo:online", 6728c2ecf20Sopenharmony_ci cacheinfo_cpu_online, cacheinfo_cpu_pre_down); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_cidevice_initcall(cacheinfo_sysfs_init); 675