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
select PM_GENERIC_DOMAINS if PM_RUNTIME
select S5P_DEV_MFC
select SRAM
select MFD_SYSCON
help
Support for SAMSUNG EXYNOS SoCs (EXYNOS4/5)
......@@ -75,6 +76,11 @@ config SOC_EXYNOS4412
default y
depends on ARCH_EXYNOS4
config SOC_EXYNOS4415
bool "SAMSUNG EXYNOS4415"
default y
depends on ARCH_EXYNOS4
config SOC_EXYNOS5250
bool "SAMSUNG EXYNOS5250"
default y
......
......@@ -17,9 +17,6 @@ obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.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)
AFLAGS_exynos-smc.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);
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_up(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[] = {
{ .compatible = "samsung,exynos4210-pmu" },
{ .compatible = "samsung,exynos4212-pmu" },
{ .compatible = "samsung,exynos4412-pmu" },
{ .compatible = "samsung,exynos4415-pmu" },
{ .compatible = "samsung,exynos5250-pmu" },
{ .compatible = "samsung,exynos5260-pmu" },
{ .compatible = "samsung,exynos5410-pmu" },
......@@ -336,6 +337,7 @@ static char const *exynos_dt_compat[] __initconst = {
"samsung,exynos4210",
"samsung,exynos4212",
"samsung,exynos4412",
"samsung,exynos4415",
"samsung,exynos5",
"samsung,exynos5250",
"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 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <asm/cputype.h>
#include <asm/cp15.h>
......@@ -30,6 +31,8 @@
#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29)
#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
* 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[] = {
{},
};
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)
{
struct device_node *node;
void __iomem *ns_sram_base_addr;
unsigned int value, i;
int ret;
......@@ -387,16 +406,9 @@ static int __init exynos_mcpm_init(void)
pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i));
}
/*
* 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);
exynos_mcpm_setup_entry_point();
iounmap(ns_sram_base_addr);
register_syscore_ops(&exynos_mcpm_syscore_ops);
return ret;
}
......
......@@ -22,6 +22,7 @@
#include <linux/of_address.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#include <asm/firmware.h>
......@@ -33,6 +34,88 @@
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
* @cpu : the cpu to power down
......@@ -43,6 +126,18 @@ extern void exynos4_secondary_startup(void);
*/
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));
}
......@@ -260,6 +355,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
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
* calibrations, then wait for it to finish
......@@ -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 = {
.smp_init_cpus = exynos_smp_init_cpus,
.smp_prepare_cpus = exynos_smp_prepare_cpus,
......
......@@ -26,6 +26,7 @@
#include <plat/pm-common.h>
#include "common.h"
#include "exynos-pmu.h"
#include "regs-pmu.h"
#include "regs-sys.h"
......
/*
* Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
* Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS - CPU PMU(Power Management Unit) support
......@@ -10,12 +10,46 @@
*/
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include "common.h"
#include "exynos-pmu.h"
#include "regs-pmu.h"
static const struct exynos_pmu_conf *exynos_pmu_config;
#define PMU_TABLE_END (-1U)
struct exynos_pmu_conf {
unsigned int offset;
unsigned int val[NUM_SYS_POWERDOWN];
};
struct exynos_pmu_data {
const struct exynos_pmu_conf *pmu_config;
const struct exynos_pmu_conf *pmu_config_extra;
void (*pmu_init)(void);
void (*powerdown_conf)(enum sys_powerdown);
};
struct exynos_pmu_context {
struct device *dev;
const struct exynos_pmu_data *pmu_data;
};
static void __iomem *pmu_base_addr;
static struct exynos_pmu_context *pmu_context;
static inline void pmu_raw_writel(u32 val, u32 offset)
{
writel_relaxed(val, pmu_base_addr + offset);
}
static inline u32 pmu_raw_readl(u32 offset)
{
return readl_relaxed(pmu_base_addr + offset);
}
static const struct exynos_pmu_conf exynos4210_pmu_config[] = {
/* { .offset = offset, .val = { AFTR, LPA, SLEEP } */
......@@ -316,6 +350,151 @@ static const struct exynos_pmu_conf exynos5250_pmu_config[] = {
{ PMU_TABLE_END,},
};
static struct exynos_pmu_conf exynos5420_pmu_config[] = {
/* { .offset = offset, .val = { AFTR, LPA, SLEEP } */
{ EXYNOS5_ARM_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_ARM_CORE1_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_DIS_IRQ_ARM_CORE1_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_DIS_IRQ_ARM_CORE1_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_ARM_CORE2_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_ARM_CORE2_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_ARM_CORE2_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_ARM_CORE3_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_ARM_CORE3_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_ARM_CORE3_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_KFC_CORE0_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE0_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE0_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_KFC_CORE1_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE1_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE1_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_KFC_CORE2_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE2_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE2_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_KFC_CORE3_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE3_LOCAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_DIS_IRQ_KFC_CORE3_CENTRAL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_ISP_ARM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_ARM_COMMON_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_KFC_COMMON_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_ARM_L2_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_KFC_L2_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_ACLKSTOP_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_CMU_SCLKSTOP_SYS_PWR_REG, { 0x1, 0x0, 0x1} },
{ EXYNOS5_CMU_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_CMU_ACLKSTOP_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_CMU_SCLKSTOP_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x1} },
{ EXYNOS5_CMU_RESET_SYSMEM_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_DRAM_FREQ_DOWN_SYS_PWR_REG, { 0x1, 0x0, 0x1} },
{ EXYNOS5_DDRPHY_DLLOFF_SYS_PWR_REG, { 0x1, 0x1, 0x1} },
{ EXYNOS5_DDRPHY_DLLLOCK_SYS_PWR_REG, { 0x1, 0x0, 0x1} },
{ EXYNOS5_APLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_MPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_VPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_EPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_BPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_CPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_DPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_IPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_KPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_MPLLUSER_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_BPLLUSER_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_RPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_SPLL_SYSCLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_TOP_BUS_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
{ EXYNOS5_TOP_RETENTION_SYS_PWR_REG, { 0x1, 0x1, 0x1} },
{ EXYNOS5_TOP_PWR_SYS_PWR_REG, { 0x3, 0x3, 0x0} },
{ EXYNOS5_TOP_BUS_SYSMEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
{ EXYNOS5_TOP_RETENTION_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x1} },
{ EXYNOS5_TOP_PWR_SYSMEM_SYS_PWR_REG, { 0x3, 0x0, 0x0} },
{ EXYNOS5_LOGIC_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_OSCCLK_GATE_SYS_PWR_REG, { 0x1, 0x0, 0x1} },
{ EXYNOS5_LOGIC_RESET_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_OSCCLK_GATE_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_INTRAM_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x3} },
{ EXYNOS5420_INTROM_MEM_SYS_PWR_REG, { 0x3, 0x0, 0x3} },
{ EXYNOS5_PAD_RETENTION_DRAM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_PAD_RETENTION_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5420_PAD_RETENTION_JTAG_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5420_PAD_RETENTION_DRAM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_UART_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_MMC0_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_MMC1_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_MMC2_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_HSI_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_EBIA_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_EBIB_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_SPI_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5420_PAD_RETENTION_DRAM_COREBLK_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_PAD_ISOLATION_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_PAD_ISOLATION_SYSMEM_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_PAD_ALV_SEL_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_XUSBXTI_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_XXTI_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_EXT_REGULATOR_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_GPIO_MODE_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_GPIO_MODE_SYSMEM_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_GPIO_MODE_MAU_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_TOP_ASB_RESET_SYS_PWR_REG, { 0x1, 0x1, 0x0} },
{ EXYNOS5_TOP_ASB_ISOLATION_SYS_PWR_REG, { 0x1, 0x0, 0x0} },
{ EXYNOS5_GSCL_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5_ISP_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5_MFC_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5_G3D_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_DISP1_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_MAU_SYS_PWR_REG, { 0x7, 0x7, 0x0} },
{ EXYNOS5420_G2D_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_MSC_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_FSYS_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_FSYS2_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_PSGEN_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_PERIC_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5420_WCORE_SYS_PWR_REG, { 0x7, 0x0, 0x0} },
{ EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_CLKSTOP_MFC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_DISP1_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_MAU_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_G2D_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_MSC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_FSYS_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_PSGEN_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_PERIC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_CLKSTOP_WCORE_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_SYSCLK_MFC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_DISP1_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_MAU_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_G2D_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_MSC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_FSYS_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_FSYS2_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_PSGEN_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_PERIC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_SYSCLK_WCORE_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_FSYS2_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_PSGEN_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_PERIC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_WCORE_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_MFC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_DISP1_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_MAU_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_G2D_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_MSC_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ EXYNOS5420_CMU_RESET_FSYS_SYS_PWR_REG, { 0x0, 0x0, 0x0} },
{ PMU_TABLE_END,},
};
static unsigned int const exynos5_list_both_cnt_feed[] = {
EXYNOS5_ARM_CORE0_OPTION,
EXYNOS5_ARM_CORE1_OPTION,
......@@ -330,13 +509,82 @@ static unsigned int const exynos5_list_both_cnt_feed[] = {
EXYNOS5_TOP_PWR_SYSMEM_OPTION,
};
static unsigned int const exynos5_list_diable_wfi_wfe[] = {
static unsigned int const exynos5_list_disable_wfi_wfe[] = {
EXYNOS5_ARM_CORE1_OPTION,
EXYNOS5_FSYS_ARM_OPTION,
EXYNOS5_ISP_ARM_OPTION,
};
static void exynos5_init_pmu(void)
static unsigned int const exynos5420_list_disable_pmu_reg[] = {
EXYNOS5_CMU_CLKSTOP_GSCL_SYS_PWR_REG,
EXYNOS5_CMU_CLKSTOP_ISP_SYS_PWR_REG,
EXYNOS5_CMU_CLKSTOP_G3D_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_DISP1_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_MAU_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_G2D_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_MSC_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_FSYS_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_PSGEN_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_PERIC_SYS_PWR_REG,
EXYNOS5420_CMU_CLKSTOP_WCORE_SYS_PWR_REG,
EXYNOS5_CMU_SYSCLK_GSCL_SYS_PWR_REG,
EXYNOS5_CMU_SYSCLK_ISP_SYS_PWR_REG,
EXYNOS5_CMU_SYSCLK_G3D_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_DISP1_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_MAU_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_G2D_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_MSC_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_FSYS_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_FSYS2_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_PSGEN_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_PERIC_SYS_PWR_REG,
EXYNOS5420_CMU_SYSCLK_WCORE_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_FSYS2_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_PSGEN_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_PERIC_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_WCORE_SYS_PWR_REG,
EXYNOS5_CMU_RESET_GSCL_SYS_PWR_REG,
EXYNOS5_CMU_RESET_ISP_SYS_PWR_REG,
EXYNOS5_CMU_RESET_G3D_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_DISP1_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_MAU_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_G2D_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_MSC_SYS_PWR_REG,
EXYNOS5420_CMU_RESET_FSYS_SYS_PWR_REG,
};
static void exynos5_power_off(void)
{
unsigned int tmp;
pr_info("Power down.\n");
tmp = pmu_raw_readl(EXYNOS_PS_HOLD_CONTROL);
tmp ^= (1 << 8);
pmu_raw_writel(tmp, EXYNOS_PS_HOLD_CONTROL);
/* Wait a little so we don't give a false warning below */
mdelay(100);
pr_err("Power down failed, please power off system manually.\n");
while (1)
;
}
void exynos5420_powerdown_conf(enum sys_powerdown mode)
{
u32 this_cluster;
this_cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1);
/*
* set the cluster id to IROM register to ensure that we wake
* up with the current cluster.
*/
pmu_raw_writel(this_cluster, EXYNOS_IROM_DATA2);
}
static void exynos5_powerdown_conf(enum sys_powerdown mode)
{
unsigned int i;
unsigned int tmp;
......@@ -344,7 +592,7 @@ static void exynos5_init_pmu(void)
/*
* Enable both SC_FEEDBACK and SC_COUNTER
*/
for (i = 0 ; i < ARRAY_SIZE(exynos5_list_both_cnt_feed) ; i++) {
for (i = 0; i < ARRAY_SIZE(exynos5_list_both_cnt_feed); i++) {
tmp = pmu_raw_readl(exynos5_list_both_cnt_feed[i]);
tmp |= (EXYNOS5_USE_SC_FEEDBACK |
EXYNOS5_USE_SC_COUNTER);
......@@ -361,11 +609,11 @@ static void exynos5_init_pmu(void)
/*
* Disable WFI/WFE on XXX_OPTION
*/
for (i = 0 ; i < ARRAY_SIZE(exynos5_list_diable_wfi_wfe) ; i++) {
tmp = pmu_raw_readl(exynos5_list_diable_wfi_wfe[i]);
for (i = 0; i < ARRAY_SIZE(exynos5_list_disable_wfi_wfe); i++) {
tmp = pmu_raw_readl(exynos5_list_disable_wfi_wfe[i]);
tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE |
EXYNOS5_OPTION_USE_STANDBYWFI);
pmu_raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]);
pmu_raw_writel(tmp, exynos5_list_disable_wfi_wfe[i]);
}
}
......@@ -373,51 +621,195 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode)
{
unsigned int i;
if (soc_is_exynos5250())
exynos5_init_pmu();
const struct exynos_pmu_data *pmu_data = pmu_context->pmu_data;
if (pmu_data->powerdown_conf)
pmu_data->powerdown_conf(mode);
for (i = 0; (exynos_pmu_config[i].offset != PMU_TABLE_END) ; i++)
pmu_raw_writel(exynos_pmu_config[i].val[mode],
exynos_pmu_config[i].offset);
if (pmu_data->pmu_config) {
for (i = 0; (pmu_data->pmu_config[i].offset != PMU_TABLE_END); i++)
pmu_raw_writel(pmu_data->pmu_config[i].val[mode],
pmu_data->pmu_config[i].offset);
}
if (soc_is_exynos4412()) {
for (i = 0; exynos4412_pmu_config[i].offset != PMU_TABLE_END ; i++)
pmu_raw_writel(exynos4412_pmu_config[i].val[mode],
exynos4412_pmu_config[i].offset);
if (pmu_data->pmu_config_extra) {
for (i = 0; pmu_data->pmu_config_extra[i].offset != PMU_TABLE_END; i++)
pmu_raw_writel(pmu_data->pmu_config_extra[i].val[mode],
pmu_data->pmu_config_extra[i].offset);
}
}
static int __init exynos_pmu_init(void)
static void exynos5250_pmu_init(void)
{
unsigned int value;
/*
* When SYS_WDTRESET is set, watchdog timer reset request
* is ignored by power management unit.
*/
value = pmu_raw_readl(EXYNOS5_AUTO_WDTRESET_DISABLE);
value &= ~EXYNOS5_SYS_WDTRESET;
pmu_raw_writel(value, EXYNOS5_AUTO_WDTRESET_DISABLE);
value = pmu_raw_readl(EXYNOS5_MASK_WDTRESET_REQUEST);
value &= ~EXYNOS5_SYS_WDTRESET;
pmu_raw_writel(value, EXYNOS5_MASK_WDTRESET_REQUEST);
}
static void exynos5420_pmu_init(void)
{
unsigned int value;
int i;
exynos_pmu_config = exynos4210_pmu_config;
if (soc_is_exynos4210()) {
exynos_pmu_config = exynos4210_pmu_config;
pr_info("EXYNOS4210 PMU Initialize\n");
} else if (soc_is_exynos4212() || soc_is_exynos4412()) {
exynos_pmu_config = exynos4x12_pmu_config;
pr_info("EXYNOS4x12 PMU Initialize\n");
} else if (soc_is_exynos5250()) {
/*
* When SYS_WDTRESET is set, watchdog timer reset request
* is ignored by power management unit.
*/
value = pmu_raw_readl(EXYNOS5_AUTO_WDTRESET_DISABLE);
value &= ~EXYNOS5_SYS_WDTRESET;
pmu_raw_writel(value, EXYNOS5_AUTO_WDTRESET_DISABLE);
value = pmu_raw_readl(EXYNOS5_MASK_WDTRESET_REQUEST);
value &= ~EXYNOS5_SYS_WDTRESET;
pmu_raw_writel(value, EXYNOS5_MASK_WDTRESET_REQUEST);
exynos_pmu_config = exynos5250_pmu_config;
pr_info("EXYNOS5250 PMU Initialize\n");
} else {
pr_info("EXYNOS: PMU not supported\n");
/*
* Set the CMU_RESET, CMU_SYSCLK and CMU_CLKSTOP registers
* for local power blocks to Low initially as per Table 8-4:
* "System-Level Power-Down Configuration Registers".
*/
for (i = 0; i < ARRAY_SIZE(exynos5420_list_disable_pmu_reg); i++)
pmu_raw_writel(0, exynos5420_list_disable_pmu_reg[i]);
/* Enable USE_STANDBY_WFI for all CORE */
pmu_raw_writel(EXYNOS5420_USE_STANDBY_WFI_ALL, S5P_CENTRAL_SEQ_OPTION);
value = pmu_raw_readl(EXYNOS_L2_OPTION(0));
value &= ~EXYNOS5_USE_RETENTION;
pmu_raw_writel(value, EXYNOS_L2_OPTION(0));
value = pmu_raw_readl(EXYNOS_L2_OPTION(1));
value &= ~EXYNOS5_USE_RETENTION;
pmu_raw_writel(value, EXYNOS_L2_OPTION(1));
/*
* If L2_COMMON is turned off, clocks related to ATB async
* bridge are gated. Thus, when ISP power is gated, LPI
* may get stuck.
*/
value = pmu_raw_readl(EXYNOS5420_LPI_MASK);
value |= EXYNOS5420_ATB_ISP_ARM;
pmu_raw_writel(value, EXYNOS5420_LPI_MASK);
value = pmu_raw_readl(EXYNOS5420_LPI_MASK1);
value |= EXYNOS5420_ATB_KFC;
pmu_raw_writel(value, EXYNOS5420_LPI_MASK1);
/* Prevent issue of new bus request from L2 memory */
value = pmu_raw_readl(EXYNOS5420_ARM_COMMON_OPTION);
value |= EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN;
pmu_raw_writel(value, EXYNOS5420_ARM_COMMON_OPTION);
value = pmu_raw_readl(EXYNOS5420_KFC_COMMON_OPTION);
value |= EXYNOS5_SKIP_DEACTIVATE_ACEACP_IN_PWDN;
pmu_raw_writel(value, EXYNOS5420_KFC_COMMON_OPTION);
/* This setting is to reduce suspend/resume time */
pmu_raw_writel(DUR_WAIT_RESET, EXYNOS5420_LOGIC_RESET_DURATION3);
/* Serialized CPU wakeup of Eagle */
pmu_raw_writel(SPREAD_ENABLE, EXYNOS5420_ARM_INTR_SPREAD_ENABLE);
pmu_raw_writel(SPREAD_USE_STANDWFI,
EXYNOS5420_ARM_INTR_SPREAD_USE_STANDBYWFI);
pmu_raw_writel(0x1, EXYNOS5420_UP_SCHEDULER);
pm_power_off = exynos5_power_off;
pr_info("EXYNOS5420 PMU initialized\n");
}
static const struct exynos_pmu_data exynos4210_pmu_data = {
.pmu_config = exynos4210_pmu_config,
};
static const struct exynos_pmu_data exynos4212_pmu_data = {
.pmu_config = exynos4x12_pmu_config,
};
static const struct exynos_pmu_data exynos4412_pmu_data = {
.pmu_config = exynos4x12_pmu_config,
.pmu_config_extra = exynos4412_pmu_config,
};
static const struct exynos_pmu_data exynos5250_pmu_data = {
.pmu_config = exynos5250_pmu_config,
.pmu_init = exynos5250_pmu_init,
.powerdown_conf = exynos5_powerdown_conf,
};
static struct exynos_pmu_data exynos5420_pmu_data = {
.pmu_config = exynos5420_pmu_config,
.pmu_init = exynos5420_pmu_init,
.powerdown_conf = exynos5420_powerdown_conf,
};
/*
* PMU platform driver and devicetree bindings.
*/
static const struct of_device_id exynos_pmu_of_device_ids[] = {
{
.compatible = "samsung,exynos4210-pmu",
.data = &exynos4210_pmu_data,
}, {
.compatible = "samsung,exynos4212-pmu",
.data = &exynos4212_pmu_data,
}, {
.compatible = "samsung,exynos4412-pmu",
.data = &exynos4412_pmu_data,
}, {
.compatible = "samsung,exynos5250-pmu",
.data = &exynos5250_pmu_data,
}, {
.compatible = "samsung,exynos5420-pmu",
.data = &exynos5420_pmu_data,
},
{ /*sentinel*/ },
};
static int exynos_pmu_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pmu_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(pmu_base_addr))
return PTR_ERR(pmu_base_addr);
pmu_context = devm_kzalloc(&pdev->dev,
sizeof(struct exynos_pmu_context),
GFP_KERNEL);
if (!pmu_context) {
dev_err(dev, "Cannot allocate memory.\n");
return -ENOMEM;
}
pmu_context->dev = dev;
match = of_match_node(exynos_pmu_of_device_ids, dev->of_node);
pmu_context->pmu_data = match->data;
if (pmu_context->pmu_data->pmu_init)
pmu_context->pmu_data->pmu_init();
platform_set_drvdata(pdev, pmu_context);
dev_dbg(dev, "Exynos PMU Driver probe done\n");
return 0;
}
arch_initcall(exynos_pmu_init);
static struct platform_driver exynos_pmu_driver = {
.driver = {
.name = "exynos-pmu",
.owner = THIS_MODULE,
.of_match_table = exynos_pmu_of_device_ids,
},
.probe = exynos_pmu_probe,
};
static int __init exynos_pmu_init(void)
{
return platform_driver_register(&exynos_pmu_driver);
}
postcore_initcall(exynos_pmu_init);
......@@ -20,6 +20,7 @@
#define S5P_USE_STANDBY_WFI0 (1 << 16)
#define S5P_USE_STANDBY_WFE0 (1 << 24)
#define S5P_USE_DELAYED_RESET_ASSERTION BIT(12)
#define EXYNOS_CORE_PO_RESET(n) ((1 << 4) << n)
#define EXYNOS_WAKEUP_FROM_LOWPWR (1 << 28)
......@@ -37,6 +38,7 @@
#define S5P_INFORM7 0x081C
#define S5P_PMU_SPARE3 0x090C
#define EXYNOS_IROM_DATA2 0x0988
#define S5P_ARM_CORE0_LOWPWR 0x1000
#define S5P_DIS_IRQ_CORE0 0x1004
#define S5P_DIS_IRQ_CENTRAL0 0x1008
......@@ -108,6 +110,8 @@
(EXYNOS_ARM_CORE0_CONFIGURATION + (0x80 * (_nr)))
#define EXYNOS_ARM_CORE_STATUS(_nr) \
(EXYNOS_ARM_CORE_CONFIGURATION(_nr) + 0x4)
#define EXYNOS_ARM_CORE_OPTION(_nr) \
(EXYNOS_ARM_CORE_CONFIGURATION(_nr) + 0x8)
#define EXYNOS_ARM_COMMON_CONFIGURATION 0x2500
#define EXYNOS_COMMON_CONFIGURATION(_nr) \
......@@ -117,6 +121,31 @@
#define EXYNOS_COMMON_OPTION(_nr) \
(EXYNOS_COMMON_CONFIGURATION(_nr) + 0x8)
#define EXYNOS_CORE_LOCAL_PWR_EN 0x3
#define EXYNOS_ARM_COMMON_STATUS 0x2504
#define EXYNOS_COMMON_OPTION(_nr) \
(EXYNOS_COMMON_CONFIGURATION(_nr) + 0x8)
#define EXYNOS_ARM_L2_CONFIGURATION 0x2600
#define EXYNOS_L2_CONFIGURATION(_nr) \
(EXYNOS_ARM_L2_CONFIGURATION + ((_nr) * 0x80))
#define EXYNOS_L2_STATUS(_nr) \
(EXYNOS_L2_CONFIGURATION(_nr) + 0x4)
#define EXYNOS_L2_OPTION(_nr) \
(EXYNOS_L2_CONFIGURATION(_nr) + 0x8)
#define EXYNOS_L2_COMMON_PWR_EN 0x3
#define EXYNOS_ARM_CORE_X_STATUS_OFFSET 0x4
#define EXYNOS5_APLL_SYSCLK_CONFIGURATION 0x2A00
#define EXYNOS5_APLL_SYSCLK_STATUS 0x2A04
#define EXYNOS5_ARM_L2_OPTION 0x2608
#define EXYNOS5_USE_RETENTION BIT(4)
#define EXYNOS5_L2RSTDISABLE_VALUE BIT(3)
#define S5P_PAD_RET_MAUDIO_OPTION 0x3028
#define S5P_PAD_RET_GPIO_OPTION 0x3108
#define S5P_PAD_RET_UART_OPTION 0x3128
......@@ -190,6 +219,7 @@
#define EXYNOS5_AUTO_WDTRESET_DISABLE 0x0408
#define EXYNOS5_MASK_WDTRESET_REQUEST 0x040C
#define EXYNOS5_USE_RETENTION BIT(4)
#define EXYNOS5_SYS_WDTRESET (1 << 20)
#define EXYNOS5_ARM_CORE0_SYS_PWR_REG 0x1000
......@@ -329,4 +359,204 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
+ MPIDR_AFFINITY_LEVEL(mpidr, 0));
}
/* Only for EXYNOS5420 */
#define EXYNOS5420_ISP_ARM_OPTION 0x2488
#define EXYNOS5420_L2RSTDISABLE_VALUE BIT(3)
#define EXYNOS5420_LPI_MASK 0x0004
#define EXYNOS5420_LPI_MASK1 0x0008
#define EXYNOS5420_UFS BIT(8)
#define EXYNOS5420_ATB_KFC BIT(13)
#define EXYNOS5420_ATB_ISP_ARM BIT(19)
#define EXYNOS5420_EMULATION BIT(31)
#define ATB_ISP_ARM BIT(12)
#define ATB_KFC BIT(13)
#define ATB_NOC BIT(14)
#define EXYNOS5420_ARM_INTR_SPREAD_ENABLE 0x0100
#define EXYNOS5420_ARM_INTR_SPREAD_USE_STANDBYWFI 0x0104
#define EXYNOS5420_UP_SCHEDULER 0x0120
#define SPREAD_ENABLE 0xF
#define SPREAD_USE_STANDWFI 0xF
#define EXYNOS5420_BB_CON1 0x0784
#define EXYNOS5420_BB_SEL_EN BIT(31)
#define EXYNOS5420_BB_PMOS_EN BIT(7)
#define EXYNOS5420_BB_1300X 0XF
#define EXYNOS5420_ARM_CORE2_SYS_PWR_REG 0x1020
#define EXYNOS5420_DIS_IRQ_ARM_CORE2_LOCAL_SYS_PWR_REG 0x1024
#define EXYNOS5420_DIS_IRQ_ARM_CORE2_CENTRAL_SYS_PWR_REG 0x1028
#define EXYNOS5420_ARM_CORE3_SYS_PWR_REG 0x1030
#define EXYNOS5420_DIS_IRQ_ARM_CORE3_LOCAL_SYS_PWR_REG 0x1034
#define EXYNOS5420_DIS_IRQ_ARM_CORE3_CENTRAL_SYS_PWR_REG 0x1038
#define EXYNOS5420_KFC_CORE0_SYS_PWR_REG 0x1040
#define EXYNOS5420_DIS_IRQ_KFC_CORE0_LOCAL_SYS_PWR_REG 0x1044
#define EXYNOS5420_DIS_IRQ_KFC_CORE0_CENTRAL_SYS_PWR_REG 0x1048
#define EXYNOS5420_KFC_CORE1_SYS_PWR_REG 0x1050
#define EXYNOS5420_DIS_IRQ_KFC_CORE1_LOCAL_SYS_PWR_REG 0x1054
#define EXYNOS5420_DIS_IRQ_KFC_CORE1_CENTRAL_SYS_PWR_REG 0x1058
#define EXYNOS5420_KFC_CORE2_SYS_PWR_REG 0x1060
#define EXYNOS5420_DIS_IRQ_KFC_CORE2_LOCAL_SYS_PWR_REG 0x1064
#define EXYNOS5420_DIS_IRQ_KFC_CORE2_CENTRAL_SYS_PWR_REG 0x1068
#define EXYNOS5420_KFC_CORE3_SYS_PWR_REG 0x1070
#define EXYNOS5420_DIS_IRQ_KFC_CORE3_LOCAL_SYS_PWR_REG 0x1074
#define EXYNOS5420_DIS_IRQ_KFC_CORE3_CENTRAL_SYS_PWR_REG 0x1078
#define EXYNOS5420_ISP_ARM_SYS_PWR_REG 0x1090
#define EXYNOS5420_DIS_IRQ_ISP_ARM_LOCAL_SYS_PWR_REG 0x1094
#define EXYNOS5420_DIS_IRQ_ISP_ARM_CENTRAL_SYS_PWR_REG 0x1098
#define EXYNOS5420_ARM_COMMON_SYS_PWR_REG 0x10A0
#define EXYNOS5420_KFC_COMMON_SYS_PWR_REG 0x10B0
#define EXYNOS5420_KFC_L2_SYS_PWR_REG 0x10D0
#define EXYNOS5420_DPLL_SYSCLK_SYS_PWR_REG 0x1158
#define EXYNOS5420_IPLL_SYSCLK_SYS_PWR_REG 0x115C
#define EXYNOS5420_KPLL_SYSCLK_SYS_PWR_REG 0x1160
#define EXYNOS5420_RPLL_SYSCLK_SYS_PWR_REG 0x1174
#define EXYNOS5420_SPLL_SYSCLK_SYS_PWR_REG 0x1178
#define EXYNOS5420_INTRAM_MEM_SYS_PWR_REG 0x11B8
#define EXYNOS5420_INTROM_MEM_SYS_PWR_REG 0x11BC
#define EXYNOS5420_ONENANDXL_MEM_SYS_PWR 0x11C0
#define EXYNOS5420_USBDEV_MEM_SYS_PWR 0x11CC
#define EXYNOS5420_USBDEV1_MEM_SYS_PWR 0x11D0
#define EXYNOS5420_SDMMC_MEM_SYS_PWR 0x11D4
#define EXYNOS5420_CSSYS_MEM_SYS_PWR 0x11D8
#define EXYNOS5420_SECSS_MEM_SYS_PWR 0x11DC
#define EXYNOS5420_ROTATOR_MEM_SYS_PWR 0x11E0
#define EXYNOS5420_INTRAM_MEM_SYS_PWR 0x11E4
#define EXYNOS5420_INTROM_MEM_SYS_PWR 0x11E8
#define EXYNOS5420_PAD_RETENTION_JTAG_SYS_PWR_REG 0x1208
#define EXYNOS5420_PAD_RETENTION_DRAM_SYS_PWR_REG 0x1210
#define EXYNOS5420_PAD_RETENTION_UART_SYS_PWR_REG 0x1214
#define EXYNOS5420_PAD_RETENTION_MMC0_SYS_PWR_REG 0x1218
#define EXYNOS5420_PAD_RETENTION_MMC1_SYS_PWR_REG 0x121C
#define EXYNOS5420_PAD_RETENTION_MMC2_SYS_PWR_REG 0x1220
#define EXYNOS5420_PAD_RETENTION_HSI_SYS_PWR_REG 0x1224
#define EXYNOS5420_PAD_RETENTION_EBIA_SYS_PWR_REG 0x1228
#define EXYNOS5420_PAD_RETENTION_EBIB_SYS_PWR_REG 0x122C
#define EXYNOS5420_PAD_RETENTION_SPI_SYS_PWR_REG 0x1230
#define EXYNOS5420_PAD_RETENTION_DRAM_COREBLK_SYS_PWR_REG 0x1234
#define EXYNOS5420_DISP1_SYS_PWR_REG 0x1410
#define EXYNOS5420_MAU_SYS_PWR_REG 0x1414
#define EXYNOS5420_G2D_SYS_PWR_REG 0x1418
#define EXYNOS5420_MSC_SYS_PWR_REG 0x141C
#define EXYNOS5420_FSYS_SYS_PWR_REG 0x1420
#define EXYNOS5420_FSYS2_SYS_PWR_REG 0x1424
#define EXYNOS5420_PSGEN_SYS_PWR_REG 0x1428
#define EXYNOS5420_PERIC_SYS_PWR_REG 0x142C
#define EXYNOS5420_WCORE_SYS_PWR_REG 0x1430
#define EXYNOS5420_CMU_CLKSTOP_DISP1_SYS_PWR_REG 0x1490
#define EXYNOS5420_CMU_CLKSTOP_MAU_SYS_PWR_REG 0x1494
#define EXYNOS5420_CMU_CLKSTOP_G2D_SYS_PWR_REG 0x1498
#define EXYNOS5420_CMU_CLKSTOP_MSC_SYS_PWR_REG 0x149C
#define EXYNOS5420_CMU_CLKSTOP_FSYS_SYS_PWR_REG 0x14A0
#define EXYNOS5420_CMU_CLKSTOP_FSYS2_SYS_PWR_REG 0x14A4
#define EXYNOS5420_CMU_CLKSTOP_PSGEN_SYS_PWR_REG 0x14A8
#define EXYNOS5420_CMU_CLKSTOP_PERIC_SYS_PWR_REG 0x14AC
#define EXYNOS5420_CMU_CLKSTOP_WCORE_SYS_PWR_REG 0x14B0
#define EXYNOS5420_CMU_SYSCLK_TOPPWR_SYS_PWR_REG 0x14BC
#define EXYNOS5420_CMU_SYSCLK_DISP1_SYS_PWR_REG 0x14D0
#define EXYNOS5420_CMU_SYSCLK_MAU_SYS_PWR_REG 0x14D4
#define EXYNOS5420_CMU_SYSCLK_G2D_SYS_PWR_REG 0x14D8
#define EXYNOS5420_CMU_SYSCLK_MSC_SYS_PWR_REG 0x14DC
#define EXYNOS5420_CMU_SYSCLK_FSYS_SYS_PWR_REG 0x14E0
#define EXYNOS5420_CMU_SYSCLK_FSYS2_SYS_PWR_REG 0x14E4
#define EXYNOS5420_CMU_SYSCLK_PSGEN_SYS_PWR_REG 0x14E8
#define EXYNOS5420_CMU_SYSCLK_PERIC_SYS_PWR_REG 0x14EC
#define EXYNOS5420_CMU_SYSCLK_WCORE_SYS_PWR_REG 0x14F0
#define EXYNOS5420_CMU_SYSCLK_SYSMEM_TOPPWR_SYS_PWR_REG 0x14F4
#define EXYNOS5420_CMU_RESET_FSYS2_SYS_PWR_REG 0x1570
#define EXYNOS5420_CMU_RESET_PSGEN_SYS_PWR_REG 0x1574
#define EXYNOS5420_CMU_RESET_PERIC_SYS_PWR_REG 0x1578
#define EXYNOS5420_CMU_RESET_WCORE_SYS_PWR_REG 0x157C
#define EXYNOS5420_CMU_RESET_DISP1_SYS_PWR_REG 0x1590
#define EXYNOS5420_CMU_RESET_MAU_SYS_PWR_REG 0x1594
#define EXYNOS5420_CMU_RESET_G2D_SYS_PWR_REG 0x1598
#define EXYNOS5420_CMU_RESET_MSC_SYS_PWR_REG 0x159C
#define EXYNOS5420_CMU_RESET_FSYS_SYS_PWR_REG 0x15A0
#define EXYNOS5420_SFR_AXI_CGDIS1 0x15E4
#define EXYNOS_ARM_CORE2_CONFIGURATION 0x2100
#define EXYNOS5420_ARM_CORE2_OPTION 0x2108
#define EXYNOS_ARM_CORE3_CONFIGURATION 0x2180
#define EXYNOS5420_ARM_CORE3_OPTION 0x2188
#define EXYNOS5420_ARM_COMMON_STATUS 0x2504
#define EXYNOS5420_ARM_COMMON_OPTION 0x2508
#define EXYNOS5420_KFC_COMMON_STATUS 0x2584
#define EXYNOS5420_KFC_COMMON_OPTION 0x2588
#define EXYNOS5420_LOGIC_RESET_DURATION3 0x2D1C
#define EXYNOS5420_PAD_RET_GPIO_OPTION 0x30C8
#define EXYNOS5420_PAD_RET_UART_OPTION 0x30E8
#define EXYNOS5420_PAD_RET_MMCA_OPTION 0x3108
#define EXYNOS5420_PAD_RET_MMCB_OPTION 0x3128
#define EXYNOS5420_PAD_RET_MMCC_OPTION 0x3148
#define EXYNOS5420_PAD_RET_HSI_OPTION 0x3168
#define EXYNOS5420_PAD_RET_SPI_OPTION 0x31C8
#define EXYNOS5420_PAD_RET_DRAM_COREBLK_OPTION 0x31E8
#define EXYNOS_PAD_RET_DRAM_OPTION 0x3008
#define EXYNOS_PAD_RET_MAUDIO_OPTION 0x3028
#define EXYNOS_PAD_RET_JTAG_OPTION 0x3048
#define EXYNOS_PAD_RET_GPIO_OPTION 0x3108
#define EXYNOS_PAD_RET_UART_OPTION 0x3128
#define EXYNOS_PAD_RET_MMCA_OPTION 0x3148
#define EXYNOS_PAD_RET_MMCB_OPTION 0x3168
#define EXYNOS_PAD_RET_EBIA_OPTION 0x3188
#define EXYNOS_PAD_RET_EBIB_OPTION 0x31A8
#define EXYNOS_PS_HOLD_CONTROL 0x330C
/* For SYS_PWR_REG */
#define EXYNOS_SYS_PWR_CFG BIT(0)
#define EXYNOS5420_MFC_CONFIGURATION 0x4060
#define EXYNOS5420_MFC_STATUS 0x4064
#define EXYNOS5420_MFC_OPTION 0x4068
#define EXYNOS5420_G3D_CONFIGURATION 0x4080
#define EXYNOS5420_G3D_STATUS 0x4084
#define EXYNOS5420_G3D_OPTION 0x4088
#define EXYNOS5420_DISP0_CONFIGURATION 0x40A0
#define EXYNOS5420_DISP0_STATUS 0x40A4
#define EXYNOS5420_DISP0_OPTION 0x40A8
#define EXYNOS5420_DISP1_CONFIGURATION 0x40C0
#define EXYNOS5420_DISP1_STATUS 0x40C4
#define EXYNOS5420_DISP1_OPTION 0x40C8
#define EXYNOS5420_MAU_CONFIGURATION 0x40E0
#define EXYNOS5420_MAU_STATUS 0x40E4
#define EXYNOS5420_MAU_OPTION 0x40E8
#define EXYNOS5420_FSYS2_OPTION 0x4168
#define EXYNOS5420_PSGEN_OPTION 0x4188
/* For EXYNOS_CENTRAL_SEQ_OPTION */
#define EXYNOS5_USE_STANDBYWFI_ARM_CORE0 BIT(16)
#define EXYNOS5_USE_STANDBYWFI_ARM_CORE1 BUT(17)
#define EXYNOS5_USE_STANDBYWFE_ARM_CORE0 BIT(24)
#define EXYNOS5_USE_STANDBYWFE_ARM_CORE1 BIT(25)
#define EXYNOS5420_ARM_USE_STANDBY_WFI0 BIT(4)
#define EXYNOS5420_ARM_USE_STANDBY_WFI1 BIT(5)
#define EXYNOS5420_ARM_USE_STANDBY_WFI2 BIT(6)
#define EXYNOS5420_ARM_USE_STANDBY_WFI3 BIT(7)
#define EXYNOS5420_KFC_USE_STANDBY_WFI0 BIT(8)
#define EXYNOS5420_KFC_USE_STANDBY_WFI1 BIT(9)
#define EXYNOS5420_KFC_USE_STANDBY_WFI2 BIT(10)
#define EXYNOS5420_KFC_USE_STANDBY_WFI3 BIT(11)
#define EXYNOS5420_ARM_USE_STANDBY_WFE0 BIT(16)
#define EXYNOS5420_ARM_USE_STANDBY_WFE1 BIT(17)
#define EXYNOS5420_ARM_USE_STANDBY_WFE2 BIT(18)
#define EXYNOS5420_ARM_USE_STANDBY_WFE3 BIT(19)
#define EXYNOS5420_KFC_USE_STANDBY_WFE0 BIT(20)
#define EXYNOS5420_KFC_USE_STANDBY_WFE1 BIT(21)
#define EXYNOS5420_KFC_USE_STANDBY_WFE2 BIT(22)
#define EXYNOS5420_KFC_USE_STANDBY_WFE3 BIT(23)
#define DUR_WAIT_RESET 0xF
#define EXYNOS5420_USE_STANDBY_WFI_ALL (EXYNOS5420_ARM_USE_STANDBY_WFI0 \
| EXYNOS5420_ARM_USE_STANDBY_WFI1 \
| EXYNOS5420_ARM_USE_STANDBY_WFI2 \
| EXYNOS5420_ARM_USE_STANDBY_WFI3 \
| EXYNOS5420_KFC_USE_STANDBY_WFI0 \
| EXYNOS5420_KFC_USE_STANDBY_WFI1 \
| EXYNOS5420_KFC_USE_STANDBY_WFI2 \
| EXYNOS5420_KFC_USE_STANDBY_WFI3)
#endif /* __ASM_ARCH_REGS_PMU_H */
......@@ -20,10 +20,12 @@
#include <linux/io.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/err.h>
#include <linux/regulator/machine.h>
#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/firmware.h>
#include <asm/mcpm.h>
#include <asm/smp_scu.h>
#include <asm/suspend.h>
......@@ -33,11 +35,14 @@
#include "common.h"
#include "regs-pmu.h"
#include "regs-sys.h"
#include "exynos-pmu.h"
#define S5P_CHECK_SLEEP 0x00000BAD
#define REG_TABLE_END (-1U)
#define EXYNOS5420_CPU_STATE 0x28
/**
* struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
* @hwirq: Hardware IRQ signal of the GIC
......@@ -69,6 +74,7 @@ struct exynos_pm_data {
unsigned int *release_ret_regs;
void (*pm_prepare)(void);
void (*pm_resume_prepare)(void);
void (*pm_resume)(void);
int (*pm_suspend)(void);
int (*cpu_suspend)(unsigned long);
......@@ -76,6 +82,9 @@ struct exynos_pm_data {
struct exynos_pm_data *pm_data;
static int exynos5420_cpu_state;
static unsigned int exynos_pmu_spare3;
/*
* GIC wake-up support
*/
......@@ -105,6 +114,23 @@ unsigned int exynos_release_ret_regs[] = {
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)
{
const struct exynos_wkup_irq *wkup_irq;
......@@ -135,14 +161,44 @@ static int exynos_cpu_do_idle(void)
pr_info("Failed to suspend the system\n");
return 1; /* Aborting suspend */
}
static int exynos_cpu_suspend(unsigned long arg)
static void exynos_flush_cache_all(void)
{
flush_cache_all();
outer_flush_all();
}
static int exynos_cpu_suspend(unsigned long arg)
{
exynos_flush_cache_all();
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)
{
/* Set wake-up mask registers */
......@@ -155,9 +211,6 @@ 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)
......@@ -172,8 +225,59 @@ static void exynos_pm_prepare(void)
pm_data->num_extra_save);
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)
{
exynos_pm_central_suspend();
......@@ -184,6 +288,24 @@ static int exynos_pm_suspend(void)
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)
{
unsigned int i;
......@@ -222,6 +344,56 @@ static void exynos_pm_resume(void)
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
*/
......@@ -256,6 +428,8 @@ static int exynos_suspend_enter(suspend_state_t state)
if (ret)
return ret;
if (pm_data->pm_resume_prepare)
pm_data->pm_resume_prepare();
s3c_pm_restore_uarts();
S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
......@@ -270,6 +444,22 @@ static int exynos_suspend_enter(suspend_state_t state)
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();
return 0;
......@@ -277,7 +467,13 @@ static int exynos_suspend_prepare(void)
static void exynos_suspend_finish(void)
{
int ret;
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 = {
......@@ -309,6 +505,17 @@ static const struct exynos_pm_data exynos5250_pm_data = {
.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[] = {
{
.compatible = "samsung,exynos4210-pmu",
......@@ -322,6 +529,9 @@ static struct of_device_id exynos_pmu_of_device_ids[] = {
}, {
.compatible = "samsung,exynos5250-pmu",
.data = &exynos5250_pm_data,
}, {
.compatible = "samsung,exynos5420-pmu",
.data = &exynos5420_pm_data,
},
{ /*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