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 */
cache_line_size_cp15(void)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
cache_line_size(void)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
get_cache_type(int level)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
ci_leaf_init(struct cacheinfo * this_leaf,enum cache_type type,unsigned int level)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
detect_cache_level(unsigned int * level_p,unsigned int * leaves_p)83 static int detect_cache_level(unsigned int *level_p, unsigned int *leaves_p)
84 {
85 unsigned int ctype, level, leaves;
86 u32 ctr, format;
87
88 /* CLIDR is not present before ARMv7/v7m */
89 if (cpu_architecture() < CPU_ARCH_ARMv7)
90 return -EOPNOTSUPP;
91
92 /* Don't try reading CLIDR if CTR declares old format */
93 ctr = read_cpuid_cachetype();
94 format = FIELD_GET(CTR_FORMAT_MASK, ctr);
95 if (format != CTR_FORMAT_ARMV7)
96 return -EOPNOTSUPP;
97
98 for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) {
99 ctype = get_cache_type(level);
100 if (ctype == CACHE_TYPE_NOCACHE) {
101 level--;
102 break;
103 }
104 /* Separate instruction and data caches */
105 leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1;
106 }
107
108 *level_p = level;
109 *leaves_p = leaves;
110
111 return 0;
112 }
113
early_cache_level(unsigned int cpu)114 int early_cache_level(unsigned int cpu)
115 {
116 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
117
118 return detect_cache_level(&this_cpu_ci->num_levels, &this_cpu_ci->num_leaves);
119 }
120
init_cache_level(unsigned int cpu)121 int init_cache_level(unsigned int cpu)
122 {
123 unsigned int level, leaves;
124 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
125 int fw_level;
126 int ret;
127
128 ret = detect_cache_level(&level, &leaves);
129 if (ret)
130 return ret;
131
132 fw_level = of_find_last_cache_level(cpu);
133
134 if (level < fw_level) {
135 /*
136 * some external caches not specified in CLIDR_EL1
137 * the information may be available in the device tree
138 * only unified external caches are considered here
139 */
140 leaves += (fw_level - level);
141 level = fw_level;
142 }
143
144 this_cpu_ci->num_levels = level;
145 this_cpu_ci->num_leaves = leaves;
146 return 0;
147 }
148
populate_cache_leaves(unsigned int cpu)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 *this_leaf = this_cpu_ci->info_list;
155 unsigned int arch = cpu_architecture();
156
157 /* CLIDR is not present before ARMv7/v7m */
158 if (arch < CPU_ARCH_ARMv7)
159 return -EOPNOTSUPP;
160
161 for (idx = 0, level = 1; level <= this_cpu_ci->num_levels &&
162 idx < this_cpu_ci->num_leaves; idx++, level++) {
163 type = get_cache_type(level);
164 if (type == CACHE_TYPE_SEPARATE) {
165 ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level);
166 ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level);
167 } else {
168 ci_leaf_init(this_leaf++, type, level);
169 }
170 }
171
172 return 0;
173 }
174