Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/loongarch/kernel/cpu-probe.c
50081 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Processor capabilities determination functions.
4
*
5
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6
*/
7
#include <linux/init.h>
8
#include <linux/kernel.h>
9
#include <linux/ptrace.h>
10
#include <linux/smp.h>
11
#include <linux/stddef.h>
12
#include <linux/export.h>
13
#include <linux/printk.h>
14
#include <linux/uaccess.h>
15
16
#include <asm/cpu-features.h>
17
#include <asm/elf.h>
18
#include <asm/fpu.h>
19
#include <asm/loongarch.h>
20
#include <asm/pgtable-bits.h>
21
#include <asm/setup.h>
22
23
/* Hardware capabilities */
24
unsigned int elf_hwcap __read_mostly;
25
EXPORT_SYMBOL_GPL(elf_hwcap);
26
27
/*
28
* Determine the FCSR mask for FPU hardware.
29
*/
30
static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c)
31
{
32
unsigned long sr, mask, fcsr, fcsr0, fcsr1;
33
34
fcsr = c->fpu_csr0;
35
mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
36
37
sr = read_csr_euen();
38
enable_fpu();
39
40
fcsr0 = fcsr & mask;
41
write_fcsr(LOONGARCH_FCSR0, fcsr0);
42
fcsr0 = read_fcsr(LOONGARCH_FCSR0);
43
44
fcsr1 = fcsr | ~mask;
45
write_fcsr(LOONGARCH_FCSR0, fcsr1);
46
fcsr1 = read_fcsr(LOONGARCH_FCSR0);
47
48
write_fcsr(LOONGARCH_FCSR0, fcsr);
49
50
write_csr_euen(sr);
51
52
c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask;
53
}
54
55
/* simd = -1/0/128/256 */
56
static unsigned int simd = -1U;
57
58
static int __init cpu_setup_simd(char *str)
59
{
60
get_option(&str, &simd);
61
pr_info("Set SIMD width = %u\n", simd);
62
63
return 0;
64
}
65
66
early_param("simd", cpu_setup_simd);
67
68
static int __init cpu_final_simd(void)
69
{
70
struct cpuinfo_loongarch *c = &cpu_data[0];
71
72
if (simd < 128) {
73
c->options &= ~LOONGARCH_CPU_LSX;
74
elf_hwcap &= ~HWCAP_LOONGARCH_LSX;
75
}
76
77
if (simd < 256) {
78
c->options &= ~LOONGARCH_CPU_LASX;
79
elf_hwcap &= ~HWCAP_LOONGARCH_LASX;
80
}
81
82
simd = 0;
83
84
if (c->options & LOONGARCH_CPU_LSX)
85
simd = 128;
86
87
if (c->options & LOONGARCH_CPU_LASX)
88
simd = 256;
89
90
pr_info("Final SIMD width = %u\n", simd);
91
92
return 0;
93
}
94
95
arch_initcall(cpu_final_simd);
96
97
static inline void set_elf_platform(int cpu, const char *plat)
98
{
99
if (cpu == 0)
100
__elf_platform = plat;
101
}
102
103
/* MAP BASE */
104
unsigned long vm_map_base;
105
EXPORT_SYMBOL(vm_map_base);
106
107
static void cpu_probe_addrbits(struct cpuinfo_loongarch *c)
108
{
109
#ifdef CONFIG_32BIT
110
c->pabits = cpu_pabits;
111
c->vabits = cpu_vabits;
112
vm_map_base = KVRANGE;
113
#else
114
c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4;
115
c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12;
116
vm_map_base = 0UL - (1UL << c->vabits);
117
#endif
118
}
119
120
static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa)
121
{
122
switch (isa) {
123
case LOONGARCH_CPU_ISA_LA64:
124
c->isa_level |= LOONGARCH_CPU_ISA_LA64;
125
fallthrough;
126
case LOONGARCH_CPU_ISA_LA32S:
127
c->isa_level |= LOONGARCH_CPU_ISA_LA32S;
128
fallthrough;
129
case LOONGARCH_CPU_ISA_LA32R:
130
c->isa_level |= LOONGARCH_CPU_ISA_LA32R;
131
break;
132
}
133
}
134
135
static void cpu_probe_common(struct cpuinfo_loongarch *c)
136
{
137
unsigned int config;
138
unsigned long asid_mask;
139
140
c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR | LOONGARCH_CPU_VINT;
141
142
elf_hwcap = HWCAP_LOONGARCH_CPUCFG;
143
144
config = read_cpucfg(LOONGARCH_CPUCFG1);
145
146
switch (config & CPUCFG1_ISA) {
147
case 0:
148
set_isa(c, LOONGARCH_CPU_ISA_LA32R);
149
break;
150
case 1:
151
set_isa(c, LOONGARCH_CPU_ISA_LA32S);
152
break;
153
case 2:
154
set_isa(c, LOONGARCH_CPU_ISA_LA64);
155
break;
156
default:
157
pr_warn("Warning: unknown ISA level\n");
158
}
159
160
if (config & CPUCFG1_PAGING)
161
c->options |= LOONGARCH_CPU_TLB;
162
if (config & CPUCFG1_IOCSR)
163
c->options |= LOONGARCH_CPU_IOCSR;
164
if (config & CPUCFG1_MSGINT)
165
c->options |= LOONGARCH_CPU_MSGINT;
166
if (config & CPUCFG1_UAL) {
167
c->options |= LOONGARCH_CPU_UAL;
168
elf_hwcap |= HWCAP_LOONGARCH_UAL;
169
}
170
if (config & CPUCFG1_CRC32) {
171
c->options |= LOONGARCH_CPU_CRC32;
172
elf_hwcap |= HWCAP_LOONGARCH_CRC32;
173
}
174
175
config = read_cpucfg(LOONGARCH_CPUCFG2);
176
if (config & CPUCFG2_LAM) {
177
c->options |= LOONGARCH_CPU_LAM;
178
elf_hwcap |= HWCAP_LOONGARCH_LAM;
179
}
180
if (config & CPUCFG2_FP) {
181
c->options |= LOONGARCH_CPU_FPU;
182
elf_hwcap |= HWCAP_LOONGARCH_FPU;
183
}
184
#ifdef CONFIG_CPU_HAS_LSX
185
if ((config & CPUCFG2_LSX) && (simd >= 128)) {
186
c->options |= LOONGARCH_CPU_LSX;
187
elf_hwcap |= HWCAP_LOONGARCH_LSX;
188
}
189
#endif
190
#ifdef CONFIG_CPU_HAS_LASX
191
if ((config & CPUCFG2_LASX) && (simd >= 256)) {
192
c->options |= LOONGARCH_CPU_LASX;
193
elf_hwcap |= HWCAP_LOONGARCH_LASX;
194
}
195
#endif
196
if (config & CPUCFG2_COMPLEX) {
197
c->options |= LOONGARCH_CPU_COMPLEX;
198
elf_hwcap |= HWCAP_LOONGARCH_COMPLEX;
199
}
200
if (config & CPUCFG2_CRYPTO) {
201
c->options |= LOONGARCH_CPU_CRYPTO;
202
elf_hwcap |= HWCAP_LOONGARCH_CRYPTO;
203
}
204
if (config & CPUCFG2_PTW) {
205
c->options |= LOONGARCH_CPU_PTW;
206
elf_hwcap |= HWCAP_LOONGARCH_PTW;
207
}
208
if (config & CPUCFG2_LSPW) {
209
c->options |= LOONGARCH_CPU_LSPW;
210
elf_hwcap |= HWCAP_LOONGARCH_LSPW;
211
}
212
if (config & CPUCFG2_LVZP) {
213
c->options |= LOONGARCH_CPU_LVZ;
214
elf_hwcap |= HWCAP_LOONGARCH_LVZ;
215
}
216
#ifdef CONFIG_CPU_HAS_LBT
217
if (config & CPUCFG2_X86BT) {
218
c->options |= LOONGARCH_CPU_LBT_X86;
219
elf_hwcap |= HWCAP_LOONGARCH_LBT_X86;
220
}
221
if (config & CPUCFG2_ARMBT) {
222
c->options |= LOONGARCH_CPU_LBT_ARM;
223
elf_hwcap |= HWCAP_LOONGARCH_LBT_ARM;
224
}
225
if (config & CPUCFG2_MIPSBT) {
226
c->options |= LOONGARCH_CPU_LBT_MIPS;
227
elf_hwcap |= HWCAP_LOONGARCH_LBT_MIPS;
228
}
229
#endif
230
231
config = read_cpucfg(LOONGARCH_CPUCFG6);
232
if (config & CPUCFG6_PMP)
233
c->options |= LOONGARCH_CPU_PMP;
234
235
config = csr_read32(LOONGARCH_CSR_ASID);
236
config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
237
asid_mask = GENMASK(config - 1, 0);
238
set_cpu_asid_mask(c, asid_mask);
239
240
config = read_csr_prcfg1();
241
c->timerbits = (config & CSR_CONF1_TMRBITS) >> CSR_CONF1_TMRBITS_SHIFT;
242
c->ksave_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0);
243
c->ksave_mask &= ~(EXC_KSAVE_MASK | PERCPU_KSAVE_MASK | KVM_KSAVE_MASK);
244
245
config = read_csr_prcfg3();
246
switch (config & CSR_CONF3_TLBTYPE) {
247
case 0:
248
c->tlbsizemtlb = 0;
249
c->tlbsizestlbsets = 0;
250
c->tlbsizestlbways = 0;
251
c->tlbsize = 0;
252
break;
253
case 1:
254
c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
255
c->tlbsizestlbsets = 0;
256
c->tlbsizestlbways = 0;
257
c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
258
break;
259
case 2:
260
c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
261
c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT);
262
c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1;
263
c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
264
break;
265
default:
266
pr_warn("Warning: unknown TLB type\n");
267
}
268
269
if (get_num_brps() + get_num_wrps())
270
c->options |= LOONGARCH_CPU_WATCH;
271
}
272
273
#define MAX_NAME_LEN 32
274
#define VENDOR_OFFSET 0
275
#define CPUNAME_OFFSET 9
276
277
static char cpu_full_name[MAX_NAME_LEN] = " - ";
278
279
static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu)
280
{
281
uint32_t config;
282
uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]);
283
uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]);
284
const char *core_name = id_to_core_name(c->processor_id);
285
286
switch (BIT(fls(c->isa_level) - 1)) {
287
case LOONGARCH_CPU_ISA_LA32R:
288
case LOONGARCH_CPU_ISA_LA32S:
289
c->cputype = CPU_LOONGSON32;
290
__cpu_family[cpu] = "Loongson-32bit";
291
break;
292
case LOONGARCH_CPU_ISA_LA64:
293
c->cputype = CPU_LOONGSON64;
294
__cpu_family[cpu] = "Loongson-64bit";
295
break;
296
}
297
298
pr_info("%s Processor probed (%s Core)\n", __cpu_family[cpu], core_name);
299
300
if (!cpu_has_iocsr) {
301
__cpu_full_name[cpu] = "Unknown";
302
return;
303
}
304
305
#ifdef CONFIG_64BIT
306
*vendor = iocsr_read64(LOONGARCH_IOCSR_VENDOR);
307
*cpuname = iocsr_read64(LOONGARCH_IOCSR_CPUNAME);
308
#else
309
*vendor = iocsr_read32(LOONGARCH_IOCSR_VENDOR) |
310
(u64)iocsr_read32(LOONGARCH_IOCSR_VENDOR + 4) << 32;
311
*cpuname = iocsr_read32(LOONGARCH_IOCSR_CPUNAME) |
312
(u64)iocsr_read32(LOONGARCH_IOCSR_CPUNAME + 4) << 32;
313
#endif
314
315
if (!__cpu_full_name[cpu]) {
316
if (((char *)vendor)[0] == 0)
317
__cpu_full_name[cpu] = "Unknown";
318
else
319
__cpu_full_name[cpu] = cpu_full_name;
320
}
321
322
config = iocsr_read32(LOONGARCH_IOCSR_FEATURES);
323
if (config & IOCSRF_CSRIPI)
324
c->options |= LOONGARCH_CPU_CSRIPI;
325
if (config & IOCSRF_EXTIOI)
326
c->options |= LOONGARCH_CPU_EXTIOI;
327
if (config & IOCSRF_FREQSCALE)
328
c->options |= LOONGARCH_CPU_SCALEFREQ;
329
if (config & IOCSRF_FLATMODE)
330
c->options |= LOONGARCH_CPU_FLATMODE;
331
if (config & IOCSRF_EIODECODE)
332
c->options |= LOONGARCH_CPU_EIODECODE;
333
if (config & IOCSRF_AVEC)
334
c->options |= LOONGARCH_CPU_AVECINT;
335
if (config & IOCSRF_REDIRECT)
336
c->options |= LOONGARCH_CPU_REDIRECTINT;
337
if (config & IOCSRF_VM)
338
c->options |= LOONGARCH_CPU_HYPERVISOR;
339
}
340
341
#ifdef CONFIG_64BIT
342
/* For use by uaccess.h */
343
u64 __ua_limit;
344
EXPORT_SYMBOL(__ua_limit);
345
#endif
346
347
const char *__cpu_family[NR_CPUS];
348
const char *__cpu_full_name[NR_CPUS];
349
const char *__elf_platform;
350
351
static void cpu_report(void)
352
{
353
struct cpuinfo_loongarch *c = &current_cpu_data;
354
355
pr_info("CPU%d revision is: %08x (%s)\n",
356
smp_processor_id(), c->processor_id, cpu_family_string());
357
if (c->options & LOONGARCH_CPU_FPU)
358
pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers);
359
}
360
361
void cpu_probe(void)
362
{
363
unsigned int cpu = smp_processor_id();
364
struct cpuinfo_loongarch *c = &current_cpu_data;
365
366
/*
367
* Set a default ELF platform, cpu probe may later
368
* overwrite it with a more precise value
369
*/
370
set_elf_platform(cpu, "loongarch");
371
372
c->cputype = CPU_UNKNOWN;
373
c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0);
374
c->fpu_vers = (read_cpucfg(LOONGARCH_CPUCFG2) & CPUCFG2_FPVERS) >> 3;
375
376
c->fpu_csr0 = FPU_CSR_RN;
377
c->fpu_mask = FPU_CSR_RSVD;
378
379
cpu_probe_common(c);
380
381
per_cpu_trap_init(cpu);
382
383
switch (c->processor_id & PRID_COMP_MASK) {
384
case PRID_COMP_LOONGSON:
385
cpu_probe_loongson(c, cpu);
386
break;
387
}
388
389
BUG_ON(!__cpu_family[cpu]);
390
BUG_ON(c->cputype == CPU_UNKNOWN);
391
392
cpu_probe_addrbits(c);
393
394
#ifdef CONFIG_64BIT
395
if (cpu == 0)
396
__ua_limit = ~((1ull << cpu_vabits) - 1);
397
#endif
398
399
cpu_report();
400
}
401
402