mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 09:02:00 +00:00
[ARM] 5349/1: VFP: Add PM code to save and restore current VFP state
When CONFIG_PM is selected, the VFP code does not have any handler installed to deal with either saving the VFP state of the current task, nor does it do anything to try and restore the VFP after a resume. On resume, the VFP will have been reset and the co-processor access control registers are in an indeterminate state (very probably the CP10 and CP11 the VFP uses will have been disabled by the ARM core reset). When this happens, resume will break as soon as it tries to unfreeze the tasks and restart scheduling. Add a sys device to allow us to hook the suspend call to save the current thread state if the thread is using VFP and a resume hook which restores the CP10/CP11 access and ensures the VFP is disabled so that the lazy swapping will take place on next access. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
c95a44329e
commit
fc0b7a2036
@ -377,6 +377,6 @@ struct op {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_PM)
|
||||
extern void vfp_save_state(void *location, u32 fpexc);
|
||||
#endif
|
||||
|
@ -172,7 +172,7 @@ process_exception:
|
||||
@ retry the faulted instruction
|
||||
ENDPROC(vfp_support_entry)
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_PM)
|
||||
ENTRY(vfp_save_state)
|
||||
@ Save the current VFP state
|
||||
@ r0 - save location
|
||||
|
@ -322,6 +322,61 @@ static void vfp_enable(void *unused)
|
||||
set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#include <linux/sysdev.h>
|
||||
|
||||
static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state)
|
||||
{
|
||||
struct thread_info *ti = current_thread_info();
|
||||
u32 fpexc = fmrx(FPEXC);
|
||||
|
||||
/* if vfp is on, then save state for resumption */
|
||||
if (fpexc & FPEXC_EN) {
|
||||
printk(KERN_DEBUG "%s: saving vfp state\n", __func__);
|
||||
vfp_save_state(&ti->vfpstate, fpexc);
|
||||
|
||||
/* disable, just in case */
|
||||
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
|
||||
}
|
||||
|
||||
/* clear any information we had about last context state */
|
||||
memset(last_VFP_context, 0, sizeof(last_VFP_context));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfp_pm_resume(struct sys_device *dev)
|
||||
{
|
||||
/* ensure we have access to the vfp */
|
||||
vfp_enable(NULL);
|
||||
|
||||
/* and disable it to ensure the next usage restores the state */
|
||||
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sysdev_class vfp_pm_sysclass = {
|
||||
.name = "vfp",
|
||||
.suspend = vfp_pm_suspend,
|
||||
.resume = vfp_pm_resume,
|
||||
};
|
||||
|
||||
static struct sys_device vfp_pm_sysdev = {
|
||||
.cls = &vfp_pm_sysclass,
|
||||
};
|
||||
|
||||
static void vfp_pm_init(void)
|
||||
{
|
||||
sysdev_class_register(&vfp_pm_sysclass);
|
||||
sysdev_register(&vfp_pm_sysdev);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
static inline void vfp_pm_init(void) { }
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#include <linux/smp.h>
|
||||
|
||||
/*
|
||||
@ -365,6 +420,7 @@ static int __init vfp_init(void)
|
||||
vfp_vector = vfp_support_entry;
|
||||
|
||||
thread_register_notifier(&vfp_notifier_block);
|
||||
vfp_pm_init();
|
||||
|
||||
/*
|
||||
* We detected VFP, and the support code is
|
||||
|
Loading…
Reference in New Issue
Block a user