No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / arm / vfp / vfp_init.c
blobd531e2e057c6942e0d57ce01c8584813431f7b7c
1 /* $NetBSD: vfp_init.c,v 1.2 2009/03/18 10:22:24 cegger Exp $ */
3 /*
4 * Copyright (c) 2008 ARM Ltd
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the company may not be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
19 * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/proc.h>
38 #include <arm/undefined.h>
39 #include <machine/cpu.h>
41 #include <arm/vfpvar.h>
42 #include <arm/vfpreg.h>
44 /*
45 * Use generic co-processor instructions to avoid assembly problems.
48 /* FMRX <X>, fpsid */
49 #define read_fpsid(X) __asm __volatile("mrc p10, 7, %0, c0, c0, 0" \
50 : "=r" (*(X)) : : "memory")
51 /* FMRX <X>, fpscr */
52 #define read_fpscr(X) __asm __volatile("mrc p10, 7, %0, c1, c0, 0" \
53 : "=r" (*(X)))
54 /* FMRX <X>, fpexc */
55 #define read_fpexc(X) __asm __volatile("mrc p10, 7, %0, c8, c0, 0" \
56 : "=r" (*(X)))
57 /* FMRX <X>, fpinst */
58 #define read_fpinst(X) __asm __volatile("mrc p10, 7, %0, c9, c0, 0" \
59 : "=r" (*(X)))
60 /* FMRX <X>, fpinst2 */
61 #define read_fpinst2(X) __asm __volatile("mrc p10, 7, %0, c10, c0, 0" \
62 : "=r" (*(X)))
63 /* FSTMD <X>, {d0-d15} */
64 #define save_vfpregs(X) __asm __volatile("stc p11, c0, [%0], {32}" : \
65 : "r" (X) : "memory")
67 /* FMXR <X>, fpscr */
68 #define write_fpscr(X) __asm __volatile("mcr p10, 7, %0, c1, c0, 0" : \
69 : "r" (X))
70 /* FMXR <X>, fpexc */
71 #define write_fpexc(X) __asm __volatile("mcr p10, 7, %0, c8, c0, 0" : \
72 : "r" (X))
73 /* FMXR <X>, fpinst */
74 #define write_fpinst(X) __asm __volatile("mcr p10, 7, %0, c9, c0, 0" : \
75 : "r" (X))
76 /* FMXR <X>, fpinst2 */
77 #define write_fpinst2(X) __asm __volatile("mcr p10, 7, %0, c10, c0, 0" : \
78 : "r" (X))
79 /* FLDMD <X>, {d0-d15} */
80 #define load_vfpregs(X) __asm __volatile("ldc p11, c0, [%0], {32}" : \
81 : "r" (X) : "memory");
83 /* The real handler for VFP bounces. */
84 static int vfp_handler(u_int, u_int, trapframe_t *, int);
86 static void vfp_load_regs(struct vfpreg *);
88 struct evcnt vfpevent_use;
89 struct evcnt vfpevent_reuse;
92 * Used to test for a VFP. The following function is installed as a coproc10
93 * handler on the undefined instruction vector and then we issue a VFP
94 * instruction. If undefined_test is non zero then the VFP did not handle
95 * the instruction so must be absent, or disabled.
98 static int undefined_test;
100 static int
101 vfp_test(u_int address, u_int instruction, trapframe_t *frame, int fault_code)
104 frame->tf_pc += INSN_SIZE;
105 ++undefined_test;
106 return(0);
109 void
110 vfp_attach(void)
112 void *uh;
113 uint32_t fpsid;
114 const char *model = NULL;
116 uh = install_coproc_handler(VFP_COPROC, vfp_test);
118 undefined_test = 0;
120 read_fpsid(&fpsid);
122 remove_coproc_handler(uh);
124 if (undefined_test != 0) {
125 aprint_normal("%s: No VFP detected\n",
126 curcpu()->ci_dev->dv_xname);
127 curcpu()->ci_vfp.vfp_id = 0;
128 return;
131 curcpu()->ci_vfp.vfp_id = fpsid;
132 switch (fpsid & ~ VFP_FPSID_REV_MSK)
134 case FPU_VFP10_ARM10E:
135 model = "VFP10 R1";
136 break;
137 case FPU_VFP11_ARM11:
138 model = "VFP11";
139 break;
140 default:
141 aprint_normal("%s: unrecognized VFP version %x\n",
142 curcpu()->ci_dev->dv_xname, fpsid);
143 fpsid = 0; /* Not recognised. */
144 return;
147 if (fpsid != 0) {
148 aprint_normal("vfp%d at %s: %s\n",
149 curcpu()->ci_dev->dv_unit, curcpu()->ci_dev->dv_xname,
150 model);
152 evcnt_attach_dynamic(&vfpevent_use, EVCNT_TYPE_MISC, NULL,
153 "VFP", "proc use");
154 evcnt_attach_dynamic(&vfpevent_reuse, EVCNT_TYPE_MISC, NULL,
155 "VFP", "proc re-use");
156 install_coproc_handler(VFP_COPROC, vfp_handler);
157 install_coproc_handler(VFP_COPROC2, vfp_handler);
160 /* The real handler for VFP bounces. */
161 static int vfp_handler(u_int address, u_int instruction, trapframe_t *frame,
162 int fault_code)
164 struct cpu_info *ci = curcpu();
165 struct pcb *pcb;
166 struct lwp *l;
168 /* This shouldn't ever happen. */
169 if (fault_code != FAULT_USER)
170 panic("VFP fault in non-user mode");
172 if (ci->ci_vfp.vfp_id == 0)
173 /* No VFP detected, just fault. */
174 return 1;
176 l = curlwp;
177 pcb = lwp_getpcb(l);
179 if ((l->l_md.md_flags & MDP_VFPUSED) && ci->ci_vfp.vfp_fpcurlwp == l) {
180 uint32_t fpexc;
182 printf("VFP bounce @%x (insn=%x) lwp=%p\n", address,
183 instruction, l);
184 read_fpexc(&fpexc);
185 if ((fpexc & VFP_FPEXC_EN) == 0)
186 printf("vfp not enabled\n");
187 vfp_saveregs_lwp(l, 1);
188 printf(" fpexc = 0x%08x fpscr = 0x%08x\n", fpexc,
189 pcb->pcb_vfp.vfp_fpscr);
190 printf(" fpinst = 0x%08x fpinst2 = 0x%08x\n",
191 pcb->pcb_vfp.vfp_fpinst,
192 pcb->pcb_vfp.vfp_fpinst2);
193 return 1;
196 if (ci->ci_vfp.vfp_fpcurlwp != NULL)
197 vfp_saveregs_cpu(ci, 1);
199 KDASSERT(ci->ci_vfp.vfp_fpcurlwp == NULL);
201 KDASSERT(pcb->pcb_vfpcpu == NULL);
203 // VFPCPU_LOCK(pcb, s);
205 pcb->pcb_vfpcpu = ci;
206 ci->ci_vfp.vfp_fpcurlwp = l;
208 // VFPCPU_UNLOCK(pcb, s);
211 * Instrument VFP usage -- if a process has not previously
212 * used the VFP, mark it as having used VFP for the first time,
213 * and count this event.
215 * If a process has used the VFP, count a "used VFP, and took
216 * a trap to use it again" event.
218 if ((l->l_md.md_flags & MDP_VFPUSED) == 0) {
219 vfpevent_use.ev_count++;
220 l->l_md.md_flags |= MDP_VFPUSED;
221 pcb->pcb_vfp.vfp_fpscr =
222 (VFP_FPSCR_DN | VFP_FPSCR_FZ); /* Runfast */
223 } else
224 vfpevent_reuse.ev_count++;
226 vfp_load_regs(&pcb->pcb_vfp);
228 /* Need to restart the faulted instruction. */
229 // frame->tf_pc -= INSN_SIZE;
230 return 0;
233 static void
234 vfp_load_regs(struct vfpreg *fregs)
236 uint32_t fpexc;
238 /* Enable the VFP (so that we can write the registers). */
239 read_fpexc(&fpexc);
240 KDASSERT((fpexc & VFP_FPEXC_EX) == 0);
241 write_fpexc(fpexc | VFP_FPEXC_EN);
243 load_vfpregs(fregs->vfp_regs);
244 write_fpscr(fregs->vfp_fpscr);
245 if (fregs->vfp_fpexc & VFP_FPEXC_EX) {
246 /* Need to restore the exception handling state. */
247 switch (curcpu()->ci_vfp.vfp_id) {
248 case FPU_VFP10_ARM10E:
249 case FPU_VFP11_ARM11:
250 write_fpinst2(fregs->vfp_fpinst2);
251 write_fpinst(fregs->vfp_fpinst);
252 break;
253 default:
254 panic("vfp_load_regs: Unsupported VFP");
257 /* Finally, restore the FPEXC and enable the VFP. */
258 write_fpexc(fregs->vfp_fpexc | VFP_FPEXC_EN);
261 void
262 vfp_saveregs_cpu(struct cpu_info *ci, int save)
264 struct lwp *l;
265 struct pcb *pcb;
266 uint32_t fpexc;
268 KDASSERT(ci == curcpu());
270 l = ci->ci_vfp.vfp_fpcurlwp;
271 if (l == NULL)
272 return;
274 pcb = lwp_getpcb(l);
275 read_fpexc(&fpexc);
277 if (save) {
278 struct vfpreg *fregs = &pcb->pcb_vfp;
281 * Enable the VFP (so we can read the registers).
282 * Make sure the exception bit is cleared so that we can
283 * safely dump the registers.
285 write_fpexc((fpexc | VFP_FPEXC_EN) & ~VFP_FPEXC_EX);
287 fregs->vfp_fpexc = fpexc;
288 if (fpexc & VFP_FPEXC_EX) {
289 /* Need to save the exception handling state */
290 switch (ci->ci_vfp.vfp_id) {
291 case FPU_VFP10_ARM10E:
292 case FPU_VFP11_ARM11:
293 read_fpinst(&fregs->vfp_fpinst);
294 read_fpinst2(&fregs->vfp_fpinst2);
295 break;
296 default:
297 panic("vfp_saveregs_cpu: Unsupported VFP");
300 read_fpscr(&fregs->vfp_fpscr);
301 save_vfpregs(fregs->vfp_regs);
303 /* Disable the VFP. */
304 write_fpexc(fpexc & ~VFP_FPEXC_EN);
305 // VFPCPU_LOCK(pcb, s);
307 pcb->pcb_vfpcpu = NULL;
308 ci->ci_vfp.vfp_fpcurlwp = NULL;
309 // VFPCPU_UNLOCK(pcb, s);
312 void
313 vfp_saveregs_lwp(struct lwp *l, int save)
315 struct cpu_info *ci = curcpu();
316 struct cpu_info *oci;
317 struct pcb *pcb;
319 pcb = lwp_getpcb(l);
320 KDASSERT(pcb != NULL);
322 // VFPCPU_LOCK(pcb, s);
324 oci = pcb->pcb_vfpcpu;
325 if (oci == NULL) {
326 // VFPCPU_UNLOCK(pcb, s);
327 return;
330 #if defined(MULTIPROCESSOR)
332 * On a multiprocessor system this is where we would send an IPI
333 * to the processor holding the VFP state for this process.
335 #error MULTIPROCESSOR
336 #else
337 KASSERT(ci->ci_vfp.vfp_fpcurlwp == l);
338 // VFPCPU_UNLOCK(pcb, s);
339 vfp_saveregs_cpu(ci, save);
340 #endif
343 void
344 vfp_savecontext(void)
346 struct cpu_info *ci = curcpu();
347 uint32_t fpexc;
349 if (ci->ci_vfp.vfp_fpcurlwp != NULL) {
350 read_fpexc(&fpexc);
351 write_fpexc(fpexc & ~VFP_FPEXC_EN);
355 void
356 vfp_loadcontext(struct lwp *l)
358 uint32_t fpexc;
360 if (curcpu()->ci_vfp.vfp_fpcurlwp == l) {
361 read_fpexc(&fpexc);
362 write_fpexc(fpexc | VFP_FPEXC_EN);