xref: /openbmc/linux/arch/riscv/kernel/cpu.c (revision 625046cd)
150acfb2bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
276d2a049SPalmer Dabbelt /*
376d2a049SPalmer Dabbelt  * Copyright (C) 2012 Regents of the University of California
476d2a049SPalmer Dabbelt  */
576d2a049SPalmer Dabbelt 
60b144c81SSunil V L #include <linux/acpi.h>
73baca1a4SAnup Patel #include <linux/cpu.h>
8255b34d7SYangyu Chen #include <linux/ctype.h>
976d2a049SPalmer Dabbelt #include <linux/init.h>
1076d2a049SPalmer Dabbelt #include <linux/seq_file.h>
1176d2a049SPalmer Dabbelt #include <linux/of.h>
120b144c81SSunil V L #include <asm/acpi.h>
13ff77cf5bSEvan Green #include <asm/cpufeature.h>
143baca1a4SAnup Patel #include <asm/csr.h>
15a9b20260SAtish Patra #include <asm/hwcap.h>
163baca1a4SAnup Patel #include <asm/sbi.h>
17f99fb607SAtish Patra #include <asm/smp.h>
1873c7c8f6SAlexandre Ghiti #include <asm/pgtable.h>
1976d2a049SPalmer Dabbelt 
arch_match_cpu_phys_id(int cpu,u64 phys_id)206514f81eSSamuel Holland bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
216514f81eSSamuel Holland {
226514f81eSSamuel Holland 	return phys_id == cpuid_to_hartid_map(cpu);
236514f81eSSamuel Holland }
246514f81eSSamuel Holland 
25b2f8cfa7SPalmer Dabbelt /*
26149820c6SJohan Hovold  * Returns the hart ID of the given device tree node, or -ENODEV if the node
27149820c6SJohan Hovold  * isn't an enabled and valid RISC-V hart node.
28b2f8cfa7SPalmer Dabbelt  */
riscv_of_processor_hartid(struct device_node * node,unsigned long * hart)29ad635e72SSunil V L int riscv_of_processor_hartid(struct device_node *node, unsigned long *hart)
3076d2a049SPalmer Dabbelt {
312ac87434SConor Dooley 	int cpu;
322ac87434SConor Dooley 
332ac87434SConor Dooley 	*hart = (unsigned long)of_get_cpu_hwid(node, 0);
342ac87434SConor Dooley 	if (*hart == ~0UL) {
352ac87434SConor Dooley 		pr_warn("Found CPU without hart ID\n");
362ac87434SConor Dooley 		return -ENODEV;
372ac87434SConor Dooley 	}
382ac87434SConor Dooley 
392ac87434SConor Dooley 	cpu = riscv_hartid_to_cpuid(*hart);
402ac87434SConor Dooley 	if (cpu < 0)
412ac87434SConor Dooley 		return cpu;
422ac87434SConor Dooley 
432ac87434SConor Dooley 	if (!cpu_possible(cpu))
442ac87434SConor Dooley 		return -ENODEV;
452ac87434SConor Dooley 
462ac87434SConor Dooley 	return 0;
472ac87434SConor Dooley }
482ac87434SConor Dooley 
riscv_early_of_processor_hartid(struct device_node * node,unsigned long * hart)49496ea826SConor Dooley int __init riscv_early_of_processor_hartid(struct device_node *node, unsigned long *hart)
502ac87434SConor Dooley {
51e3d794d5SJohan Hovold 	const char *isa;
5276d2a049SPalmer Dabbelt 
5376d2a049SPalmer Dabbelt 	if (!of_device_is_compatible(node, "riscv")) {
5476d2a049SPalmer Dabbelt 		pr_warn("Found incompatible CPU\n");
55149820c6SJohan Hovold 		return -ENODEV;
5676d2a049SPalmer Dabbelt 	}
5776d2a049SPalmer Dabbelt 
58ad635e72SSunil V L 	*hart = (unsigned long)of_get_cpu_hwid(node, 0);
59ad635e72SSunil V L 	if (*hart == ~0UL) {
6076d2a049SPalmer Dabbelt 		pr_warn("Found CPU without hart ID\n");
61149820c6SJohan Hovold 		return -ENODEV;
6276d2a049SPalmer Dabbelt 	}
6376d2a049SPalmer Dabbelt 
64e3d794d5SJohan Hovold 	if (!of_device_is_available(node)) {
65ad635e72SSunil V L 		pr_info("CPU with hartid=%lu is not available\n", *hart);
66149820c6SJohan Hovold 		return -ENODEV;
6776d2a049SPalmer Dabbelt 	}
6876d2a049SPalmer Dabbelt 
69c98f136aSConor Dooley 	if (of_property_read_string(node, "riscv,isa-base", &isa))
70c98f136aSConor Dooley 		goto old_interface;
71c98f136aSConor Dooley 
72c98f136aSConor Dooley 	if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32i", 5)) {
73c98f136aSConor Dooley 		pr_warn("CPU with hartid=%lu does not support rv32i", *hart);
74149820c6SJohan Hovold 		return -ENODEV;
7576d2a049SPalmer Dabbelt 	}
76069b0d51SConor Dooley 
77c98f136aSConor Dooley 	if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64i", 5)) {
78c98f136aSConor Dooley 		pr_warn("CPU with hartid=%lu does not support rv64i", *hart);
79c98f136aSConor Dooley 		return -ENODEV;
80c98f136aSConor Dooley 	}
81c98f136aSConor Dooley 
82c98f136aSConor Dooley 	if (!of_property_present(node, "riscv,isa-extensions"))
83149820c6SJohan Hovold 		return -ENODEV;
84069b0d51SConor Dooley 
85c98f136aSConor Dooley 	if (of_property_match_string(node, "riscv,isa-extensions", "i") < 0 ||
86c98f136aSConor Dooley 	    of_property_match_string(node, "riscv,isa-extensions", "m") < 0 ||
87c98f136aSConor Dooley 	    of_property_match_string(node, "riscv,isa-extensions", "a") < 0) {
88c98f136aSConor Dooley 		pr_warn("CPU with hartid=%lu does not support ima", *hart);
89069b0d51SConor Dooley 		return -ENODEV;
90c98f136aSConor Dooley 	}
91c98f136aSConor Dooley 
92c98f136aSConor Dooley 	return 0;
93c98f136aSConor Dooley 
94c98f136aSConor Dooley old_interface:
95496ea826SConor Dooley 	if (!riscv_isa_fallback) {
96496ea826SConor Dooley 		pr_warn("CPU with hartid=%lu is invalid: this kernel does not parse \"riscv,isa\"",
97496ea826SConor Dooley 			*hart);
98496ea826SConor Dooley 		return -ENODEV;
99496ea826SConor Dooley 	}
100496ea826SConor Dooley 
10176d2a049SPalmer Dabbelt 	if (of_property_read_string(node, "riscv,isa", &isa)) {
102c98f136aSConor Dooley 		pr_warn("CPU with hartid=%lu has no \"riscv,isa-base\" or \"riscv,isa\" property\n",
103c98f136aSConor Dooley 			*hart);
10476d2a049SPalmer Dabbelt 		return -ENODEV;
10576d2a049SPalmer Dabbelt 	}
10676d2a049SPalmer Dabbelt 
10723059893SPalmer Dabbelt 	if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32ima", 7)) {
10823059893SPalmer Dabbelt 		pr_warn("CPU with hartid=%lu does not support rv32ima", *hart);
10976d2a049SPalmer Dabbelt 		return -ENODEV;
11023059893SPalmer Dabbelt 	}
11176d2a049SPalmer Dabbelt 
11223059893SPalmer Dabbelt 	if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64ima", 7)) {
11323059893SPalmer Dabbelt 		pr_warn("CPU with hartid=%lu does not support rv64ima", *hart);
11476d2a049SPalmer Dabbelt 		return -ENODEV;
11523059893SPalmer Dabbelt 	}
11676d2a049SPalmer Dabbelt 
117ad635e72SSunil V L 	return 0;
11876d2a049SPalmer Dabbelt }
11976d2a049SPalmer Dabbelt 
120d175d699SAnup Patel /*
121d175d699SAnup Patel  * Find hart ID of the CPU DT node under which given DT node falls.
122d175d699SAnup Patel  *
123d175d699SAnup Patel  * To achieve this, we walk up the DT tree until we find an active
124d175d699SAnup Patel  * RISC-V core (HART) node and extract the cpuid from it.
125d175d699SAnup Patel  */
riscv_of_parent_hartid(struct device_node * node,unsigned long * hartid)126ad635e72SSunil V L int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid)
127d175d699SAnup Patel {
128d175d699SAnup Patel 	for (; node; node = node->parent) {
129ad635e72SSunil V L 		if (of_device_is_compatible(node, "riscv")) {
130*625046cdSAnup Patel 			*hartid = (unsigned long)of_get_cpu_hwid(node, 0);
131*625046cdSAnup Patel 			if (*hartid == ~0UL) {
132*625046cdSAnup Patel 				pr_warn("Found CPU without hart ID\n");
133*625046cdSAnup Patel 				return -ENODEV;
134*625046cdSAnup Patel 			}
135ad635e72SSunil V L 			return 0;
136ad635e72SSunil V L 		}
137d175d699SAnup Patel 	}
138d175d699SAnup Patel 
139d175d699SAnup Patel 	return -1;
140d175d699SAnup Patel }
141d175d699SAnup Patel 
142ff77cf5bSEvan Green DEFINE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
1433baca1a4SAnup Patel 
riscv_cached_mvendorid(unsigned int cpu_id)1445e9c68eaSHeiko Stuebner unsigned long riscv_cached_mvendorid(unsigned int cpu_id)
1455e9c68eaSHeiko Stuebner {
1465e9c68eaSHeiko Stuebner 	struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
1475e9c68eaSHeiko Stuebner 
1485e9c68eaSHeiko Stuebner 	return ci->mvendorid;
1495e9c68eaSHeiko Stuebner }
1505e9c68eaSHeiko Stuebner EXPORT_SYMBOL(riscv_cached_mvendorid);
1515e9c68eaSHeiko Stuebner 
riscv_cached_marchid(unsigned int cpu_id)1525e9c68eaSHeiko Stuebner unsigned long riscv_cached_marchid(unsigned int cpu_id)
1535e9c68eaSHeiko Stuebner {
1545e9c68eaSHeiko Stuebner 	struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
1555e9c68eaSHeiko Stuebner 
1565e9c68eaSHeiko Stuebner 	return ci->marchid;
1575e9c68eaSHeiko Stuebner }
1585e9c68eaSHeiko Stuebner EXPORT_SYMBOL(riscv_cached_marchid);
1595e9c68eaSHeiko Stuebner 
riscv_cached_mimpid(unsigned int cpu_id)1605e9c68eaSHeiko Stuebner unsigned long riscv_cached_mimpid(unsigned int cpu_id)
1615e9c68eaSHeiko Stuebner {
1625e9c68eaSHeiko Stuebner 	struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
1635e9c68eaSHeiko Stuebner 
1645e9c68eaSHeiko Stuebner 	return ci->mimpid;
1655e9c68eaSHeiko Stuebner }
1665e9c68eaSHeiko Stuebner EXPORT_SYMBOL(riscv_cached_mimpid);
1675e9c68eaSHeiko Stuebner 
riscv_cpuinfo_starting(unsigned int cpu)1683baca1a4SAnup Patel static int riscv_cpuinfo_starting(unsigned int cpu)
1693baca1a4SAnup Patel {
1703baca1a4SAnup Patel 	struct riscv_cpuinfo *ci = this_cpu_ptr(&riscv_cpuinfo);
1713baca1a4SAnup Patel 
1723baca1a4SAnup Patel #if IS_ENABLED(CONFIG_RISCV_SBI)
1733baca1a4SAnup Patel 	ci->mvendorid = sbi_spec_is_0_1() ? 0 : sbi_get_mvendorid();
1743baca1a4SAnup Patel 	ci->marchid = sbi_spec_is_0_1() ? 0 : sbi_get_marchid();
1753baca1a4SAnup Patel 	ci->mimpid = sbi_spec_is_0_1() ? 0 : sbi_get_mimpid();
1763baca1a4SAnup Patel #elif IS_ENABLED(CONFIG_RISCV_M_MODE)
1773baca1a4SAnup Patel 	ci->mvendorid = csr_read(CSR_MVENDORID);
1783baca1a4SAnup Patel 	ci->marchid = csr_read(CSR_MARCHID);
1793baca1a4SAnup Patel 	ci->mimpid = csr_read(CSR_MIMPID);
1803baca1a4SAnup Patel #else
1813baca1a4SAnup Patel 	ci->mvendorid = 0;
1823baca1a4SAnup Patel 	ci->marchid = 0;
1833baca1a4SAnup Patel 	ci->mimpid = 0;
1843baca1a4SAnup Patel #endif
1853baca1a4SAnup Patel 
1863baca1a4SAnup Patel 	return 0;
1873baca1a4SAnup Patel }
1883baca1a4SAnup Patel 
riscv_cpuinfo_init(void)1893baca1a4SAnup Patel static int __init riscv_cpuinfo_init(void)
1903baca1a4SAnup Patel {
1913baca1a4SAnup Patel 	int ret;
1923baca1a4SAnup Patel 
1933baca1a4SAnup Patel 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "riscv/cpuinfo:starting",
1943baca1a4SAnup Patel 				riscv_cpuinfo_starting, NULL);
1953baca1a4SAnup Patel 	if (ret < 0) {
1963baca1a4SAnup Patel 		pr_err("cpuinfo: failed to register hotplug callbacks.\n");
1973baca1a4SAnup Patel 		return ret;
1983baca1a4SAnup Patel 	}
1993baca1a4SAnup Patel 
2003baca1a4SAnup Patel 	return 0;
2013baca1a4SAnup Patel }
2025e9c68eaSHeiko Stuebner arch_initcall(riscv_cpuinfo_init);
2035e9c68eaSHeiko Stuebner 
2045e9c68eaSHeiko Stuebner #ifdef CONFIG_PROC_FS
2053baca1a4SAnup Patel 
print_isa(struct seq_file * f)20667270fb3SHeiko Stuebner static void print_isa(struct seq_file *f)
207a9b20260SAtish Patra {
2084b26d22fSAnup Patel 	seq_puts(f, "isa\t\t: ");
209effc122aSConor Dooley 
21067270fb3SHeiko Stuebner 	if (IS_ENABLED(CONFIG_32BIT))
21167270fb3SHeiko Stuebner 		seq_write(f, "rv32", 4);
21267270fb3SHeiko Stuebner 	else
21367270fb3SHeiko Stuebner 		seq_write(f, "rv64", 4);
21467270fb3SHeiko Stuebner 
215effc122aSConor Dooley 	for (int i = 0; i < riscv_isa_ext_count; i++) {
216effc122aSConor Dooley 		if (!__riscv_isa_extension_available(NULL, riscv_isa_ext[i].id))
217effc122aSConor Dooley 			continue;
218effc122aSConor Dooley 
219effc122aSConor Dooley 		/* Only multi-letter extensions are split by underscores */
220effc122aSConor Dooley 		if (strnlen(riscv_isa_ext[i].name, 2) != 1)
221effc122aSConor Dooley 			seq_puts(f, "_");
222effc122aSConor Dooley 
223effc122aSConor Dooley 		seq_printf(f, "%s", riscv_isa_ext[i].name);
224a9b20260SAtish Patra 	}
225effc122aSConor Dooley 
2264b26d22fSAnup Patel 	seq_puts(f, "\n");
22719ccf29bSPalmer Dabbelt }
22819ccf29bSPalmer Dabbelt 
print_mmu(struct seq_file * f)22973c7c8f6SAlexandre Ghiti static void print_mmu(struct seq_file *f)
23019ccf29bSPalmer Dabbelt {
23112d61a1bSJustin Stitt 	const char *sv_type;
23219ccf29bSPalmer Dabbelt 
2338810d7feSNiklas Cassel #ifdef CONFIG_MMU
23473c7c8f6SAlexandre Ghiti #if defined(CONFIG_32BIT)
23512d61a1bSJustin Stitt 	sv_type = "sv32";
23673c7c8f6SAlexandre Ghiti #elif defined(CONFIG_64BIT)
237011f09d1SQinglin Pan 	if (pgtable_l5_enabled)
23812d61a1bSJustin Stitt 		sv_type = "sv57";
239011f09d1SQinglin Pan 	else if (pgtable_l4_enabled)
24012d61a1bSJustin Stitt 		sv_type = "sv48";
24173c7c8f6SAlexandre Ghiti 	else
24212d61a1bSJustin Stitt 		sv_type = "sv39";
24373c7c8f6SAlexandre Ghiti #endif
2448810d7feSNiklas Cassel #else
24512d61a1bSJustin Stitt 	sv_type = "none";
2468810d7feSNiklas Cassel #endif /* CONFIG_MMU */
24773c7c8f6SAlexandre Ghiti 	seq_printf(f, "mmu\t\t: %s\n", sv_type);
24819ccf29bSPalmer Dabbelt }
24919ccf29bSPalmer Dabbelt 
c_start(struct seq_file * m,loff_t * pos)25076d2a049SPalmer Dabbelt static void *c_start(struct seq_file *m, loff_t *pos)
25176d2a049SPalmer Dabbelt {
252d14e99bfSAndrew Jones 	if (*pos == nr_cpu_ids)
253d14e99bfSAndrew Jones 		return NULL;
254d14e99bfSAndrew Jones 
25576d2a049SPalmer Dabbelt 	*pos = cpumask_next(*pos - 1, cpu_online_mask);
25676d2a049SPalmer Dabbelt 	if ((*pos) < nr_cpu_ids)
25776d2a049SPalmer Dabbelt 		return (void *)(uintptr_t)(1 + *pos);
25876d2a049SPalmer Dabbelt 	return NULL;
25976d2a049SPalmer Dabbelt }
26076d2a049SPalmer Dabbelt 
c_next(struct seq_file * m,void * v,loff_t * pos)26176d2a049SPalmer Dabbelt static void *c_next(struct seq_file *m, void *v, loff_t *pos)
26276d2a049SPalmer Dabbelt {
26376d2a049SPalmer Dabbelt 	(*pos)++;
26476d2a049SPalmer Dabbelt 	return c_start(m, pos);
26576d2a049SPalmer Dabbelt }
26676d2a049SPalmer Dabbelt 
c_stop(struct seq_file * m,void * v)26776d2a049SPalmer Dabbelt static void c_stop(struct seq_file *m, void *v)
26876d2a049SPalmer Dabbelt {
26976d2a049SPalmer Dabbelt }
27076d2a049SPalmer Dabbelt 
c_show(struct seq_file * m,void * v)27176d2a049SPalmer Dabbelt static int c_show(struct seq_file *m, void *v)
27276d2a049SPalmer Dabbelt {
273f99fb607SAtish Patra 	unsigned long cpu_id = (unsigned long)v - 1;
2743baca1a4SAnup Patel 	struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
2750b144c81SSunil V L 	struct device_node *node;
27667270fb3SHeiko Stuebner 	const char *compat;
27776d2a049SPalmer Dabbelt 
2784b26d22fSAnup Patel 	seq_printf(m, "processor\t: %lu\n", cpu_id);
2794b26d22fSAnup Patel 	seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id));
28067270fb3SHeiko Stuebner 	print_isa(m);
28167270fb3SHeiko Stuebner 	print_mmu(m);
2820b144c81SSunil V L 
2830b144c81SSunil V L 	if (acpi_disabled) {
2840b144c81SSunil V L 		node = of_get_cpu_node(cpu_id, NULL);
2850b144c81SSunil V L 
2860b144c81SSunil V L 		if (!of_property_read_string(node, "compatible", &compat) &&
2870b144c81SSunil V L 		    strcmp(compat, "riscv"))
2884b26d22fSAnup Patel 			seq_printf(m, "uarch\t\t: %s\n", compat);
2890b144c81SSunil V L 
2900b144c81SSunil V L 		of_node_put(node);
2910b144c81SSunil V L 	}
2920b144c81SSunil V L 
2933baca1a4SAnup Patel 	seq_printf(m, "mvendorid\t: 0x%lx\n", ci->mvendorid);
2943baca1a4SAnup Patel 	seq_printf(m, "marchid\t\t: 0x%lx\n", ci->marchid);
2953baca1a4SAnup Patel 	seq_printf(m, "mimpid\t\t: 0x%lx\n", ci->mimpid);
29676d2a049SPalmer Dabbelt 	seq_puts(m, "\n");
29776d2a049SPalmer Dabbelt 
29876d2a049SPalmer Dabbelt 	return 0;
29976d2a049SPalmer Dabbelt }
30076d2a049SPalmer Dabbelt 
30176d2a049SPalmer Dabbelt const struct seq_operations cpuinfo_op = {
30276d2a049SPalmer Dabbelt 	.start	= c_start,
30376d2a049SPalmer Dabbelt 	.next	= c_next,
30476d2a049SPalmer Dabbelt 	.stop	= c_stop,
30576d2a049SPalmer Dabbelt 	.show	= c_show
30676d2a049SPalmer Dabbelt };
30776d2a049SPalmer Dabbelt 
30876d2a049SPalmer Dabbelt #endif /* CONFIG_PROC_FS */
309