xref: /linux/arch/arm/kernel/cacheinfo.c (revision 5b0764a53235d3c77e1ed674e836e1b3fe1a4b1f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  ARM cacheinfo support
4  *
5  *  Copyright (C) 2023 Linaro Ltd.
6  *  Copyright (C) 2015 ARM Ltd.
7  *  All Rights Reserved
8  */
9 
10 #include <linux/bitfield.h>
11 #include <linux/cacheinfo.h>
12 #include <linux/of.h>
13 
14 #include <asm/cachetype.h>
15 #include <asm/cputype.h>
16 #include <asm/system_info.h>
17 
18 /* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */
19 #define CLIDR_CTYPE_SHIFT(level)	(3 * (level - 1))
20 #define CLIDR_CTYPE_MASK(level)		(7 << CLIDR_CTYPE_SHIFT(level))
21 #define CLIDR_CTYPE(clidr, level)	\
22 	(((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level))
23 
24 #define MAX_CACHE_LEVEL			7	/* Max 7 level supported */
25 
26 #define CTR_FORMAT_MASK	GENMASK(31, 29)
27 #define CTR_FORMAT_ARMV6 0
28 #define CTR_FORMAT_ARMV7 4
29 #define CTR_CWG_MASK	GENMASK(27, 24)
30 #define CTR_DSIZE_LEN_MASK GENMASK(13, 12)
31 #define CTR_ISIZE_LEN_MASK GENMASK(1, 0)
32 
33 /* Also valid for v7m */
34 static inline int cache_line_size_cp15(void)
35 {
36 	u32 ctr = read_cpuid_cachetype();
37 	u32 format = FIELD_GET(CTR_FORMAT_MASK, ctr);
38 
39 	if (format == CTR_FORMAT_ARMV7) {
40 		u32 cwg = FIELD_GET(CTR_CWG_MASK, ctr);
41 
42 		return cwg ? 4 << cwg : ARCH_DMA_MINALIGN;
43 	} else if (WARN_ON_ONCE(format != CTR_FORMAT_ARMV6)) {
44 		return ARCH_DMA_MINALIGN;
45 	}
46 
47 	return 8 << max(FIELD_GET(CTR_ISIZE_LEN_MASK, ctr),
48 			FIELD_GET(CTR_DSIZE_LEN_MASK, ctr));
49 }
50 
51 int cache_line_size(void)
52 {
53 	if (coherency_max_size != 0)
54 		return coherency_max_size;
55 
56 	/* CP15 is optional / implementation defined before ARMv6 */
57 	if (cpu_architecture() < CPU_ARCH_ARMv6)
58 		return ARCH_DMA_MINALIGN;
59 
60 	return cache_line_size_cp15();
61 }
62 EXPORT_SYMBOL_GPL(cache_line_size);
63 
64 static inline enum cache_type get_cache_type(int level)
65 {
66 	u32 clidr;
67 
68 	if (level > MAX_CACHE_LEVEL)
69 		return CACHE_TYPE_NOCACHE;
70 
71 	clidr = read_clidr();
72 
73 	return CLIDR_CTYPE(clidr, level);
74 }
75 
76 static void ci_leaf_init(struct cacheinfo *this_leaf,
77 			 enum cache_type type, unsigned int level)
78 {
79 	this_leaf->level = level;
80 	this_leaf->type = type;
81 }
82 
83 static bool clidr_present(void)
84 {
85 	/* CLIDR is not present before ARMv7/v7m */
86 	if (cpu_architecture() < CPU_ARCH_ARMv7)
87 		return false;
88 
89 	/* Don't try reading CLIDR if CTR declares old format */
90 	return FIELD_GET(CTR_FORMAT_MASK, read_cpuid_cachetype()) == CTR_FORMAT_ARMV7;
91 }
92 
93 static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
94 {
95 	unsigned int ctype, level, leaves;
96 
97 	if (!clidr_present())
98 		return -EOPNOTSUPP;
99 
100 	for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
101 		ctype = get_cache_type(level);
102 		if (ctype == CACHE_TYPE_NOCACHE) {
103 			level--;
104 			break;
105 		}
106 		/* Separate instruction and data caches */
107 		leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
108 	}
109 
110 	*level_p = level;
111 	*leaves_p = leaves;
112 
113 	return 0;
114 }
115 
116 int init_cache_level(unsigned int cpu)
117 {
118 	unsigned int level, leaves;
119 	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
120 	int fw_level;
121 	int ret;
122 
123 	ret = detect_cache_level(&level, &leaves);
124 	if (ret)
125 		return ret;
126 
127 	fw_level = of_find_last_cache_level(cpu);
128 
129 	if (level < fw_level) {
130 		/*
131 		 * some external caches not specified in CLIDR_EL1
132 		 * the information may be available in the device tree
133 		 * only unified external caches are considered here
134 		 */
135 		leaves += (fw_level - level);
136 		level = fw_level;
137 	}
138 
139 	this_cpu_ci->num_levels = level;
140 	this_cpu_ci->num_leaves = leaves;
141 	return 0;
142 }
143 
144 int early_cache_level(unsigned int cpu)
145 {
146 	return init_cache_level(cpu);
147 }
148 
149 int populate_cache_leaves(unsigned int cpu)
150 {
151 	unsigned int level, idx;
152 	enum cache_type type;
153 	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
154 	struct cacheinfo *infos = this_cpu_ci->info_list;
155 
156 	/* The device tree can describe caches CLIDR cannot fill in. */
157 	if (!clidr_present())
158 		return -ENOENT;
159 
160 	for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
161 	     idx < this_cpu_ci->num_leaves; level++) {
162 		type = get_cache_type(level);
163 		if (type == CACHE_TYPE_SEPARATE) {
164 			if (idx + 1 >= this_cpu_ci->num_leaves)
165 				break;
166 			ci_leaf_init(&infos[idx++], CACHE_TYPE_DATA, level);
167 			ci_leaf_init(&infos[idx++], CACHE_TYPE_INST, level);
168 		} else {
169 			ci_leaf_init(&infos[idx++], type, level);
170 		}
171 	}
172 
173 	return 0;
174 }
175