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