1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Processor capabilities determination functions. |
4 | * |
5 | * Copyright (C) xxxx the Anonymous |
6 | * Copyright (C) 1994 - 2006 Ralf Baechle |
7 | * Copyright (C) 2003, 2004 Maciej W. Rozycki |
8 | * Copyright (C) 2001, 2004, 2011, 2012 MIPS Technologies, Inc. |
9 | */ |
10 | |
11 | #include <linux/init.h> |
12 | #include <linux/kernel.h> |
13 | |
14 | #include <asm/bugs.h> |
15 | #include <asm/cpu.h> |
16 | #include <asm/cpu-features.h> |
17 | #include <asm/cpu-type.h> |
18 | #include <asm/elf.h> |
19 | #include <asm/fpu.h> |
20 | #include <asm/mipsregs.h> |
21 | |
22 | #include "fpu-probe.h" |
23 | |
24 | /* |
25 | * Get the FPU Implementation/Revision. |
26 | */ |
27 | static inline unsigned long cpu_get_fpu_id(void) |
28 | { |
29 | unsigned long tmp, fpu_id; |
30 | |
31 | tmp = read_c0_status(); |
32 | __enable_fpu(FPU_AS_IS); |
33 | fpu_id = read_32bit_cp1_register(CP1_REVISION); |
34 | write_c0_status(tmp); |
35 | return fpu_id; |
36 | } |
37 | |
38 | /* |
39 | * Check if the CPU has an external FPU. |
40 | */ |
41 | int __cpu_has_fpu(void) |
42 | { |
43 | return (cpu_get_fpu_id() & FPIR_IMP_MASK) != FPIR_IMP_NONE; |
44 | } |
45 | |
46 | /* |
47 | * Determine the FCSR mask for FPU hardware. |
48 | */ |
49 | static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_mips *c) |
50 | { |
51 | unsigned long sr, mask, fcsr, fcsr0, fcsr1; |
52 | |
53 | fcsr = c->fpu_csr31; |
54 | mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM; |
55 | |
56 | sr = read_c0_status(); |
57 | __enable_fpu(FPU_AS_IS); |
58 | |
59 | fcsr0 = fcsr & mask; |
60 | write_32bit_cp1_register(CP1_STATUS, fcsr0); |
61 | fcsr0 = read_32bit_cp1_register(CP1_STATUS); |
62 | |
63 | fcsr1 = fcsr | ~mask; |
64 | write_32bit_cp1_register(CP1_STATUS, fcsr1); |
65 | fcsr1 = read_32bit_cp1_register(CP1_STATUS); |
66 | |
67 | write_32bit_cp1_register(CP1_STATUS, fcsr); |
68 | |
69 | write_c0_status(sr); |
70 | |
71 | c->fpu_msk31 = ~(fcsr0 ^ fcsr1) & ~mask; |
72 | } |
73 | |
74 | /* |
75 | * Determine the IEEE 754 NaN encodings and ABS.fmt/NEG.fmt execution modes |
76 | * supported by FPU hardware. |
77 | */ |
78 | static void cpu_set_fpu_2008(struct cpuinfo_mips *c) |
79 | { |
80 | if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | |
81 | MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | |
82 | MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | |
83 | MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) { |
84 | unsigned long sr, fir, fcsr, fcsr0, fcsr1; |
85 | |
86 | sr = read_c0_status(); |
87 | __enable_fpu(FPU_AS_IS); |
88 | |
89 | fir = read_32bit_cp1_register(CP1_REVISION); |
90 | if (fir & MIPS_FPIR_HAS2008) { |
91 | fcsr = read_32bit_cp1_register(CP1_STATUS); |
92 | |
93 | /* |
94 | * MAC2008 toolchain never landed in real world, so |
95 | * we're only testing whether it can be disabled and |
96 | * don't try to enabled it. |
97 | */ |
98 | fcsr0 = fcsr & ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008 | |
99 | FPU_CSR_MAC2008); |
100 | write_32bit_cp1_register(CP1_STATUS, fcsr0); |
101 | fcsr0 = read_32bit_cp1_register(CP1_STATUS); |
102 | |
103 | fcsr1 = fcsr | FPU_CSR_ABS2008 | FPU_CSR_NAN2008; |
104 | write_32bit_cp1_register(CP1_STATUS, fcsr1); |
105 | fcsr1 = read_32bit_cp1_register(CP1_STATUS); |
106 | |
107 | write_32bit_cp1_register(CP1_STATUS, fcsr); |
108 | |
109 | if (c->isa_level & (MIPS_CPU_ISA_M32R2 | |
110 | MIPS_CPU_ISA_M64R2)) { |
111 | /* |
112 | * The bit for MAC2008 might be reused by R6 |
113 | * in future, so we only test for R2-R5. |
114 | */ |
115 | if (fcsr0 & FPU_CSR_MAC2008) |
116 | c->options |= MIPS_CPU_MAC_2008_ONLY; |
117 | } |
118 | |
119 | if (!(fcsr0 & FPU_CSR_NAN2008)) |
120 | c->options |= MIPS_CPU_NAN_LEGACY; |
121 | if (fcsr1 & FPU_CSR_NAN2008) |
122 | c->options |= MIPS_CPU_NAN_2008; |
123 | |
124 | if ((fcsr0 ^ fcsr1) & FPU_CSR_ABS2008) |
125 | c->fpu_msk31 &= ~FPU_CSR_ABS2008; |
126 | else |
127 | c->fpu_csr31 |= fcsr & FPU_CSR_ABS2008; |
128 | |
129 | if ((fcsr0 ^ fcsr1) & FPU_CSR_NAN2008) |
130 | c->fpu_msk31 &= ~FPU_CSR_NAN2008; |
131 | else |
132 | c->fpu_csr31 |= fcsr & FPU_CSR_NAN2008; |
133 | } else { |
134 | c->options |= MIPS_CPU_NAN_LEGACY; |
135 | } |
136 | |
137 | write_c0_status(sr); |
138 | } else { |
139 | c->options |= MIPS_CPU_NAN_LEGACY; |
140 | } |
141 | } |
142 | |
143 | /* |
144 | * IEEE 754 conformance mode to use. Affects the NaN encoding and the |
145 | * ABS.fmt/NEG.fmt execution mode. |
146 | */ |
147 | static enum { STRICT, LEGACY, STD2008, RELAXED } ieee754 = STRICT; |
148 | |
149 | /* |
150 | * Set the IEEE 754 NaN encodings and the ABS.fmt/NEG.fmt execution modes |
151 | * to support by the FPU emulator according to the IEEE 754 conformance |
152 | * mode selected. Note that "relaxed" straps the emulator so that it |
153 | * allows 2008-NaN binaries even for legacy processors. |
154 | */ |
155 | static void cpu_set_nofpu_2008(struct cpuinfo_mips *c) |
156 | { |
157 | c->options &= ~(MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY); |
158 | c->fpu_csr31 &= ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008); |
159 | c->fpu_msk31 &= ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008); |
160 | |
161 | switch (ieee754) { |
162 | case STRICT: |
163 | if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | |
164 | MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | |
165 | MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | |
166 | MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) { |
167 | c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY; |
168 | } else { |
169 | c->options |= MIPS_CPU_NAN_LEGACY; |
170 | c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; |
171 | } |
172 | break; |
173 | case LEGACY: |
174 | c->options |= MIPS_CPU_NAN_LEGACY; |
175 | c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; |
176 | break; |
177 | case STD2008: |
178 | c->options |= MIPS_CPU_NAN_2008; |
179 | c->fpu_csr31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; |
180 | c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; |
181 | break; |
182 | case RELAXED: |
183 | c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY; |
184 | break; |
185 | } |
186 | } |
187 | |
188 | /* |
189 | * Override the IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode |
190 | * according to the "ieee754=" parameter. |
191 | */ |
192 | static void cpu_set_nan_2008(struct cpuinfo_mips *c) |
193 | { |
194 | switch (ieee754) { |
195 | case STRICT: |
196 | mips_use_nan_legacy = !!cpu_has_nan_legacy; |
197 | mips_use_nan_2008 = !!cpu_has_nan_2008; |
198 | break; |
199 | case LEGACY: |
200 | mips_use_nan_legacy = !!cpu_has_nan_legacy; |
201 | mips_use_nan_2008 = !cpu_has_nan_legacy; |
202 | break; |
203 | case STD2008: |
204 | mips_use_nan_legacy = !cpu_has_nan_2008; |
205 | mips_use_nan_2008 = !!cpu_has_nan_2008; |
206 | break; |
207 | case RELAXED: |
208 | mips_use_nan_legacy = true; |
209 | mips_use_nan_2008 = true; |
210 | break; |
211 | } |
212 | } |
213 | |
214 | /* |
215 | * IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode override |
216 | * settings: |
217 | * |
218 | * strict: accept binaries that request a NaN encoding supported by the FPU |
219 | * legacy: only accept legacy-NaN binaries |
220 | * 2008: only accept 2008-NaN binaries |
221 | * relaxed: accept any binaries regardless of whether supported by the FPU |
222 | */ |
223 | static int __init ieee754_setup(char *s) |
224 | { |
225 | if (!s) |
226 | return -1; |
227 | else if (!strcmp(s, "strict" )) |
228 | ieee754 = STRICT; |
229 | else if (!strcmp(s, "legacy" )) |
230 | ieee754 = LEGACY; |
231 | else if (!strcmp(s, "2008" )) |
232 | ieee754 = STD2008; |
233 | else if (!strcmp(s, "relaxed" )) |
234 | ieee754 = RELAXED; |
235 | else |
236 | return -1; |
237 | |
238 | if (!(boot_cpu_data.options & MIPS_CPU_FPU)) |
239 | cpu_set_nofpu_2008(c: &boot_cpu_data); |
240 | cpu_set_nan_2008(c: &boot_cpu_data); |
241 | |
242 | return 0; |
243 | } |
244 | |
245 | early_param("ieee754" , ieee754_setup); |
246 | |
247 | /* |
248 | * Set the FIR feature flags for the FPU emulator. |
249 | */ |
250 | static void cpu_set_nofpu_id(struct cpuinfo_mips *c) |
251 | { |
252 | u32 value; |
253 | |
254 | value = 0; |
255 | if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | |
256 | MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | |
257 | MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | |
258 | MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) |
259 | value |= MIPS_FPIR_D | MIPS_FPIR_S; |
260 | if (c->isa_level & (MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | |
261 | MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | |
262 | MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) |
263 | value |= MIPS_FPIR_F64 | MIPS_FPIR_L | MIPS_FPIR_W; |
264 | if (c->options & MIPS_CPU_NAN_2008) |
265 | value |= MIPS_FPIR_HAS2008; |
266 | c->fpu_id = value; |
267 | } |
268 | |
269 | /* Determined FPU emulator mask to use for the boot CPU with "nofpu". */ |
270 | static unsigned int mips_nofpu_msk31; |
271 | |
272 | /* |
273 | * Set options for FPU hardware. |
274 | */ |
275 | void cpu_set_fpu_opts(struct cpuinfo_mips *c) |
276 | { |
277 | c->fpu_id = cpu_get_fpu_id(); |
278 | mips_nofpu_msk31 = c->fpu_msk31; |
279 | |
280 | if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | |
281 | MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | |
282 | MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | |
283 | MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) { |
284 | if (c->fpu_id & MIPS_FPIR_3D) |
285 | c->ases |= MIPS_ASE_MIPS3D; |
286 | if (c->fpu_id & MIPS_FPIR_UFRP) |
287 | c->options |= MIPS_CPU_UFR; |
288 | if (c->fpu_id & MIPS_FPIR_FREP) |
289 | c->options |= MIPS_CPU_FRE; |
290 | } |
291 | |
292 | cpu_set_fpu_fcsr_mask(c); |
293 | cpu_set_fpu_2008(c); |
294 | cpu_set_nan_2008(c); |
295 | } |
296 | |
297 | /* |
298 | * Set options for the FPU emulator. |
299 | */ |
300 | void cpu_set_nofpu_opts(struct cpuinfo_mips *c) |
301 | { |
302 | c->options &= ~MIPS_CPU_FPU; |
303 | c->fpu_msk31 = mips_nofpu_msk31; |
304 | |
305 | cpu_set_nofpu_2008(c); |
306 | cpu_set_nan_2008(c); |
307 | cpu_set_nofpu_id(c); |
308 | } |
309 | |
310 | int mips_fpu_disabled; |
311 | |
312 | static int __init fpu_disable(char *s) |
313 | { |
314 | cpu_set_nofpu_opts(c: &boot_cpu_data); |
315 | mips_fpu_disabled = 1; |
316 | |
317 | return 1; |
318 | } |
319 | |
320 | __setup("nofpu" , fpu_disable); |
321 | |
322 | |