diff options
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r-- | arch/arm/vfp/vfp.h | 2 | ||||
-rw-r--r-- | arch/arm/vfp/vfphw.S | 29 | ||||
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 65 |
3 files changed, 85 insertions, 11 deletions
diff --git a/arch/arm/vfp/vfp.h b/arch/arm/vfp/vfp.h index c85860bad585..8de86e4feada 100644 --- a/arch/arm/vfp/vfp.h +++ b/arch/arm/vfp/vfp.h | |||
@@ -377,6 +377,6 @@ struct op { | |||
377 | u32 flags; | 377 | u32 flags; |
378 | }; | 378 | }; |
379 | 379 | ||
380 | #ifdef CONFIG_SMP | 380 | #if defined(CONFIG_SMP) || defined(CONFIG_PM) |
381 | extern void vfp_save_state(void *location, u32 fpexc); | 381 | extern void vfp_save_state(void *location, u32 fpexc); |
382 | #endif | 382 | #endif |
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index a62dcf7098ba..c92a08bd6a86 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S | |||
@@ -101,9 +101,12 @@ ENTRY(vfp_support_entry) | |||
101 | VFPFSTMIA r4, r5 @ save the working registers | 101 | VFPFSTMIA r4, r5 @ save the working registers |
102 | VFPFMRX r5, FPSCR @ current status | 102 | VFPFMRX r5, FPSCR @ current status |
103 | tst r1, #FPEXC_EX @ is there additional state to save? | 103 | tst r1, #FPEXC_EX @ is there additional state to save? |
104 | VFPFMRX r6, FPINST, NE @ FPINST (only if FPEXC.EX is set) | 104 | beq 1f |
105 | tstne r1, #FPEXC_FP2V @ is there an FPINST2 to read? | 105 | VFPFMRX r6, FPINST @ FPINST (only if FPEXC.EX is set) |
106 | VFPFMRX r8, FPINST2, NE @ FPINST2 if needed (and present) | 106 | tst r1, #FPEXC_FP2V @ is there an FPINST2 to read? |
107 | beq 1f | ||
108 | VFPFMRX r8, FPINST2 @ FPINST2 if needed (and present) | ||
109 | 1: | ||
107 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 | 110 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 |
108 | @ and point r4 at the word at the | 111 | @ and point r4 at the word at the |
109 | @ start of the register dump | 112 | @ start of the register dump |
@@ -117,9 +120,12 @@ no_old_VFP_process: | |||
117 | @ FPEXC is in a safe state | 120 | @ FPEXC is in a safe state |
118 | ldmia r10, {r1, r5, r6, r8} @ load FPEXC, FPSCR, FPINST, FPINST2 | 121 | ldmia r10, {r1, r5, r6, r8} @ load FPEXC, FPSCR, FPINST, FPINST2 |
119 | tst r1, #FPEXC_EX @ is there additional state to restore? | 122 | tst r1, #FPEXC_EX @ is there additional state to restore? |
120 | VFPFMXR FPINST, r6, NE @ restore FPINST (only if FPEXC.EX is set) | 123 | beq 1f |
121 | tstne r1, #FPEXC_FP2V @ is there an FPINST2 to write? | 124 | VFPFMXR FPINST, r6 @ restore FPINST (only if FPEXC.EX is set) |
122 | VFPFMXR FPINST2, r8, NE @ FPINST2 if needed (and present) | 125 | tst r1, #FPEXC_FP2V @ is there an FPINST2 to write? |
126 | beq 1f | ||
127 | VFPFMXR FPINST2, r8 @ FPINST2 if needed (and present) | ||
128 | 1: | ||
123 | VFPFMXR FPSCR, r5 @ restore status | 129 | VFPFMXR FPSCR, r5 @ restore status |
124 | 130 | ||
125 | check_for_exception: | 131 | check_for_exception: |
@@ -166,7 +172,7 @@ process_exception: | |||
166 | @ retry the faulted instruction | 172 | @ retry the faulted instruction |
167 | ENDPROC(vfp_support_entry) | 173 | ENDPROC(vfp_support_entry) |
168 | 174 | ||
169 | #ifdef CONFIG_SMP | 175 | #if defined(CONFIG_SMP) || defined(CONFIG_PM) |
170 | ENTRY(vfp_save_state) | 176 | ENTRY(vfp_save_state) |
171 | @ Save the current VFP state | 177 | @ Save the current VFP state |
172 | @ r0 - save location | 178 | @ r0 - save location |
@@ -175,9 +181,12 @@ ENTRY(vfp_save_state) | |||
175 | VFPFSTMIA r0, r2 @ save the working registers | 181 | VFPFSTMIA r0, r2 @ save the working registers |
176 | VFPFMRX r2, FPSCR @ current status | 182 | VFPFMRX r2, FPSCR @ current status |
177 | tst r1, #FPEXC_EX @ is there additional state to save? | 183 | tst r1, #FPEXC_EX @ is there additional state to save? |
178 | VFPFMRX r3, FPINST, NE @ FPINST (only if FPEXC.EX is set) | 184 | beq 1f |
179 | tstne r1, #FPEXC_FP2V @ is there an FPINST2 to read? | 185 | VFPFMRX r3, FPINST @ FPINST (only if FPEXC.EX is set) |
180 | VFPFMRX r12, FPINST2, NE @ FPINST2 if needed (and present) | 186 | tst r1, #FPEXC_FP2V @ is there an FPINST2 to read? |
187 | beq 1f | ||
188 | VFPFMRX r12, FPINST2 @ FPINST2 if needed (and present) | ||
189 | 1: | ||
181 | stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 | 190 | stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 |
182 | mov pc, lr | 191 | mov pc, lr |
183 | ENDPROC(vfp_save_state) | 192 | ENDPROC(vfp_save_state) |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index c0d2c9bb952b..9f476a1be2ca 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -322,6 +322,61 @@ static void vfp_enable(void *unused) | |||
322 | set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); | 322 | set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); |
323 | } | 323 | } |
324 | 324 | ||
325 | #ifdef CONFIG_PM | ||
326 | #include <linux/sysdev.h> | ||
327 | |||
328 | static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state) | ||
329 | { | ||
330 | struct thread_info *ti = current_thread_info(); | ||
331 | u32 fpexc = fmrx(FPEXC); | ||
332 | |||
333 | /* if vfp is on, then save state for resumption */ | ||
334 | if (fpexc & FPEXC_EN) { | ||
335 | printk(KERN_DEBUG "%s: saving vfp state\n", __func__); | ||
336 | vfp_save_state(&ti->vfpstate, fpexc); | ||
337 | |||
338 | /* disable, just in case */ | ||
339 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | ||
340 | } | ||
341 | |||
342 | /* clear any information we had about last context state */ | ||
343 | memset(last_VFP_context, 0, sizeof(last_VFP_context)); | ||
344 | |||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | static int vfp_pm_resume(struct sys_device *dev) | ||
349 | { | ||
350 | /* ensure we have access to the vfp */ | ||
351 | vfp_enable(NULL); | ||
352 | |||
353 | /* and disable it to ensure the next usage restores the state */ | ||
354 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static struct sysdev_class vfp_pm_sysclass = { | ||
360 | .name = "vfp", | ||
361 | .suspend = vfp_pm_suspend, | ||
362 | .resume = vfp_pm_resume, | ||
363 | }; | ||
364 | |||
365 | static struct sys_device vfp_pm_sysdev = { | ||
366 | .cls = &vfp_pm_sysclass, | ||
367 | }; | ||
368 | |||
369 | static void vfp_pm_init(void) | ||
370 | { | ||
371 | sysdev_class_register(&vfp_pm_sysclass); | ||
372 | sysdev_register(&vfp_pm_sysdev); | ||
373 | } | ||
374 | |||
375 | |||
376 | #else | ||
377 | static inline void vfp_pm_init(void) { } | ||
378 | #endif /* CONFIG_PM */ | ||
379 | |||
325 | #include <linux/smp.h> | 380 | #include <linux/smp.h> |
326 | 381 | ||
327 | /* | 382 | /* |
@@ -365,12 +420,22 @@ static int __init vfp_init(void) | |||
365 | vfp_vector = vfp_support_entry; | 420 | vfp_vector = vfp_support_entry; |
366 | 421 | ||
367 | thread_register_notifier(&vfp_notifier_block); | 422 | thread_register_notifier(&vfp_notifier_block); |
423 | vfp_pm_init(); | ||
368 | 424 | ||
369 | /* | 425 | /* |
370 | * We detected VFP, and the support code is | 426 | * We detected VFP, and the support code is |
371 | * in place; report VFP support to userspace. | 427 | * in place; report VFP support to userspace. |
372 | */ | 428 | */ |
373 | elf_hwcap |= HWCAP_VFP; | 429 | elf_hwcap |= HWCAP_VFP; |
430 | #ifdef CONFIG_NEON | ||
431 | /* | ||
432 | * Check for the presence of the Advanced SIMD | ||
433 | * load/store instructions, integer and single | ||
434 | * precision floating point operations. | ||
435 | */ | ||
436 | if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100) | ||
437 | elf_hwcap |= HWCAP_NEON; | ||
438 | #endif | ||
374 | } | 439 | } |
375 | return 0; | 440 | return 0; |
376 | } | 441 | } |