mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-08 14:23:19 +00:00
ARM: S5P64X0: Add Power Management support
Add suspend-to-ram support for SMDK6440/50 Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
This commit is contained in:
parent
e2e13621b2
commit
6b6844dd54
@ -2093,7 +2093,7 @@ menu "Power management options"
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
depends on !ARCH_S5P64X0 && !ARCH_S5PC100
|
||||
depends on !ARCH_S5PC100
|
||||
depends on CPU_ARM920T || CPU_ARM926T || CPU_SA1100 || \
|
||||
CPU_V6 || CPU_V6K || CPU_V7 || CPU_XSC3 || CPU_XSCALE
|
||||
def_bool y
|
||||
|
@ -11,6 +11,8 @@ config CPU_S5P6440
|
||||
bool
|
||||
select SAMSUNG_DMADEV
|
||||
select S5P_HRT
|
||||
select S5P_SLEEP if PM
|
||||
select SAMSUNG_WAKEMASK if PM
|
||||
help
|
||||
Enable S5P6440 CPU support
|
||||
|
||||
@ -18,6 +20,8 @@ config CPU_S5P6450
|
||||
bool
|
||||
select SAMSUNG_DMADEV
|
||||
select S5P_HRT
|
||||
select S5P_SLEEP if PM
|
||||
select SAMSUNG_WAKEMASK if PM
|
||||
help
|
||||
Enable S5P6450 CPU support
|
||||
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_ARCH_S5P64X0) += cpu.o init.o clock.o dma.o
|
||||
obj-$(CONFIG_ARCH_S5P64X0) += setup-i2c0.o irq-eint.o
|
||||
obj-$(CONFIG_CPU_S5P6440) += clock-s5p6440.o
|
||||
obj-$(CONFIG_CPU_S5P6450) += clock-s5p6450.o
|
||||
obj-$(CONFIG_PM) += pm.o irq-pm.o
|
||||
|
||||
# machine support
|
||||
|
||||
|
@ -88,5 +88,6 @@
|
||||
#define S5P_PA_UART5 S5P6450_PA_UART(5)
|
||||
|
||||
#define S5P_SZ_UART SZ_256
|
||||
#define S3C_VA_UARTx(x) (S3C_VA_UART + ((x) * S3C_UART_OFFSET))
|
||||
|
||||
#endif /* __ASM_ARCH_MAP_H */
|
||||
|
117
arch/arm/mach-s5p64x0/include/mach/pm-core.h
Normal file
117
arch/arm/mach-s5p64x0/include/mach/pm-core.h
Normal file
@ -0,0 +1,117 @@
|
||||
/* linux/arch/arm/mach-s5p64x0/include/mach/pm-core.h
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* S5P64X0 - PM core support for arch/arm/plat-samsung/pm.c
|
||||
*
|
||||
* Based on PM core support for S3C64XX by Ben Dooks
|
||||
*
|
||||
* 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 <mach/regs-gpio.h>
|
||||
|
||||
static inline void s3c_pm_debug_init_uart(void)
|
||||
{
|
||||
u32 tmp = __raw_readl(S5P64X0_CLK_GATE_PCLK);
|
||||
|
||||
/*
|
||||
* As a note, since the S5P64X0 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 |= S5P64X0_CLK_GATE_PCLK_UART0;
|
||||
tmp |= S5P64X0_CLK_GATE_PCLK_UART1;
|
||||
tmp |= S5P64X0_CLK_GATE_PCLK_UART2;
|
||||
tmp |= S5P64X0_CLK_GATE_PCLK_UART3;
|
||||
|
||||
__raw_writel(tmp, S5P64X0_CLK_GATE_PCLK);
|
||||
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(S5P64X0_EINT0PEND), S5P64X0_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 << 16) - 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;
|
||||
|
||||
/*
|
||||
* S5P64X0 UART blocks only support level interrupts, so ensure that
|
||||
* when we restore unused UART blocks we force the level interrupt
|
||||
* settings.
|
||||
*/
|
||||
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 a 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;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void s3c_pm_restored_gpios(void)
|
||||
{
|
||||
/* ensure sleep mode has been cleared from the system */
|
||||
__raw_writel(0, S5P64X0_SLPEN);
|
||||
}
|
||||
|
||||
static inline void samsung_pm_saved_gpios(void)
|
||||
{
|
||||
/*
|
||||
* turn on the sleep mode and keep it there, as it seems that during
|
||||
* suspend the xCON registers get re-set and thus you can end up with
|
||||
* problems between going to sleep and resuming.
|
||||
*/
|
||||
__raw_writel(S5P64X0_SLPEN_USE_xSLP, S5P64X0_SLPEN);
|
||||
}
|
@ -41,17 +41,50 @@
|
||||
#define S5P6450_DPLL_CON S5P_CLKREG(0x50)
|
||||
#define S5P6450_DPLL_CON_K S5P_CLKREG(0x54)
|
||||
|
||||
#define S5P64X0_AHB_CON0 S5P_CLKREG(0x100)
|
||||
#define S5P64X0_CLK_SRC1 S5P_CLKREG(0x10C)
|
||||
|
||||
#define S5P64X0_SYS_ID S5P_CLKREG(0x118)
|
||||
#define S5P64X0_SYS_OTHERS S5P_CLKREG(0x11C)
|
||||
|
||||
#define S5P64X0_PWR_CFG S5P_CLKREG(0x804)
|
||||
#define S5P64X0_EINT_WAKEUP_MASK S5P_CLKREG(0x808)
|
||||
#define S5P64X0_SLEEP_CFG S5P_CLKREG(0x818)
|
||||
#define S5P64X0_PWR_STABLE S5P_CLKREG(0x828)
|
||||
|
||||
#define S5P64X0_OTHERS S5P_CLKREG(0x900)
|
||||
#define S5P64X0_WAKEUP_STAT S5P_CLKREG(0x908)
|
||||
|
||||
#define S5P64X0_INFORM0 S5P_CLKREG(0xA00)
|
||||
|
||||
#define S5P64X0_CLKDIV0_HCLK_SHIFT (8)
|
||||
#define S5P64X0_CLKDIV0_HCLK_MASK (0xF << S5P64X0_CLKDIV0_HCLK_SHIFT)
|
||||
|
||||
/* HCLK GATE Registers */
|
||||
#define S5P64X0_CLK_GATE_HCLK1_FIMGVG (1 << 2)
|
||||
#define S5P64X0_CLK_GATE_SCLK1_FIMGVG (1 << 2)
|
||||
|
||||
/* PCLK GATE Registers */
|
||||
#define S5P64X0_CLK_GATE_PCLK_UART3 (1 << 4)
|
||||
#define S5P64X0_CLK_GATE_PCLK_UART2 (1 << 3)
|
||||
#define S5P64X0_CLK_GATE_PCLK_UART1 (1 << 2)
|
||||
#define S5P64X0_CLK_GATE_PCLK_UART0 (1 << 1)
|
||||
|
||||
#define S5P64X0_PWR_CFG_MMC1_DISABLE (1 << 15)
|
||||
#define S5P64X0_PWR_CFG_MMC0_DISABLE (1 << 14)
|
||||
#define S5P64X0_PWR_CFG_RTC_TICK_DISABLE (1 << 11)
|
||||
#define S5P64X0_PWR_CFG_RTC_ALRM_DISABLE (1 << 10)
|
||||
#define S5P64X0_PWR_CFG_WFI_MASK (3 << 5)
|
||||
#define S5P64X0_PWR_CFG_WFI_SLEEP (3 << 5)
|
||||
|
||||
#define S5P64X0_SLEEP_CFG_OSC_EN (1 << 0)
|
||||
|
||||
#define S5P64X0_PWR_STABLE_PWR_CNT_VAL4 (4 << 0)
|
||||
|
||||
#define S5P6450_OTHERS_DISABLE_INT (1 << 31)
|
||||
#define S5P64X0_OTHERS_RET_UART (1 << 26)
|
||||
#define S5P64X0_OTHERS_RET_MMC1 (1 << 25)
|
||||
#define S5P64X0_OTHERS_RET_MMC0 (1 << 24)
|
||||
#define S5P64X0_OTHERS_USB_SIG_MASK (1 << 16)
|
||||
|
||||
/* Compatibility defines */
|
||||
|
@ -37,15 +37,32 @@
|
||||
#define S5P64X0_SPCON0 (S5P_VA_GPIO + 0x1A0)
|
||||
#define S5P64X0_SPCON0_LCD_SEL_MASK (0x3 << 0)
|
||||
#define S5P64X0_SPCON0_LCD_SEL_RGB (0x1 << 0)
|
||||
#define S5P64X0_SPCON1 (S5P_VA_GPIO + 0x2B0)
|
||||
|
||||
#define S5P64X0_MEM0CONSLP0 (S5P_VA_GPIO + 0x1C0)
|
||||
#define S5P64X0_MEM0CONSLP1 (S5P_VA_GPIO + 0x1C4)
|
||||
#define S5P64X0_MEM0DRVCON (S5P_VA_GPIO + 0x1D0)
|
||||
#define S5P64X0_MEM1DRVCON (S5P_VA_GPIO + 0x1D4)
|
||||
|
||||
#define S5P64X0_EINT12CON (S5P_VA_GPIO + 0x200)
|
||||
#define S5P64X0_EINT12FLTCON (S5P_VA_GPIO + 0x220)
|
||||
#define S5P64X0_EINT12MASK (S5P_VA_GPIO + 0x240)
|
||||
|
||||
/* External interrupt control registers for group0 */
|
||||
|
||||
#define EINT0CON0_OFFSET (0x900)
|
||||
#define EINT0FLTCON0_OFFSET (0x910)
|
||||
#define EINT0FLTCON1_OFFSET (0x914)
|
||||
#define EINT0MASK_OFFSET (0x920)
|
||||
#define EINT0PEND_OFFSET (0x924)
|
||||
|
||||
#define S5P64X0_EINT0CON0 (S5P_VA_GPIO + EINT0CON0_OFFSET)
|
||||
#define S5P64X0_EINT0FLTCON0 (S5P_VA_GPIO + EINT0FLTCON0_OFFSET)
|
||||
#define S5P64X0_EINT0FLTCON1 (S5P_VA_GPIO + EINT0FLTCON1_OFFSET)
|
||||
#define S5P64X0_EINT0MASK (S5P_VA_GPIO + EINT0MASK_OFFSET)
|
||||
#define S5P64X0_EINT0PEND (S5P_VA_GPIO + EINT0PEND_OFFSET)
|
||||
|
||||
#define S5P64X0_SLPEN (S5P_VA_GPIO + 0x930)
|
||||
#define S5P64X0_SLPEN_USE_xSLP (1 << 0)
|
||||
|
||||
#endif /* __ASM_ARCH_REGS_GPIO_H */
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <plat/cpu.h>
|
||||
#include <plat/regs-irqtype.h>
|
||||
#include <plat/gpio-cfg.h>
|
||||
#include <plat/pm.h>
|
||||
|
||||
#include <mach/regs-gpio.h>
|
||||
#include <mach/regs-clock.h>
|
||||
@ -134,6 +135,7 @@ static int s5p64x0_alloc_gc(void)
|
||||
ct->chip.irq_mask = irq_gc_mask_set_bit;
|
||||
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
|
||||
ct->chip.irq_set_type = s5p64x0_irq_eint_set_type;
|
||||
ct->chip.irq_set_wake = s3c_irqext_wake;
|
||||
ct->regs.ack = EINT0PEND_OFFSET;
|
||||
ct->regs.mask = EINT0MASK_OFFSET;
|
||||
irq_setup_generic_chip(gc, IRQ_MSK(16), IRQ_GC_INIT_MASK_CACHE,
|
||||
|
92
arch/arm/mach-s5p64x0/irq-pm.c
Normal file
92
arch/arm/mach-s5p64x0/irq-pm.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* linux/arch/arm/mach-s5p64x0/irq-pm.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* S5P64X0 - Interrupt handling Power Management
|
||||
*
|
||||
* Based on arch/arm/mach-s3c64xx/irq-pm.c by Ben Dooks
|
||||
*
|
||||
* 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/syscore_ops.h>
|
||||
#include <linux/serial_core.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <plat/regs-serial.h>
|
||||
#include <plat/pm.h>
|
||||
|
||||
#include <mach/regs-gpio.h>
|
||||
|
||||
static struct sleep_save irq_save[] = {
|
||||
SAVE_ITEM(S5P64X0_EINT0CON0),
|
||||
SAVE_ITEM(S5P64X0_EINT0FLTCON0),
|
||||
SAVE_ITEM(S5P64X0_EINT0FLTCON1),
|
||||
SAVE_ITEM(S5P64X0_EINT0MASK),
|
||||
};
|
||||
|
||||
static struct irq_grp_save {
|
||||
u32 con;
|
||||
u32 fltcon;
|
||||
u32 mask;
|
||||
} eint_grp_save[4];
|
||||
|
||||
static u32 irq_uart_mask[CONFIG_SERIAL_SAMSUNG_UARTS];
|
||||
|
||||
static int s5p64x0_irq_pm_suspend(void)
|
||||
{
|
||||
struct irq_grp_save *grp = eint_grp_save;
|
||||
int i;
|
||||
|
||||
S3C_PMDBG("%s: suspending IRQs\n", __func__);
|
||||
|
||||
s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
|
||||
|
||||
for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++)
|
||||
irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
|
||||
grp->con = __raw_readl(S5P64X0_EINT12CON + (i * 4));
|
||||
grp->mask = __raw_readl(S5P64X0_EINT12MASK + (i * 4));
|
||||
grp->fltcon = __raw_readl(S5P64X0_EINT12FLTCON + (i * 4));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5p64x0_irq_pm_resume(void)
|
||||
{
|
||||
struct irq_grp_save *grp = eint_grp_save;
|
||||
int i;
|
||||
|
||||
S3C_PMDBG("%s: resuming IRQs\n", __func__);
|
||||
|
||||
s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
|
||||
|
||||
for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++)
|
||||
__raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
|
||||
__raw_writel(grp->con, S5P64X0_EINT12CON + (i * 4));
|
||||
__raw_writel(grp->mask, S5P64X0_EINT12MASK + (i * 4));
|
||||
__raw_writel(grp->fltcon, S5P64X0_EINT12FLTCON + (i * 4));
|
||||
}
|
||||
|
||||
S3C_PMDBG("%s: IRQ configuration restored\n", __func__);
|
||||
}
|
||||
|
||||
static struct syscore_ops s5p64x0_irq_syscore_ops = {
|
||||
.suspend = s5p64x0_irq_pm_suspend,
|
||||
.resume = s5p64x0_irq_pm_resume,
|
||||
};
|
||||
|
||||
static int __init s5p64x0_syscore_init(void)
|
||||
{
|
||||
register_syscore_ops(&s5p64x0_irq_syscore_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
core_initcall(s5p64x0_syscore_init);
|
204
arch/arm/mach-s5p64x0/pm.c
Normal file
204
arch/arm/mach-s5p64x0/pm.c
Normal file
@ -0,0 +1,204 @@
|
||||
/* linux/arch/arm/mach-s5p64x0/pm.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* S5P64X0 Power Management Support
|
||||
*
|
||||
* Based on arch/arm/mach-s3c64xx/pm.c by Ben Dooks
|
||||
*
|
||||
* 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/suspend.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <plat/cpu.h>
|
||||
#include <plat/pm.h>
|
||||
#include <plat/regs-timer.h>
|
||||
#include <plat/wakeup-mask.h>
|
||||
|
||||
#include <mach/regs-clock.h>
|
||||
#include <mach/regs-gpio.h>
|
||||
|
||||
static struct sleep_save s5p64x0_core_save[] = {
|
||||
SAVE_ITEM(S5P64X0_APLL_CON),
|
||||
SAVE_ITEM(S5P64X0_MPLL_CON),
|
||||
SAVE_ITEM(S5P64X0_EPLL_CON),
|
||||
SAVE_ITEM(S5P64X0_EPLL_CON_K),
|
||||
SAVE_ITEM(S5P64X0_CLK_SRC0),
|
||||
SAVE_ITEM(S5P64X0_CLK_SRC1),
|
||||
SAVE_ITEM(S5P64X0_CLK_DIV0),
|
||||
SAVE_ITEM(S5P64X0_CLK_DIV1),
|
||||
SAVE_ITEM(S5P64X0_CLK_DIV2),
|
||||
SAVE_ITEM(S5P64X0_CLK_DIV3),
|
||||
SAVE_ITEM(S5P64X0_CLK_GATE_MEM0),
|
||||
SAVE_ITEM(S5P64X0_CLK_GATE_HCLK1),
|
||||
SAVE_ITEM(S5P64X0_CLK_GATE_SCLK1),
|
||||
};
|
||||
|
||||
static struct sleep_save s5p64x0_misc_save[] = {
|
||||
SAVE_ITEM(S5P64X0_AHB_CON0),
|
||||
SAVE_ITEM(S5P64X0_SPCON0),
|
||||
SAVE_ITEM(S5P64X0_SPCON1),
|
||||
SAVE_ITEM(S5P64X0_MEM0CONSLP0),
|
||||
SAVE_ITEM(S5P64X0_MEM0CONSLP1),
|
||||
SAVE_ITEM(S5P64X0_MEM0DRVCON),
|
||||
SAVE_ITEM(S5P64X0_MEM1DRVCON),
|
||||
|
||||
SAVE_ITEM(S3C64XX_TINT_CSTAT),
|
||||
};
|
||||
|
||||
/* DPLL is present only in S5P6450 */
|
||||
static struct sleep_save s5p6450_core_save[] = {
|
||||
SAVE_ITEM(S5P6450_DPLL_CON),
|
||||
SAVE_ITEM(S5P6450_DPLL_CON_K),
|
||||
};
|
||||
|
||||
void s3c_pm_configure_extint(void)
|
||||
{
|
||||
__raw_writel(s3c_irqwake_eintmask, S5P64X0_EINT_WAKEUP_MASK);
|
||||
}
|
||||
|
||||
void s3c_pm_restore_core(void)
|
||||
{
|
||||
__raw_writel(0, S5P64X0_EINT_WAKEUP_MASK);
|
||||
|
||||
s3c_pm_do_restore_core(s5p64x0_core_save,
|
||||
ARRAY_SIZE(s5p64x0_core_save));
|
||||
|
||||
if (soc_is_s5p6450())
|
||||
s3c_pm_do_restore_core(s5p6450_core_save,
|
||||
ARRAY_SIZE(s5p6450_core_save));
|
||||
|
||||
s3c_pm_do_restore(s5p64x0_misc_save, ARRAY_SIZE(s5p64x0_misc_save));
|
||||
}
|
||||
|
||||
void s3c_pm_save_core(void)
|
||||
{
|
||||
s3c_pm_do_save(s5p64x0_misc_save, ARRAY_SIZE(s5p64x0_misc_save));
|
||||
|
||||
if (soc_is_s5p6450())
|
||||
s3c_pm_do_save(s5p6450_core_save,
|
||||
ARRAY_SIZE(s5p6450_core_save));
|
||||
|
||||
s3c_pm_do_save(s5p64x0_core_save, ARRAY_SIZE(s5p64x0_core_save));
|
||||
}
|
||||
|
||||
static int s5p64x0_cpu_suspend(unsigned long arg)
|
||||
{
|
||||
unsigned long tmp = 0;
|
||||
|
||||
/*
|
||||
* Issue the standby signal into the pm unit. Note, we
|
||||
* issue a write-buffer drain just in case.
|
||||
*/
|
||||
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?");
|
||||
}
|
||||
|
||||
/* mapping of interrupts to parts of the wakeup mask */
|
||||
static struct samsung_wakeup_mask s5p64x0_wake_irqs[] = {
|
||||
{ .irq = IRQ_RTC_ALARM, .bit = S5P64X0_PWR_CFG_RTC_ALRM_DISABLE, },
|
||||
{ .irq = IRQ_RTC_TIC, .bit = S5P64X0_PWR_CFG_RTC_TICK_DISABLE, },
|
||||
{ .irq = IRQ_HSMMC0, .bit = S5P64X0_PWR_CFG_MMC0_DISABLE, },
|
||||
{ .irq = IRQ_HSMMC1, .bit = S5P64X0_PWR_CFG_MMC1_DISABLE, },
|
||||
};
|
||||
|
||||
static void s5p64x0_pm_prepare(void)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
samsung_sync_wakemask(S5P64X0_PWR_CFG,
|
||||
s5p64x0_wake_irqs, ARRAY_SIZE(s5p64x0_wake_irqs));
|
||||
|
||||
/* store the resume address in INFORM0 register */
|
||||
__raw_writel(virt_to_phys(s3c_cpu_resume), S5P64X0_INFORM0);
|
||||
|
||||
/* setup clock gating for FIMGVG block */
|
||||
__raw_writel((__raw_readl(S5P64X0_CLK_GATE_HCLK1) | \
|
||||
(S5P64X0_CLK_GATE_HCLK1_FIMGVG)), S5P64X0_CLK_GATE_HCLK1);
|
||||
__raw_writel((__raw_readl(S5P64X0_CLK_GATE_SCLK1) | \
|
||||
(S5P64X0_CLK_GATE_SCLK1_FIMGVG)), S5P64X0_CLK_GATE_SCLK1);
|
||||
|
||||
/* Configure the stabilization counter with wait time required */
|
||||
__raw_writel(S5P64X0_PWR_STABLE_PWR_CNT_VAL4, S5P64X0_PWR_STABLE);
|
||||
|
||||
/* set WFI to SLEEP mode configuration */
|
||||
tmp = __raw_readl(S5P64X0_SLEEP_CFG);
|
||||
tmp &= ~(S5P64X0_SLEEP_CFG_OSC_EN);
|
||||
__raw_writel(tmp, S5P64X0_SLEEP_CFG);
|
||||
|
||||
tmp = __raw_readl(S5P64X0_PWR_CFG);
|
||||
tmp &= ~(S5P64X0_PWR_CFG_WFI_MASK);
|
||||
tmp |= S5P64X0_PWR_CFG_WFI_SLEEP;
|
||||
__raw_writel(tmp, S5P64X0_PWR_CFG);
|
||||
|
||||
/*
|
||||
* set OTHERS register to disable interrupt before going to
|
||||
* sleep. This bit is present only in S5P6450, it is reserved
|
||||
* in S5P6440.
|
||||
*/
|
||||
if (soc_is_s5p6450()) {
|
||||
tmp = __raw_readl(S5P64X0_OTHERS);
|
||||
tmp |= S5P6450_OTHERS_DISABLE_INT;
|
||||
__raw_writel(tmp, S5P64X0_OTHERS);
|
||||
}
|
||||
|
||||
/* ensure previous wakeup state is cleared before sleeping */
|
||||
__raw_writel(__raw_readl(S5P64X0_WAKEUP_STAT), S5P64X0_WAKEUP_STAT);
|
||||
|
||||
}
|
||||
|
||||
static int s5p64x0_pm_add(struct sys_device *sysdev)
|
||||
{
|
||||
pm_cpu_prep = s5p64x0_pm_prepare;
|
||||
pm_cpu_sleep = s5p64x0_cpu_suspend;
|
||||
pm_uart_udivslot = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sysdev_driver s5p64x0_pm_driver = {
|
||||
.add = s5p64x0_pm_add,
|
||||
};
|
||||
|
||||
static __init int s5p64x0_pm_drvinit(void)
|
||||
{
|
||||
s3c_pm_init();
|
||||
|
||||
return sysdev_driver_register(&s5p64x0_sysclass, &s5p64x0_pm_driver);
|
||||
}
|
||||
arch_initcall(s5p64x0_pm_drvinit);
|
||||
|
||||
static void s5p64x0_pm_resume(void)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
tmp = __raw_readl(S5P64X0_OTHERS);
|
||||
tmp |= (S5P64X0_OTHERS_RET_MMC0 | S5P64X0_OTHERS_RET_MMC1 | \
|
||||
S5P64X0_OTHERS_RET_UART);
|
||||
__raw_writel(tmp , S5P64X0_OTHERS);
|
||||
}
|
||||
|
||||
static struct syscore_ops s5p64x0_pm_syscore_ops = {
|
||||
.resume = s5p64x0_pm_resume,
|
||||
};
|
||||
|
||||
static __init int s5p64x0_pm_syscore_init(void)
|
||||
{
|
||||
register_syscore_ops(&s5p64x0_pm_syscore_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(s5p64x0_pm_syscore_init);
|
Loading…
Reference in New Issue
Block a user