Skip to content

Commit a9ff944

Browse files
lumagRussell King (Oracle)
authored andcommitted
ARM: 9433/2: implement cacheinfo support
On ARMv7 / v7m machines read CTR and CLIDR registers to provide information regarding the cache topology. Earlier machines should describe full cache topology in the device tree. Note, this follows the ARM64 cacheinfo support and provides only minimal support required to bootstrap cache info. All useful properties should be decribed in Device Tree. Reviewed-by: Linus Walleij <[email protected]> Signed-off-by: Dmitry Baryshkov <[email protected]> Signed-off-by: Russell King (Oracle) <[email protected]>
1 parent 961d234 commit a9ff944

File tree

5 files changed

+182
-1
lines changed

5 files changed

+182
-1
lines changed

arch/arm/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ config ARM
55
select ARCH_32BIT_OFF_T
66
select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE if HAVE_KRETPROBES && FRAME_POINTER && !ARM_UNWIND
77
select ARCH_HAS_BINFMT_FLAT
8+
select ARCH_HAS_CACHE_LINE_SIZE if OF
89
select ARCH_HAS_CPU_CACHE_ALIASING
910
select ARCH_HAS_CPU_FINALIZE_INIT if MMU
1011
select ARCH_HAS_CURRENT_STACK_POINTER

arch/arm/include/asm/cache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,10 @@
2626

2727
#define __read_mostly __section(".data..read_mostly")
2828

29+
#ifndef __ASSEMBLY__
30+
#ifdef CONFIG_ARCH_HAS_CACHE_LINE_SIZE
31+
int cache_line_size(void);
32+
#endif
33+
#endif
34+
2935
#endif

arch/arm/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ obj-y += entry-armv.o
4040
endif
4141

4242
obj-$(CONFIG_MMU) += bugs.o
43+
obj-$(CONFIG_OF) += cacheinfo.o
4344
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
4445
obj-$(CONFIG_ISA_DMA_API) += dma.o
4546
obj-$(CONFIG_FIQ) += fiq.o fiqasm.o

arch/arm/kernel/cacheinfo.c

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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(27, 24)
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 */
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+
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+
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+
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+
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+
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+
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+
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+
}

include/linux/cacheinfo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ static inline int get_cpu_cacheinfo_id(int cpu, int level)
147147
return ci ? ci->id : -1;
148148
}
149149

150-
#ifdef CONFIG_ARM64
150+
#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
151151
#define use_arch_cache_info() (true)
152152
#else
153153
#define use_arch_cache_info() (false)

0 commit comments

Comments
 (0)