forked from Minki/linux
e461894dc2
StrongARM core uses RCSR SMR bit to tell to bootloader that it was reset by entering the sleep mode. After we have resumed, there is little point in having that bit enabled. Moreover, if this bit is set before reboot, the bootloader can become confused. Thus clear the SMR bit on resume just before clearing the scratchpad (resume address) register. Cc: stable@vger.kernel.org Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
127 lines
2.6 KiB
C
127 lines
2.6 KiB
C
/*
|
|
* SA1100 Power Management Routines
|
|
*
|
|
* Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License.
|
|
*
|
|
* History:
|
|
*
|
|
* 2001-02-06: Cliff Brake Initial code
|
|
*
|
|
* 2001-02-25: Sukjae Cho <sjcho@east.isi.edu> &
|
|
* Chester Kuo <chester@linux.org.tw>
|
|
* Save more value for the resume function! Support
|
|
* Bitsy/Assabet/Freebird board
|
|
*
|
|
* 2001-08-29: Nicolas Pitre <nico@fluxnic.net>
|
|
* Cleaned up, pushed platform dependent stuff
|
|
* in the platform specific files.
|
|
*
|
|
* 2002-05-27: Nicolas Pitre Killed sleep.h and the kmalloced save array.
|
|
* Storage is local on the stack now.
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/time.h>
|
|
|
|
#include <mach/hardware.h>
|
|
#include <asm/memory.h>
|
|
#include <asm/suspend.h>
|
|
#include <asm/mach/time.h>
|
|
|
|
extern int sa1100_finish_suspend(unsigned long);
|
|
|
|
#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
|
|
#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
|
|
|
|
/*
|
|
* List of global SA11x0 peripheral registers to preserve.
|
|
* More ones like CP and general purpose register values are preserved
|
|
* on the stack and then the stack pointer is stored last in sleep.S.
|
|
*/
|
|
enum { SLEEP_SAVE_GPDR, SLEEP_SAVE_GAFR,
|
|
SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
|
|
|
|
SLEEP_SAVE_Ser1SDCR0,
|
|
|
|
SLEEP_SAVE_COUNT
|
|
};
|
|
|
|
|
|
static int sa11x0_pm_enter(suspend_state_t state)
|
|
{
|
|
unsigned long gpio, sleep_save[SLEEP_SAVE_COUNT];
|
|
|
|
gpio = GPLR;
|
|
|
|
/* save vital registers */
|
|
SAVE(GPDR);
|
|
SAVE(GAFR);
|
|
|
|
SAVE(PPDR);
|
|
SAVE(PPSR);
|
|
SAVE(PPAR);
|
|
SAVE(PSDR);
|
|
|
|
SAVE(Ser1SDCR0);
|
|
|
|
/* Clear previous reset status */
|
|
RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
|
|
|
|
/* set resume return address */
|
|
PSPR = virt_to_phys(cpu_resume);
|
|
|
|
/* go zzz */
|
|
cpu_suspend(0, sa1100_finish_suspend);
|
|
|
|
/*
|
|
* Ensure not to come back here if it wasn't intended
|
|
*/
|
|
RCSR = RCSR_SMR;
|
|
PSPR = 0;
|
|
|
|
/*
|
|
* Ensure interrupt sources are disabled; we will re-init
|
|
* the interrupt subsystem via the device manager.
|
|
*/
|
|
ICLR = 0;
|
|
ICCR = 1;
|
|
ICMR = 0;
|
|
|
|
/* restore registers */
|
|
RESTORE(GPDR);
|
|
RESTORE(GAFR);
|
|
|
|
RESTORE(PPDR);
|
|
RESTORE(PPSR);
|
|
RESTORE(PPAR);
|
|
RESTORE(PSDR);
|
|
|
|
RESTORE(Ser1SDCR0);
|
|
|
|
GPSR = gpio;
|
|
GPCR = ~gpio;
|
|
|
|
/*
|
|
* Clear the peripheral sleep-hold bit.
|
|
*/
|
|
PSSR = PSSR_PH;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct platform_suspend_ops sa11x0_pm_ops = {
|
|
.enter = sa11x0_pm_enter,
|
|
.valid = suspend_valid_only_mem,
|
|
};
|
|
|
|
int __init sa11x0_pm_init(void)
|
|
{
|
|
suspend_set_ops(&sa11x0_pm_ops);
|
|
return 0;
|
|
}
|