linux/arch/mips/kernel/fpu-probe.c
<<
>>
Prefs
   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
 322