fpu.h 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. #include <linux/sched.h>
  13. #include <linux/sched/task_stack.h>
  14. #include <linux/ptrace.h>
  15. #include <linux/thread_info.h>
  16. #include <linux/bitops.h>
  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/ptrace.h>
  23. #include <asm/processor.h>
  24. #include <asm/current.h>
  25. #include <asm/msa.h>
  26. #ifdef CONFIG_MIPS_MT_FPAFF
  27. #include <asm/mips_mt.h>
  28. #endif
  29. struct sigcontext;
  30. struct sigcontext32;
  31. extern void _init_fpu(unsigned int);
  32. extern void _save_fp(struct task_struct *);
  33. extern void _restore_fp(struct task_struct *);
  34. /*
  35. * This enum specifies a mode in which we want the FPU to operate, for cores
  36. * which implement the Status.FR bit. Note that the bottom bit of the value
  37. * purposefully matches the desired value of the Status.FR bit.
  38. */
  39. enum fpu_mode {
  40. FPU_32BIT = 0, /* FR = 0 */
  41. FPU_64BIT, /* FR = 1, FRE = 0 */
  42. FPU_AS_IS,
  43. FPU_HYBRID, /* FR = 1, FRE = 1 */
  44. #define FPU_FR_MASK 0x1
  45. };
  46. #define __disable_fpu() \
  47. do { \
  48. clear_c0_status(ST0_CU1); \
  49. disable_fpu_hazard(); \
  50. } while (0)
  51. static inline int __enable_fpu(enum fpu_mode mode)
  52. {
  53. int fr;
  54. switch (mode) {
  55. case FPU_AS_IS:
  56. /* just enable the FPU in its current mode */
  57. set_c0_status(ST0_CU1);
  58. enable_fpu_hazard();
  59. return 0;
  60. case FPU_HYBRID:
  61. if (!cpu_has_fre)
  62. return SIGFPE;
  63. /* set FRE */
  64. set_c0_config5(MIPS_CONF5_FRE);
  65. goto fr_common;
  66. case FPU_64BIT:
  67. #if !(defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6) \
  68. || defined(CONFIG_64BIT))
  69. /* we only have a 32-bit FPU */
  70. return SIGFPE;
  71. #endif
  72. /* fall through */
  73. case FPU_32BIT:
  74. if (cpu_has_fre) {
  75. /* clear FRE */
  76. clear_c0_config5(MIPS_CONF5_FRE);
  77. }
  78. fr_common:
  79. /* set CU1 & change FR appropriately */
  80. fr = (int)mode & FPU_FR_MASK;
  81. change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
  82. enable_fpu_hazard();
  83. /* check FR has the desired value */
  84. if (!!(read_c0_status() & ST0_FR) == !!fr)
  85. return 0;
  86. /* unsupported FR value */
  87. __disable_fpu();
  88. return SIGFPE;
  89. default:
  90. BUG();
  91. }
  92. return SIGFPE;
  93. }
  94. #define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU)
  95. static inline int __is_fpu_owner(void)
  96. {
  97. return test_thread_flag(TIF_USEDFPU);
  98. }
  99. static inline int is_fpu_owner(void)
  100. {
  101. return cpu_has_fpu && __is_fpu_owner();
  102. }
  103. static inline int __own_fpu(void)
  104. {
  105. enum fpu_mode mode;
  106. int ret;
  107. if (test_thread_flag(TIF_HYBRID_FPREGS))
  108. mode = FPU_HYBRID;
  109. else
  110. mode = !test_thread_flag(TIF_32BIT_FPREGS);
  111. ret = __enable_fpu(mode);
  112. if (ret)
  113. return ret;
  114. KSTK_STATUS(current) |= ST0_CU1;
  115. if (mode == FPU_64BIT || mode == FPU_HYBRID)
  116. KSTK_STATUS(current) |= ST0_FR;
  117. else /* mode == FPU_32BIT */
  118. KSTK_STATUS(current) &= ~ST0_FR;
  119. set_thread_flag(TIF_USEDFPU);
  120. return 0;
  121. }
  122. static inline int own_fpu_inatomic(int restore)
  123. {
  124. int ret = 0;
  125. if (cpu_has_fpu && !__is_fpu_owner()) {
  126. ret = __own_fpu();
  127. if (restore && !ret)
  128. _restore_fp(current);
  129. }
  130. return ret;
  131. }
  132. static inline int own_fpu(int restore)
  133. {
  134. int ret;
  135. preempt_disable();
  136. ret = own_fpu_inatomic(restore);
  137. preempt_enable();
  138. return ret;
  139. }
  140. static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
  141. {
  142. if (is_msa_enabled()) {
  143. if (save) {
  144. save_msa(tsk);
  145. tsk->thread.fpu.fcr31 =
  146. read_32bit_cp1_register(CP1_STATUS);
  147. }
  148. disable_msa();
  149. clear_tsk_thread_flag(tsk, TIF_USEDMSA);
  150. __disable_fpu();
  151. } else if (is_fpu_owner()) {
  152. if (save)
  153. _save_fp(tsk);
  154. __disable_fpu();
  155. } else {
  156. /* FPU should not have been left enabled with no owner */
  157. WARN(read_c0_status() & ST0_CU1,
  158. "Orphaned FPU left enabled");
  159. }
  160. KSTK_STATUS(tsk) &= ~ST0_CU1;
  161. clear_tsk_thread_flag(tsk, TIF_USEDFPU);
  162. }
  163. static inline void lose_fpu(int save)
  164. {
  165. preempt_disable();
  166. lose_fpu_inatomic(save, current);
  167. preempt_enable();
  168. }
  169. static inline int init_fpu(void)
  170. {
  171. unsigned int fcr31 = current->thread.fpu.fcr31;
  172. int ret = 0;
  173. if (cpu_has_fpu) {
  174. unsigned int config5;
  175. ret = __own_fpu();
  176. if (ret)
  177. return ret;
  178. if (!cpu_has_fre) {
  179. _init_fpu(fcr31);
  180. return 0;
  181. }
  182. /*
  183. * Ensure FRE is clear whilst running _init_fpu, since
  184. * single precision FP instructions are used. If FRE
  185. * was set then we'll just end up initialising all 32
  186. * 64b registers.
  187. */
  188. config5 = clear_c0_config5(MIPS_CONF5_FRE);
  189. enable_fpu_hazard();
  190. _init_fpu(fcr31);
  191. /* Restore FRE */
  192. write_c0_config5(config5);
  193. enable_fpu_hazard();
  194. } else
  195. fpu_emulator_init_fpu();
  196. return ret;
  197. }
  198. static inline void save_fp(struct task_struct *tsk)
  199. {
  200. if (cpu_has_fpu)
  201. _save_fp(tsk);
  202. }
  203. static inline void restore_fp(struct task_struct *tsk)
  204. {
  205. if (cpu_has_fpu)
  206. _restore_fp(tsk);
  207. }
  208. static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
  209. {
  210. if (tsk == current) {
  211. preempt_disable();
  212. if (is_fpu_owner())
  213. _save_fp(current);
  214. preempt_enable();
  215. }
  216. return tsk->thread.fpu.fpr;
  217. }
  218. #endif /* _ASM_FPU_H */