Commit | Line | Data |
---|---|---|
5d425c18 SH |
1 | /* |
2 | * ARM64 cacheinfo support | |
3 | * | |
4 | * Copyright (C) 2015 ARM Ltd. | |
5 | * All Rights Reserved | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
12 | * kind, whether express or implied; without even the implied warranty | |
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
8571890e | 20 | #include <linux/acpi.h> |
5d425c18 | 21 | #include <linux/cacheinfo.h> |
5d425c18 SH |
22 | #include <linux/of.h> |
23 | ||
5d425c18 SH |
24 | #define MAX_CACHE_LEVEL 7 /* Max 7 level supported */ |
25 | /* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */ | |
26 | #define CLIDR_CTYPE_SHIFT(level) (3 * (level - 1)) | |
27 | #define CLIDR_CTYPE_MASK(level) (7 << CLIDR_CTYPE_SHIFT(level)) | |
28 | #define CLIDR_CTYPE(clidr, level) \ | |
29 | (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level)) | |
30 | ||
31 | static inline enum cache_type get_cache_type(int level) | |
32 | { | |
33 | u64 clidr; | |
34 | ||
35 | if (level > MAX_CACHE_LEVEL) | |
36 | return CACHE_TYPE_NOCACHE; | |
adf75899 | 37 | clidr = read_sysreg(clidr_el1); |
5d425c18 SH |
38 | return CLIDR_CTYPE(clidr, level); |
39 | } | |
40 | ||
5d425c18 SH |
41 | static void ci_leaf_init(struct cacheinfo *this_leaf, |
42 | enum cache_type type, unsigned int level) | |
43 | { | |
5d425c18 SH |
44 | this_leaf->level = level; |
45 | this_leaf->type = type; | |
5d425c18 SH |
46 | } |
47 | ||
48 | static int __init_cache_level(unsigned int cpu) | |
49 | { | |
8571890e | 50 | unsigned int ctype, level, leaves, fw_level; |
5d425c18 SH |
51 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); |
52 | ||
53 | for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) { | |
54 | ctype = get_cache_type(level); | |
55 | if (ctype == CACHE_TYPE_NOCACHE) { | |
56 | level--; | |
57 | break; | |
58 | } | |
59 | /* Separate instruction and data caches */ | |
60 | leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; | |
61 | } | |
62 | ||
8571890e JL |
63 | if (acpi_disabled) |
64 | fw_level = of_find_last_cache_level(cpu); | |
65 | else | |
66 | fw_level = acpi_find_last_cache_level(cpu); | |
67 | ||
68 | if (level < fw_level) { | |
9a802431 SH |
69 | /* |
70 | * some external caches not specified in CLIDR_EL1 | |
71 | * the information may be available in the device tree | |
72 | * only unified external caches are considered here | |
73 | */ | |
8571890e JL |
74 | leaves += (fw_level - level); |
75 | level = fw_level; | |
9a802431 SH |
76 | } |
77 | ||
5d425c18 SH |
78 | this_cpu_ci->num_levels = level; |
79 | this_cpu_ci->num_leaves = leaves; | |
80 | return 0; | |
81 | } | |
82 | ||
83 | static int __populate_cache_leaves(unsigned int cpu) | |
84 | { | |
85 | unsigned int level, idx; | |
86 | enum cache_type type; | |
87 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | |
88 | struct cacheinfo *this_leaf = this_cpu_ci->info_list; | |
89 | ||
90 | for (idx = 0, level = 1; level <= this_cpu_ci->num_levels && | |
91 | idx < this_cpu_ci->num_leaves; idx++, level++) { | |
92 | type = get_cache_type(level); | |
93 | if (type == CACHE_TYPE_SEPARATE) { | |
94 | ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); | |
95 | ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level); | |
96 | } else { | |
97 | ci_leaf_init(this_leaf++, type, level); | |
98 | } | |
99 | } | |
100 | return 0; | |
101 | } | |
102 | ||
103 | DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level) | |
104 | DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves) |