diff options
Diffstat (limited to 'arch/arm/vfp/vfpmodule.c')
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 65 |
1 files changed, 65 insertions, 0 deletions
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 | } |