Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2005-2018 Andes Technology Corporation
  3
  4#include <linux/sched.h>
  5#include <linux/signal.h>
  6#include <linux/sched/signal.h>
  7#include <asm/processor.h>
  8#include <asm/user.h>
  9#include <asm/io.h>
 10#include <asm/bitfield.h>
 11#include <asm/fpu.h>
 12
 13const struct fpu_struct init_fpuregs = {
 14	.fd_regs = {[0 ... 31] = sNAN64},
 15	.fpcsr = FPCSR_INIT,
 16#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
 17	.UDF_IEX_trap = 0
 18#endif
 19};
 20
 21void save_fpu(struct task_struct *tsk)
 22{
 23	unsigned int fpcfg, fpcsr;
 24
 25	enable_fpu();
 26	fpcfg = ((__nds32__fmfcfg() & FPCFG_mskFREG) >> FPCFG_offFREG);
 27	switch (fpcfg) {
 28	case SP32_DP32_reg:
 29		asm volatile ("fsdi $fd31, [%0+0xf8]\n\t"
 30			      "fsdi $fd30, [%0+0xf0]\n\t"
 31			      "fsdi $fd29, [%0+0xe8]\n\t"
 32			      "fsdi $fd28, [%0+0xe0]\n\t"
 33			      "fsdi $fd27, [%0+0xd8]\n\t"
 34			      "fsdi $fd26, [%0+0xd0]\n\t"
 35			      "fsdi $fd25, [%0+0xc8]\n\t"
 36			      "fsdi $fd24, [%0+0xc0]\n\t"
 37			      "fsdi $fd23, [%0+0xb8]\n\t"
 38			      "fsdi $fd22, [%0+0xb0]\n\t"
 39			      "fsdi $fd21, [%0+0xa8]\n\t"
 40			      "fsdi $fd20, [%0+0xa0]\n\t"
 41			      "fsdi $fd19, [%0+0x98]\n\t"
 42			      "fsdi $fd18, [%0+0x90]\n\t"
 43			      "fsdi $fd17, [%0+0x88]\n\t"
 44			      "fsdi $fd16, [%0+0x80]\n\t"
 45			      :	/* no output */
 46			      : "r" (&tsk->thread.fpu)
 47			      : "memory");
 48		fallthrough;
 49	case SP32_DP16_reg:
 50		asm volatile ("fsdi $fd15, [%0+0x78]\n\t"
 51			      "fsdi $fd14, [%0+0x70]\n\t"
 52			      "fsdi $fd13, [%0+0x68]\n\t"
 53			      "fsdi $fd12, [%0+0x60]\n\t"
 54			      "fsdi $fd11, [%0+0x58]\n\t"
 55			      "fsdi $fd10, [%0+0x50]\n\t"
 56			      "fsdi $fd9,  [%0+0x48]\n\t"
 57			      "fsdi $fd8,  [%0+0x40]\n\t"
 58			      :	/* no output */
 59			      : "r" (&tsk->thread.fpu)
 60			      : "memory");
 61		fallthrough;
 62	case SP16_DP8_reg:
 63		asm volatile ("fsdi $fd7,  [%0+0x38]\n\t"
 64			      "fsdi $fd6,  [%0+0x30]\n\t"
 65			      "fsdi $fd5,  [%0+0x28]\n\t"
 66			      "fsdi $fd4,  [%0+0x20]\n\t"
 67			      :	/* no output */
 68			      : "r" (&tsk->thread.fpu)
 69			      : "memory");
 70		fallthrough;
 71	case SP8_DP4_reg:
 72		asm volatile ("fsdi $fd3,  [%1+0x18]\n\t"
 73			      "fsdi $fd2,  [%1+0x10]\n\t"
 74			      "fsdi $fd1,  [%1+0x8]\n\t"
 75			      "fsdi $fd0,  [%1+0x0]\n\t"
 76			      "fmfcsr	%0\n\t"
 77			      "swi  %0, [%1+0x100]\n\t"
 78			      : "=&r" (fpcsr)
 79			      : "r"(&tsk->thread.fpu)
 80			      : "memory");
 81	}
 82	disable_fpu();
 83}
 84
 85void load_fpu(const struct fpu_struct *fpregs)
 86{
 87	unsigned int fpcfg, fpcsr;
 88
 89	enable_fpu();
 90	fpcfg = ((__nds32__fmfcfg() & FPCFG_mskFREG) >> FPCFG_offFREG);
 91	switch (fpcfg) {
 92	case SP32_DP32_reg:
 93		asm volatile ("fldi $fd31, [%0+0xf8]\n\t"
 94			      "fldi $fd30, [%0+0xf0]\n\t"
 95			      "fldi $fd29, [%0+0xe8]\n\t"
 96			      "fldi $fd28, [%0+0xe0]\n\t"
 97			      "fldi $fd27, [%0+0xd8]\n\t"
 98			      "fldi $fd26, [%0+0xd0]\n\t"
 99			      "fldi $fd25, [%0+0xc8]\n\t"
100			      "fldi $fd24, [%0+0xc0]\n\t"
101			      "fldi $fd23, [%0+0xb8]\n\t"
102			      "fldi $fd22, [%0+0xb0]\n\t"
103			      "fldi $fd21, [%0+0xa8]\n\t"
104			      "fldi $fd20, [%0+0xa0]\n\t"
105			      "fldi $fd19, [%0+0x98]\n\t"
106			      "fldi $fd18, [%0+0x90]\n\t"
107			      "fldi $fd17, [%0+0x88]\n\t"
108			      "fldi $fd16, [%0+0x80]\n\t"
109			      :	/* no output */
110			      : "r" (fpregs));
111		fallthrough;
112	case SP32_DP16_reg:
113		asm volatile ("fldi $fd15, [%0+0x78]\n\t"
114			      "fldi $fd14, [%0+0x70]\n\t"
115			      "fldi $fd13, [%0+0x68]\n\t"
116			      "fldi $fd12, [%0+0x60]\n\t"
117			      "fldi $fd11, [%0+0x58]\n\t"
118			      "fldi $fd10, [%0+0x50]\n\t"
119			      "fldi $fd9,  [%0+0x48]\n\t"
120			      "fldi $fd8,  [%0+0x40]\n\t"
121			      :	/* no output */
122			      : "r" (fpregs));
123		fallthrough;
124	case SP16_DP8_reg:
125		asm volatile ("fldi $fd7,  [%0+0x38]\n\t"
126			      "fldi $fd6,  [%0+0x30]\n\t"
127			      "fldi $fd5,  [%0+0x28]\n\t"
128			      "fldi $fd4,  [%0+0x20]\n\t"
129			      :	/* no output */
130			      : "r" (fpregs));
131		fallthrough;
132	case SP8_DP4_reg:
133		asm volatile ("fldi $fd3,  [%1+0x18]\n\t"
134			      "fldi $fd2,  [%1+0x10]\n\t"
135			      "fldi $fd1,  [%1+0x8]\n\t"
136			      "fldi $fd0,  [%1+0x0]\n\t"
137			      "lwi  %0, [%1+0x100]\n\t"
138			      "fmtcsr	%0\n\t":"=&r" (fpcsr)
139			      : "r"(fpregs));
140	}
141	disable_fpu();
142}
143void store_fpu_for_suspend(void)
144{
145#ifdef CONFIG_LAZY_FPU
146	if (last_task_used_math != NULL)
147		save_fpu(last_task_used_math);
148	last_task_used_math = NULL;
149#else
150	if (!used_math())
151		return;
152	unlazy_fpu(current);
153#endif
154	clear_fpu(task_pt_regs(current));
155}
156inline void do_fpu_context_switch(struct pt_regs *regs)
157{
158	/* Enable to use FPU. */
159
160	if (!user_mode(regs)) {
161		pr_err("BUG: FPU is used in kernel mode.\n");
162		BUG();
163		return;
164	}
165
166	enable_ptreg_fpu(regs);
167#ifdef CONFIG_LAZY_FPU	//Lazy FPU is used
168	if (last_task_used_math == current)
169		return;
170	if (last_task_used_math != NULL)
171		/* Other processes fpu state, save away */
172		save_fpu(last_task_used_math);
173	last_task_used_math = current;
174#endif
175	if (used_math()) {
176		load_fpu(&current->thread.fpu);
177	} else {
178		/* First time FPU user.  */
179		load_fpu(&init_fpuregs);
180#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
181		current->thread.fpu.UDF_IEX_trap = init_fpuregs.UDF_IEX_trap;
182#endif
183		set_used_math();
184	}
185
186}
187
188inline void fill_sigfpe_signo(unsigned int fpcsr, int *signo)
189{
190	if (fpcsr & FPCSR_mskOVFT)
191		*signo = FPE_FLTOVF;
192#ifndef CONFIG_SUPPORT_DENORMAL_ARITHMETIC
193	else if (fpcsr & FPCSR_mskUDFT)
194		*signo = FPE_FLTUND;
195#endif
196	else if (fpcsr & FPCSR_mskIVOT)
197		*signo = FPE_FLTINV;
198	else if (fpcsr & FPCSR_mskDBZT)
199		*signo = FPE_FLTDIV;
200	else if (fpcsr & FPCSR_mskIEXT)
201		*signo = FPE_FLTRES;
202}
203
204inline void handle_fpu_exception(struct pt_regs *regs)
205{
206	unsigned int fpcsr;
207	int si_code = 0, si_signo = SIGFPE;
208#if IS_ENABLED(CONFIG_SUPPORT_DENORMAL_ARITHMETIC)
209	unsigned long redo_except = FPCSR_mskDNIT|FPCSR_mskUDFT|FPCSR_mskIEXT;
210#else
211	unsigned long redo_except = FPCSR_mskDNIT;
212#endif
213
214	lose_fpu();
215	fpcsr = current->thread.fpu.fpcsr;
216
217	if (fpcsr & redo_except) {
218		si_signo = do_fpuemu(regs, &current->thread.fpu);
219		fpcsr = current->thread.fpu.fpcsr;
220		if (!si_signo) {
221			current->thread.fpu.fpcsr &= ~(redo_except);
222			goto done;
223		}
224	} else if (fpcsr & FPCSR_mskRIT) {
225		if (!user_mode(regs))
226			do_exit(SIGILL);
227		si_signo = SIGILL;
228	}
229
230	switch (si_signo) {
231	case SIGFPE:
232		fill_sigfpe_signo(fpcsr, &si_code);
233		break;
234	case SIGILL:
235		show_regs(regs);
236		si_code = ILL_COPROC;
237		break;
238	case SIGBUS:
239		si_code = BUS_ADRERR;
240		break;
241	default:
242		break;
243	}
244
245	force_sig_fault(si_signo, si_code,
246			(void __user *)instruction_pointer(regs));
247done:
248	own_fpu();
249}
250
251bool do_fpu_exception(unsigned int subtype, struct pt_regs *regs)
252{
253	int done = true;
254	/* Coprocessor disabled exception */
255	if (subtype == FPU_DISABLE_EXCEPTION) {
256		preempt_disable();
257		do_fpu_context_switch(regs);
258		preempt_enable();
259	}
260	/* Coprocessor exception such as underflow and overflow */
261	else if (subtype == FPU_EXCEPTION)
262		handle_fpu_exception(regs);
263	else
264		done = false;
265	return done;
266}