162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ARM64 cacheinfo support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 ARM Ltd. 662306a36Sopenharmony_ci * All Rights Reserved 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/acpi.h> 1062306a36Sopenharmony_ci#include <linux/cacheinfo.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define MAX_CACHE_LEVEL 7 /* Max 7 level supported */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciint cache_line_size(void) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci if (coherency_max_size != 0) 1862306a36Sopenharmony_ci return coherency_max_size; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci return cache_line_size_of_cpu(); 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cache_line_size); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic inline enum cache_type get_cache_type(int level) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci u64 clidr; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (level > MAX_CACHE_LEVEL) 2962306a36Sopenharmony_ci return CACHE_TYPE_NOCACHE; 3062306a36Sopenharmony_ci clidr = read_sysreg(clidr_el1); 3162306a36Sopenharmony_ci return CLIDR_CTYPE(clidr, level); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void ci_leaf_init(struct cacheinfo *this_leaf, 3562306a36Sopenharmony_ci enum cache_type type, unsigned int level) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci this_leaf->level = level; 3862306a36Sopenharmony_ci this_leaf->type = type; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void detect_cache_level(unsigned int *level_p, unsigned int *leaves_p) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned int ctype, level, leaves; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) { 4662306a36Sopenharmony_ci ctype = get_cache_type(level); 4762306a36Sopenharmony_ci if (ctype == CACHE_TYPE_NOCACHE) { 4862306a36Sopenharmony_ci level--; 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci /* Separate instruction and data caches */ 5262306a36Sopenharmony_ci leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci *level_p = level; 5662306a36Sopenharmony_ci *leaves_p = leaves; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciint early_cache_level(unsigned int cpu) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci detect_cache_level(&this_cpu_ci->num_levels, &this_cpu_ci->num_leaves); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciint init_cache_level(unsigned int cpu) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci unsigned int level, leaves; 7162306a36Sopenharmony_ci int fw_level, ret; 7262306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci detect_cache_level(&level, &leaves); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (acpi_disabled) { 7762306a36Sopenharmony_ci fw_level = of_find_last_cache_level(cpu); 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci ret = acpi_get_cache_info(cpu, &fw_level, NULL); 8062306a36Sopenharmony_ci if (ret < 0) 8162306a36Sopenharmony_ci fw_level = 0; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (level < fw_level) { 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * some external caches not specified in CLIDR_EL1 8762306a36Sopenharmony_ci * the information may be available in the device tree 8862306a36Sopenharmony_ci * only unified external caches are considered here 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci leaves += (fw_level - level); 9162306a36Sopenharmony_ci level = fw_level; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci this_cpu_ci->num_levels = level; 9562306a36Sopenharmony_ci this_cpu_ci->num_leaves = leaves; 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint populate_cache_leaves(unsigned int cpu) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned int level, idx; 10262306a36Sopenharmony_ci enum cache_type type; 10362306a36Sopenharmony_ci struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 10462306a36Sopenharmony_ci struct cacheinfo *this_leaf = this_cpu_ci->info_list; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (idx = 0, level = 1; level <= this_cpu_ci->num_levels && 10762306a36Sopenharmony_ci idx < this_cpu_ci->num_leaves; idx++, level++) { 10862306a36Sopenharmony_ci type = get_cache_type(level); 10962306a36Sopenharmony_ci if (type == CACHE_TYPE_SEPARATE) { 11062306a36Sopenharmony_ci ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); 11162306a36Sopenharmony_ci ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci ci_leaf_init(this_leaf++, type, level); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 118