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, EMULATED, 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 case EMULATED: 164 if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | 165 MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | 166 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | 167 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) { 168 c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY; 169 } else { 170 c->options |= MIPS_CPU_NAN_LEGACY; 171 c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; 172 } 173 break; 174 case LEGACY: 175 c->options |= MIPS_CPU_NAN_LEGACY; 176 c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; 177 break; 178 case STD2008: 179 c->options |= MIPS_CPU_NAN_2008; 180 c->fpu_csr31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; 181 c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008; 182 break; 183 case RELAXED: 184 c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY; 185 break; 186 } 187 } 188 189 /* 190 * Override the IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode 191 * according to the "ieee754=" parameter. 192 */ 193 static void cpu_set_nan_2008(struct cpuinfo_mips *c) 194 { 195 switch (ieee754) { 196 case STRICT: 197 mips_use_nan_legacy = !!cpu_has_nan_legacy; 198 mips_use_nan_2008 = !!cpu_has_nan_2008; 199 break; 200 case LEGACY: 201 mips_use_nan_legacy = !!cpu_has_nan_legacy; 202 mips_use_nan_2008 = !cpu_has_nan_legacy; 203 break; 204 case STD2008: 205 mips_use_nan_legacy = !cpu_has_nan_2008; 206 mips_use_nan_2008 = !!cpu_has_nan_2008; 207 break; 208 case EMULATED: 209 /* Pretend ABS2008/NAN2008 options are dynamic */ 210 c->fpu_msk31 &= ~(FPU_CSR_NAN2008 | FPU_CSR_ABS2008); 211 fallthrough; 212 case RELAXED: 213 mips_use_nan_legacy = true; 214 mips_use_nan_2008 = true; 215 break; 216 } 217 } 218 219 /* 220 * IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode override 221 * settings: 222 * 223 * strict: accept binaries that request a NaN encoding supported by the FPU 224 * legacy: only accept legacy-NaN binaries 225 * 2008: only accept 2008-NaN binaries 226 * relaxed: accept any binaries regardless of whether supported by the FPU 227 */ 228 static int __init ieee754_setup(char *s) 229 { 230 if (!s) 231 return -1; 232 else if (!strcmp(s, "strict")) 233 ieee754 = STRICT; 234 else if (!strcmp(s, "emulated")) 235 ieee754 = EMULATED; 236 else if (!strcmp(s, "legacy")) 237 ieee754 = LEGACY; 238 else if (!strcmp(s, "2008")) 239 ieee754 = STD2008; 240 else if (!strcmp(s, "relaxed")) 241 ieee754 = RELAXED; 242 else 243 return -1; 244 245 if (!(boot_cpu_data.options & MIPS_CPU_FPU)) 246 cpu_set_nofpu_2008(&boot_cpu_data); 247 cpu_set_nan_2008(&boot_cpu_data); 248 249 return 0; 250 } 251 252 early_param("ieee754", ieee754_setup); 253 254 /* 255 * Set the FIR feature flags for the FPU emulator. 256 */ 257 static void cpu_set_nofpu_id(struct cpuinfo_mips *c) 258 { 259 u32 value; 260 261 value = 0; 262 if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | 263 MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | 264 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | 265 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) 266 value |= MIPS_FPIR_D | MIPS_FPIR_S; 267 if (c->isa_level & (MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | 268 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | 269 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) 270 value |= MIPS_FPIR_F64 | MIPS_FPIR_L | MIPS_FPIR_W; 271 if (c->options & MIPS_CPU_NAN_2008) 272 value |= MIPS_FPIR_HAS2008; 273 c->fpu_id = value; 274 } 275 276 /* Determined FPU emulator mask to use for the boot CPU with "nofpu". */ 277 static unsigned int mips_nofpu_msk31; 278 279 /* 280 * Set options for FPU hardware. 281 */ 282 void cpu_set_fpu_opts(struct cpuinfo_mips *c) 283 { 284 c->fpu_id = cpu_get_fpu_id(); 285 mips_nofpu_msk31 = c->fpu_msk31; 286 287 if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | 288 MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | 289 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | 290 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) { 291 if (c->fpu_id & MIPS_FPIR_3D) 292 c->ases |= MIPS_ASE_MIPS3D; 293 if (c->fpu_id & MIPS_FPIR_UFRP) 294 c->options |= MIPS_CPU_UFR; 295 if (c->fpu_id & MIPS_FPIR_FREP) 296 c->options |= MIPS_CPU_FRE; 297 } 298 299 cpu_set_fpu_fcsr_mask(c); 300 cpu_set_fpu_2008(c); 301 cpu_set_nan_2008(c); 302 } 303 304 /* 305 * Set options for the FPU emulator. 306 */ 307 void cpu_set_nofpu_opts(struct cpuinfo_mips *c) 308 { 309 c->options &= ~MIPS_CPU_FPU; 310 c->fpu_msk31 = mips_nofpu_msk31; 311 312 cpu_set_nofpu_2008(c); 313 cpu_set_nan_2008(c); 314 cpu_set_nofpu_id(c); 315 } 316 317 int mips_fpu_disabled; 318 319 static int __init fpu_disable(char *s) 320 { 321 cpu_set_nofpu_opts(&boot_cpu_data); 322 mips_fpu_disabled = 1; 323 324 return 1; 325 } 326 327 __setup("nofpu", fpu_disable); 328 329