Merge branch 'v3.19-next/pm-samsung-2' of http://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/soc
Merge "1st Round of Samsung PM updates for v3.19" from Kukjin Kim: Samsung PM (v2) updates for v3.19 - added fix build with ARM_CPU_SUSPEND=n based on previous tags/samsung-pm - Refactor the pm code to use DT based lookup instead of using "soc_is_exynosxxxx" - Firmware supporting suspend and resume to excute of low level operations to enter and leave power mode for exynos : introduce suspend() and resume() firmware operations - Fix AFTR mode on boards with secure firmware enabled and allows exynos cpuidle driver usage on exynos4x12 SoCs - Fix build with PM_SLEEP=n and ARM_EXYNOS_CPUIDLE=y - SWRESET is needed to boot secondary CPU on exynos3250 * 'v3.19-next/pm-samsung-2' of http://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung: ARM: EXYNOS: Fix build with ARM_CPU_SUSPEND=n ARM: EXYNOS: SWRESET is needed to boot secondary CPU on exynos3250 ARM: EXYNOS: Fix build with PM_SLEEP=n and ARM_EXYNOS_CPUIDLE=y ARM: EXYNOS: allow driver usage on Exynos4x12 SoCs ARM: EXYNOS: fix register setup for AFTR mode code ARM: EXYNOS: add secure firmware support to AFTR mode code ARM: firmware: add AFTR mode support to firmware do_idle method ARM: EXYNOS: replace EXYNOS_BOOT_VECTOR_* macros by static inlines ARM: EXYNOS: Add support for firmware-assisted suspend/resume ARM: firmware: Introduce suspend and resume operations ARM: EXYNOS: Refactor the pm code to use DT based lookup ARM: EXYNOS: Move Disabling of JPEG USE_RETENTION for exynos5250 to pmu.c Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
e7638e7a49
16 changed files with 559 additions and 301 deletions
|
@ -7,32 +7,14 @@ world, which changes the way some things have to be initialized. This makes
|
|||
a need to provide an interface for such platforms to specify available firmware
|
||||
operations and call them when needed.
|
||||
|
||||
Firmware operations can be specified using struct firmware_ops
|
||||
|
||||
struct firmware_ops {
|
||||
/*
|
||||
* Enters CPU idle mode
|
||||
*/
|
||||
int (*do_idle)(void);
|
||||
/*
|
||||
* Sets boot address of specified physical CPU
|
||||
*/
|
||||
int (*set_cpu_boot_addr)(int cpu, unsigned long boot_addr);
|
||||
/*
|
||||
* Boots specified physical CPU
|
||||
*/
|
||||
int (*cpu_boot)(int cpu);
|
||||
/*
|
||||
* Initializes L2 cache
|
||||
*/
|
||||
int (*l2x0_init)(void);
|
||||
};
|
||||
|
||||
and then registered with register_firmware_ops function
|
||||
Firmware operations can be specified by filling in a struct firmware_ops
|
||||
with appropriate callbacks and then registering it with register_firmware_ops()
|
||||
function.
|
||||
|
||||
void register_firmware_ops(const struct firmware_ops *ops)
|
||||
|
||||
the ops pointer must be non-NULL.
|
||||
The ops pointer must be non-NULL. More information about struct firmware_ops
|
||||
and its members can be found in arch/arm/include/asm/firmware.h header.
|
||||
|
||||
There is a default, empty set of operations provided, so there is no need to
|
||||
set anything if platform does not require firmware operations.
|
||||
|
|
|
@ -28,7 +28,7 @@ struct firmware_ops {
|
|||
/*
|
||||
* Enters CPU idle mode
|
||||
*/
|
||||
int (*do_idle)(void);
|
||||
int (*do_idle)(unsigned long mode);
|
||||
/*
|
||||
* Sets boot address of specified physical CPU
|
||||
*/
|
||||
|
@ -41,6 +41,14 @@ struct firmware_ops {
|
|||
* Initializes L2 cache
|
||||
*/
|
||||
int (*l2x0_init)(void);
|
||||
/*
|
||||
* Enter system-wide suspend.
|
||||
*/
|
||||
int (*suspend)(void);
|
||||
/*
|
||||
* Restore state of privileged hardware after system-wide suspend.
|
||||
*/
|
||||
int (*resume)(void);
|
||||
};
|
||||
|
||||
/* Global pointer for current firmware_ops structure, can't be NULL. */
|
||||
|
|
|
@ -123,4 +123,9 @@ config EXYNOS5420_MCPM
|
|||
This is needed to provide CPU and cluster power management
|
||||
on Exynos5420 implementing big.LITTLE.
|
||||
|
||||
config EXYNOS_CPU_SUSPEND
|
||||
bool
|
||||
select ARM_CPU_SUSPEND
|
||||
default PM_SLEEP || ARM_EXYNOS_CPUIDLE
|
||||
|
||||
endif
|
||||
|
|
|
@ -11,7 +11,8 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) += -I$(srctree)/$(src)/include -I$(srctree)
|
|||
|
||||
obj-$(CONFIG_ARCH_EXYNOS) += exynos.o pmu.o exynos-smc.o firmware.o
|
||||
|
||||
obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o
|
||||
obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o sleep.o
|
||||
obj-$(CONFIG_PM_SLEEP) += suspend.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
|
||||
|
||||
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
|
||||
|
@ -21,6 +22,7 @@ CFLAGS_hotplug.o += -march=armv7-a
|
|||
|
||||
plus_sec := $(call as-instr,.arch_extension sec,+sec)
|
||||
AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec)
|
||||
AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
|
||||
|
||||
obj-$(CONFIG_EXYNOS5420_MCPM) += mcpm-exynos.o
|
||||
CFLAGS_mcpm-exynos.o += -march=armv7-a
|
||||
|
|
|
@ -111,11 +111,19 @@ IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK)
|
|||
#define soc_is_exynos5() (soc_is_exynos5250() || soc_is_exynos5410() || \
|
||||
soc_is_exynos5420() || soc_is_exynos5800())
|
||||
|
||||
extern u32 cp15_save_diag;
|
||||
extern u32 cp15_save_power;
|
||||
|
||||
extern void __iomem *sysram_ns_base_addr;
|
||||
extern void __iomem *sysram_base_addr;
|
||||
extern void __iomem *pmu_base_addr;
|
||||
void exynos_sysram_init(void);
|
||||
|
||||
enum {
|
||||
FW_DO_IDLE_SLEEP,
|
||||
FW_DO_IDLE_AFTR,
|
||||
};
|
||||
|
||||
void exynos_firmware_init(void);
|
||||
|
||||
extern u32 exynos_get_eint_wake_mask(void);
|
||||
|
@ -127,6 +135,7 @@ static inline void exynos_pm_init(void) {}
|
|||
#endif
|
||||
|
||||
extern void exynos_cpu_resume(void);
|
||||
extern void exynos_cpu_resume_ns(void);
|
||||
|
||||
extern struct smp_operations exynos_smp_ops;
|
||||
|
||||
|
@ -155,6 +164,10 @@ extern int exynos_cpu_power_state(int cpu);
|
|||
extern void exynos_cluster_power_down(int cluster);
|
||||
extern void exynos_cluster_power_up(int cluster);
|
||||
extern int exynos_cluster_power_state(int cluster);
|
||||
extern void exynos_cpu_save_register(void);
|
||||
extern void exynos_cpu_restore_register(void);
|
||||
extern void exynos_pm_central_suspend(void);
|
||||
extern int exynos_pm_central_resume(void);
|
||||
extern void exynos_enter_aftr(void);
|
||||
|
||||
extern void s5p_init_cpu(void __iomem *cpuid_addr);
|
||||
|
|
|
@ -318,7 +318,10 @@ static void __init exynos_dt_machine_init(void)
|
|||
exynos_sysram_init();
|
||||
|
||||
if (of_machine_is_compatible("samsung,exynos4210") ||
|
||||
of_machine_is_compatible("samsung,exynos5250"))
|
||||
of_machine_is_compatible("samsung,exynos4212") ||
|
||||
(of_machine_is_compatible("samsung,exynos4412") &&
|
||||
of_machine_is_compatible("samsung,trats2")) ||
|
||||
of_machine_is_compatible("samsung,exynos5250"))
|
||||
platform_device_register(&exynos_cpuidle);
|
||||
|
||||
platform_device_register_simple("exynos-cpufreq", -1, NULL, 0);
|
||||
|
|
|
@ -14,16 +14,44 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include <mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "smc.h"
|
||||
|
||||
static int exynos_do_idle(void)
|
||||
#define EXYNOS_SLEEP_MAGIC 0x00000bad
|
||||
#define EXYNOS_AFTR_MAGIC 0xfcba0d10
|
||||
#define EXYNOS_BOOT_ADDR 0x8
|
||||
#define EXYNOS_BOOT_FLAG 0xc
|
||||
|
||||
static void exynos_save_cp15(void)
|
||||
{
|
||||
exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
|
||||
/* Save Power control and Diagnostic registers */
|
||||
asm ("mrc p15, 0, %0, c15, c0, 0\n"
|
||||
"mrc p15, 0, %1, c15, c0, 1\n"
|
||||
: "=r" (cp15_save_power), "=r" (cp15_save_diag)
|
||||
: : "cc");
|
||||
}
|
||||
|
||||
static int exynos_do_idle(unsigned long mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case FW_DO_IDLE_AFTR:
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_save_cp15();
|
||||
__raw_writel(virt_to_phys(exynos_cpu_resume_ns),
|
||||
sysram_ns_base_addr + 0x24);
|
||||
__raw_writel(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
|
||||
exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
|
||||
break;
|
||||
case FW_DO_IDLE_SLEEP:
|
||||
exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -69,10 +97,43 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_cpu_suspend(unsigned long arg)
|
||||
{
|
||||
flush_cache_all();
|
||||
outer_flush_all();
|
||||
|
||||
exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
|
||||
|
||||
pr_info("Failed to suspend the system\n");
|
||||
writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int exynos_suspend(void)
|
||||
{
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_save_cp15();
|
||||
|
||||
writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
|
||||
writel(virt_to_phys(exynos_cpu_resume_ns),
|
||||
sysram_ns_base_addr + EXYNOS_BOOT_ADDR);
|
||||
|
||||
return cpu_suspend(0, exynos_cpu_suspend);
|
||||
}
|
||||
|
||||
static int exynos_resume(void)
|
||||
{
|
||||
writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct firmware_ops exynos_firmware_ops = {
|
||||
.do_idle = exynos_do_idle,
|
||||
.do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
|
||||
.set_cpu_boot_addr = exynos_set_cpu_boot_addr,
|
||||
.cpu_boot = exynos_cpu_boot,
|
||||
.suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
|
||||
.resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
|
||||
};
|
||||
|
||||
void __init exynos_firmware_init(void)
|
||||
|
|
|
@ -120,6 +120,26 @@ static inline void __iomem *cpu_boot_reg(int cpu)
|
|||
return boot_reg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set wake up by local power mode and execute software reset for given core.
|
||||
*
|
||||
* Currently this is needed only when booting secondary CPU on Exynos3250.
|
||||
*/
|
||||
static void exynos_core_restart(u32 core_id)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (!of_machine_is_compatible("samsung,exynos3250"))
|
||||
return;
|
||||
|
||||
val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
|
||||
val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
|
||||
pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));
|
||||
|
||||
pr_info("CPU%u: Software reset\n", core_id);
|
||||
pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write pen_release in a way that is guaranteed to be visible to all
|
||||
* observers, irrespective of whether they're taking part in coherency
|
||||
|
@ -196,6 +216,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
exynos_core_restart(core_id);
|
||||
|
||||
/*
|
||||
* Send the secondary CPU a soft interrupt, thereby causing
|
||||
* the boot monitor to read the system wide flags register,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
|
||||
* Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* EXYNOS - Power Management support
|
||||
|
@ -15,109 +15,44 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/smp_scu.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include <plat/pm-common.h>
|
||||
#include <plat/regs-srom.h>
|
||||
|
||||
#include <mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "regs-pmu.h"
|
||||
#include "regs-sys.h"
|
||||
|
||||
/**
|
||||
* struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
|
||||
* @hwirq: Hardware IRQ signal of the GIC
|
||||
* @mask: Mask in PMU wake-up mask register
|
||||
*/
|
||||
struct exynos_wkup_irq {
|
||||
unsigned int hwirq;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
/*
|
||||
* GIC wake-up support
|
||||
*/
|
||||
|
||||
static u32 exynos_irqwake_intmask = 0xffffffff;
|
||||
|
||||
static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
|
||||
{ 76, BIT(1) }, /* RTC alarm */
|
||||
{ 77, BIT(2) }, /* RTC tick */
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
|
||||
{ 75, BIT(1) }, /* RTC alarm */
|
||||
{ 76, BIT(2) }, /* RTC tick */
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
|
||||
static inline void __iomem *exynos_boot_vector_addr(void)
|
||||
{
|
||||
const struct exynos_wkup_irq *wkup_irq;
|
||||
|
||||
if (soc_is_exynos5250())
|
||||
wkup_irq = exynos5250_wkup_irq;
|
||||
else
|
||||
wkup_irq = exynos4_wkup_irq;
|
||||
|
||||
while (wkup_irq->mask) {
|
||||
if (wkup_irq->hwirq == data->hwirq) {
|
||||
if (!state)
|
||||
exynos_irqwake_intmask |= wkup_irq->mask;
|
||||
else
|
||||
exynos_irqwake_intmask &= ~wkup_irq->mask;
|
||||
return 0;
|
||||
}
|
||||
++wkup_irq;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
if (samsung_rev() == EXYNOS4210_REV_1_1)
|
||||
return pmu_base_addr + S5P_INFORM7;
|
||||
else if (samsung_rev() == EXYNOS4210_REV_1_0)
|
||||
return sysram_base_addr + 0x24;
|
||||
return pmu_base_addr + S5P_INFORM0;
|
||||
}
|
||||
|
||||
#define EXYNOS_BOOT_VECTOR_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \
|
||||
pmu_base_addr + S5P_INFORM7 : \
|
||||
(samsung_rev() == EXYNOS4210_REV_1_0 ? \
|
||||
(sysram_base_addr + 0x24) : \
|
||||
pmu_base_addr + S5P_INFORM0))
|
||||
#define EXYNOS_BOOT_VECTOR_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \
|
||||
pmu_base_addr + S5P_INFORM6 : \
|
||||
(samsung_rev() == EXYNOS4210_REV_1_0 ? \
|
||||
(sysram_base_addr + 0x20) : \
|
||||
pmu_base_addr + S5P_INFORM1))
|
||||
static inline void __iomem *exynos_boot_vector_flag(void)
|
||||
{
|
||||
if (samsung_rev() == EXYNOS4210_REV_1_1)
|
||||
return pmu_base_addr + S5P_INFORM6;
|
||||
else if (samsung_rev() == EXYNOS4210_REV_1_0)
|
||||
return sysram_base_addr + 0x20;
|
||||
return pmu_base_addr + S5P_INFORM1;
|
||||
}
|
||||
|
||||
#define S5P_CHECK_AFTR 0xFCBA0D10
|
||||
#define S5P_CHECK_SLEEP 0x00000BAD
|
||||
|
||||
/* For Cortex-A9 Diagnostic and Power control register */
|
||||
static unsigned int save_arm_register[2];
|
||||
|
||||
static void exynos_cpu_save_register(void)
|
||||
void exynos_cpu_save_register(void)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
|
@ -134,7 +69,7 @@ static void exynos_cpu_save_register(void)
|
|||
save_arm_register[1] = tmp;
|
||||
}
|
||||
|
||||
static void exynos_cpu_restore_register(void)
|
||||
void exynos_cpu_restore_register(void)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
|
@ -153,7 +88,7 @@ static void exynos_cpu_restore_register(void)
|
|||
: "cc");
|
||||
}
|
||||
|
||||
static void exynos_pm_central_suspend(void)
|
||||
void exynos_pm_central_suspend(void)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
|
@ -161,9 +96,13 @@ static void exynos_pm_central_suspend(void)
|
|||
tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
|
||||
tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
|
||||
pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
|
||||
|
||||
/* Setting SEQ_OPTION register */
|
||||
pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
|
||||
S5P_CENTRAL_SEQ_OPTION);
|
||||
}
|
||||
|
||||
static int exynos_pm_central_resume(void)
|
||||
int exynos_pm_central_resume(void)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
|
@ -194,17 +133,26 @@ static void exynos_set_wakeupmask(long mask)
|
|||
|
||||
static void exynos_cpu_set_boot_vector(long flags)
|
||||
{
|
||||
__raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR);
|
||||
__raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG);
|
||||
__raw_writel(virt_to_phys(exynos_cpu_resume),
|
||||
exynos_boot_vector_addr());
|
||||
__raw_writel(flags, exynos_boot_vector_flag());
|
||||
}
|
||||
|
||||
static int exynos_aftr_finisher(unsigned long flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
exynos_set_wakeupmask(0x0000ff3e);
|
||||
exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
|
||||
/* Set value of power down register for aftr mode */
|
||||
exynos_sys_powerdown_conf(SYS_AFTR);
|
||||
cpu_do_idle();
|
||||
|
||||
ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR);
|
||||
if (ret == -ENOSYS) {
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_cpu_save_register();
|
||||
exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
|
||||
cpu_do_idle();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -214,196 +162,16 @@ void exynos_enter_aftr(void)
|
|||
cpu_pm_enter();
|
||||
|
||||
exynos_pm_central_suspend();
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_cpu_save_register();
|
||||
|
||||
cpu_suspend(0, exynos_aftr_finisher);
|
||||
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
|
||||
scu_enable(S5P_VA_SCU);
|
||||
exynos_cpu_restore_register();
|
||||
if (call_firmware_op(resume) == -ENOSYS)
|
||||
exynos_cpu_restore_register();
|
||||
}
|
||||
|
||||
exynos_pm_central_resume();
|
||||
|
||||
cpu_pm_exit();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Set wake-up mask registers */
|
||||
pmu_raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
|
||||
pmu_raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
|
||||
|
||||
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 = pmu_raw_readl(EXYNOS5_JPEG_MEM_OPTION);
|
||||
tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
|
||||
pmu_raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
|
||||
}
|
||||
|
||||
/* Set value of power down register for sleep mode */
|
||||
|
||||
exynos_sys_powerdown_conf(SYS_SLEEP);
|
||||
pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
|
||||
|
||||
/* ensure at least INFORM0 has the resume address */
|
||||
|
||||
pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
|
||||
}
|
||||
|
||||
static int exynos_pm_suspend(void)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
exynos_pm_central_suspend();
|
||||
|
||||
/* Setting SEQ_OPTION register */
|
||||
|
||||
tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
|
||||
pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
|
||||
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_cpu_save_register();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_pm_resume(void)
|
||||
{
|
||||
if (exynos_pm_central_resume())
|
||||
goto early_wakeup;
|
||||
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_cpu_restore_register();
|
||||
|
||||
/* For release retention */
|
||||
|
||||
pmu_raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
|
||||
pmu_raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION);
|
||||
pmu_raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION);
|
||||
pmu_raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION);
|
||||
pmu_raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION);
|
||||
pmu_raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
|
||||
pmu_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 (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
scu_enable(S5P_VA_SCU);
|
||||
|
||||
early_wakeup:
|
||||
|
||||
/* Clear SLEEP mode set in INFORM1 */
|
||||
pmu_raw_writel(0x0, S5P_INFORM1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static struct syscore_ops exynos_pm_syscore_ops = {
|
||||
.suspend = exynos_pm_suspend,
|
||||
.resume = exynos_pm_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
* Suspend Ops
|
||||
*/
|
||||
|
||||
static int exynos_suspend_enter(suspend_state_t state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
s3c_pm_debug_init();
|
||||
|
||||
S3C_PMDBG("%s: suspending the system...\n", __func__);
|
||||
|
||||
S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
|
||||
exynos_irqwake_intmask, exynos_get_eint_wake_mask());
|
||||
|
||||
if (exynos_irqwake_intmask == -1U
|
||||
&& exynos_get_eint_wake_mask() == -1U) {
|
||||
pr_err("%s: No wake-up sources!\n", __func__);
|
||||
pr_err("%s: Aborting sleep\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s3c_pm_save_uarts();
|
||||
exynos_pm_prepare();
|
||||
flush_cache_all();
|
||||
s3c_pm_check_store();
|
||||
|
||||
ret = cpu_suspend(0, exynos_cpu_suspend);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
s3c_pm_restore_uarts();
|
||||
|
||||
S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
|
||||
pmu_raw_readl(S5P_WAKEUP_STAT));
|
||||
|
||||
s3c_pm_check_restore();
|
||||
|
||||
S3C_PMDBG("%s: resuming the system...\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_suspend_prepare(void)
|
||||
{
|
||||
s3c_pm_check_prepare();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_suspend_finish(void)
|
||||
{
|
||||
s3c_pm_check_cleanup();
|
||||
}
|
||||
|
||||
static const struct platform_suspend_ops exynos_suspend_ops = {
|
||||
.enter = exynos_suspend_enter,
|
||||
.prepare = exynos_suspend_prepare,
|
||||
.finish = exynos_suspend_finish,
|
||||
.valid = suspend_valid_only_mem,
|
||||
};
|
||||
|
||||
void __init exynos_pm_init(void)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
/* Platform-specific GIC callback */
|
||||
gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
|
||||
|
||||
/* All wakeup disable */
|
||||
tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
|
||||
tmp |= ((0xFF << 8) | (0x1F << 1));
|
||||
pmu_raw_writel(tmp, S5P_WAKEUP_MASK);
|
||||
|
||||
register_syscore_ops(&exynos_pm_syscore_ops);
|
||||
suspend_set_ops(&exynos_suspend_ops);
|
||||
}
|
||||
|
|
|
@ -264,6 +264,7 @@ static const struct exynos_pmu_conf exynos5250_pmu_config[] = {
|
|||
{ EXYNOS5_INTRAM_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
|
||||
{ EXYNOS5_INTROM_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
|
||||
{ EXYNOS5_JPEG_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
|
||||
{ EXYNOS5_JPEG_MEM_OPTION, { 0x10, 0x10, 0x0} },
|
||||
{ EXYNOS5_HSI_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
|
||||
{ EXYNOS5_MCUIOP_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
|
||||
{ EXYNOS5_SATA_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#define S5P_USE_STANDBY_WFI0 (1 << 16)
|
||||
#define S5P_USE_STANDBY_WFE0 (1 << 24)
|
||||
|
||||
#define EXYNOS_CORE_PO_RESET(n) ((1 << 4) << n)
|
||||
#define EXYNOS_WAKEUP_FROM_LOWPWR (1 << 28)
|
||||
#define EXYNOS_SWRESET 0x0400
|
||||
#define EXYNOS5440_SWRESET 0x00C4
|
||||
|
||||
|
@ -124,6 +126,7 @@
|
|||
#define S5P_PAD_RET_EBIB_OPTION 0x31A8
|
||||
|
||||
#define S5P_CORE_LOCAL_PWR_EN 0x3
|
||||
#define S5P_CORE_WAKEUP_FROM_LOCAL_CFG (0x3 << 8)
|
||||
|
||||
/* Only for EXYNOS4210 */
|
||||
#define S5P_CMU_CLKSTOP_LCD1_LOWPWR 0x1154
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include "smc.h"
|
||||
|
||||
#define CPU_MASK 0xff0ffff0
|
||||
#define CPU_CORTEX_A9 0x410fc090
|
||||
|
@ -55,3 +56,30 @@ ENTRY(exynos_cpu_resume)
|
|||
#endif
|
||||
b cpu_resume
|
||||
ENDPROC(exynos_cpu_resume)
|
||||
|
||||
.align
|
||||
|
||||
ENTRY(exynos_cpu_resume_ns)
|
||||
mrc p15, 0, r0, c0, c0, 0
|
||||
ldr r1, =CPU_MASK
|
||||
and r0, r0, r1
|
||||
ldr r1, =CPU_CORTEX_A9
|
||||
cmp r0, r1
|
||||
bne skip_cp15
|
||||
|
||||
adr r0, cp15_save_power
|
||||
ldr r1, [r0]
|
||||
adr r0, cp15_save_diag
|
||||
ldr r2, [r0]
|
||||
mov r0, #SMC_CMD_C15RESUME
|
||||
dsb
|
||||
smc #0
|
||||
skip_cp15:
|
||||
b cpu_resume
|
||||
ENDPROC(exynos_cpu_resume_ns)
|
||||
.globl cp15_save_diag
|
||||
cp15_save_diag:
|
||||
.long 0 @ cp15 diagnostic
|
||||
.globl cp15_save_power
|
||||
cp15_save_power:
|
||||
.long 0 @ cp15 power control
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#define SMC_CMD_L2X0INVALL (-24)
|
||||
#define SMC_CMD_L2X0DEBUG (-25)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3);
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif
|
||||
|
|
356
arch/arm/mach-exynos/suspend.c
Normal file
356
arch/arm/mach-exynos/suspend.c
Normal file
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* EXYNOS - Suspend 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/cpu_pm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/smp_scu.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include <plat/pm-common.h>
|
||||
#include <plat/regs-srom.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "regs-pmu.h"
|
||||
#include "regs-sys.h"
|
||||
|
||||
#define S5P_CHECK_SLEEP 0x00000BAD
|
||||
|
||||
#define REG_TABLE_END (-1U)
|
||||
|
||||
/**
|
||||
* struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
|
||||
* @hwirq: Hardware IRQ signal of the GIC
|
||||
* @mask: Mask in PMU wake-up mask register
|
||||
*/
|
||||
struct exynos_wkup_irq {
|
||||
unsigned int hwirq;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
struct exynos_pm_data {
|
||||
const struct exynos_wkup_irq *wkup_irq;
|
||||
struct sleep_save *extra_save;
|
||||
int num_extra_save;
|
||||
unsigned int wake_disable_mask;
|
||||
unsigned int *release_ret_regs;
|
||||
|
||||
void (*pm_prepare)(void);
|
||||
void (*pm_resume)(void);
|
||||
int (*pm_suspend)(void);
|
||||
int (*cpu_suspend)(unsigned long);
|
||||
};
|
||||
|
||||
struct exynos_pm_data *pm_data;
|
||||
|
||||
/*
|
||||
* GIC wake-up support
|
||||
*/
|
||||
|
||||
static u32 exynos_irqwake_intmask = 0xffffffff;
|
||||
|
||||
static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
|
||||
{ 76, BIT(1) }, /* RTC alarm */
|
||||
{ 77, BIT(2) }, /* RTC tick */
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
|
||||
{ 75, BIT(1) }, /* RTC alarm */
|
||||
{ 76, BIT(2) }, /* RTC tick */
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
unsigned int exynos_release_ret_regs[] = {
|
||||
S5P_PAD_RET_MAUDIO_OPTION,
|
||||
S5P_PAD_RET_GPIO_OPTION,
|
||||
S5P_PAD_RET_UART_OPTION,
|
||||
S5P_PAD_RET_MMCA_OPTION,
|
||||
S5P_PAD_RET_MMCB_OPTION,
|
||||
S5P_PAD_RET_EBIA_OPTION,
|
||||
S5P_PAD_RET_EBIB_OPTION,
|
||||
REG_TABLE_END,
|
||||
};
|
||||
|
||||
static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
|
||||
{
|
||||
const struct exynos_wkup_irq *wkup_irq;
|
||||
|
||||
if (!pm_data->wkup_irq)
|
||||
return -ENOENT;
|
||||
wkup_irq = pm_data->wkup_irq;
|
||||
|
||||
while (wkup_irq->mask) {
|
||||
if (wkup_irq->hwirq == data->hwirq) {
|
||||
if (!state)
|
||||
exynos_irqwake_intmask |= wkup_irq->mask;
|
||||
else
|
||||
exynos_irqwake_intmask &= ~wkup_irq->mask;
|
||||
return 0;
|
||||
}
|
||||
++wkup_irq;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int exynos_cpu_do_idle(void)
|
||||
{
|
||||
/* issue the standby signal into the pm unit. */
|
||||
cpu_do_idle();
|
||||
|
||||
pr_info("Failed to suspend the system\n");
|
||||
return 1; /* Aborting suspend */
|
||||
}
|
||||
|
||||
static int exynos_cpu_suspend(unsigned long arg)
|
||||
{
|
||||
flush_cache_all();
|
||||
outer_flush_all();
|
||||
return exynos_cpu_do_idle();
|
||||
}
|
||||
|
||||
static void exynos_pm_set_wakeup_mask(void)
|
||||
{
|
||||
/* Set wake-up mask registers */
|
||||
pmu_raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
|
||||
pmu_raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
|
||||
}
|
||||
|
||||
static void exynos_pm_enter_sleep_mode(void)
|
||||
{
|
||||
/* Set value of power down register for sleep mode */
|
||||
exynos_sys_powerdown_conf(SYS_SLEEP);
|
||||
pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
|
||||
|
||||
/* ensure at least INFORM0 has the resume address */
|
||||
pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
|
||||
}
|
||||
|
||||
static void exynos_pm_prepare(void)
|
||||
{
|
||||
/* Set wake-up mask registers */
|
||||
exynos_pm_set_wakeup_mask();
|
||||
|
||||
s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
|
||||
|
||||
if (pm_data->extra_save)
|
||||
s3c_pm_do_save(pm_data->extra_save,
|
||||
pm_data->num_extra_save);
|
||||
|
||||
exynos_pm_enter_sleep_mode();
|
||||
}
|
||||
|
||||
static int exynos_pm_suspend(void)
|
||||
{
|
||||
exynos_pm_central_suspend();
|
||||
|
||||
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_cpu_save_register();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_pm_release_retention(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; (pm_data->release_ret_regs[i] != REG_TABLE_END); i++)
|
||||
pmu_raw_writel(EXYNOS_WAKEUP_FROM_LOWPWR,
|
||||
pm_data->release_ret_regs[i]);
|
||||
}
|
||||
|
||||
static void exynos_pm_resume(void)
|
||||
{
|
||||
u32 cpuid = read_cpuid_part();
|
||||
|
||||
if (exynos_pm_central_resume())
|
||||
goto early_wakeup;
|
||||
|
||||
/* For release retention */
|
||||
exynos_pm_release_retention();
|
||||
|
||||
if (pm_data->extra_save)
|
||||
s3c_pm_do_restore_core(pm_data->extra_save,
|
||||
pm_data->num_extra_save);
|
||||
|
||||
s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
|
||||
|
||||
if (cpuid == ARM_CPU_PART_CORTEX_A9)
|
||||
scu_enable(S5P_VA_SCU);
|
||||
|
||||
if (call_firmware_op(resume) == -ENOSYS
|
||||
&& cpuid == ARM_CPU_PART_CORTEX_A9)
|
||||
exynos_cpu_restore_register();
|
||||
|
||||
early_wakeup:
|
||||
|
||||
/* Clear SLEEP mode set in INFORM1 */
|
||||
pmu_raw_writel(0x0, S5P_INFORM1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend Ops
|
||||
*/
|
||||
|
||||
static int exynos_suspend_enter(suspend_state_t state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
s3c_pm_debug_init();
|
||||
|
||||
S3C_PMDBG("%s: suspending the system...\n", __func__);
|
||||
|
||||
S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
|
||||
exynos_irqwake_intmask, exynos_get_eint_wake_mask());
|
||||
|
||||
if (exynos_irqwake_intmask == -1U
|
||||
&& exynos_get_eint_wake_mask() == -1U) {
|
||||
pr_err("%s: No wake-up sources!\n", __func__);
|
||||
pr_err("%s: Aborting sleep\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s3c_pm_save_uarts();
|
||||
if (pm_data->pm_prepare)
|
||||
pm_data->pm_prepare();
|
||||
flush_cache_all();
|
||||
s3c_pm_check_store();
|
||||
|
||||
ret = call_firmware_op(suspend);
|
||||
if (ret == -ENOSYS)
|
||||
ret = cpu_suspend(0, pm_data->cpu_suspend);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
s3c_pm_restore_uarts();
|
||||
|
||||
S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
|
||||
pmu_raw_readl(S5P_WAKEUP_STAT));
|
||||
|
||||
s3c_pm_check_restore();
|
||||
|
||||
S3C_PMDBG("%s: resuming the system...\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_suspend_prepare(void)
|
||||
{
|
||||
s3c_pm_check_prepare();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_suspend_finish(void)
|
||||
{
|
||||
s3c_pm_check_cleanup();
|
||||
}
|
||||
|
||||
static const struct platform_suspend_ops exynos_suspend_ops = {
|
||||
.enter = exynos_suspend_enter,
|
||||
.prepare = exynos_suspend_prepare,
|
||||
.finish = exynos_suspend_finish,
|
||||
.valid = suspend_valid_only_mem,
|
||||
};
|
||||
|
||||
static const struct exynos_pm_data exynos4_pm_data = {
|
||||
.wkup_irq = exynos4_wkup_irq,
|
||||
.wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
|
||||
.release_ret_regs = exynos_release_ret_regs,
|
||||
.pm_suspend = exynos_pm_suspend,
|
||||
.pm_resume = exynos_pm_resume,
|
||||
.pm_prepare = exynos_pm_prepare,
|
||||
.cpu_suspend = exynos_cpu_suspend,
|
||||
};
|
||||
|
||||
static const struct exynos_pm_data exynos5250_pm_data = {
|
||||
.wkup_irq = exynos5250_wkup_irq,
|
||||
.wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
|
||||
.release_ret_regs = exynos_release_ret_regs,
|
||||
.extra_save = exynos5_sys_save,
|
||||
.num_extra_save = ARRAY_SIZE(exynos5_sys_save),
|
||||
.pm_suspend = exynos_pm_suspend,
|
||||
.pm_resume = exynos_pm_resume,
|
||||
.pm_prepare = exynos_pm_prepare,
|
||||
.cpu_suspend = exynos_cpu_suspend,
|
||||
};
|
||||
|
||||
static struct of_device_id exynos_pmu_of_device_ids[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos4210-pmu",
|
||||
.data = &exynos4_pm_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos4212-pmu",
|
||||
.data = &exynos4_pm_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos4412-pmu",
|
||||
.data = &exynos4_pm_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5250-pmu",
|
||||
.data = &exynos5250_pm_data,
|
||||
},
|
||||
{ /*sentinel*/ },
|
||||
};
|
||||
|
||||
static struct syscore_ops exynos_pm_syscore_ops;
|
||||
|
||||
void __init exynos_pm_init(void)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
u32 tmp;
|
||||
|
||||
of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
|
||||
if (!match) {
|
||||
pr_err("Failed to find PMU node\n");
|
||||
return;
|
||||
}
|
||||
pm_data = (struct exynos_pm_data *) match->data;
|
||||
|
||||
/* Platform-specific GIC callback */
|
||||
gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
|
||||
|
||||
/* All wakeup disable */
|
||||
tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
|
||||
tmp |= pm_data->wake_disable_mask;
|
||||
pmu_raw_writel(tmp, S5P_WAKEUP_MASK);
|
||||
|
||||
exynos_pm_syscore_ops.suspend = pm_data->pm_suspend;
|
||||
exynos_pm_syscore_ops.resume = pm_data->pm_resume;
|
||||
|
||||
register_syscore_ops(&exynos_pm_syscore_ops);
|
||||
suspend_set_ops(&exynos_suspend_ops);
|
||||
}
|
|
@ -49,7 +49,7 @@ static int tegra114_idle_power_down(struct cpuidle_device *dev,
|
|||
call_firmware_op(prepare_idle);
|
||||
|
||||
/* Do suspend by ourselves if the firmware does not implement it */
|
||||
if (call_firmware_op(do_idle) == -ENOSYS)
|
||||
if (call_firmware_op(do_idle, 0) == -ENOSYS)
|
||||
cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o
|
|||
# PM support
|
||||
|
||||
obj-$(CONFIG_PM_SLEEP) += pm-common.o
|
||||
obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm-common.o
|
||||
obj-$(CONFIG_SAMSUNG_PM) += pm.o
|
||||
obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o
|
||||
obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o
|
||||
|
|
Loading…
Add table
Reference in a new issue