559ba23799
This patch simplifies Exynos PM initialization and makes it multiplatform friendly by replacing initcalls used originally to invoke all the initialization code with explicit function calls. In addition, an useless subsys_interface is removed, as all its .add_dev callback did was setting two function pointers. Signed-off-by: Tomasz Figa <t.figa@samsung.com> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
205 lines
4.9 KiB
C
205 lines
4.9 KiB
C
/*
|
|
* Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com
|
|
*
|
|
* EXYNOS - Power Management support
|
|
*
|
|
* Based on arch/arm/mach-s3c2410/pm.c
|
|
* Copyright (c) 2006 Simtec Electronics
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
*
|
|
* 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/syscore_ops.h>
|
|
#include <linux/io.h>
|
|
#include <linux/err.h>
|
|
#include <linux/clk.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/hardware/cache-l2x0.h>
|
|
#include <asm/smp_scu.h>
|
|
|
|
#include <plat/cpu.h>
|
|
#include <plat/pm.h>
|
|
#include <plat/pll.h>
|
|
#include <plat/regs-srom.h>
|
|
|
|
#include <mach/map.h>
|
|
#include <mach/pm-core.h>
|
|
|
|
#include "common.h"
|
|
#include "regs-pmu.h"
|
|
|
|
static struct sleep_save exynos5_sys_save[] = {
|
|
SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
|
|
};
|
|
|
|
static struct sleep_save exynos_core_save[] = {
|
|
/* SROM side */
|
|
SAVE_ITEM(S5P_SROM_BW),
|
|
SAVE_ITEM(S5P_SROM_BC0),
|
|
SAVE_ITEM(S5P_SROM_BC1),
|
|
SAVE_ITEM(S5P_SROM_BC2),
|
|
SAVE_ITEM(S5P_SROM_BC3),
|
|
};
|
|
|
|
|
|
/* For Cortex-A9 Diagnostic and Power control register */
|
|
static unsigned int save_arm_register[2];
|
|
|
|
static int exynos_cpu_suspend(unsigned long arg)
|
|
{
|
|
#ifdef CONFIG_CACHE_L2X0
|
|
outer_flush_all();
|
|
#endif
|
|
|
|
if (soc_is_exynos5250())
|
|
flush_cache_all();
|
|
|
|
/* issue the standby signal into the pm unit. */
|
|
cpu_do_idle();
|
|
|
|
pr_info("Failed to suspend the system\n");
|
|
return 1; /* Aborting suspend */
|
|
}
|
|
|
|
static void exynos_pm_prepare(void)
|
|
{
|
|
unsigned int tmp;
|
|
|
|
s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
|
|
|
|
if (soc_is_exynos5250()) {
|
|
s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save));
|
|
/* Disable USE_RETENTION of JPEG_MEM_OPTION */
|
|
tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
|
|
tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
|
|
__raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
|
|
}
|
|
|
|
/* Set value of power down register for sleep mode */
|
|
|
|
exynos_sys_powerdown_conf(SYS_SLEEP);
|
|
__raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
|
|
|
|
/* ensure at least INFORM0 has the resume address */
|
|
|
|
__raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0);
|
|
}
|
|
|
|
static int exynos_pm_suspend(void)
|
|
{
|
|
unsigned long tmp;
|
|
|
|
/* Setting Central Sequence Register for power down mode */
|
|
|
|
tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
|
|
tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
|
|
__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
|
|
|
|
/* Setting SEQ_OPTION register */
|
|
|
|
tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
|
|
__raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
|
|
|
|
if (!soc_is_exynos5250()) {
|
|
/* Save Power control register */
|
|
asm ("mrc p15, 0, %0, c15, c0, 0"
|
|
: "=r" (tmp) : : "cc");
|
|
save_arm_register[0] = tmp;
|
|
|
|
/* Save Diagnostic register */
|
|
asm ("mrc p15, 0, %0, c15, c0, 1"
|
|
: "=r" (tmp) : : "cc");
|
|
save_arm_register[1] = tmp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void exynos_pm_resume(void)
|
|
{
|
|
unsigned long tmp;
|
|
|
|
/*
|
|
* If PMU failed while entering sleep mode, WFI will be
|
|
* ignored by PMU and then exiting cpu_do_idle().
|
|
* S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
|
|
* in this situation.
|
|
*/
|
|
tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
|
|
if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
|
|
tmp |= S5P_CENTRAL_LOWPWR_CFG;
|
|
__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
|
|
/* clear the wakeup state register */
|
|
__raw_writel(0x0, S5P_WAKEUP_STAT);
|
|
/* No need to perform below restore code */
|
|
goto early_wakeup;
|
|
}
|
|
if (!soc_is_exynos5250()) {
|
|
/* Restore Power control register */
|
|
tmp = save_arm_register[0];
|
|
asm volatile ("mcr p15, 0, %0, c15, c0, 0"
|
|
: : "r" (tmp)
|
|
: "cc");
|
|
|
|
/* Restore Diagnostic register */
|
|
tmp = save_arm_register[1];
|
|
asm volatile ("mcr p15, 0, %0, c15, c0, 1"
|
|
: : "r" (tmp)
|
|
: "cc");
|
|
}
|
|
|
|
/* For release retention */
|
|
|
|
__raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
|
|
__raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION);
|
|
__raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION);
|
|
__raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION);
|
|
__raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION);
|
|
__raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
|
|
__raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION);
|
|
|
|
if (soc_is_exynos5250())
|
|
s3c_pm_do_restore(exynos5_sys_save,
|
|
ARRAY_SIZE(exynos5_sys_save));
|
|
|
|
s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
|
|
|
|
if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250())
|
|
scu_enable(S5P_VA_SCU);
|
|
|
|
early_wakeup:
|
|
|
|
/* Clear SLEEP mode set in INFORM1 */
|
|
__raw_writel(0x0, S5P_INFORM1);
|
|
|
|
return;
|
|
}
|
|
|
|
static struct syscore_ops exynos_pm_syscore_ops = {
|
|
.suspend = exynos_pm_suspend,
|
|
.resume = exynos_pm_resume,
|
|
};
|
|
|
|
void __init exynos_pm_init(void)
|
|
{
|
|
u32 tmp;
|
|
|
|
pm_cpu_prep = exynos_pm_prepare;
|
|
pm_cpu_sleep = exynos_cpu_suspend;
|
|
|
|
s3c_pm_init();
|
|
|
|
/* All wakeup disable */
|
|
tmp = __raw_readl(S5P_WAKEUP_MASK);
|
|
tmp |= ((0xFF << 8) | (0x1F << 1));
|
|
__raw_writel(tmp, S5P_WAKEUP_MASK);
|
|
|
|
register_syscore_ops(&exynos_pm_syscore_ops);
|
|
}
|