diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/arm/vfp/vfpmodule.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/arm/vfp/vfpmodule.c')
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c new file mode 100644 index 000000000000..3aeedd2afc70 --- /dev/null +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -0,0 +1,288 @@ | |||
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> | ||
12 | #include <linux/config.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/signal.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <asm/vfp.h> | ||
19 | |||
20 | #include "vfpinstr.h" | ||
21 | #include "vfp.h" | ||
22 | |||
23 | /* | ||
24 | * Our undef handlers (in entry.S) | ||
25 | */ | ||
26 | void vfp_testing_entry(void); | ||
27 | void vfp_support_entry(void); | ||
28 | |||
29 | void (*vfp_vector)(void) = vfp_testing_entry; | ||
30 | union vfp_state *last_VFP_context; | ||
31 | |||
32 | /* | ||
33 | * Dual-use variable. | ||
34 | * Used in startup: set to non-zero if VFP checks fail | ||
35 | * After startup, holds VFP architecture | ||
36 | */ | ||
37 | unsigned int VFP_arch; | ||
38 | |||
39 | /* | ||
40 | * Per-thread VFP initialisation. | ||
41 | */ | ||
42 | void vfp_flush_thread(union vfp_state *vfp) | ||
43 | { | ||
44 | memset(vfp, 0, sizeof(union vfp_state)); | ||
45 | |||
46 | vfp->hard.fpexc = FPEXC_ENABLE; | ||
47 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; | ||
48 | |||
49 | /* | ||
50 | * Disable VFP to ensure we initialise it first. | ||
51 | */ | ||
52 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); | ||
53 | |||
54 | /* | ||
55 | * Ensure we don't try to overwrite our newly initialised | ||
56 | * state information on the first fault. | ||
57 | */ | ||
58 | if (last_VFP_context == vfp) | ||
59 | last_VFP_context = NULL; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Per-thread VFP cleanup. | ||
64 | */ | ||
65 | void vfp_release_thread(union vfp_state *vfp) | ||
66 | { | ||
67 | if (last_VFP_context == vfp) | ||
68 | last_VFP_context = NULL; | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * Raise a SIGFPE for the current process. | ||
73 | * sicode describes the signal being raised. | ||
74 | */ | ||
75 | void vfp_raise_sigfpe(unsigned int sicode, struct pt_regs *regs) | ||
76 | { | ||
77 | siginfo_t info; | ||
78 | |||
79 | memset(&info, 0, sizeof(info)); | ||
80 | |||
81 | info.si_signo = SIGFPE; | ||
82 | info.si_code = sicode; | ||
83 | info.si_addr = (void *)(instruction_pointer(regs) - 4); | ||
84 | |||
85 | /* | ||
86 | * This is the same as NWFPE, because it's not clear what | ||
87 | * this is used for | ||
88 | */ | ||
89 | current->thread.error_code = 0; | ||
90 | current->thread.trap_no = 6; | ||
91 | |||
92 | force_sig_info(SIGFPE, &info, current); | ||
93 | } | ||
94 | |||
95 | static void vfp_panic(char *reason) | ||
96 | { | ||
97 | int i; | ||
98 | |||
99 | printk(KERN_ERR "VFP: Error: %s\n", reason); | ||
100 | printk(KERN_ERR "VFP: EXC 0x%08x SCR 0x%08x INST 0x%08x\n", | ||
101 | fmrx(FPEXC), fmrx(FPSCR), fmrx(FPINST)); | ||
102 | for (i = 0; i < 32; i += 2) | ||
103 | printk(KERN_ERR "VFP: s%2u: 0x%08x s%2u: 0x%08x\n", | ||
104 | i, vfp_get_float(i), i+1, vfp_get_float(i+1)); | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Process bitmask of exception conditions. | ||
109 | */ | ||
110 | static void vfp_raise_exceptions(u32 exceptions, u32 inst, u32 fpscr, struct pt_regs *regs) | ||
111 | { | ||
112 | int si_code = 0; | ||
113 | |||
114 | pr_debug("VFP: raising exceptions %08x\n", exceptions); | ||
115 | |||
116 | if (exceptions == (u32)-1) { | ||
117 | vfp_panic("unhandled bounce"); | ||
118 | vfp_raise_sigfpe(0, regs); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * If any of the status flags are set, update the FPSCR. | ||
124 | * Comparison instructions always return at least one of | ||
125 | * these flags set. | ||
126 | */ | ||
127 | if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V)) | ||
128 | fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V); | ||
129 | |||
130 | fpscr |= exceptions; | ||
131 | |||
132 | fmxr(FPSCR, fpscr); | ||
133 | |||
134 | #define RAISE(stat,en,sig) \ | ||
135 | if (exceptions & stat && fpscr & en) \ | ||
136 | si_code = sig; | ||
137 | |||
138 | /* | ||
139 | * These are arranged in priority order, least to highest. | ||
140 | */ | ||
141 | RAISE(FPSCR_IXC, FPSCR_IXE, FPE_FLTRES); | ||
142 | RAISE(FPSCR_UFC, FPSCR_UFE, FPE_FLTUND); | ||
143 | RAISE(FPSCR_OFC, FPSCR_OFE, FPE_FLTOVF); | ||
144 | RAISE(FPSCR_IOC, FPSCR_IOE, FPE_FLTINV); | ||
145 | |||
146 | if (si_code) | ||
147 | vfp_raise_sigfpe(si_code, regs); | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Emulate a VFP instruction. | ||
152 | */ | ||
153 | static u32 vfp_emulate_instruction(u32 inst, u32 fpscr, struct pt_regs *regs) | ||
154 | { | ||
155 | u32 exceptions = (u32)-1; | ||
156 | |||
157 | pr_debug("VFP: emulate: INST=0x%08x SCR=0x%08x\n", inst, fpscr); | ||
158 | |||
159 | if (INST_CPRTDO(inst)) { | ||
160 | if (!INST_CPRT(inst)) { | ||
161 | /* | ||
162 | * CPDO | ||
163 | */ | ||
164 | if (vfp_single(inst)) { | ||
165 | exceptions = vfp_single_cpdo(inst, fpscr); | ||
166 | } else { | ||
167 | exceptions = vfp_double_cpdo(inst, fpscr); | ||
168 | } | ||
169 | } else { | ||
170 | /* | ||
171 | * A CPRT instruction can not appear in FPINST2, nor | ||
172 | * can it cause an exception. Therefore, we do not | ||
173 | * have to emulate it. | ||
174 | */ | ||
175 | } | ||
176 | } else { | ||
177 | /* | ||
178 | * A CPDT instruction can not appear in FPINST2, nor can | ||
179 | * it cause an exception. Therefore, we do not have to | ||
180 | * emulate it. | ||
181 | */ | ||
182 | } | ||
183 | return exceptions; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Package up a bounce condition. | ||
188 | */ | ||
189 | void VFP9_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) | ||
190 | { | ||
191 | u32 fpscr, orig_fpscr, exceptions, inst; | ||
192 | |||
193 | pr_debug("VFP: bounce: trigger %08x fpexc %08x\n", trigger, fpexc); | ||
194 | |||
195 | /* | ||
196 | * Enable access to the VFP so we can handle the bounce. | ||
197 | */ | ||
198 | fmxr(FPEXC, fpexc & ~(FPEXC_EXCEPTION|FPEXC_INV|FPEXC_UFC|FPEXC_IOC)); | ||
199 | |||
200 | orig_fpscr = fpscr = fmrx(FPSCR); | ||
201 | |||
202 | /* | ||
203 | * If we are running with inexact exceptions enabled, we need to | ||
204 | * emulate the trigger instruction. Note that as we're emulating | ||
205 | * the trigger instruction, we need to increment PC. | ||
206 | */ | ||
207 | if (fpscr & FPSCR_IXE) { | ||
208 | regs->ARM_pc += 4; | ||
209 | goto emulate; | ||
210 | } | ||
211 | |||
212 | barrier(); | ||
213 | |||
214 | /* | ||
215 | * Modify fpscr to indicate the number of iterations remaining | ||
216 | */ | ||
217 | if (fpexc & FPEXC_EXCEPTION) { | ||
218 | u32 len; | ||
219 | |||
220 | len = fpexc + (1 << FPEXC_LENGTH_BIT); | ||
221 | |||
222 | fpscr &= ~FPSCR_LENGTH_MASK; | ||
223 | fpscr |= (len & FPEXC_LENGTH_MASK) << (FPSCR_LENGTH_BIT - FPEXC_LENGTH_BIT); | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * Handle the first FP instruction. We used to take note of the | ||
228 | * FPEXC bounce reason, but this appears to be unreliable. | ||
229 | * Emulate the bounced instruction instead. | ||
230 | */ | ||
231 | inst = fmrx(FPINST); | ||
232 | exceptions = vfp_emulate_instruction(inst, fpscr, regs); | ||
233 | if (exceptions) | ||
234 | vfp_raise_exceptions(exceptions, inst, orig_fpscr, regs); | ||
235 | |||
236 | /* | ||
237 | * If there isn't a second FP instruction, exit now. | ||
238 | */ | ||
239 | if (!(fpexc & FPEXC_FPV2)) | ||
240 | return; | ||
241 | |||
242 | /* | ||
243 | * The barrier() here prevents fpinst2 being read | ||
244 | * before the condition above. | ||
245 | */ | ||
246 | barrier(); | ||
247 | trigger = fmrx(FPINST2); | ||
248 | fpscr = fmrx(FPSCR); | ||
249 | |||
250 | emulate: | ||
251 | exceptions = vfp_emulate_instruction(trigger, fpscr, regs); | ||
252 | if (exceptions) | ||
253 | vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * VFP support code initialisation. | ||
258 | */ | ||
259 | static int __init vfp_init(void) | ||
260 | { | ||
261 | unsigned int vfpsid; | ||
262 | |||
263 | /* | ||
264 | * First check that there is a VFP that we can use. | ||
265 | * The handler is already setup to just log calls, so | ||
266 | * we just need to read the VFPSID register. | ||
267 | */ | ||
268 | vfpsid = fmrx(FPSID); | ||
269 | |||
270 | printk(KERN_INFO "VFP support v0.3: "); | ||
271 | if (VFP_arch) { | ||
272 | printk("not present\n"); | ||
273 | } else if (vfpsid & FPSID_NODOUBLE) { | ||
274 | printk("no double precision support\n"); | ||
275 | } else { | ||
276 | VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */ | ||
277 | printk("implementor %02x architecture %d part %02x variant %x rev %x\n", | ||
278 | (vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT, | ||
279 | (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT, | ||
280 | (vfpsid & FPSID_PART_MASK) >> FPSID_PART_BIT, | ||
281 | (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, | ||
282 | (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); | ||
283 | vfp_vector = vfp_support_entry; | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | late_initcall(vfp_init); | ||