]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/arm/vfp/vfpmodule.c | |
3 | * | |
4 | * Copyright (C) 2004 ARM Limited. | |
5 | * Written by Deep Blue Solutions Limited. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include <linux/module.h> | |
1da177e4 LT |
12 | #include <linux/types.h> |
13 | #include <linux/kernel.h> | |
14 | #include <linux/signal.h> | |
15 | #include <linux/sched.h> | |
16 | #include <linux/init.h> | |
d6551e88 RK |
17 | |
18 | #include <asm/thread_notify.h> | |
1da177e4 LT |
19 | #include <asm/vfp.h> |
20 | ||
21 | #include "vfpinstr.h" | |
22 | #include "vfp.h" | |
23 | ||
24 | /* | |
25 | * Our undef handlers (in entry.S) | |
26 | */ | |
27 | void vfp_testing_entry(void); | |
28 | void vfp_support_entry(void); | |
5d4cae5f | 29 | void vfp_null_entry(void); |
1da177e4 | 30 | |
5d4cae5f | 31 | void (*vfp_vector)(void) = vfp_null_entry; |
c6428464 | 32 | union vfp_state *last_VFP_context[NR_CPUS]; |
1da177e4 LT |
33 | |
34 | /* | |
35 | * Dual-use variable. | |
36 | * Used in startup: set to non-zero if VFP checks fail | |
37 | * After startup, holds VFP architecture | |
38 | */ | |
39 | unsigned int VFP_arch; | |
40 | ||
d6551e88 | 41 | static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) |
1da177e4 | 42 | { |
d6551e88 | 43 | struct thread_info *thread = v; |
681a4991 | 44 | union vfp_state *vfp; |
c6428464 | 45 | __u32 cpu = thread->cpu; |
1da177e4 | 46 | |
681a4991 | 47 | if (likely(cmd == THREAD_NOTIFY_SWITCH)) { |
c6428464 CM |
48 | u32 fpexc = fmrx(FPEXC); |
49 | ||
50 | #ifdef CONFIG_SMP | |
51 | /* | |
52 | * On SMP, if VFP is enabled, save the old state in | |
53 | * case the thread migrates to a different CPU. The | |
54 | * restoring is done lazily. | |
55 | */ | |
228adef1 | 56 | if ((fpexc & FPEXC_EN) && last_VFP_context[cpu]) { |
c6428464 CM |
57 | vfp_save_state(last_VFP_context[cpu], fpexc); |
58 | last_VFP_context[cpu]->hard.cpu = cpu; | |
59 | } | |
60 | /* | |
61 | * Thread migration, just force the reloading of the | |
62 | * state on the new CPU in case the VFP registers | |
63 | * contain stale data. | |
64 | */ | |
65 | if (thread->vfpstate.hard.cpu != cpu) | |
66 | last_VFP_context[cpu] = NULL; | |
67 | #endif | |
68 | ||
681a4991 RK |
69 | /* |
70 | * Always disable VFP so we can lazily save/restore the | |
71 | * old state. | |
72 | */ | |
228adef1 | 73 | fmxr(FPEXC, fpexc & ~FPEXC_EN); |
681a4991 RK |
74 | return NOTIFY_DONE; |
75 | } | |
76 | ||
77 | vfp = &thread->vfpstate; | |
78 | if (cmd == THREAD_NOTIFY_FLUSH) { | |
d6551e88 RK |
79 | /* |
80 | * Per-thread VFP initialisation. | |
81 | */ | |
82 | memset(vfp, 0, sizeof(union vfp_state)); | |
1da177e4 | 83 | |
228adef1 | 84 | vfp->hard.fpexc = FPEXC_EN; |
d6551e88 | 85 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; |
1da177e4 | 86 | |
d6551e88 RK |
87 | /* |
88 | * Disable VFP to ensure we initialise it first. | |
89 | */ | |
228adef1 | 90 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); |
d6551e88 RK |
91 | } |
92 | ||
681a4991 | 93 | /* flush and release case: Per-thread VFP cleanup. */ |
c6428464 CM |
94 | if (last_VFP_context[cpu] == vfp) |
95 | last_VFP_context[cpu] = NULL; | |
681a4991 | 96 | |
d6551e88 | 97 | return NOTIFY_DONE; |
1da177e4 LT |
98 | } |
99 | ||
d6551e88 RK |
100 | static struct notifier_block vfp_notifier_block = { |
101 | .notifier_call = vfp_notifier, | |
102 | }; | |
103 | ||
1da177e4 LT |
104 | /* |
105 | * Raise a SIGFPE for the current process. | |
106 | * sicode describes the signal being raised. | |
107 | */ | |
108 | void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) | |
109 | { | |
110 | siginfo_t info; | |
111 | ||
112 | memset(&info, 0, sizeof(info)); | |
113 | ||
114 | info.si_signo = SIGFPE; | |
115 | info.si_code = sicode; | |
35d59fc5 | 116 | info.si_addr = (void __user *)(instruction_pointer(regs) - 4); |
1da177e4 LT |
117 | |
118 | /* | |
119 | * This is the same as NWFPE, because it's not clear what | |
120 | * this is used for | |
121 | */ | |
122 | current->thread.error_code = 0; | |
123 | current->thread.trap_no = 6; | |
124 | ||
da41119a | 125 | send_sig_info(SIGFPE, &info, current); |
1da177e4 LT |
126 | } |
127 | ||
c98929c0 | 128 | static void vfp_panic(char *reason, u32 inst) |
1da177e4 LT |
129 | { |
130 | int i; | |
131 | ||
132 | printk(KERN_ERR "VFP: Error: %s\n", reason); | |
133 | printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n", | |
c98929c0 | 134 | fmrx(FPEXC), fmrx(FPSCR), inst); |
1da177e4 LT |
135 | for (i = 0; i < 32; i += 2) |
136 | printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n", | |
137 | i, vfp_get_float(i), i+1, vfp_get_float(i+1)); | |
138 | } | |
139 | ||
140 | /* | |
141 | * Process bitmask of exception conditions. | |
142 | */ | |
143 | static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) | |
144 | { | |
145 | int si_code = 0; | |
146 | ||
147 | pr_debug("VFP: raising exceptions %08x\n", exceptions); | |
148 | ||
7c6f2514 | 149 | if (exceptions == VFP_EXCEPTION_ERROR) { |
c98929c0 | 150 | vfp_panic("unhandled bounce", inst); |
1da177e4 LT |
151 | vfp_raise_sigfpe(0, regs); |
152 | return; | |
153 | } | |
154 | ||
155 | /* | |
c98929c0 | 156 | * Update the FPSCR with the additional exception flags. |
1da177e4 LT |
157 | * Comparison instructions always return at least one of |
158 | * these flags set. | |
159 | */ | |
1da177e4 LT |
160 | fpscr |= exceptions; |
161 | ||
162 | fmxr(FPSCR, fpscr); | |
163 | ||
164 | #define RAISE(stat,en,sig) \ | |
165 | if (exceptions & stat && fpscr & en) \ | |
166 | si_code = sig; | |
167 | ||
168 | /* | |
169 | * These are arranged in priority order, least to highest. | |
170 | */ | |
e0f205d9 | 171 | RAISE(FPSCR_DZC, FPSCR_DZE, FPE_FLTDIV); |
1da177e4 LT |
172 | RAISE(FPSCR_IXC, FPSCR_IXE, FPE_FLTRES); |
173 | RAISE(FPSCR_UFC, FPSCR_UFE, FPE_FLTUND); | |
174 | RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); | |
175 | RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); | |
176 | ||
177 | if (si_code) | |
178 | vfp_raise_sigfpe(si_code, regs); | |
179 | } | |
180 | ||
181 | /* | |
182 | * Emulate a VFP instruction. | |
183 | */ | |
184 | static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) | |
185 | { | |
7c6f2514 | 186 | u32 exceptions = VFP_EXCEPTION_ERROR; |
1da177e4 LT |
187 | |
188 | pr_debug("VFP: emulate: INST=0x%08x SCR=0x%08x\n", inst, fpscr); | |
189 | ||
190 | if (INST_CPRTDO(inst)) { | |
191 | if (!INST_CPRT(inst)) { | |
192 | /* | |
193 | * CPDO | |
194 | */ | |
195 | if (vfp_single(inst)) { | |
196 | exceptions = vfp_single_cpdo(inst, fpscr); | |
197 | } else { | |
198 | exceptions = vfp_double_cpdo(inst, fpscr); | |
199 | } | |
200 | } else { | |
201 | /* | |
202 | * A CPRT instruction can not appear in FPINST2, nor | |
203 | * can it cause an exception. Therefore, we do not | |
204 | * have to emulate it. | |
205 | */ | |
206 | } | |
207 | } else { | |
208 | /* | |
209 | * A CPDT instruction can not appear in FPINST2, nor can | |
210 | * it cause an exception. Therefore, we do not have to | |
211 | * emulate it. | |
212 | */ | |
213 | } | |
928bd1b4 | 214 | return exceptions & ~VFP_NAN_FLAG; |
1da177e4 LT |
215 | } |
216 | ||
217 | /* | |
218 | * Package up a bounce condition. | |
219 | */ | |
c98929c0 | 220 | void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) |
1da177e4 | 221 | { |
c98929c0 | 222 | u32 fpscr, orig_fpscr, fpsid, exceptions; |
1da177e4 LT |
223 | |
224 | pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); | |
225 | ||
226 | /* | |
c98929c0 CM |
227 | * At this point, FPEXC can have the following configuration: |
228 | * | |
229 | * EX DEX IXE | |
230 | * 0 1 x - synchronous exception | |
231 | * 1 x 0 - asynchronous exception | |
232 | * 1 x 1 - sychronous on VFP subarch 1 and asynchronous on later | |
233 | * 0 0 1 - synchronous on VFP9 (non-standard subarch 1 | |
234 | * implementation), undefined otherwise | |
235 | * | |
236 | * Clear various bits and enable access to the VFP so we can | |
237 | * handle the bounce. | |
1da177e4 | 238 | */ |
c98929c0 | 239 | fmxr(FPEXC, fpexc & ~(FPEXC_EX|FPEXC_DEX|FPEXC_FP2V|FPEXC_VV|FPEXC_TRAP_MASK)); |
1da177e4 | 240 | |
c98929c0 | 241 | fpsid = fmrx(FPSID); |
1da177e4 LT |
242 | orig_fpscr = fpscr = fmrx(FPSCR); |
243 | ||
244 | /* | |
c98929c0 | 245 | * Check for the special VFP subarch 1 and FPSCR.IXE bit case |
1da177e4 | 246 | */ |
c98929c0 CM |
247 | if ((fpsid & FPSID_ARCH_MASK) == (1 << FPSID_ARCH_BIT) |
248 | && (fpscr & FPSCR_IXE)) { | |
249 | /* | |
250 | * Synchronous exception, emulate the trigger instruction | |
251 | */ | |
1da177e4 LT |
252 | goto emulate; |
253 | } | |
254 | ||
c98929c0 CM |
255 | if (fpexc & FPEXC_EX) { |
256 | /* | |
257 | * Asynchronous exception. The instruction is read from FPINST | |
258 | * and the interrupted instruction has to be restarted. | |
259 | */ | |
260 | trigger = fmrx(FPINST); | |
261 | regs->ARM_pc -= 4; | |
262 | } else if (!(fpexc & FPEXC_DEX)) { | |
263 | /* | |
264 | * Illegal combination of bits. It can be caused by an | |
265 | * unallocated VFP instruction but with FPSCR.IXE set and not | |
266 | * on VFP subarch 1. | |
267 | */ | |
268 | vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); | |
269 | return; | |
270 | } | |
1da177e4 LT |
271 | |
272 | /* | |
c98929c0 CM |
273 | * Modify fpscr to indicate the number of iterations remaining. |
274 | * If FPEXC.EX is 0, FPEXC.DEX is 1 and the FPEXC.VV bit indicates | |
275 | * whether FPEXC.VECITR or FPSCR.LEN is used. | |
1da177e4 | 276 | */ |
c98929c0 | 277 | if (fpexc & (FPEXC_EX | FPEXC_VV)) { |
1da177e4 LT |
278 | u32 len; |
279 | ||
280 | len = fpexc + (1 << FPEXC_LENGTH_BIT); | |
281 | ||
282 | fpscr &= ~FPSCR_LENGTH_MASK; | |
283 | fpscr |= (len & FPEXC_LENGTH_MASK) << (FPSCR_LENGTH_BIT - FPEXC_LENGTH_BIT); | |
284 | } | |
285 | ||
286 | /* | |
287 | * Handle the first FP instruction. We used to take note of the | |
288 | * FPEXC bounce reason, but this appears to be unreliable. | |
289 | * Emulate the bounced instruction instead. | |
290 | */ | |
c98929c0 | 291 | exceptions = vfp_emulate_instruction(trigger, fpscr, regs); |
1da177e4 | 292 | if (exceptions) |
c98929c0 | 293 | vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); |
1da177e4 LT |
294 | |
295 | /* | |
c98929c0 CM |
296 | * If there isn't a second FP instruction, exit now. Note that |
297 | * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. | |
1da177e4 | 298 | */ |
c98929c0 | 299 | if (fpexc ^ (FPEXC_EX | FPEXC_FP2V)) |
1da177e4 LT |
300 | return; |
301 | ||
302 | /* | |
303 | * The barrier() here prevents fpinst2 being read | |
304 | * before the condition above. | |
305 | */ | |
306 | barrier(); | |
307 | trigger = fmrx(FPINST2); | |
1da177e4 LT |
308 | |
309 | emulate: | |
c98929c0 | 310 | exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); |
1da177e4 LT |
311 | if (exceptions) |
312 | vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); | |
313 | } | |
efe90d27 | 314 | |
8e140362 RK |
315 | static void vfp_enable(void *unused) |
316 | { | |
317 | u32 access = get_copro_access(); | |
318 | ||
319 | /* | |
320 | * Enable full access to VFP (cp10 and cp11) | |
321 | */ | |
322 | set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); | |
323 | } | |
324 | ||
325 | #include <linux/smp.h> | |
326 | ||
1da177e4 LT |
327 | /* |
328 | * VFP support code initialisation. | |
329 | */ | |
330 | static int __init vfp_init(void) | |
331 | { | |
332 | unsigned int vfpsid; | |
efe90d27 | 333 | unsigned int cpu_arch = cpu_architecture(); |
efe90d27 | 334 | |
c98929c0 CM |
335 | if (cpu_arch >= CPU_ARCH_ARMv6) |
336 | vfp_enable(NULL); | |
1da177e4 LT |
337 | |
338 | /* | |
339 | * First check that there is a VFP that we can use. | |
340 | * The handler is already setup to just log calls, so | |
341 | * we just need to read the VFPSID register. | |
342 | */ | |
5d4cae5f | 343 | vfp_vector = vfp_testing_entry; |
b9338a78 | 344 | barrier(); |
1da177e4 | 345 | vfpsid = fmrx(FPSID); |
8e140362 | 346 | barrier(); |
5d4cae5f | 347 | vfp_vector = vfp_null_entry; |
1da177e4 LT |
348 | |
349 | printk(KERN_INFO "VFP support v0.3: "); | |
c98929c0 | 350 | if (VFP_arch) |
1da177e4 | 351 | printk("not present\n"); |
c98929c0 | 352 | else if (vfpsid & FPSID_NODOUBLE) { |
1da177e4 LT |
353 | printk("no double precision support\n"); |
354 | } else { | |
8691e5a8 | 355 | smp_call_function(vfp_enable, NULL, 1); |
8e140362 | 356 | |
1da177e4 LT |
357 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ |
358 | printk("implementor %02x architecture %d part %02x variant %x rev %x\n", | |
359 | (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT, | |
360 | (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT, | |
361 | (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, | |
362 | (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, | |
363 | (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); | |
efe90d27 | 364 | |
1da177e4 | 365 | vfp_vector = vfp_support_entry; |
d6551e88 RK |
366 | |
367 | thread_register_notifier(&vfp_notifier_block); | |
efe90d27 RK |
368 | |
369 | /* | |
370 | * We detected VFP, and the support code is | |
371 | * in place; report VFP support to userspace. | |
372 | */ | |
373 | elf_hwcap |= HWCAP_VFP; | |
2bedbdf4 CM |
374 | #ifdef CONFIG_NEON |
375 | /* | |
376 | * Check for the presence of the Advanced SIMD | |
377 | * load/store instructions, integer and single | |
378 | * precision floating point operations. | |
379 | */ | |
380 | if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100) | |
381 | elf_hwcap |= HWCAP_NEON; | |
382 | #endif | |
1da177e4 LT |
383 | } |
384 | return 0; | |
385 | } | |
386 | ||
387 | late_initcall(vfp_init); |