blob: dcc983196aa26005a2016fc3971cb8aa46f9a29f [file] [log] [blame]
Mark Rutlanddf857412014-07-16 16:32:44 +01001/*
2 * Record and handle CPU attributes.
3 *
4 * Copyright (C) 2014 ARM Ltd.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#include <asm/arch_timer.h>
18#include <asm/cachetype.h>
19#include <asm/cpu.h>
20#include <asm/cputype.h>
Andre Przywarae116a372014-11-14 15:54:09 +000021#include <asm/cpufeature.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010022
Mark Rutland59ccc0d2014-07-16 16:32:45 +010023#include <linux/bitops.h>
Ard Biesheuvel80c517b2014-08-08 12:51:39 +010024#include <linux/bug.h>
Catalin Marinase47b0202016-05-31 15:55:03 +010025#include <linux/compat.h>
26#include <linux/elf.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010027#include <linux/init.h>
Mark Rutland127161a2014-07-16 16:32:46 +010028#include <linux/kernel.h>
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010029#include <linux/personality.h>
Ard Biesheuvel80c517b2014-08-08 12:51:39 +010030#include <linux/preempt.h>
Mark Rutland59ccc0d2014-07-16 16:32:45 +010031#include <linux/printk.h>
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010032#include <linux/seq_file.h>
33#include <linux/sched.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010034#include <linux/smp.h>
Yang Shi92e788b2015-11-18 10:48:55 -080035#include <linux/delay.h>
jianxin.pan753d6722017-07-05 20:04:42 +080036#ifdef CONFIG_AMLOGIC_CPU_INFO
37#include <linux/amlogic/cpu_version.h>
38#endif
39
Mark Rutlanddf857412014-07-16 16:32:44 +010040
41/*
42 * In case the boot CPU is hotpluggable, we record its initial state and
43 * current state separately. Certain system registers may contain different
44 * values depending on configuration at or after reset.
45 */
46DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data);
47static struct cpuinfo_arm64 boot_cpu_data;
48
Mark Rutland59ccc0d2014-07-16 16:32:45 +010049static char *icache_policy_str[] = {
50 [ICACHE_POLICY_RESERVED] = "RESERVED/UNKNOWN",
51 [ICACHE_POLICY_AIVIVT] = "AIVIVT",
52 [ICACHE_POLICY_VIPT] = "VIPT",
53 [ICACHE_POLICY_PIPT] = "PIPT",
54};
55
56unsigned long __icache_flags;
57
Dave Martin9299b242015-07-30 16:36:25 +010058static const char *const hwcap_str[] = {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010059 "fp",
60 "asimd",
61 "evtstrm",
62 "aes",
63 "pmull",
64 "sha1",
65 "sha2",
66 "crc32",
67 "atomics",
Suzuki K Poulosebf500612016-01-26 15:52:46 +000068 "fphp",
69 "asimdhp",
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010070 NULL
71};
72
73#ifdef CONFIG_COMPAT
Dave Martin9299b242015-07-30 16:36:25 +010074static const char *const compat_hwcap_str[] = {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010075 "swp",
76 "half",
77 "thumb",
78 "26bit",
79 "fastmult",
80 "fpa",
81 "vfp",
82 "edsp",
83 "java",
84 "iwmmxt",
85 "crunch",
86 "thumbee",
87 "neon",
88 "vfpv3",
89 "vfpv3d16",
90 "tls",
91 "vfpv4",
92 "idiva",
93 "idivt",
94 "vfpd32",
95 "lpae",
Julien Grallf228b492016-05-10 15:40:31 +010096 "evtstrm",
97 NULL
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010098};
99
Dave Martin9299b242015-07-30 16:36:25 +0100100static const char *const compat_hwcap2_str[] = {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100101 "aes",
102 "pmull",
103 "sha1",
104 "sha2",
105 "crc32",
106 NULL
107};
108#endif /* CONFIG_COMPAT */
109
110static int c_show(struct seq_file *m, void *v)
111{
112 int i, j;
Catalin Marinase47b0202016-05-31 15:55:03 +0100113 bool compat = personality(current->personality) == PER_LINUX32;
jianxin.pan753d6722017-07-05 20:04:42 +0800114#ifdef CONFIG_AMLOGIC_CPU_INFO
115 unsigned char chipid[CHIPID_LEN];
116#endif
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100117
118 for_each_online_cpu(i) {
119 struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
120 u32 midr = cpuinfo->reg_midr;
121
122 /*
123 * glibc reads /proc/cpuinfo to determine the number of
124 * online processors, looking for lines beginning with
125 * "processor". Give glibc what it expects.
126 */
127 seq_printf(m, "processor\t: %d\n", i);
Catalin Marinase47b0202016-05-31 15:55:03 +0100128 if (compat)
129 seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
130 MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100131
Yang Shi92e788b2015-11-18 10:48:55 -0800132 seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
133 loops_per_jiffy / (500000UL/HZ),
134 loops_per_jiffy / (5000UL/HZ) % 100);
135
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100136 /*
137 * Dump out the common processor features in a single line.
138 * Userspace should read the hwcaps with getauxval(AT_HWCAP)
139 * rather than attempting to parse this, but there's a body of
140 * software which does already (at least for 32-bit).
141 */
142 seq_puts(m, "Features\t:");
Catalin Marinase47b0202016-05-31 15:55:03 +0100143 if (compat) {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100144#ifdef CONFIG_COMPAT
145 for (j = 0; compat_hwcap_str[j]; j++)
146 if (compat_elf_hwcap & (1 << j))
147 seq_printf(m, " %s", compat_hwcap_str[j]);
148
149 for (j = 0; compat_hwcap2_str[j]; j++)
150 if (compat_elf_hwcap2 & (1 << j))
151 seq_printf(m, " %s", compat_hwcap2_str[j]);
152#endif /* CONFIG_COMPAT */
153 } else {
154 for (j = 0; hwcap_str[j]; j++)
155 if (elf_hwcap & (1 << j))
156 seq_printf(m, " %s", hwcap_str[j]);
157 }
158 seq_puts(m, "\n");
159
160 seq_printf(m, "CPU implementer\t: 0x%02x\n",
161 MIDR_IMPLEMENTOR(midr));
162 seq_printf(m, "CPU architecture: 8\n");
163 seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
164 seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
165 seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
166 }
jianxin.pan753d6722017-07-05 20:04:42 +0800167#ifdef CONFIG_AMLOGIC_CPU_INFO
168 cpuinfo_get_chipid(chipid, CHIPID_LEN);
169 seq_puts(m, "Serial\t\t: ");
170 for (i = 0; i < 16; i++)
171 seq_printf(m, "%02x", chipid[i]);
172 seq_puts(m, "\n");
173#endif
Kevin Kim6a613fe2018-11-29 18:38:05 +0900174 system_rev = 0x0400;
175 seq_printf(m, "Hardware\t: %s\n", machine_name);
176 seq_printf(m, "Revision\t: %04x\n\n", system_rev);
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100177 return 0;
178}
179
180static void *c_start(struct seq_file *m, loff_t *pos)
181{
182 return *pos < 1 ? (void *)1 : NULL;
183}
184
185static void *c_next(struct seq_file *m, void *v, loff_t *pos)
186{
187 ++*pos;
188 return NULL;
189}
190
191static void c_stop(struct seq_file *m, void *v)
192{
193}
194
195const struct seq_operations cpuinfo_op = {
196 .start = c_start,
197 .next = c_next,
198 .stop = c_stop,
199 .show = c_show
200};
201
Steve Capperf8d9f922016-07-08 16:01:13 +0100202
203static struct kobj_type cpuregs_kobj_type = {
204 .sysfs_ops = &kobj_sysfs_ops,
205};
206
207/*
208 * The ARM ARM uses the phrase "32-bit register" to describe a register
209 * whose upper 32 bits are RES0 (per C5.1.1, ARM DDI 0487A.i), however
210 * no statement is made as to whether the upper 32 bits will or will not
211 * be made use of in future, and between ARM DDI 0487A.c and ARM DDI
212 * 0487A.d CLIDR_EL1 was expanded from 32-bit to 64-bit.
213 *
214 * Thus, while both MIDR_EL1 and REVIDR_EL1 are described as 32-bit
215 * registers, we expose them both as 64 bit values to cater for possible
216 * future expansion without an ABI break.
217 */
218#define kobj_to_cpuinfo(kobj) container_of(kobj, struct cpuinfo_arm64, kobj)
219#define CPUREGS_ATTR_RO(_name, _field) \
220 static ssize_t _name##_show(struct kobject *kobj, \
221 struct kobj_attribute *attr, char *buf) \
222 { \
223 struct cpuinfo_arm64 *info = kobj_to_cpuinfo(kobj); \
224 \
225 if (info->reg_midr) \
226 return sprintf(buf, "0x%016x\n", info->reg_##_field); \
227 else \
228 return 0; \
229 } \
230 static struct kobj_attribute cpuregs_attr_##_name = __ATTR_RO(_name)
231
232CPUREGS_ATTR_RO(midr_el1, midr);
233CPUREGS_ATTR_RO(revidr_el1, revidr);
234
235static struct attribute *cpuregs_id_attrs[] = {
236 &cpuregs_attr_midr_el1.attr,
237 &cpuregs_attr_revidr_el1.attr,
238 NULL
239};
240
241static struct attribute_group cpuregs_attr_group = {
242 .attrs = cpuregs_id_attrs,
243 .name = "identification"
244};
245
246static int cpuid_add_regs(int cpu)
247{
248 int rc;
249 struct device *dev;
250 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
251
252 dev = get_cpu_device(cpu);
253 if (!dev) {
254 rc = -ENODEV;
255 goto out;
256 }
257 rc = kobject_add(&info->kobj, &dev->kobj, "regs");
258 if (rc)
259 goto out;
260 rc = sysfs_create_group(&info->kobj, &cpuregs_attr_group);
261 if (rc)
262 kobject_del(&info->kobj);
263out:
264 return rc;
265}
266
267static int cpuid_remove_regs(int cpu)
268{
269 struct device *dev;
270 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
271
272 dev = get_cpu_device(cpu);
273 if (!dev)
274 return -ENODEV;
275 if (info->kobj.parent) {
276 sysfs_remove_group(&info->kobj, &cpuregs_attr_group);
277 kobject_del(&info->kobj);
278 }
279
280 return 0;
281}
282
283static int cpuid_callback(struct notifier_block *nb,
284 unsigned long action, void *hcpu)
285{
286 int rc = 0;
287 unsigned long cpu = (unsigned long)hcpu;
288
289 switch (action & ~CPU_TASKS_FROZEN) {
290 case CPU_ONLINE:
291 rc = cpuid_add_regs(cpu);
292 break;
293 case CPU_DEAD:
294 rc = cpuid_remove_regs(cpu);
295 break;
296 }
297
298 return notifier_from_errno(rc);
299}
300
301static int __init cpuinfo_regs_init(void)
302{
303 int cpu;
304
305 cpu_notifier_register_begin();
306
307 for_each_possible_cpu(cpu) {
308 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
309
310 kobject_init(&info->kobj, &cpuregs_kobj_type);
311 if (cpu_online(cpu))
312 cpuid_add_regs(cpu);
313 }
314 __hotcpu_notifier(cpuid_callback, 0);
315
316 cpu_notifier_register_done();
317 return 0;
318}
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100319static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
320{
321 unsigned int cpu = smp_processor_id();
322 u32 l1ip = CTR_L1IP(info->reg_ctr);
323
Ard Biesheuvel169c0182014-08-08 12:51:40 +0100324 if (l1ip != ICACHE_POLICY_PIPT) {
325 /*
326 * VIPT caches are non-aliasing if the VA always equals the PA
327 * in all bit positions that are covered by the index. This is
328 * the case if the size of a way (# of sets * line size) does
329 * not exceed PAGE_SIZE.
330 */
331 u32 waysize = icache_get_numsets() * icache_get_linesize();
332
333 if (l1ip != ICACHE_POLICY_VIPT || waysize > PAGE_SIZE)
334 set_bit(ICACHEF_ALIASING, &__icache_flags);
335 }
Ard Biesheuvela3a80542014-08-05 10:25:55 +0100336 if (l1ip == ICACHE_POLICY_AIVIVT)
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100337 set_bit(ICACHEF_AIVIVT, &__icache_flags);
338
Mark Rutlandea171962014-08-01 10:23:20 +0100339 pr_info("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu);
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100340}
341
Mark Rutlanddf857412014-07-16 16:32:44 +0100342static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
343{
344 info->reg_cntfrq = arch_timer_get_cntfrq();
345 info->reg_ctr = read_cpuid_cachetype();
Mark Rutland1cc6ed92016-03-04 12:54:05 +0000346 info->reg_dczid = read_cpuid(DCZID_EL0);
Mark Rutlanddf857412014-07-16 16:32:44 +0100347 info->reg_midr = read_cpuid_id();
Steve Capperf8d9f922016-07-08 16:01:13 +0100348 info->reg_revidr = read_cpuid(REVIDR_EL1);
Mark Rutlanddf857412014-07-16 16:32:44 +0100349
Mark Rutland1cc6ed92016-03-04 12:54:05 +0000350 info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1);
351 info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1);
352 info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1);
353 info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1);
354 info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
355 info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
356 info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);
357 info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);
358 info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
Mark Rutlanddf857412014-07-16 16:32:44 +0100359
Suzuki K Poulosea6dc3cd2016-04-18 10:28:35 +0100360 /* Update the 32bit ID registers only if AArch32 is implemented */
361 if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
362 info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
363 info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
364 info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
365 info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
366 info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
367 info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
368 info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
369 info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
370 info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
371 info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
372 info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
373 info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
374 info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100375
Suzuki K Poulosea6dc3cd2016-04-18 10:28:35 +0100376 info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
377 info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
378 info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
379 }
Mark Rutland80639d42015-01-07 10:31:56 +0000380
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100381 cpuinfo_detect_icache_policy(info);
Mark Rutlanddf857412014-07-16 16:32:44 +0100382}
383
384void cpuinfo_store_cpu(void)
385{
386 struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
387 __cpuinfo_store_cpu(info);
Suzuki K. Poulose3086d392015-10-19 14:24:46 +0100388 update_cpu_features(smp_processor_id(), info, &boot_cpu_data);
Mark Rutlanddf857412014-07-16 16:32:44 +0100389}
390
391void __init cpuinfo_store_boot_cpu(void)
392{
393 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0);
394 __cpuinfo_store_cpu(info);
395
396 boot_cpu_data = *info;
Suzuki K. Poulose3c739b52015-10-19 14:24:45 +0100397 init_cpu_features(&boot_cpu_data);
Mark Rutlanddf857412014-07-16 16:32:44 +0100398}
Steve Capperf8d9f922016-07-08 16:01:13 +0100399
400device_initcall(cpuinfo_regs_init);