Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.18 201 lines 4.1 kB view raw
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(void); 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 FPU_32BIT & FPU_64BIT 40 * purposefully have the values 0 & 1 respectively, so that an integer value 41 * of Status.FR can be trivially casted to the corresponding enum fpu_mode. 42 */ 43enum fpu_mode { 44 FPU_32BIT = 0, /* FR = 0 */ 45 FPU_64BIT, /* FR = 1 */ 46 FPU_AS_IS, 47}; 48 49static inline int __enable_fpu(enum fpu_mode mode) 50{ 51 int fr; 52 53 switch (mode) { 54 case FPU_AS_IS: 55 /* just enable the FPU in its current mode */ 56 set_c0_status(ST0_CU1); 57 enable_fpu_hazard(); 58 return 0; 59 60 case FPU_64BIT: 61#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT)) 62 /* we only have a 32-bit FPU */ 63 return SIGFPE; 64#endif 65 /* fall through */ 66 case FPU_32BIT: 67 /* set CU1 & change FR appropriately */ 68 fr = (int)mode; 69 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); 70 enable_fpu_hazard(); 71 72 /* check FR has the desired value */ 73 return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE; 74 75 default: 76 BUG(); 77 } 78 79 return SIGFPE; 80} 81 82#define __disable_fpu() \ 83do { \ 84 clear_c0_status(ST0_CU1); \ 85 disable_fpu_hazard(); \ 86} while (0) 87 88#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU) 89 90static inline int __is_fpu_owner(void) 91{ 92 return test_thread_flag(TIF_USEDFPU); 93} 94 95static inline int is_fpu_owner(void) 96{ 97 return cpu_has_fpu && __is_fpu_owner(); 98} 99 100static inline int __own_fpu(void) 101{ 102 enum fpu_mode mode; 103 int ret; 104 105 mode = !test_thread_flag(TIF_32BIT_FPREGS); 106 ret = __enable_fpu(mode); 107 if (ret) 108 return ret; 109 110 KSTK_STATUS(current) |= ST0_CU1; 111 if (mode == FPU_64BIT) 112 KSTK_STATUS(current) |= ST0_FR; 113 else /* mode == FPU_32BIT */ 114 KSTK_STATUS(current) &= ~ST0_FR; 115 116 set_thread_flag(TIF_USEDFPU); 117 return 0; 118} 119 120static inline int own_fpu_inatomic(int restore) 121{ 122 int ret = 0; 123 124 if (cpu_has_fpu && !__is_fpu_owner()) { 125 ret = __own_fpu(); 126 if (restore && !ret) 127 _restore_fp(current); 128 } 129 return ret; 130} 131 132static inline int own_fpu(int restore) 133{ 134 int ret; 135 136 preempt_disable(); 137 ret = own_fpu_inatomic(restore); 138 preempt_enable(); 139 return ret; 140} 141 142static inline void lose_fpu(int save) 143{ 144 preempt_disable(); 145 if (is_msa_enabled()) { 146 if (save) { 147 save_msa(current); 148 current->thread.fpu.fcr31 = 149 read_32bit_cp1_register(CP1_STATUS); 150 } 151 disable_msa(); 152 clear_thread_flag(TIF_USEDMSA); 153 } else if (is_fpu_owner()) { 154 if (save) 155 _save_fp(current); 156 __disable_fpu(); 157 } 158 KSTK_STATUS(current) &= ~ST0_CU1; 159 clear_thread_flag(TIF_USEDFPU); 160 preempt_enable(); 161} 162 163static inline int init_fpu(void) 164{ 165 int ret = 0; 166 167 if (cpu_has_fpu) { 168 ret = __own_fpu(); 169 if (!ret) 170 _init_fpu(); 171 } else 172 fpu_emulator_init_fpu(); 173 174 return ret; 175} 176 177static inline void save_fp(struct task_struct *tsk) 178{ 179 if (cpu_has_fpu) 180 _save_fp(tsk); 181} 182 183static inline void restore_fp(struct task_struct *tsk) 184{ 185 if (cpu_has_fpu) 186 _restore_fp(tsk); 187} 188 189static inline union fpureg *get_fpu_regs(struct task_struct *tsk) 190{ 191 if (tsk == current) { 192 preempt_disable(); 193 if (is_fpu_owner()) 194 _save_fp(current); 195 preempt_enable(); 196 } 197 198 return tsk->thread.fpu.fpr; 199} 200 201#endif /* _ASM_FPU_H */