xref: /linux/arch/riscv/include/asm/cpufeature.h (revision 6e5ce7f2eae3c7c36dd1709efaac34820a34d538)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Copyright 2022-2024 Rivos, Inc
4  */
5 
6 #ifndef _ASM_CPUFEATURE_H
7 #define _ASM_CPUFEATURE_H
8 
9 #include <linux/bitmap.h>
10 #include <linux/jump_label.h>
11 #include <asm/hwcap.h>
12 #include <asm/alternative-macros.h>
13 #include <asm/errno.h>
14 
15 /*
16  * These are probed via a device_initcall(), via either the SBI or directly
17  * from the corresponding CSRs.
18  */
19 struct riscv_cpuinfo {
20 	unsigned long mvendorid;
21 	unsigned long marchid;
22 	unsigned long mimpid;
23 };
24 
25 struct riscv_isainfo {
26 	DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX);
27 };
28 
29 DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
30 
31 DECLARE_PER_CPU(long, misaligned_access_speed);
32 
33 /* Per-cpu ISA extensions. */
34 extern struct riscv_isainfo hart_isa[NR_CPUS];
35 
36 void riscv_user_isa_enable(void);
37 
38 #ifdef CONFIG_RISCV_MISALIGNED
39 bool unaligned_ctl_available(void);
40 bool check_unaligned_access_emulated_all_cpus(void);
41 void unaligned_emulation_finish(void);
42 #else
43 static inline bool unaligned_ctl_available(void)
44 {
45 	return false;
46 }
47 
48 static inline bool check_unaligned_access_emulated(int cpu)
49 {
50 	return false;
51 }
52 
53 static inline void unaligned_emulation_finish(void) {}
54 #endif
55 
56 DECLARE_STATIC_KEY_FALSE(fast_unaligned_access_speed_key);
57 
58 static __always_inline bool has_fast_unaligned_accesses(void)
59 {
60 	return static_branch_likely(&fast_unaligned_access_speed_key);
61 }
62 
63 unsigned long riscv_get_elf_hwcap(void);
64 
65 struct riscv_isa_ext_data {
66 	const unsigned int id;
67 	const char *name;
68 	const char *property;
69 	const unsigned int *subset_ext_ids;
70 	const unsigned int subset_ext_size;
71 };
72 
73 extern const struct riscv_isa_ext_data riscv_isa_ext[];
74 extern const size_t riscv_isa_ext_count;
75 extern bool riscv_isa_fallback;
76 
77 unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
78 
79 bool __riscv_isa_extension_available(const unsigned long *isa_bitmap, unsigned int bit);
80 #define riscv_isa_extension_available(isa_bitmap, ext)	\
81 	__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_##ext)
82 
83 static __always_inline bool
84 riscv_has_extension_likely(const unsigned long ext)
85 {
86 	compiletime_assert(ext < RISCV_ISA_EXT_MAX,
87 			   "ext must be < RISCV_ISA_EXT_MAX");
88 
89 	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
90 		asm_volatile_goto(
91 		ALTERNATIVE("j	%l[l_no]", "nop", 0, %[ext], 1)
92 		:
93 		: [ext] "i" (ext)
94 		:
95 		: l_no);
96 	} else {
97 		if (!__riscv_isa_extension_available(NULL, ext))
98 			goto l_no;
99 	}
100 
101 	return true;
102 l_no:
103 	return false;
104 }
105 
106 static __always_inline bool
107 riscv_has_extension_unlikely(const unsigned long ext)
108 {
109 	compiletime_assert(ext < RISCV_ISA_EXT_MAX,
110 			   "ext must be < RISCV_ISA_EXT_MAX");
111 
112 	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE)) {
113 		asm_volatile_goto(
114 		ALTERNATIVE("nop", "j	%l[l_yes]", 0, %[ext], 1)
115 		:
116 		: [ext] "i" (ext)
117 		:
118 		: l_yes);
119 	} else {
120 		if (__riscv_isa_extension_available(NULL, ext))
121 			goto l_yes;
122 	}
123 
124 	return false;
125 l_yes:
126 	return true;
127 }
128 
129 static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
130 {
131 	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) && riscv_has_extension_likely(ext))
132 		return true;
133 
134 	return __riscv_isa_extension_available(hart_isa[cpu].isa, ext);
135 }
136 
137 static __always_inline bool riscv_cpu_has_extension_unlikely(int cpu, const unsigned long ext)
138 {
139 	if (IS_ENABLED(CONFIG_RISCV_ALTERNATIVE) && riscv_has_extension_unlikely(ext))
140 		return true;
141 
142 	return __riscv_isa_extension_available(hart_isa[cpu].isa, ext);
143 }
144 
145 #endif
146