diff options
Diffstat (limited to 'arch/mips/kernel/pm.c')
| -rw-r--r-- | arch/mips/kernel/pm.c | 99 | 
1 files changed, 99 insertions, 0 deletions
diff --git a/arch/mips/kernel/pm.c b/arch/mips/kernel/pm.c new file mode 100644 index 000000000000..fefdf39d3df3 --- /dev/null +++ b/arch/mips/kernel/pm.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 Imagination Technologies Ltd. + * + * This program is free software; you can redistribute	it and/or modify it + * under  the terms of	the GNU General	 Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + * + * CPU PM notifiers for saving/restoring general CPU state. + */ + +#include <linux/cpu_pm.h> +#include <linux/init.h> + +#include <asm/dsp.h> +#include <asm/fpu.h> +#include <asm/mmu_context.h> +#include <asm/pm.h> +#include <asm/watch.h> + +/* Used by PM helper macros in asm/pm.h */ +struct mips_static_suspend_state mips_static_suspend_state; + +/** + * mips_cpu_save() - Save general CPU state. + * Ensures that general CPU context is saved, notably FPU and DSP. + */ +static int mips_cpu_save(void) +{ +	/* Save FPU state */ +	lose_fpu(1); + +	/* Save DSP state */ +	save_dsp(current); + +	return 0; +} + +/** + * mips_cpu_restore() - Restore general CPU state. + * Restores important CPU context. + */ +static void mips_cpu_restore(void) +{ +	unsigned int cpu = smp_processor_id(); + +	/* Restore ASID */ +	if (current->mm) +		write_c0_entryhi(cpu_asid(cpu, current->mm)); + +	/* Restore DSP state */ +	restore_dsp(current); + +	/* Restore UserLocal */ +	if (cpu_has_userlocal) +		write_c0_userlocal(current_thread_info()->tp_value); + +	/* Restore watch registers */ +	__restore_watch(); +} + +/** + * mips_pm_notifier() - Notifier for preserving general CPU context. + * @self:	Notifier block. + * @cmd:	CPU PM event. + * @v:		Private data (unused). + * + * This is called when a CPU power management event occurs, and is used to + * ensure that important CPU context is preserved across a CPU power down. + */ +static int mips_pm_notifier(struct notifier_block *self, unsigned long cmd, +			    void *v) +{ +	int ret; + +	switch (cmd) { +	case CPU_PM_ENTER: +		ret = mips_cpu_save(); +		if (ret) +			return NOTIFY_STOP; +		break; +	case CPU_PM_ENTER_FAILED: +	case CPU_PM_EXIT: +		mips_cpu_restore(); +		break; +	} + +	return NOTIFY_OK; +} + +static struct notifier_block mips_pm_notifier_block = { +	.notifier_call = mips_pm_notifier, +}; + +static int __init mips_pm_init(void) +{ +	return cpu_pm_register_notifier(&mips_pm_notifier_block); +} +arch_initcall(mips_pm_init);  |