summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/pm.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-08 11:31:16 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-08 11:31:16 -0700
commit3f17ea6dea8ba5668873afa54628a91aaa3fb1c0 (patch)
treeafbeb2accd4c2199ddd705ae943995b143a0af02 /arch/arm/mach-exynos/pm.c
parent1860e379875dfe7271c649058aeddffe5afd9d0d (diff)
parent1a5700bc2d10cd379a795fd2bb377a190af5acd4 (diff)
downloadblackbird-op-linux-3f17ea6dea8ba5668873afa54628a91aaa3fb1c0.tar.gz
blackbird-op-linux-3f17ea6dea8ba5668873afa54628a91aaa3fb1c0.zip
Merge branch 'next' (accumulated 3.16 merge window patches) into master
Now that 3.15 is released, this merges the 'next' branch into 'master', bringing us to the normal situation where my 'master' branch is the merge window. * accumulated work in next: (6809 commits) ufs: sb mutex merge + mutex_destroy powerpc: update comments for generic idle conversion cris: update comments for generic idle conversion idle: remove cpu_idle() forward declarations nbd: zero from and len fields in NBD_CMD_DISCONNECT. mm: convert some level-less printks to pr_* MAINTAINERS: adi-buildroot-devel is moderated MAINTAINERS: add linux-api for review of API/ABI changes mm/kmemleak-test.c: use pr_fmt for logging fs/dlm/debug_fs.c: replace seq_printf by seq_puts fs/dlm/lockspace.c: convert simple_str to kstr fs/dlm/config.c: convert simple_str to kstr mm: mark remap_file_pages() syscall as deprecated mm: memcontrol: remove unnecessary memcg argument from soft limit functions mm: memcontrol: clean up memcg zoneinfo lookup mm/memblock.c: call kmemleak directly from memblock_(alloc|free) mm/mempool.c: update the kmemleak stack trace for mempool allocations lib/radix-tree.c: update the kmemleak stack trace for radix tree allocations mm: introduce kmemleak_update_trace() mm/kmemleak.c: use %u to print ->checksum ...
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r--arch/arm/mach-exynos/pm.c219
1 files changed, 189 insertions, 30 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index 15af0ceb0a66..87c0d34c7fba 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -16,6 +16,7 @@
#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>
@@ -26,7 +27,6 @@
#include <asm/smp_scu.h>
#include <asm/suspend.h>
-#include <plat/cpu.h>
#include <plat/pm-common.h>
#include <plat/pll.h>
#include <plat/regs-srom.h>
@@ -100,9 +100,141 @@ static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
return -ENOENT;
}
+/**
+ * exynos_core_power_down : power down the specified cpu
+ * @cpu : the cpu to power down
+ *
+ * Power down the specified cpu. The sequence must be finished by a
+ * call to cpu_do_idle()
+ *
+ */
+void exynos_cpu_power_down(int cpu)
+{
+ __raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+}
+
+/**
+ * exynos_cpu_power_up : power up the specified cpu
+ * @cpu : the cpu to power up
+ *
+ * Power up the specified cpu
+ */
+void exynos_cpu_power_up(int cpu)
+{
+ __raw_writel(S5P_CORE_LOCAL_PWR_EN,
+ EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+}
+
+/**
+ * exynos_cpu_power_state : returns the power state of the cpu
+ * @cpu : the cpu to retrieve the power state from
+ *
+ */
+int exynos_cpu_power_state(int cpu)
+{
+ return (__raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
+ S5P_CORE_LOCAL_PWR_EN);
+}
+
+/**
+ * exynos_cluster_power_down : power down the specified cluster
+ * @cluster : the cluster to power down
+ */
+void exynos_cluster_power_down(int cluster)
+{
+ __raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
+}
+
+/**
+ * exynos_cluster_power_up : power up the specified cluster
+ * @cluster : the cluster to power up
+ */
+void exynos_cluster_power_up(int cluster)
+{
+ __raw_writel(S5P_CORE_LOCAL_PWR_EN,
+ EXYNOS_COMMON_CONFIGURATION(cluster));
+}
+
+/**
+ * exynos_cluster_power_state : returns the power state of the cluster
+ * @cluster : the cluster to retrieve the power state from
+ *
+ */
+int exynos_cluster_power_state(int cluster)
+{
+ return (__raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
+ S5P_CORE_LOCAL_PWR_EN);
+}
+
+#define EXYNOS_BOOT_VECTOR_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \
+ S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
+ (sysram_base_addr + 0x24) : S5P_INFORM0))
+#define EXYNOS_BOOT_VECTOR_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \
+ S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
+ (sysram_base_addr + 0x20) : S5P_INFORM1))
+
+#define S5P_CHECK_AFTR 0xFCBA0D10
+#define S5P_CHECK_SLEEP 0x00000BAD
+
+/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
+static void exynos_set_wakeupmask(long mask)
+{
+ __raw_writel(mask, S5P_WAKEUP_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);
+}
+
+void exynos_enter_aftr(void)
+{
+ 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);
+}
+
/* For Cortex-A9 Diagnostic and Power control register */
static unsigned int save_arm_register[2];
+static void exynos_cpu_save_register(void)
+{
+ unsigned long tmp;
+
+ /* Save Power control register */
+ asm ("mrc p15, 0, %0, c15, c0, 0"
+ : "=r" (tmp) : : "cc");
+
+ save_arm_register[0] = tmp;
+
+ /* Save Diagnostic register */
+ asm ("mrc p15, 0, %0, c15, c0, 1"
+ : "=r" (tmp) : : "cc");
+
+ save_arm_register[1] = tmp;
+}
+
+static void exynos_cpu_restore_register(void)
+{
+ unsigned long tmp;
+
+ /* Restore Power control register */
+ tmp = save_arm_register[0];
+
+ asm volatile ("mcr p15, 0, %0, c15, c0, 0"
+ : : "r" (tmp)
+ : "cc");
+
+ /* Restore Diagnostic register */
+ tmp = save_arm_register[1];
+
+ asm volatile ("mcr p15, 0, %0, c15, c0, 1"
+ : : "r" (tmp)
+ : "cc");
+}
+
static int exynos_cpu_suspend(unsigned long arg)
{
#ifdef CONFIG_CACHE_L2X0
@@ -147,37 +279,34 @@ static void exynos_pm_prepare(void)
__raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
}
-static int exynos_pm_suspend(void)
+static void exynos_pm_central_suspend(void)
{
unsigned long tmp;
/* Setting Central Sequence Register for power down mode */
-
tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
+}
+
+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);
__raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
- if (!soc_is_exynos5250()) {
- /* Save Power control register */
- asm ("mrc p15, 0, %0, c15, c0, 0"
- : "=r" (tmp) : : "cc");
- save_arm_register[0] = tmp;
-
- /* Save Diagnostic register */
- asm ("mrc p15, 0, %0, c15, c0, 1"
- : "=r" (tmp) : : "cc");
- save_arm_register[1] = tmp;
- }
+ if (!soc_is_exynos5250())
+ exynos_cpu_save_register();
return 0;
}
-static void exynos_pm_resume(void)
+static int exynos_pm_central_resume(void)
{
unsigned long tmp;
@@ -194,22 +323,20 @@ static void exynos_pm_resume(void)
/* clear the wakeup state register */
__raw_writel(0x0, S5P_WAKEUP_STAT);
/* No need to perform below restore code */
- goto early_wakeup;
- }
- if (!soc_is_exynos5250()) {
- /* Restore Power control register */
- tmp = save_arm_register[0];
- asm volatile ("mcr p15, 0, %0, c15, c0, 0"
- : : "r" (tmp)
- : "cc");
-
- /* Restore Diagnostic register */
- tmp = save_arm_register[1];
- asm volatile ("mcr p15, 0, %0, c15, c0, 1"
- : : "r" (tmp)
- : "cc");
+ return -1;
}
+ return 0;
+}
+
+static void exynos_pm_resume(void)
+{
+ if (exynos_pm_central_resume())
+ goto early_wakeup;
+
+ if (!soc_is_exynos5250())
+ exynos_cpu_restore_register();
+
/* For release retention */
__raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
@@ -226,7 +353,7 @@ static void exynos_pm_resume(void)
s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
- if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250())
+ if (!soc_is_exynos5250())
scu_enable(S5P_VA_SCU);
early_wakeup:
@@ -304,10 +431,42 @@ static const struct platform_suspend_ops exynos_suspend_ops = {
.valid = suspend_valid_only_mem,
};
+static int exynos_cpu_pm_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ int cpu = smp_processor_id();
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (cpu == 0) {
+ exynos_pm_central_suspend();
+ exynos_cpu_save_register();
+ }
+ break;
+
+ case CPU_PM_EXIT:
+ if (cpu == 0) {
+ if (!soc_is_exynos5250())
+ scu_enable(S5P_VA_SCU);
+ exynos_cpu_restore_register();
+ exynos_pm_central_resume();
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block exynos_cpu_pm_notifier_block = {
+ .notifier_call = exynos_cpu_pm_notifier,
+};
+
void __init exynos_pm_init(void)
{
u32 tmp;
+ cpu_pm_register_notifier(&exynos_cpu_pm_notifier_block);
+
/* Platform-specific GIC callback */
gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
OpenPOWER on IntegriCloud