diff options
-rw-r--r-- | drivers/base/cacheinfo.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index f19d50bd8925..2376628c599c 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c | |||
@@ -88,7 +88,120 @@ static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, | |||
88 | { | 88 | { |
89 | return sib_leaf->of_node == this_leaf->of_node; | 89 | return sib_leaf->of_node == this_leaf->of_node; |
90 | } | 90 | } |
91 | |||
92 | /* OF properties to query for a given cache type */ | ||
93 | struct cache_type_info { | ||
94 | const char *size_prop; | ||
95 | const char *line_size_props[2]; | ||
96 | const char *nr_sets_prop; | ||
97 | }; | ||
98 | |||
99 | static const struct cache_type_info cache_type_info[] = { | ||
100 | { | ||
101 | .size_prop = "cache-size", | ||
102 | .line_size_props = { "cache-line-size", | ||
103 | "cache-block-size", }, | ||
104 | .nr_sets_prop = "cache-sets", | ||
105 | }, { | ||
106 | .size_prop = "i-cache-size", | ||
107 | .line_size_props = { "i-cache-line-size", | ||
108 | "i-cache-block-size", }, | ||
109 | .nr_sets_prop = "i-cache-sets", | ||
110 | }, { | ||
111 | .size_prop = "d-cache-size", | ||
112 | .line_size_props = { "d-cache-line-size", | ||
113 | "d-cache-block-size", }, | ||
114 | .nr_sets_prop = "d-cache-sets", | ||
115 | }, | ||
116 | }; | ||
117 | |||
118 | static inline int get_cacheinfo_idx(enum cache_type type) | ||
119 | { | ||
120 | if (type == CACHE_TYPE_UNIFIED) | ||
121 | return 0; | ||
122 | return type; | ||
123 | } | ||
124 | |||
125 | static void cache_size(struct cacheinfo *this_leaf) | ||
126 | { | ||
127 | const char *propname; | ||
128 | const __be32 *cache_size; | ||
129 | int ct_idx; | ||
130 | |||
131 | ct_idx = get_cacheinfo_idx(this_leaf->type); | ||
132 | propname = cache_type_info[ct_idx].size_prop; | ||
133 | |||
134 | cache_size = of_get_property(this_leaf->of_node, propname, NULL); | ||
135 | if (cache_size) | ||
136 | this_leaf->size = of_read_number(cache_size, 1); | ||
137 | } | ||
138 | |||
139 | /* not cache_line_size() because that's a macro in include/linux/cache.h */ | ||
140 | static void cache_get_line_size(struct cacheinfo *this_leaf) | ||
141 | { | ||
142 | const __be32 *line_size; | ||
143 | int i, lim, ct_idx; | ||
144 | |||
145 | ct_idx = get_cacheinfo_idx(this_leaf->type); | ||
146 | lim = ARRAY_SIZE(cache_type_info[ct_idx].line_size_props); | ||
147 | |||
148 | for (i = 0; i < lim; i++) { | ||
149 | const char *propname; | ||
150 | |||
151 | propname = cache_type_info[ct_idx].line_size_props[i]; | ||
152 | line_size = of_get_property(this_leaf->of_node, propname, NULL); | ||
153 | if (line_size) | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | if (line_size) | ||
158 | this_leaf->coherency_line_size = of_read_number(line_size, 1); | ||
159 | } | ||
160 | |||
161 | static void cache_nr_sets(struct cacheinfo *this_leaf) | ||
162 | { | ||
163 | const char *propname; | ||
164 | const __be32 *nr_sets; | ||
165 | int ct_idx; | ||
166 | |||
167 | ct_idx = get_cacheinfo_idx(this_leaf->type); | ||
168 | propname = cache_type_info[ct_idx].nr_sets_prop; | ||
169 | |||
170 | nr_sets = of_get_property(this_leaf->of_node, propname, NULL); | ||
171 | if (nr_sets) | ||
172 | this_leaf->number_of_sets = of_read_number(nr_sets, 1); | ||
173 | } | ||
174 | |||
175 | static void cache_associativity(struct cacheinfo *this_leaf) | ||
176 | { | ||
177 | unsigned int line_size = this_leaf->coherency_line_size; | ||
178 | unsigned int nr_sets = this_leaf->number_of_sets; | ||
179 | unsigned int size = this_leaf->size; | ||
180 | |||
181 | /* | ||
182 | * If the cache is fully associative, there is no need to | ||
183 | * check the other properties. | ||
184 | */ | ||
185 | if (!(nr_sets == 1) && (nr_sets > 0 && size > 0 && line_size > 0)) | ||
186 | this_leaf->ways_of_associativity = (size / nr_sets) / line_size; | ||
187 | } | ||
188 | |||
189 | static void cache_of_override_properties(unsigned int cpu) | ||
190 | { | ||
191 | int index; | ||
192 | struct cacheinfo *this_leaf; | ||
193 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
194 | |||
195 | for (index = 0; index < cache_leaves(cpu); index++) { | ||
196 | this_leaf = this_cpu_ci->info_list + index; | ||
197 | cache_size(this_leaf); | ||
198 | cache_get_line_size(this_leaf); | ||
199 | cache_nr_sets(this_leaf); | ||
200 | cache_associativity(this_leaf); | ||
201 | } | ||
202 | } | ||
91 | #else | 203 | #else |
204 | static void cache_of_override_properties(unsigned int cpu) { } | ||
92 | static inline int cache_setup_of_node(unsigned int cpu) { return 0; } | 205 | static inline int cache_setup_of_node(unsigned int cpu) { return 0; } |
93 | static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, | 206 | static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, |
94 | struct cacheinfo *sib_leaf) | 207 | struct cacheinfo *sib_leaf) |
@@ -171,6 +284,12 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) | |||
171 | } | 284 | } |
172 | } | 285 | } |
173 | 286 | ||
287 | static void cache_override_properties(unsigned int cpu) | ||
288 | { | ||
289 | if (of_have_populated_dt()) | ||
290 | return cache_of_override_properties(cpu); | ||
291 | } | ||
292 | |||
174 | static void free_cache_attributes(unsigned int cpu) | 293 | static void free_cache_attributes(unsigned int cpu) |
175 | { | 294 | { |
176 | if (!per_cpu_cacheinfo(cpu)) | 295 | if (!per_cpu_cacheinfo(cpu)) |
@@ -216,6 +335,8 @@ static int detect_cache_attributes(unsigned int cpu) | |||
216 | pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); | 335 | pr_warn("Unable to detect cache hierarchy for CPU %d\n", cpu); |
217 | goto free_ci; | 336 | goto free_ci; |
218 | } | 337 | } |
338 | |||
339 | cache_override_properties(cpu); | ||
219 | return 0; | 340 | return 0; |
220 | 341 | ||
221 | free_ci: | 342 | free_ci: |