linux/arch/mips/include/asm/fpu.h
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2002 MontaVista Software Inc.
   3 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License as published by the
   7 * Free Software Foundation;  either version 2 of the  License, or (at your
   8 * option) any later version.
   9 */
  10#ifndef _ASM_FPU_H
  11#define _ASM_FPU_H
  12
  13#include <linux/sched.h>
  14#include <linux/thread_info.h>
  15#include <linux/bitops.h>
  16
  17#include <asm/mipsregs.h>
  18#include <asm/cpu.h>
  19#include <asm/cpu-features.h>
  20#include <asm/fpu_emulator.h>
  21#include <asm/hazards.h>
  22#include <asm/processor.h>
  23#include <asm/current.h>
  24#include <asm/msa.h>
  25
  26#ifdef CONFIG_MIPS_MT_FPAFF
  27#include <asm/mips_mt.h>
  28#endif
  29
  30struct sigcontext;
  31struct sigcontext32;
  32
  33extern void _init_fpu(unsigned int);
  34extern void _save_fp(struct task_struct *);
  35extern void _restore_fp(struct task_struct *);
  36
  37/*
  38 * This enum specifies a mode in which we want the FPU to operate, for cores
  39 * which implement the Status.FR bit. Note that the bottom bit of the value
  40 * purposefully matches the desired value of the Status.FR bit.
  41 */
  42enum fpu_mode {
  43        FPU_32BIT = 0,          /* FR = 0 */
  44        FPU_64BIT,              /* FR = 1, FRE = 0 */
  45        FPU_AS_IS,
  46        FPU_HYBRID,             /* FR = 1, FRE = 1 */
  47
  48#define FPU_FR_MASK             0x1
  49};
  50
  51#define __disable_fpu()                                                 \
  52do {                                                                    \
  53        clear_c0_status(ST0_CU1);                                       \
  54        disable_fpu_hazard();                                           \
  55} while (0)
  56
  57static inline int __enable_fpu(enum fpu_mode mode)
  58{
  59        int fr;
  60
  61        switch (mode) {
  62        case FPU_AS_IS:
  63                /* just enable the FPU in its current mode */
  64                set_c0_status(ST0_CU1);
  65                enable_fpu_hazard();
  66                return 0;
  67
  68        case FPU_HYBRID:
  69                if (!cpu_has_fre)
  70                        return SIGFPE;
  71
  72                /* set FRE */
  73                set_c0_config5(MIPS_CONF5_FRE);
  74                goto fr_common;
  75
  76        case FPU_64BIT:
  77#if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \
  78      || defined(CONFIG_64BIT))
  79                /* we only have a 32-bit FPU */
  80                return SIGFPE;
  81#endif
  82                /* fall through */
  83        case FPU_32BIT:
  84                if (cpu_has_fre) {
  85                        /* clear FRE */
  86                        clear_c0_config5(MIPS_CONF5_FRE);
  87                }
  88fr_common:
  89                /* set CU1 & change FR appropriately */
  90                fr = (int)mode & FPU_FR_MASK;
  91                change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
  92                enable_fpu_hazard();
  93
  94                /* check FR has the desired value */
  95                if (!!(read_c0_status() & ST0_FR) == !!fr)
  96                        return 0;
  97
  98                /* unsupported FR value */
  99                __disable_fpu();
 100                return SIGFPE;
 101
 102        default:
 103                BUG();
 104        }
 105
 106        return SIGFPE;
 107}
 108
 109#define clear_fpu_owner()       clear_thread_flag(TIF_USEDFPU)
 110
 111static inline int __is_fpu_owner(void)
 112{
 113        return test_thread_flag(TIF_USEDFPU);
 114}
 115
 116static inline int is_fpu_owner(void)
 117{
 118        return cpu_has_fpu && __is_fpu_owner();
 119}
 120
 121static inline int __own_fpu(void)
 122{
 123        enum fpu_mode mode;
 124        int ret;
 125
 126        if (test_thread_flag(TIF_HYBRID_FPREGS))
 127                mode = FPU_HYBRID;
 128        else
 129                mode = !test_thread_flag(TIF_32BIT_FPREGS);
 130
 131        ret = __enable_fpu(mode);
 132        if (ret)
 133                return ret;
 134
 135        KSTK_STATUS(current) |= ST0_CU1;
 136        if (mode == FPU_64BIT || mode == FPU_HYBRID)
 137                KSTK_STATUS(current) |= ST0_FR;
 138        else /* mode == FPU_32BIT */
 139                KSTK_STATUS(current) &= ~ST0_FR;
 140
 141        set_thread_flag(TIF_USEDFPU);
 142        return 0;
 143}
 144
 145static inline int own_fpu_inatomic(int restore)
 146{
 147        int ret = 0;
 148
 149        if (cpu_has_fpu && !__is_fpu_owner()) {
 150                ret = __own_fpu();
 151                if (restore && !ret)
 152                        _restore_fp(current);
 153        }
 154        return ret;
 155}
 156
 157static inline int own_fpu(int restore)
 158{
 159        int ret;
 160
 161        preempt_disable();
 162        ret = own_fpu_inatomic(restore);
 163        preempt_enable();
 164        return ret;
 165}
 166
 167static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
 168{
 169        if (is_msa_enabled()) {
 170                if (save) {
 171                        save_msa(tsk);
 172                        tsk->thread.fpu.fcr31 =
 173                                        read_32bit_cp1_register(CP1_STATUS);
 174                }
 175                disable_msa();
 176                clear_tsk_thread_flag(tsk, TIF_USEDMSA);
 177                __disable_fpu();
 178        } else if (is_fpu_owner()) {
 179                if (save)
 180                        _save_fp(tsk);
 181                __disable_fpu();
 182        } else {
 183                /* FPU should not have been left enabled with no owner */
 184                WARN(read_c0_status() & ST0_CU1,
 185                     "Orphaned FPU left enabled");
 186        }
 187        KSTK_STATUS(tsk) &= ~ST0_CU1;
 188        clear_tsk_thread_flag(tsk, TIF_USEDFPU);
 189}
 190
 191static inline void lose_fpu(int save)
 192{
 193        preempt_disable();
 194        lose_fpu_inatomic(save, current);
 195        preempt_enable();
 196}
 197
 198static inline int init_fpu(void)
 199{
 200        unsigned int fcr31 = current->thread.fpu.fcr31;
 201        int ret = 0;
 202
 203        if (cpu_has_fpu) {
 204                unsigned int config5;
 205
 206                ret = __own_fpu();
 207                if (ret)
 208                        return ret;
 209
 210                if (!cpu_has_fre) {
 211                        _init_fpu(fcr31);
 212
 213                        return 0;
 214                }
 215
 216                /*
 217                 * Ensure FRE is clear whilst running _init_fpu, since
 218                 * single precision FP instructions are used. If FRE
 219                 * was set then we'll just end up initialising all 32
 220                 * 64b registers.
 221                 */
 222                config5 = clear_c0_config5(MIPS_CONF5_FRE);
 223                enable_fpu_hazard();
 224
 225                _init_fpu(fcr31);
 226
 227                /* Restore FRE */
 228                write_c0_config5(config5);
 229                enable_fpu_hazard();
 230        } else
 231                fpu_emulator_init_fpu();
 232
 233        return ret;
 234}
 235
 236static inline void save_fp(struct task_struct *tsk)
 237{
 238        if (cpu_has_fpu)
 239                _save_fp(tsk);
 240}
 241
 242static inline void restore_fp(struct task_struct *tsk)
 243{
 244        if (cpu_has_fpu)
 245                _restore_fp(tsk);
 246}
 247
 248static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
 249{
 250        if (tsk == current) {
 251                preempt_disable();
 252                if (is_fpu_owner())
 253                        _save_fp(current);
 254                preempt_enable();
 255        }
 256
 257        return tsk->thread.fpu.fpr;
 258}
 259
 260#endif /* _ASM_FPU_H */
 261