fpu-probe.c (8279B)
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 */ 27static 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 */ 41int __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 */ 49static 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 */ 78static 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 */ 147static 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 */ 155static 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 */ 192static 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 */ 223static 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(&boot_cpu_data); 240 cpu_set_nan_2008(&boot_cpu_data); 241 242 return 0; 243} 244 245early_param("ieee754", ieee754_setup); 246 247/* 248 * Set the FIR feature flags for the FPU emulator. 249 */ 250static 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". */ 270static unsigned int mips_nofpu_msk31; 271 272/* 273 * Set options for FPU hardware. 274 */ 275void 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 */ 300void 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 310int mips_fpu_disabled; 311 312static int __init fpu_disable(char *s) 313{ 314 cpu_set_nofpu_opts(&boot_cpu_data); 315 mips_fpu_disabled = 1; 316 317 return 1; 318} 319 320__setup("nofpu", fpu_disable); 321