File Index
Files and directories
Sign in
FILE

cpu-probe.c

File preview with syntax highlighting.

linux-6.19.14/arch/loongarch/kernel/cpu-probe.c C
  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 */
 24unsigned int elf_hwcap __read_mostly;
 25EXPORT_SYMBOL_GPL(elf_hwcap);
 26
 27/*
 28 * Determine the FCSR mask for FPU hardware.
 29 */
 30static 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 */
 56static unsigned int simd = -1U;
 57
 58static 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
 66early_param("simd", cpu_setup_simd);
 67
 68static 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
 95arch_initcall(cpu_final_simd);
 96
 97static inline void set_elf_platform(int cpu, const char *plat)
 98{
 99	if (cpu == 0)
100		__elf_platform = plat;
101}
102
103/* MAP BASE */
104unsigned long vm_map_base;
105EXPORT_SYMBOL(vm_map_base);
106
107static 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
120static 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
135static 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
277static char cpu_full_name[MAX_NAME_LEN] = "        -        ";
278
279static 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 */
343u64 __ua_limit;
344EXPORT_SYMBOL(__ua_limit);
345#endif
346
347const char *__cpu_family[NR_CPUS];
348const char *__cpu_full_name[NR_CPUS];
349const char *__elf_platform;
350
351static 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
361void 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}
~/ Read-only file preview.