Commit 5c5ee5e7 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'samsung-exynos-v3.19' of...

Merge tag 'samsung-exynos-v3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/soc

Pull "Samsung exynos updates in arch/arm/mach-exynos/ for v3.19" from Kukjin Kim:

- add SOC_EXYNOS4415 config to be used in audio driver
- add support platform driver for exynos PMU
- move PMU specific definitions from common.h to exynos-pmu.h
- for exynos5420, add support PMU and Suspend-to-RAM
  use MCPM call backs and call regulator core suspend prepare
  and finish functions

NOTE:
including v3.19-next/non-critical-fixes, v3.19-next/cleanup-samsung
and v3.19-next/pm-samsung-2 branches

* tag 'samsung-exynos-v3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung:
  ARM: EXYNOS: Call regulator core suspend prepare and finish functions
  ARM: EXYNOS: Use MCPM call-backs to support S2R on exynos5420
  ARM: EXYNOS: Add Suspend-to-RAM support for exynos5420
  ARM: EXYNOS: Add PMU support for exynos5420
  ARM: EXYNOS: Move PMU specific definitions from common.h
  ARM: EXYNOS: Add platform driver support for Exynos PMU
  ARM: EXYNOS: Add support for exynos4415 SoC
  ARM: EXYNOS: fix typo in static struct name "exynos5_list_diable_wfi_wfe"
  ARM: EXYNOS: Fix CPU idle clock down after CPU off
  ARM: EXYNOS: Remove unneeded __ref annotation for cpu_die function
  ARM: EXYNOS: Move code from hotplug.c to platsmp.c
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 6febbf47 c645a598
...@@ -24,6 +24,7 @@ menuconfig ARCH_EXYNOS ...@@ -24,6 +24,7 @@ menuconfig ARCH_EXYNOS
select PM_GENERIC_DOMAINS if PM_RUNTIME select PM_GENERIC_DOMAINS if PM_RUNTIME
select S5P_DEV_MFC select S5P_DEV_MFC
select SRAM select SRAM
select MFD_SYSCON
help help
Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5) Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5)
...@@ -75,6 +76,11 @@ config SOC_EXYNOS4412 ...@@ -75,6 +76,11 @@ config SOC_EXYNOS4412
default y default y
depends on ARCH_EXYNOS4 depends on ARCH_EXYNOS4
config SOC_EXYNOS4415
bool "SAMSUNG EXYNOS4415"
default y
depends on ARCH_EXYNOS4
config SOC_EXYNOS5250 config SOC_EXYNOS5250
bool "SAMSUNG EXYNOS5250" bool "SAMSUNG EXYNOS5250"
default y default y
......
...@@ -17,9 +17,6 @@ obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o ...@@ -17,9 +17,6 @@ obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
CFLAGS_hotplug.o += -march=armv7-a
plus_sec := $(call as-instr,.arch_extension sec,+sec) plus_sec := $(call as-instr,.arch_extension sec,+sec)
AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec)
AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec) AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec)
......
...@@ -139,25 +139,6 @@ extern void exynos_cpu_resume_ns(void); ...@@ -139,25 +139,6 @@ extern void exynos_cpu_resume_ns(void);
extern struct smp_operations exynos_smp_ops; extern struct smp_operations exynos_smp_ops;
extern void exynos_cpu_die(unsigned int cpu);
/* PMU(Power Management Unit) support */
#define PMU_TABLE_END (-1U)
enum sys_powerdown {
SYS_AFTR,
SYS_LPA,
SYS_SLEEP,
NUM_SYS_POWERDOWN,
};
struct exynos_pmu_conf {
unsigned int offset;
unsigned int val[NUM_SYS_POWERDOWN];
};
extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
extern void exynos_cpu_power_down(int cpu); extern void exynos_cpu_power_down(int cpu);
extern void exynos_cpu_power_up(int cpu); extern void exynos_cpu_power_up(int cpu);
extern int exynos_cpu_power_state(int cpu); extern int exynos_cpu_power_state(int cpu);
......
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Header for EXYNOS PMU Driver 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.
*/
#ifndef __EXYNOS_PMU_H
#define __EXYNOS_PMU_H
enum sys_powerdown {
SYS_AFTR,
SYS_LPA,
SYS_SLEEP,
NUM_SYS_POWERDOWN,
};
extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
#endif /* __EXYNOS_PMU_H */
...@@ -252,6 +252,7 @@ static const struct of_device_id exynos_dt_pmu_match[] = { ...@@ -252,6 +252,7 @@ static const struct of_device_id exynos_dt_pmu_match[] = {
{ .compatible = "samsung,exynos4210-pmu" }, { .compatible = "samsung,exynos4210-pmu" },
{ .compatible = "samsung,exynos4212-pmu" }, { .compatible = "samsung,exynos4212-pmu" },
{ .compatible = "samsung,exynos4412-pmu" }, { .compatible = "samsung,exynos4412-pmu" },
{ .compatible = "samsung,exynos4415-pmu" },
{ .compatible = "samsung,exynos5250-pmu" }, { .compatible = "samsung,exynos5250-pmu" },
{ .compatible = "samsung,exynos5260-pmu" }, { .compatible = "samsung,exynos5260-pmu" },
{ .compatible = "samsung,exynos5410-pmu" }, { .compatible = "samsung,exynos5410-pmu" },
...@@ -336,6 +337,7 @@ static char const *exynos_dt_compat[] __initconst = { ...@@ -336,6 +337,7 @@ static char const *exynos_dt_compat[] __initconst = {
"samsung,exynos4210", "samsung,exynos4210",
"samsung,exynos4212", "samsung,exynos4212",
"samsung,exynos4412", "samsung,exynos4412",
"samsung,exynos4415",
"samsung,exynos5", "samsung,exynos5",
"samsung,exynos5250", "samsung,exynos5250",
"samsung,exynos5260", "samsung,exynos5260",
......
/*
* Cloned from linux/arch/arm/mach-realview/hotplug.c
*
* Copyright (C) 2002 ARM Ltd.
* All Rights Reserved
*
* 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/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h>
#include "common.h"
#include "regs-pmu.h"
static inline void cpu_leave_lowpower(void)
{
unsigned int v;
asm volatile(
"mrc p15, 0, %0, c1, c0, 0\n"
" orr %0, %0, %1\n"
" mcr p15, 0, %0, c1, c0, 0\n"
" mrc p15, 0, %0, c1, c0, 1\n"
" orr %0, %0, %2\n"
" mcr p15, 0, %0, c1, c0, 1\n"
: "=&r" (v)
: "Ir" (CR_C), "Ir" (0x40)
: "cc");
}
static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
{
u32 mpidr = cpu_logical_map(cpu);
u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
for (;;) {
/* Turn the CPU off on next WFI instruction. */
exynos_cpu_power_down(core_id);
wfi();
if (pen_release == core_id) {
/*
* OK, proper wakeup, we're done
*/
break;
}
/*
* Getting here, means that we have come out of WFI without
* having been woken up - this shouldn't happen
*
* Just note it happening - when we're woken, we can report
* its occurrence.
*/
(*spurious)++;
}
}
/*
* platform-specific code to shutdown a CPU
*
* Called with IRQs disabled
*/
void __ref exynos_cpu_die(unsigned int cpu)
{
int spurious = 0;
v7_exit_coherency_flush(louis);
platform_do_lowpower(cpu, &spurious);
/*
* bring this CPU back into the world of cache
* coherency, and then restore interrupts
*/
cpu_leave_lowpower();
if (spurious)
pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/cp15.h> #include <asm/cp15.h>
...@@ -30,6 +31,8 @@ ...@@ -30,6 +31,8 @@
#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29) #define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29)
#define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30) #define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30)
static void __iomem *ns_sram_base_addr;
/* /*
* The common v7_exit_coherency_flush API could not be used because of the * The common v7_exit_coherency_flush API could not be used because of the
* Erratum 799270 workaround. This macro is the same as the common one (in * Erratum 799270 workaround. This macro is the same as the common one (in
...@@ -318,10 +321,26 @@ static const struct of_device_id exynos_dt_mcpm_match[] = { ...@@ -318,10 +321,26 @@ static const struct of_device_id exynos_dt_mcpm_match[] = {
{}, {},
}; };
static void exynos_mcpm_setup_entry_point(void)
{
/*
* U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr
* as part of secondary_cpu_start(). Let's redirect it to the
* mcpm_entry_point(). This is done during both secondary boot-up as
* well as system resume.
*/
__raw_writel(0xe59f0000, ns_sram_base_addr); /* ldr r0, [pc, #0] */
__raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx r0 */
__raw_writel(virt_to_phys(mcpm_entry_point), ns_sram_base_addr + 8);
}
static struct syscore_ops exynos_mcpm_syscore_ops = {
.resume = exynos_mcpm_setup_entry_point,
};
static int __init exynos_mcpm_init(void) static int __init exynos_mcpm_init(void)
{ {
struct device_node *node; struct device_node *node;
void __iomem *ns_sram_base_addr;
unsigned int value, i; unsigned int value, i;
int ret; int ret;
...@@ -387,16 +406,9 @@ static int __init exynos_mcpm_init(void) ...@@ -387,16 +406,9 @@ static int __init exynos_mcpm_init(void)
pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i)); pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i));
} }
/* exynos_mcpm_setup_entry_point();
* U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr
* as part of secondary_cpu_start(). Let's redirect it to the
* mcpm_entry_point().
*/
__raw_writel(0xe59f0000, ns_sram_base_addr); /* ldr r0, [pc, #0] */
__raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx r0 */
__raw_writel(virt_to_phys(mcpm_entry_point), ns_sram_base_addr + 8);
iounmap(ns_sram_base_addr); register_syscore_ops(&exynos_mcpm_syscore_ops);
return ret; return ret;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include <asm/smp_scu.h> #include <asm/smp_scu.h>
#include <asm/firmware.h> #include <asm/firmware.h>
...@@ -33,6 +34,88 @@ ...@@ -33,6 +34,88 @@
extern void exynos4_secondary_startup(void); extern void exynos4_secondary_startup(void);
/*
* Set or clear the USE_DELAYED_RESET_ASSERTION option, set on Exynos4 SoCs
* during hot-(un)plugging CPUx.
*
* The feature can be cleared safely during first boot of secondary CPU.
*
* Exynos4 SoCs require setting USE_DELAYED_RESET_ASSERTION during powering
* down a CPU so the CPU idle clock down feature could properly detect global
* idle state when CPUx is off.
*/
static void exynos_set_delayed_reset_assertion(u32 core_id, bool enable)
{
if (soc_is_exynos4()) {
unsigned int tmp;
tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id));
if (enable)
tmp |= S5P_USE_DELAYED_RESET_ASSERTION;
else
tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION);
pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id));
}
}
#ifdef CONFIG_HOTPLUG_CPU
static inline void cpu_leave_lowpower(u32 core_id)
{
unsigned int v;
asm volatile(
"mrc p15, 0, %0, c1, c0, 0\n"
" orr %0, %0, %1\n"
" mcr p15, 0, %0, c1, c0, 0\n"
" mrc p15, 0, %0, c1, c0, 1\n"
" orr %0, %0, %2\n"
" mcr p15, 0, %0, c1, c0, 1\n"
: "=&r" (v)
: "Ir" (CR_C), "Ir" (0x40)
: "cc");
exynos_set_delayed_reset_assertion(core_id, false);
}
static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
{
u32 mpidr = cpu_logical_map(cpu);
u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
for (;;) {
/* Turn the CPU off on next WFI instruction. */
exynos_cpu_power_down(core_id);
/*
* Exynos4 SoCs require setting
* USE_DELAYED_RESET_ASSERTION so the CPU idle
* clock down feature could properly detect
* global idle state when CPUx is off.
*/
exynos_set_delayed_reset_assertion(core_id, true);
wfi();
if (pen_release == core_id) {
/*
* OK, proper wakeup, we're done
*/
break;
}
/*
* Getting here, means that we have come out of WFI without
* having been woken up - this shouldn't happen
*
* Just note it happening - when we're woken, we can report
* its occurrence.
*/
(*spurious)++;
}
}
#endif /* CONFIG_HOTPLUG_CPU */
/** /**
* exynos_core_power_down : power down the specified cpu * exynos_core_power_down : power down the specified cpu
* @cpu : the cpu to power down * @cpu : the cpu to power down
...@@ -43,6 +126,18 @@ extern void exynos4_secondary_startup(void); ...@@ -43,6 +126,18 @@ extern void exynos4_secondary_startup(void);
*/ */
void exynos_cpu_power_down(int cpu) void exynos_cpu_power_down(int cpu)
{ {
if (cpu == 0 && (of_machine_is_compatible("samsung,exynos5420") ||
of_machine_is_compatible("samsung,exynos5800"))) {
/*
* Bypass power down for CPU0 during suspend. Check for
* the SYS_PWR_REG value to decide if we are suspending
* the system.
*/
int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
if (!(val & S5P_CORE_LOCAL_PWR_EN))
return;
}
pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu)); pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
} }
...@@ -260,6 +355,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) ...@@ -260,6 +355,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
udelay(10); udelay(10);
} }
/* No harm if this is called during first boot of secondary CPU */
exynos_set_delayed_reset_assertion(core_id, false);
/* /*
* now the secondary core is starting up let it run its * now the secondary core is starting up let it run its
* calibrations, then wait for it to finish * calibrations, then wait for it to finish
...@@ -341,6 +439,33 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) ...@@ -341,6 +439,33 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
} }
} }
#ifdef CONFIG_HOTPLUG_CPU
/*
* platform-specific code to shutdown a CPU
*
* Called with IRQs disabled
*/
static void exynos_cpu_die(unsigned int cpu)
{
int spurious = 0;
u32 mpidr = cpu_logical_map(cpu);
u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
v7_exit_coherency_flush(louis);
platform_do_lowpower(cpu, &spurious);
/*
* bring this CPU back into the world of cache
* coherency, and then restore interrupts
*/
cpu_leave_lowpower(core_id);
if (spurious)
pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
}
#endif /* CONFIG_HOTPLUG_CPU */
struct smp_operations exynos_smp_ops __initdata = { struct smp_operations exynos_smp_ops __initdata = {
.smp_init_cpus = exynos_smp_init_cpus, .smp_init_cpus = exynos_smp_init_cpus,
.smp_prepare_cpus = exynos_smp_prepare_cpus, .smp_prepare_cpus = exynos_smp_prepare_cpus,
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <plat/pm-common.h> #include <plat/pm-common.h>
#include "common.h" #include "common.h"
#include "exynos-pmu.h"
#include "regs-pmu.h" #include "regs-pmu.h"
#include "regs-sys.h" #include "regs-sys.h"
......
This diff is collapsed.
This diff is collapsed.
...@@ -20,10 +20,12 @@ ...@@ -20,10 +20,12 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/irqchip/arm-gic.h> #include <linux/irqchip/arm-gic.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/regulator/machine.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h> #include <asm/hardware/cache-l2x0.h>
#include <asm/firmware.h> #include <asm/firmware.h>
#include <asm/mcpm.h>
#include <asm/smp_scu.h> #include <asm/smp_scu.h>
#include <asm/suspend.h> #include <asm/suspend.h>
...@@ -33,11 +35,14 @@ ...@@ -33,11 +35,14 @@
#include "common.h" #include "common.h"
#include "regs-pmu.h" #include "regs-pmu.h"
#include "regs-sys.h" #include "regs-sys.h"
#include "exynos-pmu.h"
#define S5P_CHECK_SLEEP 0x00000BAD #define S5P_CHECK_SLEEP 0x00000BAD
#define REG_TABLE_END (-1U) #define REG_TABLE_END (-1U)
#define EXYNOS5420_CPU_STATE 0x28
/** /**
* struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
* @hwirq: Hardware IRQ signal of the GIC * @hwirq: Hardware IRQ signal of the GIC
...@@ -69,6 +74,7 @@ struct exynos_pm_data { ...@@ -69,6 +74,7 @@ struct exynos_pm_data {
unsigned int *release_ret_regs; unsigned int *release_ret_regs;
void (*pm_prepare)(void); void (*pm_prepare)(void);
void (*pm_resume_prepare)(void);
void (*pm_resume)(void); void (*pm_resume)(void);
int (*pm_suspend)(void); int (*pm_suspend)(void);
int (*cpu_suspend)(unsigned long); int (*cpu_suspend)(unsigned long);
...@@ -76,6 +82,9 @@ struct exynos_pm_data { ...@@ -76,6 +82,9 @@ struct exynos_pm_data {
struct exynos_pm_data *pm_data; struct exynos_pm_data *pm_data;
static int exynos5420_cpu_state;
static unsigned int exynos_pmu_spare3;
/* /*
* GIC wake-up support * GIC wake-up support
*/ */
...@@ -105,6 +114,23 @@ unsigned int exynos_release_ret_regs[] = { ...@@ -105,6 +114,23 @@ unsigned int exynos_release_ret_regs[] = {
REG_TABLE_END, REG_TABLE_END,
}; };
unsigned int exynos5420_release_ret_regs[] = {
EXYNOS_PAD_RET_DRAM_OPTION,
EXYNOS_PAD_RET_MAUDIO_OPTION,
EXYNOS_PAD_RET_JTAG_OPTION,
EXYNOS5420_PAD_RET_GPIO_OPTION,
EXYNOS5420_PAD_RET_UART_OPTION,
EXYNOS5420_PAD_RET_MMCA_OPTION,
EXYNOS5420_PAD_RET_MMCB_OPTION,
EXYNOS5420_PAD_RET_MMCC_OPTION,
EXYNOS5420_PAD_RET_HSI_OPTION,
EXYNOS_PAD_RET_EBIA_OPTION,
EXYNOS_PAD_RET_EBIB_OPTION,
EXYNOS5420_PAD_RET_SPI_OPTION,
EXYNOS5420_PAD_RET_DRAM_COREBLK_OPTION,
REG_TABLE_END,
};
static int exynos_irq_set_wake(struct irq_data *data, unsigned int state) static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
{ {
const struct exynos_wkup_irq *wkup_irq; const struct exynos_wkup_irq *wkup_irq;
...@@ -135,14 +161,44 @@ static int exynos_cpu_do_idle(void) ...@@ -135,14 +161,44 @@ static int exynos_cpu_do_idle(void)
pr_info("Failed to suspend the system\n"); pr_info("Failed to suspend the system\n");
return 1; /* Aborting suspend */ return 1; /* Aborting suspend */
} }
static void exynos_flush_cache_all(void)
static int exynos_cpu_suspend(unsigned long arg)
{ {
flush_cache_all(); flush_cache_all();
outer_flush_all(); outer_flush_all();
}
static int exynos_cpu_suspend(unsigned long arg)
{
exynos_flush_cache_all();
return exynos_cpu_do_idle(); return exynos_cpu_do_idle();
} }
static int exynos5420_cpu_suspend(unsigned long arg)
{
/* MCPM works with HW CPU identifiers */
unsigned int mpidr = read_cpuid_mpidr();
unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
__raw_writel(0x0, sysram_base_addr + EXYNOS5420_CPU_STATE);
if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) {
mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume);
/*
* Residency value passed to mcpm_cpu_suspend back-end
* has to be given clear semantics. Set to 0 as a
* temporary value.
*/
mcpm_cpu_suspend(0);
}
pr_info("Failed to suspend the system\n");
/* return value != 0 means failure */
return 1;
}
static void exynos_pm_set_wakeup_mask(void) static void exynos_pm_set_wakeup_mask(void)
{ {
/* Set wake-up mask registers */ /* Set wake-up mask registers */
...@@ -155,9 +211,6 @@ static void exynos_pm_enter_sleep_mode(void) ...@@ -155,9 +211,6 @@ static void exynos_pm_enter_sleep_mode(void)
/* Set value of power down register for sleep mode */ /* Set value of power down register for sleep mode */
exynos_sys_powerdown_conf(SYS_SLEEP); exynos_sys_powerdown_conf(SYS_SLEEP);
pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1); 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) static void exynos_pm_prepare(void)
...@@ -172,8 +225,59 @@ static void exynos_pm_prepare(void) ...@@ -172,8 +225,59 @@ static void exynos_pm_prepare(void)
pm_data->num_extra_save); pm_data->num_extra_save);
exynos_pm_enter_sleep_mode(); exynos_pm_enter_sleep_mode();
/* ensure at least INFORM0 has the resume address */
pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
}
static void exynos5420_pm_prepare(void)
{
unsigned int tmp;
/* Set wake-up mask registers */
exynos_pm_set_wakeup_mask();
s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
exynos_pmu_spare3 = pmu_raw_readl(S5P_PMU_SPARE3);
/*
* The cpu state needs to be saved and restored so that the
* secondary CPUs will enter low power start. Though the U-Boot
* is setting the cpu state with low power flag, the kernel
* needs to restore it back in case, the primary cpu fails to
* suspend for any reason.
*/
exynos5420_cpu_state = __raw_readl(sysram_base_addr +
EXYNOS5420_CPU_STATE);
exynos_pm_enter_sleep_mode();
/* ensure at least INFORM0 has the resume address */
if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM))
pmu_raw_writel(virt_to_phys(mcpm_entry_point), S5P_INFORM0);
tmp = pmu_raw_readl(EXYNOS5_ARM_L2_OPTION);
tmp &= ~EXYNOS5_USE_RETENTION;
pmu_raw_writel(tmp, EXYNOS5_ARM_L2_OPTION);
tmp = pmu_raw_readl(EXYNOS5420_SFR_AXI_CGDIS1);
tmp |= EXYNOS5420_UFS;
pmu_raw_writel(tmp, EXYNOS5420_SFR_AXI_CGDIS1);
tmp = pmu_raw_readl(EXYNOS5420_ARM_COMMON_OPTION);
tmp &= ~EXYNOS5420_L2RSTDISABLE_VALUE;
pmu_raw_writel(tmp, EXYNOS5420_ARM_COMMON_OPTION);
tmp = pmu_raw_readl(EXYNOS5420_FSYS2_OPTION);
tmp |= EXYNOS5420_EMULATION;
pmu_raw_writel(tmp, EXYNOS5420_FSYS2_OPTION);
tmp = pmu_raw_readl(EXYNOS5420_PSGEN_OPTION);
tmp |= EXYNOS5420_EMULATION;
pmu_raw_writel(tmp, EXYNOS5420_PSGEN_OPTION);
} }
static int exynos_pm_suspend(void) static int exynos_pm_suspend(void)
{ {
exynos_pm_central_suspend(); exynos_pm_central_suspend();
...@@ -184,6 +288,24 @@ static int exynos_pm_suspend(void) ...@@ -184,6 +288,24 @@ static int exynos_pm_suspend(void)
return 0; return 0;
} }
static int exynos5420_pm_suspend(void)
{
u32 this_cluster;
exynos_pm_central_suspend();
/* Setting SEQ_OPTION register */
this_cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1);
if (!this_cluster)
pmu_raw_writel(EXYNOS5420_ARM_USE_STANDBY_WFI0,
S5P_CENTRAL_SEQ_OPTION);
else
pmu_raw_writel(EXYNOS5420_KFC_USE_STANDBY_WFI0,
S5P_CENTRAL_SEQ_OPTION);
return 0;
}
static void exynos_pm_release_retention(void) static void exynos_pm_release_retention(void)
{ {
unsigned int i; unsigned int i;
...@@ -222,6 +344,56 @@ static void exynos_pm_resume(void) ...@@ -222,6 +344,56 @@ static void exynos_pm_resume(void)
pmu_raw_writel(0x0, S5P_INFORM1); pmu_raw_writel(0x0, S5P_INFORM1);
} }
static void exynos5420_prepare_pm_resume(void)
{
if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM))
WARN_ON(mcpm_cpu_powered_up());
}
static void exynos5420_pm_resume(void)
{
unsigned long tmp;
/* Restore the CPU0 low power state register */
tmp = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
pmu_raw_writel(tmp | S5P_CORE_LOCAL_PWR_EN,
EXYNOS5_ARM_CORE0_SYS_PWR_REG);
/* Restore the sysram cpu state register */
__raw_writel(exynos5420_cpu_state,
sysram_base_addr + EXYNOS5420_CPU_STATE);
pmu_raw_writel(EXYNOS5420_USE_STANDBY_WFI_ALL,
S5P_CENTRAL_SEQ_OPTION);
if (exynos_pm_central_resume())
goto early_wakeup;
/* For release retention */
exynos_pm_release_retention();
pmu_raw_writel(exynos_pmu_spare3, S5P_PMU_SPARE3);
s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
early_wakeup:
tmp = pmu_raw_readl(EXYNOS5420_SFR_AXI_CGDIS1);
tmp &= ~EXYNOS5420_UFS;
pmu_raw_writel(tmp, EXYNOS5420_SFR_AXI_CGDIS1);
tmp = pmu_raw_readl(EXYNOS5420_FSYS2_OPTION);
tmp &= ~EXYNOS5420_EMULATION;
pmu_raw_writel(tmp, EXYNOS5420_FSYS2_OPTION);
tmp = pmu_raw_readl(EXYNOS5420_PSGEN_OPTION);
tmp &= ~EXYNOS5420_EMULATION;
pmu_raw_writel(tmp, EXYNOS5420_PSGEN_OPTION);
/* Clear SLEEP mode set in INFORM1 */
pmu_raw_writel(0x0, S5P_INFORM1);
}
/* /*
* Suspend Ops * Suspend Ops
*/ */
...@@ -256,6 +428,8 @@ static int exynos_suspend_enter(suspend_state_t state) ...@@ -256,6 +428,8 @@ static int exynos_suspend_enter(suspend_state_t state)
if (ret) if (ret)
return ret; return ret;
if (pm_data->pm_resume_prepare)
pm_data->pm_resume_prepare();
s3c_pm_restore_uarts(); s3c_pm_restore_uarts();
S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
...@@ -270,6 +444,22 @@ static int exynos_suspend_enter(suspend_state_t state) ...@@ -270,6 +444,22 @@ static int exynos_suspend_enter(suspend_state_t state)
static int exynos_suspend_prepare(void) static int exynos_suspend_prepare(void)
{ {
int ret;
/*
* REVISIT: It would be better if struct platform_suspend_ops
* .prepare handler get the suspend_state_t as a parameter to
* avoid hard-coding the suspend to mem state. It's safe to do
* it now only because the suspend_valid_only_mem function is
* used as the .valid callback used to check if a given state
* is supported by the platform anyways.
*/
ret = regulator_suspend_prepare(PM_SUSPEND_MEM);
if (ret) {
pr_err("Failed to prepare regulators for suspend (%d)\n", ret);
return ret;
}
s3c_pm_check_prepare(); s3c_pm_check_prepare();
return 0; return 0;
...@@ -277,7 +467,13 @@ static int exynos_suspend_prepare(void) ...@@ -277,7 +467,13 @@ static int exynos_suspend_prepare(void)
static void exynos_suspend_finish(void) static void exynos_suspend_finish(void)
{ {
int ret;
s3c_pm_check_cleanup(); s3c_pm_check_cleanup();
ret = regulator_suspend_finish();
if (ret)
pr_warn("Failed to resume regulators from suspend (%d)\n", ret);
} }
static const struct platform_suspend_ops exynos_suspend_ops = { static const struct platform_suspend_ops exynos_suspend_ops = {
...@@ -309,6 +505,17 @@ static const struct exynos_pm_data exynos5250_pm_data = { ...@@ -309,6 +505,17 @@ static const struct exynos_pm_data exynos5250_pm_data = {
.cpu_suspend = exynos_cpu_suspend, .cpu_suspend = exynos_cpu_suspend,
}; };
static struct exynos_pm_data exynos5420_pm_data = {
.wkup_irq = exynos5250_wkup_irq,
.wake_disable_mask = (0x7F << 7) | (0x1F << 1),
.release_ret_regs = exynos5420_release_ret_regs,
.pm_resume_prepare = exynos5420_prepare_pm_resume,
.pm_resume = exynos5420_pm_resume,
.pm_suspend = exynos5420_pm_suspend,
.pm_prepare = exynos5420_pm_prepare,
.cpu_suspend = exynos5420_cpu_suspend,
};
static struct of_device_id exynos_pmu_of_device_ids[] = { static struct of_device_id exynos_pmu_of_device_ids[] = {
{ {
.compatible = "samsung,exynos4210-pmu", .compatible = "samsung,exynos4210-pmu",
...@@ -322,6 +529,9 @@ static struct of_device_id exynos_pmu_of_device_ids[] = { ...@@ -322,6 +529,9 @@ static struct of_device_id exynos_pmu_of_device_ids[] = {
}, { }, {
.compatible = "samsung,exynos5250-pmu", .compatible = "samsung,exynos5250-pmu",
.data = &exynos5250_pm_data, .data = &exynos5250_pm_data,
}, {
.compatible = "samsung,exynos5420-pmu",
.data = &exynos5420_pm_data,
}, },
{ /*sentinel*/ }, { /*sentinel*/ },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment