mirror of
https://github.com/torvalds/linux.git
synced 2024-09-25 09:23:11 +00:00
[ARM] S3C64XX: Initial support for PM (suspend to RAM)
Add the initial support for the S3C64XX based systems to use suspend-to-RAM to sleep. Includes basic debugging for use with the SMDK6410 usign the LEDs on the baseboard. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
This commit is contained in:
parent
4b637dc231
commit
bd117bd161
16
arch/arm/mach-s3c6400/include/mach/regs-clock.h
Normal file
16
arch/arm/mach-s3c6400/include/mach/regs-clock.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/* linux/arch/arm/mach-s3c6400/include/mach/regs-clock.h
|
||||||
|
*
|
||||||
|
* Copyright 2008 Openmoko, Inc.
|
||||||
|
* Copyright 2008 Simtec Electronics
|
||||||
|
* http://armlinux.simtec.co.uk/
|
||||||
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
|
*
|
||||||
|
* S3C64XX - clock register compatibility with s3c24xx
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <plat/regs-clock.h>
|
||||||
|
|
|
@ -71,6 +71,15 @@ config S3C2410_PM_DEBUG
|
||||||
Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt>
|
Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt>
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
config S3C_PM_DEBUG_LED_SMDK
|
||||||
|
bool "SMDK LED suspend/resume debugging"
|
||||||
|
depends on PM && (MACH_SMDK6410)
|
||||||
|
help
|
||||||
|
Say Y here to enable the use of the SMDK LEDs on the baseboard
|
||||||
|
for debugging of the state of the suspend and resume process.
|
||||||
|
|
||||||
|
Note, this currently only works for S3C64XX based SMDK boards.
|
||||||
|
|
||||||
config S3C2410_PM_CHECK
|
config S3C2410_PM_CHECK
|
||||||
bool "S3C2410 PM Suspend Memory CRC"
|
bool "S3C2410 PM Suspend Memory CRC"
|
||||||
depends on PM && CRC32
|
depends on PM && CRC32
|
||||||
|
|
|
@ -127,6 +127,18 @@ extern void s3c_pm_dbg(const char *msg, ...);
|
||||||
#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt)
|
#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
|
||||||
|
/**
|
||||||
|
* s3c_pm_debug_smdkled() - Debug PM suspend/resume via SMDK Board LEDs
|
||||||
|
* @set: set bits for the state of the LEDs
|
||||||
|
* @clear: clear bits for the state of the LEDs.
|
||||||
|
*/
|
||||||
|
extern void s3c_pm_debug_smdkled(u32 set, u32 clear);
|
||||||
|
|
||||||
|
#else
|
||||||
|
static inline void s3c_pm_debug_smdkled(u32 set, u32 clear) { }
|
||||||
|
#endif /* CONFIG_S3C_PM_DEBUG_LED_SMDK */
|
||||||
|
|
||||||
/* suspend memory checking */
|
/* suspend memory checking */
|
||||||
|
|
||||||
#ifdef CONFIG_S3C2410_PM_CHECK
|
#ifdef CONFIG_S3C2410_PM_CHECK
|
||||||
|
|
|
@ -21,11 +21,10 @@
|
||||||
|
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
|
#include <mach/map.h>
|
||||||
|
|
||||||
#include <plat/regs-serial.h>
|
#include <plat/regs-serial.h>
|
||||||
#include <mach/regs-clock.h>
|
#include <mach/regs-clock.h>
|
||||||
#include <mach/regs-gpio.h>
|
|
||||||
#include <mach/regs-mem.h>
|
|
||||||
#include <mach/regs-irq.h>
|
#include <mach/regs-irq.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
|
||||||
|
@ -326,6 +325,9 @@ static int s3c_pm_enter(suspend_state_t state)
|
||||||
|
|
||||||
S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
|
S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
|
||||||
|
|
||||||
|
/* LEDs should now be 1110 */
|
||||||
|
s3c_pm_debug_smdkled(1 << 1, 0);
|
||||||
|
|
||||||
s3c_pm_check_restore();
|
s3c_pm_check_restore();
|
||||||
|
|
||||||
/* ok, let's return from sleep */
|
/* ok, let's return from sleep */
|
||||||
|
|
|
@ -24,6 +24,11 @@ obj-y += gpiolib.o
|
||||||
obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o
|
obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o
|
||||||
obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o
|
obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o
|
||||||
|
|
||||||
|
# PM support
|
||||||
|
|
||||||
|
obj-$(CONFIG_PM) += pm.o
|
||||||
|
obj-$(CONFIG_PM) += sleep.o
|
||||||
|
|
||||||
# Device setup
|
# Device setup
|
||||||
|
|
||||||
obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
|
obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
|
||||||
|
|
|
@ -157,6 +157,7 @@
|
||||||
|
|
||||||
#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE)
|
#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE)
|
||||||
#define IRQ_EINT(x) S3C_EINT(x)
|
#define IRQ_EINT(x) S3C_EINT(x)
|
||||||
|
#define IRQ_EINT_BIT(x) ((x) - S3C_EINT(0))
|
||||||
|
|
||||||
/* Next the external interrupt groups. These are similar to the IRQ_EINT(x)
|
/* Next the external interrupt groups. These are similar to the IRQ_EINT(x)
|
||||||
* that they are sourced from the GPIO pins but with a different scheme for
|
* that they are sourced from the GPIO pins but with a different scheme for
|
||||||
|
|
98
arch/arm/plat-s3c64xx/include/plat/pm-core.h
Normal file
98
arch/arm/plat-s3c64xx/include/plat/pm-core.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/* linux/arch/arm/plat-s3c64xx/include/plat/pm-core.h
|
||||||
|
*
|
||||||
|
* Copyright 2008 Openmoko, Inc.
|
||||||
|
* Copyright 2008 Simtec Electronics
|
||||||
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
|
* http://armlinux.simtec.co.uk/
|
||||||
|
*
|
||||||
|
* S3C64XX - PM core support for arch/arm/plat-s3c/pm.c
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <plat/regs-gpio.h>
|
||||||
|
|
||||||
|
static inline void s3c_pm_debug_init_uart(void)
|
||||||
|
{
|
||||||
|
u32 tmp = __raw_readl(S3C_PCLK_GATE);
|
||||||
|
|
||||||
|
/* As a note, since the S3C64XX UARTs generally have multiple
|
||||||
|
* clock sources, we simply enable PCLK at the moment and hope
|
||||||
|
* that the resume settings for the UART are suitable for the
|
||||||
|
* use with PCLK.
|
||||||
|
*/
|
||||||
|
|
||||||
|
tmp |= S3C_CLKCON_PCLK_UART0;
|
||||||
|
tmp |= S3C_CLKCON_PCLK_UART1;
|
||||||
|
tmp |= S3C_CLKCON_PCLK_UART2;
|
||||||
|
tmp |= S3C_CLKCON_PCLK_UART3;
|
||||||
|
|
||||||
|
__raw_writel(tmp, S3C_PCLK_GATE);
|
||||||
|
udelay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void s3c_pm_arch_prepare_irqs(void)
|
||||||
|
{
|
||||||
|
/* VIC should have already been taken care of */
|
||||||
|
|
||||||
|
/* clear any pending EINT0 interrupts */
|
||||||
|
__raw_writel(__raw_readl(S3C64XX_EINT0PEND), S3C64XX_EINT0PEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void s3c_pm_arch_stop_clocks(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void s3c_pm_arch_show_resume_irqs(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make these defines, we currently do not have any need to change
|
||||||
|
* the IRQ wake controls depending on the CPU we are running on */
|
||||||
|
|
||||||
|
#define s3c_irqwake_eintallow ((1 << 28) - 1)
|
||||||
|
#define s3c_irqwake_intallow (0)
|
||||||
|
|
||||||
|
static inline void s3c_pm_arch_update_uart(void __iomem *regs,
|
||||||
|
struct pm_uart_save *save)
|
||||||
|
{
|
||||||
|
u32 ucon = __raw_readl(regs + S3C2410_UCON);
|
||||||
|
u32 ucon_clk = ucon & S3C6400_UCON_CLKMASK;
|
||||||
|
u32 save_clk = save->ucon & S3C6400_UCON_CLKMASK;
|
||||||
|
u32 new_ucon;
|
||||||
|
u32 delta;
|
||||||
|
|
||||||
|
/* S3C64XX UART blocks only support level interrupts, so ensure that
|
||||||
|
* when we restore unused UART blocks we force the level interrupt
|
||||||
|
* settigs. */
|
||||||
|
save->ucon |= S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL;
|
||||||
|
|
||||||
|
/* We have a constraint on changing the clock type of the UART
|
||||||
|
* between UCLKx and PCLK, so ensure that when we restore UCON
|
||||||
|
* that the CLK field is correctly modified if the bootloader
|
||||||
|
* has changed anything.
|
||||||
|
*/
|
||||||
|
if (ucon_clk != save_clk) {
|
||||||
|
new_ucon = save->ucon;
|
||||||
|
delta = ucon_clk ^ save_clk;
|
||||||
|
|
||||||
|
/* change from UCLKx => wrong PCLK,
|
||||||
|
* either UCLK can be tested for by a bit-test
|
||||||
|
* with UCLK0 */
|
||||||
|
if (ucon_clk & S3C6400_UCON_UCLK0 &&
|
||||||
|
!(save_clk & S3C6400_UCON_UCLK0) &&
|
||||||
|
delta & S3C6400_UCON_PCLK2) {
|
||||||
|
new_ucon &= ~S3C6400_UCON_UCLK0;
|
||||||
|
} else if (delta == S3C6400_UCON_PCLK2) {
|
||||||
|
/* as an precaution, don't change from
|
||||||
|
* PCLK2 => PCLK or vice-versa */
|
||||||
|
new_ucon ^= S3C6400_UCON_PCLK2;
|
||||||
|
}
|
||||||
|
|
||||||
|
S3C_PMDBG("ucon change %04x => %04x (save=%04x)\n",
|
||||||
|
ucon, new_ucon, save->ucon);
|
||||||
|
save->ucon = new_ucon;
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@
|
||||||
#define S3C_HCLK_GATE S3C_CLKREG(0x30)
|
#define S3C_HCLK_GATE S3C_CLKREG(0x30)
|
||||||
#define S3C_PCLK_GATE S3C_CLKREG(0x34)
|
#define S3C_PCLK_GATE S3C_CLKREG(0x34)
|
||||||
#define S3C_SCLK_GATE S3C_CLKREG(0x38)
|
#define S3C_SCLK_GATE S3C_CLKREG(0x38)
|
||||||
|
#define S3C_MEM0_GATE S3C_CLKREG(0x3C)
|
||||||
|
|
||||||
/* CLKDIV0 */
|
/* CLKDIV0 */
|
||||||
#define S3C6400_CLKDIV0_MFC_MASK (0xf << 28)
|
#define S3C6400_CLKDIV0_MFC_MASK (0xf << 28)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/sysdev.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
|
|
||||||
#include <mach/map.h>
|
#include <mach/map.h>
|
||||||
#include <plat/cpu.h>
|
#include <plat/cpu.h>
|
||||||
|
#include <plat/pm.h>
|
||||||
|
|
||||||
#define eint_offset(irq) ((irq) - IRQ_EINT(0))
|
#define eint_offset(irq) ((irq) - IRQ_EINT(0))
|
||||||
#define eint_irq_to_bit(irq) (1 << eint_offset(irq))
|
#define eint_irq_to_bit(irq) (1 << eint_offset(irq))
|
||||||
|
@ -134,6 +136,7 @@ static struct irq_chip s3c_irq_eint = {
|
||||||
.mask_ack = s3c_irq_eint_maskack,
|
.mask_ack = s3c_irq_eint_maskack,
|
||||||
.ack = s3c_irq_eint_ack,
|
.ack = s3c_irq_eint_ack,
|
||||||
.set_type = s3c_irq_eint_set_type,
|
.set_type = s3c_irq_eint_set_type,
|
||||||
|
.set_wake = s3c_irqext_wake,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* s3c_irq_demux_eint
|
/* s3c_irq_demux_eint
|
||||||
|
|
186
arch/arm/plat-s3c64xx/pm.c
Normal file
186
arch/arm/plat-s3c64xx/pm.c
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/* linux/arch/arm/plat-s3c64xx/pm.c
|
||||||
|
*
|
||||||
|
* Copyright 2008 Openmoko, Inc.
|
||||||
|
* Copyright 2008 Simtec Electronics
|
||||||
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
|
* http://armlinux.simtec.co.uk/
|
||||||
|
*
|
||||||
|
* S3C64XX CPU PM support.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
|
#include <linux/serial_core.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include <mach/map.h>
|
||||||
|
|
||||||
|
#include <plat/pm.h>
|
||||||
|
#include <plat/regs-sys.h>
|
||||||
|
#include <plat/regs-gpio.h>
|
||||||
|
#include <plat/regs-clock.h>
|
||||||
|
#include <plat/regs-syscon-power.h>
|
||||||
|
#include <plat/regs-gpio-memport.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
|
||||||
|
#include <plat/gpio-bank-n.h>
|
||||||
|
|
||||||
|
void s3c_pm_debug_smdkled(u32 set, u32 clear)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
local_irq_save(flags);
|
||||||
|
reg = __raw_readl(S3C64XX_GPNCON);
|
||||||
|
reg &= ~(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) |
|
||||||
|
S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15));
|
||||||
|
reg |= S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) |
|
||||||
|
S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15);
|
||||||
|
__raw_writel(reg, S3C64XX_GPNCON);
|
||||||
|
|
||||||
|
reg = __raw_readl(S3C64XX_GPNDAT);
|
||||||
|
reg &= ~(clear << 12);
|
||||||
|
reg |= set << 12;
|
||||||
|
__raw_writel(reg, S3C64XX_GPNDAT);
|
||||||
|
|
||||||
|
local_irq_restore(flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct sleep_save core_save[] = {
|
||||||
|
SAVE_ITEM(S3C_APLL_LOCK),
|
||||||
|
SAVE_ITEM(S3C_MPLL_LOCK),
|
||||||
|
SAVE_ITEM(S3C_EPLL_LOCK),
|
||||||
|
SAVE_ITEM(S3C_CLK_SRC),
|
||||||
|
SAVE_ITEM(S3C_CLK_DIV0),
|
||||||
|
SAVE_ITEM(S3C_CLK_DIV1),
|
||||||
|
SAVE_ITEM(S3C_CLK_DIV2),
|
||||||
|
SAVE_ITEM(S3C_CLK_OUT),
|
||||||
|
SAVE_ITEM(S3C_HCLK_GATE),
|
||||||
|
SAVE_ITEM(S3C_PCLK_GATE),
|
||||||
|
SAVE_ITEM(S3C_SCLK_GATE),
|
||||||
|
SAVE_ITEM(S3C_MEM0_GATE),
|
||||||
|
|
||||||
|
SAVE_ITEM(S3C_EPLL_CON1),
|
||||||
|
SAVE_ITEM(S3C_EPLL_CON0),
|
||||||
|
|
||||||
|
SAVE_ITEM(S3C64XX_MEM0DRVCON),
|
||||||
|
SAVE_ITEM(S3C64XX_MEM1DRVCON),
|
||||||
|
|
||||||
|
#ifndef CONFIG_CPU_FREQ
|
||||||
|
SAVE_ITEM(S3C_APLL_CON),
|
||||||
|
SAVE_ITEM(S3C_MPLL_CON),
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sleep_save misc_save[] = {
|
||||||
|
SAVE_ITEM(S3C64XX_AHB_CON0),
|
||||||
|
SAVE_ITEM(S3C64XX_AHB_CON1),
|
||||||
|
SAVE_ITEM(S3C64XX_AHB_CON2),
|
||||||
|
|
||||||
|
SAVE_ITEM(S3C64XX_SPCON),
|
||||||
|
|
||||||
|
SAVE_ITEM(S3C64XX_MEM0CONSTOP),
|
||||||
|
SAVE_ITEM(S3C64XX_MEM1CONSTOP),
|
||||||
|
SAVE_ITEM(S3C64XX_MEM0CONSLP0),
|
||||||
|
SAVE_ITEM(S3C64XX_MEM0CONSLP1),
|
||||||
|
SAVE_ITEM(S3C64XX_MEM1CONSLP),
|
||||||
|
};
|
||||||
|
|
||||||
|
void s3c_pm_configure_extint(void)
|
||||||
|
{
|
||||||
|
__raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void s3c_pm_save_gpios(void)
|
||||||
|
{
|
||||||
|
/* currently, unless the bootloader does something really stupid
|
||||||
|
* the gpio blocks should be maintained over their sleep.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void s3c_pm_restore_gpios(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void s3c_pm_restore_core(void)
|
||||||
|
{
|
||||||
|
__raw_writel(0, S3C64XX_EINT_MASK);
|
||||||
|
|
||||||
|
s3c_pm_debug_smdkled(1 << 2, 0);
|
||||||
|
|
||||||
|
s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
|
||||||
|
s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
|
||||||
|
}
|
||||||
|
|
||||||
|
void s3c_pm_save_core(void)
|
||||||
|
{
|
||||||
|
s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
|
||||||
|
s3c_pm_do_save(core_save, ARRAY_SIZE(core_save));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* since both s3c6400 and s3c6410 share the same sleep pm calls, we
|
||||||
|
* put the per-cpu code in here until any new cpu comes along and changes
|
||||||
|
* this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <plat/regs-gpio.h>
|
||||||
|
|
||||||
|
static void s3c64xx_cpu_suspend(void)
|
||||||
|
{
|
||||||
|
unsigned long tmp;
|
||||||
|
|
||||||
|
/* set our standby method to sleep */
|
||||||
|
|
||||||
|
tmp = __raw_readl(S3C64XX_PWR_CFG);
|
||||||
|
tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK;
|
||||||
|
tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP;
|
||||||
|
__raw_writel(tmp, S3C64XX_PWR_CFG);
|
||||||
|
|
||||||
|
/* clear any old wakeup */
|
||||||
|
|
||||||
|
__raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT),
|
||||||
|
S3C64XX_WAKEUP_STAT);
|
||||||
|
|
||||||
|
/* set the LED state to 0110 over sleep */
|
||||||
|
s3c_pm_debug_smdkled(3 << 1, 0xf);
|
||||||
|
|
||||||
|
/* issue the standby signal into the pm unit. Note, we
|
||||||
|
* issue a write-buffer drain just in case */
|
||||||
|
|
||||||
|
tmp = 0;
|
||||||
|
|
||||||
|
asm("b 1f\n\t"
|
||||||
|
".align 5\n\t"
|
||||||
|
"1:\n\t"
|
||||||
|
"mcr p15, 0, %0, c7, c10, 5\n\t"
|
||||||
|
"mcr p15, 0, %0, c7, c10, 4\n\t"
|
||||||
|
"mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp));
|
||||||
|
|
||||||
|
/* we should never get past here */
|
||||||
|
|
||||||
|
panic("sleep resumed to originator?");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void s3c64xx_pm_prepare(void)
|
||||||
|
{
|
||||||
|
/* store address of resume. */
|
||||||
|
__raw_writel(virt_to_phys(s3c_cpu_resume), S3C64XX_INFORM0);
|
||||||
|
|
||||||
|
/* ensure previous wakeup state is cleared before sleeping */
|
||||||
|
__raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s3c64xx_pm_init(void)
|
||||||
|
{
|
||||||
|
pm_cpu_prep = s3c64xx_pm_prepare;
|
||||||
|
pm_cpu_sleep = s3c64xx_cpu_suspend;
|
||||||
|
pm_uart_udivslot = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
arch_initcall(s3c64xx_pm_init);
|
144
arch/arm/plat-s3c64xx/sleep.S
Normal file
144
arch/arm/plat-s3c64xx/sleep.S
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/* linux/0arch/arm/plat-s3c64xx/sleep.S
|
||||||
|
*
|
||||||
|
* Copyright 2008 Openmoko, Inc.
|
||||||
|
* Copyright 2008 Simtec Electronics
|
||||||
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
|
* http://armlinux.simtec.co.uk/
|
||||||
|
*
|
||||||
|
* S3C64XX CPU sleep code
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <asm/assembler.h>
|
||||||
|
#include <mach/map.h>
|
||||||
|
|
||||||
|
#undef S3C64XX_VA_GPIO
|
||||||
|
#define S3C64XX_VA_GPIO (0x0)
|
||||||
|
|
||||||
|
#include <plat/regs-gpio.h>
|
||||||
|
#include <plat/gpio-bank-n.h>
|
||||||
|
|
||||||
|
#define LL_UART (S3C_PA_UART + (0x400 * CONFIG_S3C_LOWLEVEL_UART_PORT))
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
/* s3c_cpu_save
|
||||||
|
*
|
||||||
|
* Save enough processor state to allow the restart of the pm.c
|
||||||
|
* code after resume.
|
||||||
|
*
|
||||||
|
* entry:
|
||||||
|
* r0 = pointer to the save block
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(s3c_cpu_save)
|
||||||
|
stmfd sp!, { r4 - r12, lr }
|
||||||
|
|
||||||
|
mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID
|
||||||
|
mrc p15, 0, r5, c3, c0, 0 @ Domain ID
|
||||||
|
mrc p15, 0, r6, c2, c0, 0 @ Translation Table BASE0
|
||||||
|
mrc p15, 0, r7, c2, c0, 1 @ Translation Table BASE1
|
||||||
|
mrc p15, 0, r8, c2, c0, 2 @ Translation Table Control
|
||||||
|
mrc p15, 0, r9, c1, c0, 0 @ Control register
|
||||||
|
mrc p15, 0, r10, c1, c0, 1 @ Auxiliary control register
|
||||||
|
mrc p15, 0, r11, c1, c0, 2 @ Co-processor access controls
|
||||||
|
|
||||||
|
stmia r0, { r4 - r13 } @ Save CP registers and SP
|
||||||
|
|
||||||
|
@@ save our state to ram
|
||||||
|
bl s3c_pm_cb_flushcache
|
||||||
|
|
||||||
|
@@ call final suspend code
|
||||||
|
ldr r0, =pm_cpu_sleep
|
||||||
|
ldr pc, [r0]
|
||||||
|
|
||||||
|
@@ return to the caller, after the MMU is turned on.
|
||||||
|
@@ restore the last bits of the stack and return.
|
||||||
|
resume_with_mmu:
|
||||||
|
ldmfd sp!, { r4 - r12, pc } @ return, from sp from s3c_cpu_save
|
||||||
|
|
||||||
|
.data
|
||||||
|
|
||||||
|
/* the next bit is code, but it requires easy access to the
|
||||||
|
* s3c_sleep_save_phys data before the MMU is switched on, so
|
||||||
|
* we store the code that needs this variable in the .data where
|
||||||
|
* the value can be written to (the .text segment is RO).
|
||||||
|
*/
|
||||||
|
|
||||||
|
.global s3c_sleep_save_phys
|
||||||
|
s3c_sleep_save_phys:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
/* Sleep magic, the word before the resume entry point so that the
|
||||||
|
* bootloader can check for a resumeable image. */
|
||||||
|
|
||||||
|
.word 0x2bedf00d
|
||||||
|
|
||||||
|
/* s3c_cpu_reusme
|
||||||
|
*
|
||||||
|
* This is the entry point, stored by whatever method the bootloader
|
||||||
|
* requires to get the kernel runnign again. This code expects to be
|
||||||
|
* entered with no caches live and the MMU disabled. It will then
|
||||||
|
* restore the MMU and other basic CP registers saved and restart
|
||||||
|
* the kernel C code to finish the resume code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(s3c_cpu_resume)
|
||||||
|
msr cpsr_c, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
|
||||||
|
ldr r2, =LL_UART /* for debug */
|
||||||
|
|
||||||
|
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
|
||||||
|
/* Initialise the GPIO state if we are debugging via the SMDK LEDs,
|
||||||
|
* as the uboot version supplied resets these to inputs during the
|
||||||
|
* resume checks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ldr r3, =S3C64XX_PA_GPIO
|
||||||
|
ldr r0, [ r3, #S3C64XX_GPNCON ]
|
||||||
|
bic r0, r0, #(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) | \
|
||||||
|
S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15))
|
||||||
|
orr r0, r0, #(S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) | \
|
||||||
|
S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15))
|
||||||
|
str r0, [ r3, #S3C64XX_GPNCON ]
|
||||||
|
|
||||||
|
ldr r0, [ r3, #S3C64XX_GPNDAT ]
|
||||||
|
bic r0, r0, #0xf << 12 @ GPN12..15
|
||||||
|
orr r0, r0, #1 << 15 @ GPN15
|
||||||
|
str r0, [ r3, #S3C64XX_GPNDAT ]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* __v6_setup from arch/arm/mm/proc-v6.S, ensure that the caches
|
||||||
|
* are thoroughly cleaned just in case the bootloader didn't do it
|
||||||
|
* for us. */
|
||||||
|
mov r0, #0
|
||||||
|
mcr p15, 0, r0, c7, c14, 0 @ clean+invalidate D cache
|
||||||
|
mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
|
||||||
|
mcr p15, 0, r0, c7, c15, 0 @ clean+invalidate cache
|
||||||
|
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||||
|
@@mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs
|
||||||
|
@@mcr p15, 0, r0, c7, c7, 0 @ Invalidate I + D caches
|
||||||
|
|
||||||
|
ldr r0, s3c_sleep_save_phys
|
||||||
|
ldmia r0, { r4 - r13 }
|
||||||
|
|
||||||
|
mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID
|
||||||
|
mcr p15, 0, r5, c3, c0, 0 @ Domain ID
|
||||||
|
mcr p15, 0, r6, c2, c0, 0 @ Translation Table BASE0
|
||||||
|
mcr p15, 0, r7, c2, c0, 1 @ Translation Table BASE1
|
||||||
|
mcr p15, 0, r8, c2, c0, 2 @ Translation Table Control
|
||||||
|
mcr p15, 0, r10, c1, c0, 1 @ Auxiliary control register
|
||||||
|
|
||||||
|
mov r0, #0 @ restore copro access controls
|
||||||
|
mcr p15, 0, r11, c1, c0, 2 @ Co-processor access controls
|
||||||
|
mcr p15, 0, r0, c7, c5, 4
|
||||||
|
|
||||||
|
ldr r2, =resume_with_mmu
|
||||||
|
mcr p15, 0, r9, c1, c0, 0 /* turn mmu back on */
|
||||||
|
nop
|
||||||
|
mov pc, r2 /* jump back */
|
||||||
|
|
||||||
|
.end
|
Loading…
Reference in New Issue
Block a user