Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * fpu.c - save/restore of Floating Point Unit Registers on task switch
0004  *
0005  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
0006  */
0007 
0008 #include <linux/sched.h>
0009 #include <asm/fpu.h>
0010 
0011 #ifdef CONFIG_ISA_ARCOMPACT
0012 
0013 /*
0014  * To save/restore FPU regs, simplest scheme would use LR/SR insns.
0015  * However since SR serializes the pipeline, an alternate "hack" can be used
0016  * which uses the FPU Exchange insn (DEXCL) to r/w FPU regs.
0017  *
0018  * Store to 64bit dpfp1 reg from a pair of core regs:
0019  *   dexcl1 0, r1, r0  ; where r1:r0 is the 64 bit val
0020  *
0021  * Read from dpfp1 into pair of core regs (w/o clobbering dpfp1)
0022  *   mov_s    r3, 0
0023  *   daddh11  r1, r3, r3   ; get "hi" into r1 (dpfp1 unchanged)
0024  *   dexcl1   r0, r1, r3   ; get "low" into r0 (dpfp1 low clobbered)
0025  *   dexcl1    0, r1, r0   ; restore dpfp1 to orig value
0026  *
0027  * However we can tweak the read, so that read-out of outgoing task's FPU regs
0028  * and write of incoming task's regs happen in one shot. So all the work is
0029  * done before context switch
0030  */
0031 
0032 void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
0033 {
0034     unsigned int *saveto = &prev->thread.fpu.aux_dpfp[0].l;
0035     unsigned int *readfrom = &next->thread.fpu.aux_dpfp[0].l;
0036 
0037     const unsigned int zero = 0;
0038 
0039     __asm__ __volatile__(
0040         "daddh11  %0, %2, %2\n"
0041         "dexcl1   %1, %3, %4\n"
0042         : "=&r" (*(saveto + 1)), /* early clobber must here */
0043           "=&r" (*(saveto))
0044         : "r" (zero), "r" (*(readfrom + 1)), "r" (*(readfrom))
0045     );
0046 
0047     __asm__ __volatile__(
0048         "daddh22  %0, %2, %2\n"
0049         "dexcl2   %1, %3, %4\n"
0050         : "=&r"(*(saveto + 3)), /* early clobber must here */
0051           "=&r"(*(saveto + 2))
0052         : "r" (zero), "r" (*(readfrom + 3)), "r" (*(readfrom + 2))
0053     );
0054 }
0055 
0056 #else
0057 
0058 void fpu_init_task(struct pt_regs *regs)
0059 {
0060     const unsigned int fwe = 0x80000000;
0061 
0062     /* default rounding mode */
0063     write_aux_reg(ARC_REG_FPU_CTRL, 0x100);
0064 
0065     /* Initialize to zero: setting requires FWE be set */
0066     write_aux_reg(ARC_REG_FPU_STATUS, fwe);
0067 }
0068 
0069 void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
0070 {
0071     struct arc_fpu *save = &prev->thread.fpu;
0072     struct arc_fpu *restore = &next->thread.fpu;
0073     const unsigned int fwe = 0x80000000;
0074 
0075     save->ctrl = read_aux_reg(ARC_REG_FPU_CTRL);
0076     save->status = read_aux_reg(ARC_REG_FPU_STATUS);
0077 
0078     write_aux_reg(ARC_REG_FPU_CTRL, restore->ctrl);
0079     write_aux_reg(ARC_REG_FPU_STATUS, (fwe | restore->status));
0080 }
0081 
0082 #endif