| 1 | |
| 2 | |
| 3 | * fpu.c - save/restore of Floating Point Unit Registers on task switch
|
| 4 | *
|
| 5 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
|
| 6 |
|
| 7 |
|
| 8 | #include <linux/sched.h>
|
| 9 | #include <asm/fpu.h>
|
| 10 |
|
| 11 | #ifdef CONFIG_ISA_ARCOMPACT
|
| 12 |
|
| 13 | |
| 14 | * To save/restore FPU regs, simplest scheme would use LR/SR insns.
|
| 15 | * However since SR serializes the pipeline, an alternate "hack" can be used
|
| 16 | * which uses the FPU Exchange insn (DEXCL) to r/w FPU regs.
|
| 17 | *
|
| 18 | * Store to 64bit dpfp1 reg from a pair of core regs:
|
| 19 | * dexcl1 0, r1, r0 ; where r1:r0 is the 64 bit val
|
| 20 | *
|
| 21 | * Read from dpfp1 into pair of core regs (w/o clobbering dpfp1)
|
| 22 | * mov_s r3, 0
|
| 23 | * daddh11 r1, r3, r3 ; get "hi" into r1 (dpfp1 unchanged)
|
| 24 | * dexcl1 r0, r1, r3 ; get "low" into r0 (dpfp1 low clobbered)
|
| 25 | * dexcl1 0, r1, r0 ; restore dpfp1 to orig value
|
| 26 | *
|
| 27 | * However we can tweak the read, so that read-out of outgoing task's FPU regs
|
| 28 | * and write of incoming task's regs happen in one shot. So all the work is
|
| 29 | * done before context switch
|
| 30 |
|
| 31 |
|
| 32 | void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
|
| 33 | {
|
| 34 | unsigned int *saveto = &prev->thread.fpu.aux_dpfp[0].l;
|
| 35 | unsigned int *readfrom = &next->thread.fpu.aux_dpfp[0].l;
|
| 36 |
|
| 37 | const unsigned int zero = 0;
|
| 38 |
|
| 39 | __asm__ __volatile__(
|
| 40 | "daddh11 %0, %2, %2\n"
|
| 41 | "dexcl1 %1, %3, %4\n"
|
| 42 | : "=&r" (*(saveto + 1)),
|
| 43 | "=&r" (*(saveto))
|
| 44 | : "r" (zero), "r" (*(readfrom + 1)), "r" (*(readfrom))
|
| 45 | );
|
| 46 |
|
| 47 | __asm__ __volatile__(
|
| 48 | "daddh22 %0, %2, %2\n"
|
| 49 | "dexcl2 %1, %3, %4\n"
|
| 50 | : "=&r"(*(saveto + 3)),
|
| 51 | "=&r"(*(saveto + 2))
|
| 52 | : "r" (zero), "r" (*(readfrom + 3)), "r" (*(readfrom + 2))
|
| 53 | );
|
| 54 | }
|
| 55 |
|
| 56 | #else
|
| 57 |
|
| 58 | void fpu_init_task(struct pt_regs *regs)
|
| 59 | {
|
| 60 | const unsigned int fwe = 0x80000000;
|
| 61 |
|
| 62 |
|
| 63 | write_aux_reg(ARC_REG_FPU_CTRL, 0x100);
|
| 64 |
|
| 65 |
|
| 66 | write_aux_reg(ARC_REG_FPU_STATUS, fwe);
|
| 67 | }
|
| 68 |
|
| 69 | void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
|
| 70 | {
|
| 71 | struct arc_fpu *save = &prev->thread.fpu;
|
| 72 | struct arc_fpu *restore = &next->thread.fpu;
|
| 73 | const unsigned int fwe = 0x80000000;
|
| 74 |
|
| 75 | save->ctrl = read_aux_reg(ARC_REG_FPU_CTRL);
|
| 76 | save->status = read_aux_reg(ARC_REG_FPU_STATUS);
|
| 77 |
|
| 78 | write_aux_reg(ARC_REG_FPU_CTRL, restore->ctrl);
|
| 79 | write_aux_reg(ARC_REG_FPU_STATUS, (fwe | restore->status));
|
| 80 | }
|
| 81 |
|
| 82 | #endif
|