Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Processor capabilities determination functions.
0004  *
0005  * Copyright (C) xxxx  the Anonymous
0006  * Copyright (C) 1994 - 2006 Ralf Baechle
0007  * Copyright (C) 2003, 2004  Maciej W. Rozycki
0008  * Copyright (C) 2001, 2004, 2011, 2012  MIPS Technologies, Inc.
0009  */
0010 
0011 #include <linux/init.h>
0012 #include <linux/kernel.h>
0013 
0014 #include <asm/bugs.h>
0015 #include <asm/cpu.h>
0016 #include <asm/cpu-features.h>
0017 #include <asm/cpu-type.h>
0018 #include <asm/elf.h>
0019 #include <asm/fpu.h>
0020 #include <asm/mipsregs.h>
0021 
0022 #include "fpu-probe.h"
0023 
0024 /*
0025  * Get the FPU Implementation/Revision.
0026  */
0027 static inline unsigned long cpu_get_fpu_id(void)
0028 {
0029     unsigned long tmp, fpu_id;
0030 
0031     tmp = read_c0_status();
0032     __enable_fpu(FPU_AS_IS);
0033     fpu_id = read_32bit_cp1_register(CP1_REVISION);
0034     write_c0_status(tmp);
0035     return fpu_id;
0036 }
0037 
0038 /*
0039  * Check if the CPU has an external FPU.
0040  */
0041 int __cpu_has_fpu(void)
0042 {
0043     return (cpu_get_fpu_id() & FPIR_IMP_MASK) != FPIR_IMP_NONE;
0044 }
0045 
0046 /*
0047  * Determine the FCSR mask for FPU hardware.
0048  */
0049 static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_mips *c)
0050 {
0051     unsigned long sr, mask, fcsr, fcsr0, fcsr1;
0052 
0053     fcsr = c->fpu_csr31;
0054     mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
0055 
0056     sr = read_c0_status();
0057     __enable_fpu(FPU_AS_IS);
0058 
0059     fcsr0 = fcsr & mask;
0060     write_32bit_cp1_register(CP1_STATUS, fcsr0);
0061     fcsr0 = read_32bit_cp1_register(CP1_STATUS);
0062 
0063     fcsr1 = fcsr | ~mask;
0064     write_32bit_cp1_register(CP1_STATUS, fcsr1);
0065     fcsr1 = read_32bit_cp1_register(CP1_STATUS);
0066 
0067     write_32bit_cp1_register(CP1_STATUS, fcsr);
0068 
0069     write_c0_status(sr);
0070 
0071     c->fpu_msk31 = ~(fcsr0 ^ fcsr1) & ~mask;
0072 }
0073 
0074 /*
0075  * Determine the IEEE 754 NaN encodings and ABS.fmt/NEG.fmt execution modes
0076  * supported by FPU hardware.
0077  */
0078 static void cpu_set_fpu_2008(struct cpuinfo_mips *c)
0079 {
0080     if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
0081                 MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
0082                 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
0083                 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) {
0084         unsigned long sr, fir, fcsr, fcsr0, fcsr1;
0085 
0086         sr = read_c0_status();
0087         __enable_fpu(FPU_AS_IS);
0088 
0089         fir = read_32bit_cp1_register(CP1_REVISION);
0090         if (fir & MIPS_FPIR_HAS2008) {
0091             fcsr = read_32bit_cp1_register(CP1_STATUS);
0092 
0093             /*
0094              * MAC2008 toolchain never landed in real world, so
0095              * we're only testing whether it can be disabled and
0096              *  don't try to enabled it.
0097              */
0098             fcsr0 = fcsr & ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008 |
0099                      FPU_CSR_MAC2008);
0100             write_32bit_cp1_register(CP1_STATUS, fcsr0);
0101             fcsr0 = read_32bit_cp1_register(CP1_STATUS);
0102 
0103             fcsr1 = fcsr | FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
0104             write_32bit_cp1_register(CP1_STATUS, fcsr1);
0105             fcsr1 = read_32bit_cp1_register(CP1_STATUS);
0106 
0107             write_32bit_cp1_register(CP1_STATUS, fcsr);
0108 
0109             if (c->isa_level & (MIPS_CPU_ISA_M32R2 |
0110                         MIPS_CPU_ISA_M64R2)) {
0111                 /*
0112                  * The bit for MAC2008 might be reused by R6
0113                  * in future, so we only test for R2-R5.
0114                  */
0115                 if (fcsr0 & FPU_CSR_MAC2008)
0116                     c->options |= MIPS_CPU_MAC_2008_ONLY;
0117             }
0118 
0119             if (!(fcsr0 & FPU_CSR_NAN2008))
0120                 c->options |= MIPS_CPU_NAN_LEGACY;
0121             if (fcsr1 & FPU_CSR_NAN2008)
0122                 c->options |= MIPS_CPU_NAN_2008;
0123 
0124             if ((fcsr0 ^ fcsr1) & FPU_CSR_ABS2008)
0125                 c->fpu_msk31 &= ~FPU_CSR_ABS2008;
0126             else
0127                 c->fpu_csr31 |= fcsr & FPU_CSR_ABS2008;
0128 
0129             if ((fcsr0 ^ fcsr1) & FPU_CSR_NAN2008)
0130                 c->fpu_msk31 &= ~FPU_CSR_NAN2008;
0131             else
0132                 c->fpu_csr31 |= fcsr & FPU_CSR_NAN2008;
0133         } else {
0134             c->options |= MIPS_CPU_NAN_LEGACY;
0135         }
0136 
0137         write_c0_status(sr);
0138     } else {
0139         c->options |= MIPS_CPU_NAN_LEGACY;
0140     }
0141 }
0142 
0143 /*
0144  * IEEE 754 conformance mode to use.  Affects the NaN encoding and the
0145  * ABS.fmt/NEG.fmt execution mode.
0146  */
0147 static enum { STRICT, LEGACY, STD2008, RELAXED } ieee754 = STRICT;
0148 
0149 /*
0150  * Set the IEEE 754 NaN encodings and the ABS.fmt/NEG.fmt execution modes
0151  * to support by the FPU emulator according to the IEEE 754 conformance
0152  * mode selected.  Note that "relaxed" straps the emulator so that it
0153  * allows 2008-NaN binaries even for legacy processors.
0154  */
0155 static void cpu_set_nofpu_2008(struct cpuinfo_mips *c)
0156 {
0157     c->options &= ~(MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY);
0158     c->fpu_csr31 &= ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008);
0159     c->fpu_msk31 &= ~(FPU_CSR_ABS2008 | FPU_CSR_NAN2008);
0160 
0161     switch (ieee754) {
0162     case STRICT:
0163         if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
0164                     MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
0165                     MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
0166                     MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) {
0167             c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY;
0168         } else {
0169             c->options |= MIPS_CPU_NAN_LEGACY;
0170             c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
0171         }
0172         break;
0173     case LEGACY:
0174         c->options |= MIPS_CPU_NAN_LEGACY;
0175         c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
0176         break;
0177     case STD2008:
0178         c->options |= MIPS_CPU_NAN_2008;
0179         c->fpu_csr31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
0180         c->fpu_msk31 |= FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
0181         break;
0182     case RELAXED:
0183         c->options |= MIPS_CPU_NAN_2008 | MIPS_CPU_NAN_LEGACY;
0184         break;
0185     }
0186 }
0187 
0188 /*
0189  * Override the IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode
0190  * according to the "ieee754=" parameter.
0191  */
0192 static void cpu_set_nan_2008(struct cpuinfo_mips *c)
0193 {
0194     switch (ieee754) {
0195     case STRICT:
0196         mips_use_nan_legacy = !!cpu_has_nan_legacy;
0197         mips_use_nan_2008 = !!cpu_has_nan_2008;
0198         break;
0199     case LEGACY:
0200         mips_use_nan_legacy = !!cpu_has_nan_legacy;
0201         mips_use_nan_2008 = !cpu_has_nan_legacy;
0202         break;
0203     case STD2008:
0204         mips_use_nan_legacy = !cpu_has_nan_2008;
0205         mips_use_nan_2008 = !!cpu_has_nan_2008;
0206         break;
0207     case RELAXED:
0208         mips_use_nan_legacy = true;
0209         mips_use_nan_2008 = true;
0210         break;
0211     }
0212 }
0213 
0214 /*
0215  * IEEE 754 NaN encoding and ABS.fmt/NEG.fmt execution mode override
0216  * settings:
0217  *
0218  * strict:  accept binaries that request a NaN encoding supported by the FPU
0219  * legacy:  only accept legacy-NaN binaries
0220  * 2008:    only accept 2008-NaN binaries
0221  * relaxed: accept any binaries regardless of whether supported by the FPU
0222  */
0223 static int __init ieee754_setup(char *s)
0224 {
0225     if (!s)
0226         return -1;
0227     else if (!strcmp(s, "strict"))
0228         ieee754 = STRICT;
0229     else if (!strcmp(s, "legacy"))
0230         ieee754 = LEGACY;
0231     else if (!strcmp(s, "2008"))
0232         ieee754 = STD2008;
0233     else if (!strcmp(s, "relaxed"))
0234         ieee754 = RELAXED;
0235     else
0236         return -1;
0237 
0238     if (!(boot_cpu_data.options & MIPS_CPU_FPU))
0239         cpu_set_nofpu_2008(&boot_cpu_data);
0240     cpu_set_nan_2008(&boot_cpu_data);
0241 
0242     return 0;
0243 }
0244 
0245 early_param("ieee754", ieee754_setup);
0246 
0247 /*
0248  * Set the FIR feature flags for the FPU emulator.
0249  */
0250 static void cpu_set_nofpu_id(struct cpuinfo_mips *c)
0251 {
0252     u32 value;
0253 
0254     value = 0;
0255     if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
0256                 MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
0257                 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
0258                 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6))
0259         value |= MIPS_FPIR_D | MIPS_FPIR_S;
0260     if (c->isa_level & (MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
0261                 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
0262                 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6))
0263         value |= MIPS_FPIR_F64 | MIPS_FPIR_L | MIPS_FPIR_W;
0264     if (c->options & MIPS_CPU_NAN_2008)
0265         value |= MIPS_FPIR_HAS2008;
0266     c->fpu_id = value;
0267 }
0268 
0269 /* Determined FPU emulator mask to use for the boot CPU with "nofpu".  */
0270 static unsigned int mips_nofpu_msk31;
0271 
0272 /*
0273  * Set options for FPU hardware.
0274  */
0275 void cpu_set_fpu_opts(struct cpuinfo_mips *c)
0276 {
0277     c->fpu_id = cpu_get_fpu_id();
0278     mips_nofpu_msk31 = c->fpu_msk31;
0279 
0280     if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
0281                 MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
0282                 MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 |
0283                 MIPS_CPU_ISA_M32R6 | MIPS_CPU_ISA_M64R6)) {
0284         if (c->fpu_id & MIPS_FPIR_3D)
0285             c->ases |= MIPS_ASE_MIPS3D;
0286         if (c->fpu_id & MIPS_FPIR_UFRP)
0287             c->options |= MIPS_CPU_UFR;
0288         if (c->fpu_id & MIPS_FPIR_FREP)
0289             c->options |= MIPS_CPU_FRE;
0290     }
0291 
0292     cpu_set_fpu_fcsr_mask(c);
0293     cpu_set_fpu_2008(c);
0294     cpu_set_nan_2008(c);
0295 }
0296 
0297 /*
0298  * Set options for the FPU emulator.
0299  */
0300 void cpu_set_nofpu_opts(struct cpuinfo_mips *c)
0301 {
0302     c->options &= ~MIPS_CPU_FPU;
0303     c->fpu_msk31 = mips_nofpu_msk31;
0304 
0305     cpu_set_nofpu_2008(c);
0306     cpu_set_nan_2008(c);
0307     cpu_set_nofpu_id(c);
0308 }
0309 
0310 int mips_fpu_disabled;
0311 
0312 static int __init fpu_disable(char *s)
0313 {
0314     cpu_set_nofpu_opts(&boot_cpu_data);
0315     mips_fpu_disabled = 1;
0316 
0317     return 1;
0318 }
0319 
0320 __setup("nofpu", fpu_disable);
0321