diff options
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r-- | arch/arm64/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/cacheinfo.c | 128 | ||||
-rw-r--r-- | arch/arm64/kernel/cpuinfo.c | 12 |
3 files changed, 129 insertions, 13 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index eaa77ed7766a..77d3d9568333 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile | |||
@@ -16,7 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ | |||
16 | entry-fpsimd.o process.o ptrace.o setup.o signal.o \ | 16 | entry-fpsimd.o process.o ptrace.o setup.o signal.o \ |
17 | sys.o stacktrace.o time.o traps.o io.o vdso.o \ | 17 | sys.o stacktrace.o time.o traps.o io.o vdso.o \ |
18 | hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ | 18 | hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ |
19 | cpuinfo.o cpu_errata.o alternative.o | 19 | cpuinfo.o cpu_errata.o alternative.o cacheinfo.o |
20 | 20 | ||
21 | arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ | 21 | arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ |
22 | sys_compat.o \ | 22 | sys_compat.o \ |
diff --git a/arch/arm64/kernel/cacheinfo.c b/arch/arm64/kernel/cacheinfo.c new file mode 100644 index 000000000000..b8629d52fba9 --- /dev/null +++ b/arch/arm64/kernel/cacheinfo.c | |||
@@ -0,0 +1,128 @@ | |||
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 | |||
20 | #include <linux/bitops.h> | ||
21 | #include <linux/cacheinfo.h> | ||
22 | #include <linux/cpu.h> | ||
23 | #include <linux/compiler.h> | ||
24 | #include <linux/of.h> | ||
25 | |||
26 | #include <asm/cachetype.h> | ||
27 | #include <asm/processor.h> | ||
28 | |||
29 | #define MAX_CACHE_LEVEL 7 /* Max 7 level supported */ | ||
30 | /* Ctypen, bits[3(n - 1) + 2 : 3(n - 1)], for n = 1 to 7 */ | ||
31 | #define CLIDR_CTYPE_SHIFT(level) (3 * (level - 1)) | ||
32 | #define CLIDR_CTYPE_MASK(level) (7 << CLIDR_CTYPE_SHIFT(level)) | ||
33 | #define CLIDR_CTYPE(clidr, level) \ | ||
34 | (((clidr) & CLIDR_CTYPE_MASK(level)) >> CLIDR_CTYPE_SHIFT(level)) | ||
35 | |||
36 | static inline enum cache_type get_cache_type(int level) | ||
37 | { | ||
38 | u64 clidr; | ||
39 | |||
40 | if (level > MAX_CACHE_LEVEL) | ||
41 | return CACHE_TYPE_NOCACHE; | ||
42 | asm volatile ("mrs %x0, clidr_el1" : "=r" (clidr)); | ||
43 | return CLIDR_CTYPE(clidr, level); | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * Cache Size Selection Register(CSSELR) selects which Cache Size ID | ||
48 | * Register(CCSIDR) is accessible by specifying the required cache | ||
49 | * level and the cache type. We need to ensure that no one else changes | ||
50 | * CSSELR by calling this in non-preemtible context | ||
51 | */ | ||
52 | u64 __attribute_const__ cache_get_ccsidr(u64 csselr) | ||
53 | { | ||
54 | u64 ccsidr; | ||
55 | |||
56 | WARN_ON(preemptible()); | ||
57 | |||
58 | /* Put value into CSSELR */ | ||
59 | asm volatile("msr csselr_el1, %x0" : : "r" (csselr)); | ||
60 | isb(); | ||
61 | /* Read result out of CCSIDR */ | ||
62 | asm volatile("mrs %x0, ccsidr_el1" : "=r" (ccsidr)); | ||
63 | |||
64 | return ccsidr; | ||
65 | } | ||
66 | |||
67 | static void ci_leaf_init(struct cacheinfo *this_leaf, | ||
68 | enum cache_type type, unsigned int level) | ||
69 | { | ||
70 | bool is_icache = type & CACHE_TYPE_INST; | ||
71 | u64 tmp = cache_get_ccsidr((level - 1) << 1 | is_icache); | ||
72 | |||
73 | this_leaf->level = level; | ||
74 | this_leaf->type = type; | ||
75 | this_leaf->coherency_line_size = CACHE_LINESIZE(tmp); | ||
76 | this_leaf->number_of_sets = CACHE_NUMSETS(tmp); | ||
77 | this_leaf->ways_of_associativity = CACHE_ASSOCIATIVITY(tmp); | ||
78 | this_leaf->size = this_leaf->number_of_sets * | ||
79 | this_leaf->coherency_line_size * this_leaf->ways_of_associativity; | ||
80 | this_leaf->attributes = | ||
81 | ((tmp & CCSIDR_EL1_WRITE_THROUGH) ? CACHE_WRITE_THROUGH : 0) | | ||
82 | ((tmp & CCSIDR_EL1_WRITE_BACK) ? CACHE_WRITE_BACK : 0) | | ||
83 | ((tmp & CCSIDR_EL1_READ_ALLOCATE) ? CACHE_READ_ALLOCATE : 0) | | ||
84 | ((tmp & CCSIDR_EL1_WRITE_ALLOCATE) ? CACHE_WRITE_ALLOCATE : 0); | ||
85 | } | ||
86 | |||
87 | static int __init_cache_level(unsigned int cpu) | ||
88 | { | ||
89 | unsigned int ctype, level, leaves; | ||
90 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
91 | |||
92 | for (level = 1, leaves = 0; level <= MAX_CACHE_LEVEL; level++) { | ||
93 | ctype = get_cache_type(level); | ||
94 | if (ctype == CACHE_TYPE_NOCACHE) { | ||
95 | level--; | ||
96 | break; | ||
97 | } | ||
98 | /* Separate instruction and data caches */ | ||
99 | leaves += (ctype == CACHE_TYPE_SEPARATE) ? 2 : 1; | ||
100 | } | ||
101 | |||
102 | this_cpu_ci->num_levels = level; | ||
103 | this_cpu_ci->num_leaves = leaves; | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int __populate_cache_leaves(unsigned int cpu) | ||
108 | { | ||
109 | unsigned int level, idx; | ||
110 | enum cache_type type; | ||
111 | struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); | ||
112 | struct cacheinfo *this_leaf = this_cpu_ci->info_list; | ||
113 | |||
114 | for (idx = 0, level = 1; level <= this_cpu_ci->num_levels && | ||
115 | idx < this_cpu_ci->num_leaves; idx++, level++) { | ||
116 | type = get_cache_type(level); | ||
117 | if (type == CACHE_TYPE_SEPARATE) { | ||
118 | ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); | ||
119 | ci_leaf_init(this_leaf++, CACHE_TYPE_INST, level); | ||
120 | } else { | ||
121 | ci_leaf_init(this_leaf++, type, level); | ||
122 | } | ||
123 | } | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level) | ||
128 | DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves) | ||
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 07d435cf2eea..49782282a027 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c | |||
@@ -231,15 +231,3 @@ void __init cpuinfo_store_boot_cpu(void) | |||
231 | 231 | ||
232 | boot_cpu_data = *info; | 232 | boot_cpu_data = *info; |
233 | } | 233 | } |
234 | |||
235 | u64 __attribute_const__ icache_get_ccsidr(void) | ||
236 | { | ||
237 | u64 ccsidr; | ||
238 | |||
239 | WARN_ON(preemptible()); | ||
240 | |||
241 | /* Select L1 I-cache and read its size ID register */ | ||
242 | asm("msr csselr_el1, %1; isb; mrs %0, ccsidr_el1" | ||
243 | : "=r"(ccsidr) : "r"(1L)); | ||
244 | return ccsidr; | ||
245 | } | ||