1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
On Tue, 2008-07-01 at 06:23 +0100, Dirk Behme wrote:
> Catalin Marinas wrote:
> > But, anyway, if you want a patch, Harry is updating it to a recent
> > kernel.
>
> Any news on this? I think there are some people wanting a patch ;)
See below for a preliminary patch updated to 2.6.26-rc8. Note that I
don't plan to submit it in its current form but clean it up a bit first.
Show the cache type of ARMv7 CPUs
From: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
---
arch/arm/kernel/setup.c | 137 +++++++++++++++++++++++++++++++++++++++++++++-
include/asm-arm/system.h | 18 ++++++
2 files changed, 153 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 5ae0eb2..0cd238d 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -256,6 +256,24 @@ static const char *proc_arch[] = {
"?(17)",
};
+static const char *v7_cache_policy[4] = {
+ "reserved",
+ "AVIVT",
+ "VIPT",
+ "PIPT",
+};
+
+static const char *v7_cache_type[8] = {
+ "none",
+ "instruction only",
+ "data only",
+ "separate instruction and data",
+ "unified",
+ "unknown type",
+ "unknown type",
+ "unknown type",
+};
+
#define CACHE_TYPE(x) (((x) >> 25) & 15)
#define CACHE_S(x) ((x) & (1 << 24))
#define CACHE_DSIZE(x) (((x) >> 12) & 4095) /* only if S=1 */
@@ -266,6 +284,22 @@ static const char *proc_arch[] = {
#define CACHE_M(y) ((y) & (1 << 2))
#define CACHE_LINE(y) ((y) & 3)
+#define CACHE_TYPE_V7(x) (((x) >> 14) & 3)
+#define CACHE_UNIFIED(x) ((((x) >> 27) & 7)+1)
+#define CACHE_COHERENT(x) ((((x) >> 24) & 7)+1)
+
+#define CACHE_ID_LEVEL_MASK 7
+#define CACHE_ID_LEVEL_BITS 3
+
+#define CACHE_LINE_V7(v) ((1 << (((v) & 7)+4)))
+#define CACHE_ASSOC_V7(v) ((((v) >> 3) & ((1<<10)-1))+1)
+#define CACHE_SETS_V7(v) ((((v) >> 13) & ((1<<15)-1))+1)
+#define CACHE_SIZE_V7(v) (CACHE_LINE_V7(v)*CACHE_ASSOC_V7(v)*CACHE_SETS_V7(v))
+#define CACHE_WA_V7(v) (((v) & (1<<28)) != 0)
+#define CACHE_RA_V7(v) (((v) & (1<<29)) != 0)
+#define CACHE_WB_V7(v) (((v) & (1<<30)) != 0)
+#define CACHE_WT_V7(v) (((v) & (1<<31)) != 0)
+
static inline void dump_cache(const char *prefix, int cpu, unsigned int cache)
{
unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0);
@@ -279,11 +313,57 @@ static inline void dump_cache(const char *prefix, int cpu, unsigned int cache)
CACHE_LINE(cache)));
}
+static void dump_v7_cache(const char *type, int cpu, unsigned int level)
+{
+ unsigned int cachesize;
+
+ write_extended_cpuid(2,0,0,0,level); /* Set the cache size selection register */
+ write_extended_cpuid(0,7,5,4,0); /* Prefetch flush to wait for above */
+ cachesize = read_extended_cpuid(1,0,0,0);
+
+ printk("CPU%u: %s cache: %d bytes, associativity %d, %d byte lines, %d sets,\n supports%s%s%s%s\n",
+ cpu, type,
+ CACHE_SIZE_V7(cachesize),CACHE_ASSOC_V7(cachesize),
+ CACHE_LINE_V7(cachesize),CACHE_SETS_V7(cachesize),
+ CACHE_WA_V7(cachesize) ? " WA" : "",
+ CACHE_RA_V7(cachesize) ? " RA" : "",
+ CACHE_WB_V7(cachesize) ? " WB" : "",
+ CACHE_WT_V7(cachesize) ? " WT" : "");
+}
+
static void __init dump_cpu_info(int cpu)
{
unsigned int info = read_cpuid(CPUID_CACHETYPE);
- if (info != processor_id) {
+ if (info != processor_id && (info & (1 << 31))) {
+ /* ARMv7 style of cache info register */
+ unsigned int id = read_extended_cpuid(1,0,0,1);
+ unsigned int level = 0;
+ printk("CPU%u: L1 I %s cache. Caches unified at level %u, coherent at level %u\n",
+ cpu,
+ v7_cache_policy[CACHE_TYPE_V7(info)],
+ CACHE_UNIFIED(id),
+ CACHE_COHERENT(id));
+
+ while (id & CACHE_ID_LEVEL_MASK) {
+ printk("CPU%u: Level %u cache is %s\n",
+ cpu, (level >> 1)+1, v7_cache_type[id & CACHE_ID_LEVEL_MASK]);
+
+ if (id & 1) {
+ /* Dump I at this level */
+ dump_v7_cache("I", cpu, level | 1);
+ }
+
+ if (id & (4 | 2)) {
+ /* Dump D or unified at this level */
+ dump_v7_cache((id & 4) ? "unified" : "D", cpu, level);
+ }
+
+ /* Next level out */
+ level += 2;
+ id >>= CACHE_ID_LEVEL_BITS;
+ }
+ } else if (info != processor_id) {
printk("CPU%u: D %s %s cache\n", cpu, cache_is_vivt() ? "VIVT" : "VIPT",
cache_types[CACHE_TYPE(info)]);
if (CACHE_S(info)) {
@@ -916,6 +996,30 @@ c_show_cache(struct seq_file *m, const char *type, unsigned int cache)
CACHE_LINE(cache)));
}
+static void c_show_v7_cache(struct seq_file *m, const char *type, unsigned int levelselect)
+{
+ unsigned int cachesize;
+ unsigned int level = (levelselect >> 1) + 1;
+
+ write_extended_cpuid(2,0,0,0,levelselect); /* Set the cache size selection register */
+ write_extended_cpuid(0,7,5,4,0); /* Prefetch flush to wait for above */
+ cachesize = read_extended_cpuid(1,0,0,0);
+
+ seq_printf(m, "L%u %s size\t\t: %d bytes\n"
+ "L%u %s assoc\t\t: %d\n"
+ "L%u %s line length\t: %d\n"
+ "L%u %s sets\t\t: %d\n"
+ "L%u %s supports\t\t:%s%s%s%s\n",
+ level, type, CACHE_SIZE_V7(cachesize),
+ level, type, CACHE_ASSOC_V7(cachesize),
+ level, type, CACHE_LINE_V7(cachesize),
+ level, type, CACHE_SETS_V7(cachesize),
+ level, type, CACHE_WA_V7(cachesize) ? " WA" : "",
+ CACHE_RA_V7(cachesize) ? " RA" : "",
+ CACHE_WB_V7(cachesize) ? " WB" : "",
+ CACHE_WT_V7(cachesize) ? " WT" : "");
+}
+
static int c_show(struct seq_file *m, void *v)
{
int i;
@@ -971,7 +1075,36 @@ static int c_show(struct seq_file *m, void *v)
{
unsigned int cache_info = read_cpuid(CPUID_CACHETYPE);
- if (cache_info != processor_id) {
+ if (cache_info != processor_id && (cache_info & (1<<31))) {
+ /* V7 style of cache info register */
+ unsigned int id = read_extended_cpuid(1,0,0,1);
+ unsigned int levelselect = 0;
+ seq_printf(m, "L1 I cache\t:%s\n"
+ "Cache unification level\t: %u\n"
+ "Cache coherency level\t: %u\n",
+ v7_cache_policy[CACHE_TYPE_V7(cache_info)],
+ CACHE_UNIFIED(id),
+ CACHE_COHERENT(id));
+
+ while (id & CACHE_ID_LEVEL_MASK) {
+ seq_printf(m, "Level %u cache\t\t: %s\n",
+ (levelselect >> 1)+1, v7_cache_type[id & CACHE_ID_LEVEL_MASK]);
+
+ if (id & 1) {
+ /* Dump I at this level */
+ c_show_v7_cache(m, "I", levelselect | 1);
+ }
+
+ if (id & (4 | 2)) {
+ /* Dump D or unified at this level */
+ c_show_v7_cache(m, (id & 4) ? "cache" : "D", levelselect);
+ }
+
+ /* Next level out */
+ levelselect += 2;
+ id >>= CACHE_ID_LEVEL_BITS;
+ }
+ } else if (cache_info != processor_id) {
seq_printf(m, "Cache type\t: %s\n"
"Cache clean\t: %s\n"
"Cache lockdown\t: %s\n"
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 514af79..704738e 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -74,6 +74,24 @@
: "cc"); \
__val; \
})
+#define read_extended_cpuid(op1,op2,op3,op4) \
+ ({ \
+ unsigned int __val; \
+ asm("mrc p15," __stringify(op1) ",%0,c" __stringify(op2)",c" __stringify(op3)"," __stringify(op4) \
+ : "=r" (__val) \
+ : \
+ : "cc"); \
+ __val; \
+ })
+
+#define write_extended_cpuid(op1,op2,op3,op4,v) \
+ ({ \
+ unsigned int __val = v; \
+ asm("mcr p15," __stringify(op1) ",%0,c" __stringify(op2)",c" __stringify(op3)"," __stringify(op4) \
+ : \
+ : "r" (__val) \
+ : "cc"); \
+ })
#else
extern unsigned int processor_id;
#define read_cpuid(reg) (processor_id)
--
Catalin
|