aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2015-01-08 05:42:34 -0500
committerCatalin Marinas <catalin.marinas@arm.com>2015-01-15 06:55:07 -0500
commit5d425c18653731af62831d30a4fa023d532657a9 (patch)
tree0dff152cf14260f272bf30d8d28269c10935e90f /arch
parent26a945caf381225c9a1e68f14826a884c08ea9cb (diff)
arm64: kernel: add support for cpu cache information
This patch adds support for cacheinfo on ARM64. On ARMv8, the cache hierarchy can be identified through Cache Level ID (CLIDR) register while the cache geometry is provided by Cache Size ID (CCSIDR) register. Since the architecture doesn't provide any way of detecting the cpus sharing particular cache, device tree is used for the same purpose. Signed-off-by: Sudeep Holla <sudeep.holla@arm.com> Cc: Will Deacon <will.deacon@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm64/include/asm/cachetype.h29
-rw-r--r--arch/arm64/kernel/Makefile2
-rw-r--r--arch/arm64/kernel/cacheinfo.c128
-rw-r--r--arch/arm64/kernel/cpuinfo.c12
4 files changed, 152 insertions, 19 deletions
diff --git a/arch/arm64/include/asm/cachetype.h b/arch/arm64/include/asm/cachetype.h
index 4c631a0a3609..da2fc9e3cedd 100644
--- a/arch/arm64/include/asm/cachetype.h
+++ b/arch/arm64/include/asm/cachetype.h
@@ -39,24 +39,41 @@
39 39
40extern unsigned long __icache_flags; 40extern unsigned long __icache_flags;
41 41
42/*
43 * NumSets, bits[27:13] - (Number of sets in cache) - 1
44 * Associativity, bits[12:3] - (Associativity of cache) - 1
45 * LineSize, bits[2:0] - (Log2(Number of words in cache line)) - 2
46 */
47#define CCSIDR_EL1_WRITE_THROUGH BIT(31)
48#define CCSIDR_EL1_WRITE_BACK BIT(30)
49#define CCSIDR_EL1_READ_ALLOCATE BIT(29)
50#define CCSIDR_EL1_WRITE_ALLOCATE BIT(28)
42#define CCSIDR_EL1_LINESIZE_MASK 0x7 51#define CCSIDR_EL1_LINESIZE_MASK 0x7
43#define CCSIDR_EL1_LINESIZE(x) ((x) & CCSIDR_EL1_LINESIZE_MASK) 52#define CCSIDR_EL1_LINESIZE(x) ((x) & CCSIDR_EL1_LINESIZE_MASK)
44 53#define CCSIDR_EL1_ASSOCIATIVITY_SHIFT 3
54#define CCSIDR_EL1_ASSOCIATIVITY_MASK 0x3ff
55#define CCSIDR_EL1_ASSOCIATIVITY(x) \
56 (((x) >> CCSIDR_EL1_ASSOCIATIVITY_SHIFT) & CCSIDR_EL1_ASSOCIATIVITY_MASK)
45#define CCSIDR_EL1_NUMSETS_SHIFT 13 57#define CCSIDR_EL1_NUMSETS_SHIFT 13
46#define CCSIDR_EL1_NUMSETS_MASK (0x7fff << CCSIDR_EL1_NUMSETS_SHIFT) 58#define CCSIDR_EL1_NUMSETS_MASK 0x7fff
47#define CCSIDR_EL1_NUMSETS(x) \ 59#define CCSIDR_EL1_NUMSETS(x) \
48 (((x) & CCSIDR_EL1_NUMSETS_MASK) >> CCSIDR_EL1_NUMSETS_SHIFT) 60 (((x) >> CCSIDR_EL1_NUMSETS_SHIFT) & CCSIDR_EL1_NUMSETS_MASK)
61
62#define CACHE_LINESIZE(x) (16 << CCSIDR_EL1_LINESIZE(x))
63#define CACHE_NUMSETS(x) (CCSIDR_EL1_NUMSETS(x) + 1)
64#define CACHE_ASSOCIATIVITY(x) (CCSIDR_EL1_ASSOCIATIVITY(x) + 1)
49 65
50extern u64 __attribute_const__ icache_get_ccsidr(void); 66extern u64 __attribute_const__ cache_get_ccsidr(u64 csselr);
51 67
68/* Helpers for Level 1 Instruction cache csselr = 1L */
52static inline int icache_get_linesize(void) 69static inline int icache_get_linesize(void)
53{ 70{
54 return 16 << CCSIDR_EL1_LINESIZE(icache_get_ccsidr()); 71 return CACHE_LINESIZE(cache_get_ccsidr(1L));
55} 72}
56 73
57static inline int icache_get_numsets(void) 74static inline int icache_get_numsets(void)
58{ 75{
59 return 1 + CCSIDR_EL1_NUMSETS(icache_get_ccsidr()); 76 return CACHE_NUMSETS(cache_get_ccsidr(1L));
60} 77}
61 78
62/* 79/*
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
21arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ 21arm64-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
36static 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 */
52u64 __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
67static 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
87static 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
107static 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
127DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level)
128DEFINE_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
235u64 __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}