aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>2014-09-25 05:02:45 -0400
committerKukjin Kim <kgene.kim@samsung.com>2014-10-20 11:06:36 -0400
commit0d713cf1a6286aae6a12affab0887dbe2a4fbb83 (patch)
tree19a22890d12339ebfbd2d31fbe5843dc14cbc085
parent42d5dc37866610448453859bf2c4be57fce3834c (diff)
ARM: EXYNOS: Fix build with PM_SLEEP=n and ARM_EXYNOS_CPUIDLE=y
Fix building of exynos_defconfig with CONFIG_PM_SLEEP disabled and CONFIG_ARM_EXYNOS_CPUIDLE enabled by: * adding EXYNOS_CPU_SUSPEND config option * always building sleep.o * building pm.o if EXYNOS_CPU_SUSPEND is enabled * moving suspend specific code from pm.c to suspend.c * enabling pm-common.o build also for EXYNOS_CPU_SUSPEND option [ Please note that there are no changes in the code moved from pm.c to suspend.c except making few functions non-static and cleaning up includes. ] Also while at it update Copyright dates. The build error messages: drivers/built-in.o: In function `exynos_enter_core0_aftr': /home/bzolnier/linux/drivers/cpuidle/cpuidle-exynos.c:36: undefined reference to `cpu_suspend' arch/arm/mach-exynos/built-in.o:(.data+0x74): undefined reference to `exynos_enter_aftr' make: *** [vmlinux] Error 1 This patch has been tested on Exynos4210 based Origen board. Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r--arch/arm/mach-exynos/Kconfig5
-rw-r--r--arch/arm/mach-exynos/Makefile5
-rw-r--r--arch/arm/mach-exynos/common.h4
-rw-r--r--arch/arm/mach-exynos/pm.c338
-rw-r--r--arch/arm/mach-exynos/suspend.c356
-rw-r--r--arch/arm/plat-samsung/Makefile1
6 files changed, 374 insertions, 335 deletions
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 2d0240f241b8..46f3c0d0d01f 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -123,4 +123,9 @@ config EXYNOS5420_MCPM
123 This is needed to provide CPU and cluster power management 123 This is needed to provide CPU and cluster power management
124 on Exynos5420 implementing big.LITTLE. 124 on Exynos5420 implementing big.LITTLE.
125 125
126config EXYNOS_CPU_SUSPEND
127 bool
128 select ARM_CPU_SUSPEND
129 default PM_SLEEP || ARM_EXYNOS_CPUIDLE
130
126endif 131endif
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 45bef21a3b66..3d680f2a5520 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -9,9 +9,10 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) += -I$(srctree)/$(src)/include -I$(srctree)
9 9
10# Core 10# Core
11 11
12obj-$(CONFIG_ARCH_EXYNOS) += exynos.o pmu.o exynos-smc.o firmware.o 12obj-$(CONFIG_ARCH_EXYNOS) += exynos.o pmu.o exynos-smc.o firmware.o sleep.o
13 13
14obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o 14obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o
15obj-$(CONFIG_PM_SLEEP) += suspend.o
15obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o 16obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
16 17
17obj-$(CONFIG_SMP) += platsmp.o headsmp.o 18obj-$(CONFIG_SMP) += platsmp.o headsmp.o
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index 2d830df0f647..ef95cb1cc00a 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -164,6 +164,10 @@ extern int exynos_cpu_power_state(int cpu);
164extern void exynos_cluster_power_down(int cluster); 164extern void exynos_cluster_power_down(int cluster);
165extern void exynos_cluster_power_up(int cluster); 165extern void exynos_cluster_power_up(int cluster);
166extern int exynos_cluster_power_state(int cluster); 166extern int exynos_cluster_power_state(int cluster);
167extern void exynos_cpu_save_register(void);
168extern void exynos_cpu_restore_register(void);
169extern void exynos_pm_central_suspend(void);
170extern int exynos_pm_central_resume(void);
167extern void exynos_enter_aftr(void); 171extern void exynos_enter_aftr(void);
168 172
169extern void s5p_init_cpu(void __iomem *cpuid_addr); 173extern void s5p_init_cpu(void __iomem *cpuid_addr);
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index 16c5c3206f14..4f10fa6bfe10 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. 2 * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com 3 * http://www.samsung.com
4 * 4 *
5 * EXYNOS - Power Management support 5 * EXYNOS - Power Management support
@@ -15,119 +15,20 @@
15 15
16#include <linux/init.h> 16#include <linux/init.h>
17#include <linux/suspend.h> 17#include <linux/suspend.h>
18#include <linux/syscore_ops.h>
19#include <linux/cpu_pm.h> 18#include <linux/cpu_pm.h>
20#include <linux/io.h> 19#include <linux/io.h>
21#include <linux/irqchip/arm-gic.h>
22#include <linux/err.h> 20#include <linux/err.h>
23#include <linux/clk.h>
24 21
25#include <asm/cacheflush.h>
26#include <asm/firmware.h> 22#include <asm/firmware.h>
27#include <asm/hardware/cache-l2x0.h>
28#include <asm/smp_scu.h> 23#include <asm/smp_scu.h>
29#include <asm/suspend.h> 24#include <asm/suspend.h>
30 25
31#include <plat/pm-common.h> 26#include <plat/pm-common.h>
32#include <plat/regs-srom.h>
33
34#include <mach/map.h>
35 27
36#include "common.h" 28#include "common.h"
37#include "regs-pmu.h" 29#include "regs-pmu.h"
38#include "regs-sys.h" 30#include "regs-sys.h"
39 31
40#define REG_TABLE_END (-1U)
41
42/**
43 * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
44 * @hwirq: Hardware IRQ signal of the GIC
45 * @mask: Mask in PMU wake-up mask register
46 */
47struct exynos_wkup_irq {
48 unsigned int hwirq;
49 u32 mask;
50};
51
52static struct sleep_save exynos5_sys_save[] = {
53 SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
54};
55
56static struct sleep_save exynos_core_save[] = {
57 /* SROM side */
58 SAVE_ITEM(S5P_SROM_BW),
59 SAVE_ITEM(S5P_SROM_BC0),
60 SAVE_ITEM(S5P_SROM_BC1),
61 SAVE_ITEM(S5P_SROM_BC2),
62 SAVE_ITEM(S5P_SROM_BC3),
63};
64
65struct exynos_pm_data {
66 const struct exynos_wkup_irq *wkup_irq;
67 struct sleep_save *extra_save;
68 int num_extra_save;
69 unsigned int wake_disable_mask;
70 unsigned int *release_ret_regs;
71
72 void (*pm_prepare)(void);
73 void (*pm_resume)(void);
74 int (*pm_suspend)(void);
75 int (*cpu_suspend)(unsigned long);
76};
77
78struct exynos_pm_data *pm_data;
79
80/*
81 * GIC wake-up support
82 */
83
84static u32 exynos_irqwake_intmask = 0xffffffff;
85
86static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
87 { 76, BIT(1) }, /* RTC alarm */
88 { 77, BIT(2) }, /* RTC tick */
89 { /* sentinel */ },
90};
91
92static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
93 { 75, BIT(1) }, /* RTC alarm */
94 { 76, BIT(2) }, /* RTC tick */
95 { /* sentinel */ },
96};
97
98unsigned int exynos_release_ret_regs[] = {
99 S5P_PAD_RET_MAUDIO_OPTION,
100 S5P_PAD_RET_GPIO_OPTION,
101 S5P_PAD_RET_UART_OPTION,
102 S5P_PAD_RET_MMCA_OPTION,
103 S5P_PAD_RET_MMCB_OPTION,
104 S5P_PAD_RET_EBIA_OPTION,
105 S5P_PAD_RET_EBIB_OPTION,
106 REG_TABLE_END,
107};
108
109static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
110{
111 const struct exynos_wkup_irq *wkup_irq;
112
113 if (!pm_data->wkup_irq)
114 return -ENOENT;
115 wkup_irq = pm_data->wkup_irq;
116
117 while (wkup_irq->mask) {
118 if (wkup_irq->hwirq == data->hwirq) {
119 if (!state)
120 exynos_irqwake_intmask |= wkup_irq->mask;
121 else
122 exynos_irqwake_intmask &= ~wkup_irq->mask;
123 return 0;
124 }
125 ++wkup_irq;
126 }
127
128 return -ENOENT;
129}
130
131static inline void __iomem *exynos_boot_vector_addr(void) 32static inline void __iomem *exynos_boot_vector_addr(void)
132{ 33{
133 if (samsung_rev() == EXYNOS4210_REV_1_1) 34 if (samsung_rev() == EXYNOS4210_REV_1_1)
@@ -147,12 +48,11 @@ static inline void __iomem *exynos_boot_vector_flag(void)
147} 48}
148 49
149#define S5P_CHECK_AFTR 0xFCBA0D10 50#define S5P_CHECK_AFTR 0xFCBA0D10
150#define S5P_CHECK_SLEEP 0x00000BAD
151 51
152/* For Cortex-A9 Diagnostic and Power control register */ 52/* For Cortex-A9 Diagnostic and Power control register */
153static unsigned int save_arm_register[2]; 53static unsigned int save_arm_register[2];
154 54
155static void exynos_cpu_save_register(void) 55void exynos_cpu_save_register(void)
156{ 56{
157 unsigned long tmp; 57 unsigned long tmp;
158 58
@@ -169,7 +69,7 @@ static void exynos_cpu_save_register(void)
169 save_arm_register[1] = tmp; 69 save_arm_register[1] = tmp;
170} 70}
171 71
172static void exynos_cpu_restore_register(void) 72void exynos_cpu_restore_register(void)
173{ 73{
174 unsigned long tmp; 74 unsigned long tmp;
175 75
@@ -188,7 +88,7 @@ static void exynos_cpu_restore_register(void)
188 : "cc"); 88 : "cc");
189} 89}
190 90
191static void exynos_pm_central_suspend(void) 91void exynos_pm_central_suspend(void)
192{ 92{
193 unsigned long tmp; 93 unsigned long tmp;
194 94
@@ -202,7 +102,7 @@ static void exynos_pm_central_suspend(void)
202 S5P_CENTRAL_SEQ_OPTION); 102 S5P_CENTRAL_SEQ_OPTION);
203} 103}
204 104
205static int exynos_pm_central_resume(void) 105int exynos_pm_central_resume(void)
206{ 106{
207 unsigned long tmp; 107 unsigned long tmp;
208 108
@@ -275,231 +175,3 @@ void exynos_enter_aftr(void)
275 175
276 cpu_pm_exit(); 176 cpu_pm_exit();
277} 177}
278
279static int exynos_cpu_do_idle(void)
280{
281 /* issue the standby signal into the pm unit. */
282 cpu_do_idle();
283
284 pr_info("Failed to suspend the system\n");
285 return 1; /* Aborting suspend */
286}
287
288static int exynos_cpu_suspend(unsigned long arg)
289{
290 flush_cache_all();
291 outer_flush_all();
292 return exynos_cpu_do_idle();
293}
294
295static void exynos_pm_set_wakeup_mask(void)
296{
297 /* Set wake-up mask registers */
298 pmu_raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
299 pmu_raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
300}
301
302static void exynos_pm_enter_sleep_mode(void)
303{
304 /* Set value of power down register for sleep mode */
305 exynos_sys_powerdown_conf(SYS_SLEEP);
306 pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
307
308 /* ensure at least INFORM0 has the resume address */
309 pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
310}
311
312static void exynos_pm_prepare(void)
313{
314 /* Set wake-up mask registers */
315 exynos_pm_set_wakeup_mask();
316
317 s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
318
319 if (pm_data->extra_save)
320 s3c_pm_do_save(pm_data->extra_save,
321 pm_data->num_extra_save);
322
323 exynos_pm_enter_sleep_mode();
324}
325
326static int exynos_pm_suspend(void)
327{
328 exynos_pm_central_suspend();
329
330 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
331 exynos_cpu_save_register();
332
333 return 0;
334}
335
336static void exynos_pm_release_retention(void)
337{
338 unsigned int i;
339
340 for (i = 0; (pm_data->release_ret_regs[i] != REG_TABLE_END); i++)
341 pmu_raw_writel(EXYNOS_WAKEUP_FROM_LOWPWR,
342 pm_data->release_ret_regs[i]);
343}
344
345static void exynos_pm_resume(void)
346{
347 u32 cpuid = read_cpuid_part();
348
349 if (exynos_pm_central_resume())
350 goto early_wakeup;
351
352 /* For release retention */
353 exynos_pm_release_retention();
354
355 if (pm_data->extra_save)
356 s3c_pm_do_restore_core(pm_data->extra_save,
357 pm_data->num_extra_save);
358
359 s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
360
361 if (cpuid == ARM_CPU_PART_CORTEX_A9)
362 scu_enable(S5P_VA_SCU);
363
364 if (call_firmware_op(resume) == -ENOSYS
365 && cpuid == ARM_CPU_PART_CORTEX_A9)
366 exynos_cpu_restore_register();
367
368early_wakeup:
369
370 /* Clear SLEEP mode set in INFORM1 */
371 pmu_raw_writel(0x0, S5P_INFORM1);
372}
373
374/*
375 * Suspend Ops
376 */
377
378static int exynos_suspend_enter(suspend_state_t state)
379{
380 int ret;
381
382 s3c_pm_debug_init();
383
384 S3C_PMDBG("%s: suspending the system...\n", __func__);
385
386 S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
387 exynos_irqwake_intmask, exynos_get_eint_wake_mask());
388
389 if (exynos_irqwake_intmask == -1U
390 && exynos_get_eint_wake_mask() == -1U) {
391 pr_err("%s: No wake-up sources!\n", __func__);
392 pr_err("%s: Aborting sleep\n", __func__);
393 return -EINVAL;
394 }
395
396 s3c_pm_save_uarts();
397 if (pm_data->pm_prepare)
398 pm_data->pm_prepare();
399 flush_cache_all();
400 s3c_pm_check_store();
401
402 ret = call_firmware_op(suspend);
403 if (ret == -ENOSYS)
404 ret = cpu_suspend(0, pm_data->cpu_suspend);
405 if (ret)
406 return ret;
407
408 s3c_pm_restore_uarts();
409
410 S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
411 pmu_raw_readl(S5P_WAKEUP_STAT));
412
413 s3c_pm_check_restore();
414
415 S3C_PMDBG("%s: resuming the system...\n", __func__);
416
417 return 0;
418}
419
420static int exynos_suspend_prepare(void)
421{
422 s3c_pm_check_prepare();
423
424 return 0;
425}
426
427static void exynos_suspend_finish(void)
428{
429 s3c_pm_check_cleanup();
430}
431
432static const struct platform_suspend_ops exynos_suspend_ops = {
433 .enter = exynos_suspend_enter,
434 .prepare = exynos_suspend_prepare,
435 .finish = exynos_suspend_finish,
436 .valid = suspend_valid_only_mem,
437};
438
439static const struct exynos_pm_data exynos4_pm_data = {
440 .wkup_irq = exynos4_wkup_irq,
441 .wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
442 .release_ret_regs = exynos_release_ret_regs,
443 .pm_suspend = exynos_pm_suspend,
444 .pm_resume = exynos_pm_resume,
445 .pm_prepare = exynos_pm_prepare,
446 .cpu_suspend = exynos_cpu_suspend,
447};
448
449static const struct exynos_pm_data exynos5250_pm_data = {
450 .wkup_irq = exynos5250_wkup_irq,
451 .wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
452 .release_ret_regs = exynos_release_ret_regs,
453 .extra_save = exynos5_sys_save,
454 .num_extra_save = ARRAY_SIZE(exynos5_sys_save),
455 .pm_suspend = exynos_pm_suspend,
456 .pm_resume = exynos_pm_resume,
457 .pm_prepare = exynos_pm_prepare,
458 .cpu_suspend = exynos_cpu_suspend,
459};
460
461static struct of_device_id exynos_pmu_of_device_ids[] = {
462 {
463 .compatible = "samsung,exynos4210-pmu",
464 .data = &exynos4_pm_data,
465 }, {
466 .compatible = "samsung,exynos4212-pmu",
467 .data = &exynos4_pm_data,
468 }, {
469 .compatible = "samsung,exynos4412-pmu",
470 .data = &exynos4_pm_data,
471 }, {
472 .compatible = "samsung,exynos5250-pmu",
473 .data = &exynos5250_pm_data,
474 },
475 { /*sentinel*/ },
476};
477
478static struct syscore_ops exynos_pm_syscore_ops;
479
480void __init exynos_pm_init(void)
481{
482 const struct of_device_id *match;
483 u32 tmp;
484
485 of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
486 if (!match) {
487 pr_err("Failed to find PMU node\n");
488 return;
489 }
490 pm_data = (struct exynos_pm_data *) match->data;
491
492 /* Platform-specific GIC callback */
493 gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
494
495 /* All wakeup disable */
496 tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
497 tmp |= pm_data->wake_disable_mask;
498 pmu_raw_writel(tmp, S5P_WAKEUP_MASK);
499
500 exynos_pm_syscore_ops.suspend = pm_data->pm_suspend;
501 exynos_pm_syscore_ops.resume = pm_data->pm_resume;
502
503 register_syscore_ops(&exynos_pm_syscore_ops);
504 suspend_set_ops(&exynos_suspend_ops);
505}
diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c
new file mode 100644
index 000000000000..f5d9773066eb
--- /dev/null
+++ b/arch/arm/mach-exynos/suspend.c
@@ -0,0 +1,356 @@
1/*
2 * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * EXYNOS - Suspend support
6 *
7 * Based on arch/arm/mach-s3c2410/pm.c
8 * Copyright (c) 2006 Simtec Electronics
9 * Ben Dooks <ben@simtec.co.uk>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/init.h>
17#include <linux/suspend.h>
18#include <linux/syscore_ops.h>
19#include <linux/cpu_pm.h>
20#include <linux/io.h>
21#include <linux/irqchip/arm-gic.h>
22#include <linux/err.h>
23
24#include <asm/cacheflush.h>
25#include <asm/hardware/cache-l2x0.h>
26#include <asm/firmware.h>
27#include <asm/smp_scu.h>
28#include <asm/suspend.h>
29
30#include <plat/pm-common.h>
31#include <plat/regs-srom.h>
32
33#include "common.h"
34#include "regs-pmu.h"
35#include "regs-sys.h"
36
37#define S5P_CHECK_SLEEP 0x00000BAD
38
39#define REG_TABLE_END (-1U)
40
41/**
42 * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
43 * @hwirq: Hardware IRQ signal of the GIC
44 * @mask: Mask in PMU wake-up mask register
45 */
46struct exynos_wkup_irq {
47 unsigned int hwirq;
48 u32 mask;
49};
50
51static struct sleep_save exynos5_sys_save[] = {
52 SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
53};
54
55static struct sleep_save exynos_core_save[] = {
56 /* SROM side */
57 SAVE_ITEM(S5P_SROM_BW),
58 SAVE_ITEM(S5P_SROM_BC0),
59 SAVE_ITEM(S5P_SROM_BC1),
60 SAVE_ITEM(S5P_SROM_BC2),
61 SAVE_ITEM(S5P_SROM_BC3),
62};
63
64struct exynos_pm_data {
65 const struct exynos_wkup_irq *wkup_irq;
66 struct sleep_save *extra_save;
67 int num_extra_save;
68 unsigned int wake_disable_mask;
69 unsigned int *release_ret_regs;
70
71 void (*pm_prepare)(void);
72 void (*pm_resume)(void);
73 int (*pm_suspend)(void);
74 int (*cpu_suspend)(unsigned long);
75};
76
77struct exynos_pm_data *pm_data;
78
79/*
80 * GIC wake-up support
81 */
82
83static u32 exynos_irqwake_intmask = 0xffffffff;
84
85static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
86 { 76, BIT(1) }, /* RTC alarm */
87 { 77, BIT(2) }, /* RTC tick */
88 { /* sentinel */ },
89};
90
91static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
92 { 75, BIT(1) }, /* RTC alarm */
93 { 76, BIT(2) }, /* RTC tick */
94 { /* sentinel */ },
95};
96
97unsigned int exynos_release_ret_regs[] = {
98 S5P_PAD_RET_MAUDIO_OPTION,
99 S5P_PAD_RET_GPIO_OPTION,
100 S5P_PAD_RET_UART_OPTION,
101 S5P_PAD_RET_MMCA_OPTION,
102 S5P_PAD_RET_MMCB_OPTION,
103 S5P_PAD_RET_EBIA_OPTION,
104 S5P_PAD_RET_EBIB_OPTION,
105 REG_TABLE_END,
106};
107
108static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
109{
110 const struct exynos_wkup_irq *wkup_irq;
111
112 if (!pm_data->wkup_irq)
113 return -ENOENT;
114 wkup_irq = pm_data->wkup_irq;
115
116 while (wkup_irq->mask) {
117 if (wkup_irq->hwirq == data->hwirq) {
118 if (!state)
119 exynos_irqwake_intmask |= wkup_irq->mask;
120 else
121 exynos_irqwake_intmask &= ~wkup_irq->mask;
122 return 0;
123 }
124 ++wkup_irq;
125 }
126
127 return -ENOENT;
128}
129
130static int exynos_cpu_do_idle(void)
131{
132 /* issue the standby signal into the pm unit. */
133 cpu_do_idle();
134
135 pr_info("Failed to suspend the system\n");
136 return 1; /* Aborting suspend */
137}
138
139static int exynos_cpu_suspend(unsigned long arg)
140{
141 flush_cache_all();
142 outer_flush_all();
143 return exynos_cpu_do_idle();
144}
145
146static void exynos_pm_set_wakeup_mask(void)
147{
148 /* Set wake-up mask registers */
149 pmu_raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
150 pmu_raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
151}
152
153static void exynos_pm_enter_sleep_mode(void)
154{
155 /* Set value of power down register for sleep mode */
156 exynos_sys_powerdown_conf(SYS_SLEEP);
157 pmu_raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
158
159 /* ensure at least INFORM0 has the resume address */
160 pmu_raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
161}
162
163static void exynos_pm_prepare(void)
164{
165 /* Set wake-up mask registers */
166 exynos_pm_set_wakeup_mask();
167
168 s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
169
170 if (pm_data->extra_save)
171 s3c_pm_do_save(pm_data->extra_save,
172 pm_data->num_extra_save);
173
174 exynos_pm_enter_sleep_mode();
175}
176
177static int exynos_pm_suspend(void)
178{
179 exynos_pm_central_suspend();
180
181 if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
182 exynos_cpu_save_register();
183
184 return 0;
185}
186
187static void exynos_pm_release_retention(void)
188{
189 unsigned int i;
190
191 for (i = 0; (pm_data->release_ret_regs[i] != REG_TABLE_END); i++)
192 pmu_raw_writel(EXYNOS_WAKEUP_FROM_LOWPWR,
193 pm_data->release_ret_regs[i]);
194}
195
196static void exynos_pm_resume(void)
197{
198 u32 cpuid = read_cpuid_part();
199
200 if (exynos_pm_central_resume())
201 goto early_wakeup;
202
203 /* For release retention */
204 exynos_pm_release_retention();
205
206 if (pm_data->extra_save)
207 s3c_pm_do_restore_core(pm_data->extra_save,
208 pm_data->num_extra_save);
209
210 s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
211
212 if (cpuid == ARM_CPU_PART_CORTEX_A9)
213 scu_enable(S5P_VA_SCU);
214
215 if (call_firmware_op(resume) == -ENOSYS
216 && cpuid == ARM_CPU_PART_CORTEX_A9)
217 exynos_cpu_restore_register();
218
219early_wakeup:
220
221 /* Clear SLEEP mode set in INFORM1 */
222 pmu_raw_writel(0x0, S5P_INFORM1);
223}
224
225/*
226 * Suspend Ops
227 */
228
229static int exynos_suspend_enter(suspend_state_t state)
230{
231 int ret;
232
233 s3c_pm_debug_init();
234
235 S3C_PMDBG("%s: suspending the system...\n", __func__);
236
237 S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
238 exynos_irqwake_intmask, exynos_get_eint_wake_mask());
239
240 if (exynos_irqwake_intmask == -1U
241 && exynos_get_eint_wake_mask() == -1U) {
242 pr_err("%s: No wake-up sources!\n", __func__);
243 pr_err("%s: Aborting sleep\n", __func__);
244 return -EINVAL;
245 }
246
247 s3c_pm_save_uarts();
248 if (pm_data->pm_prepare)
249 pm_data->pm_prepare();
250 flush_cache_all();
251 s3c_pm_check_store();
252
253 ret = call_firmware_op(suspend);
254 if (ret == -ENOSYS)
255 ret = cpu_suspend(0, pm_data->cpu_suspend);
256 if (ret)
257 return ret;
258
259 s3c_pm_restore_uarts();
260
261 S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
262 pmu_raw_readl(S5P_WAKEUP_STAT));
263
264 s3c_pm_check_restore();
265
266 S3C_PMDBG("%s: resuming the system...\n", __func__);
267
268 return 0;
269}
270
271static int exynos_suspend_prepare(void)
272{
273 s3c_pm_check_prepare();
274
275 return 0;
276}
277
278static void exynos_suspend_finish(void)
279{
280 s3c_pm_check_cleanup();
281}
282
283static const struct platform_suspend_ops exynos_suspend_ops = {
284 .enter = exynos_suspend_enter,
285 .prepare = exynos_suspend_prepare,
286 .finish = exynos_suspend_finish,
287 .valid = suspend_valid_only_mem,
288};
289
290static const struct exynos_pm_data exynos4_pm_data = {
291 .wkup_irq = exynos4_wkup_irq,
292 .wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
293 .release_ret_regs = exynos_release_ret_regs,
294 .pm_suspend = exynos_pm_suspend,
295 .pm_resume = exynos_pm_resume,
296 .pm_prepare = exynos_pm_prepare,
297 .cpu_suspend = exynos_cpu_suspend,
298};
299
300static const struct exynos_pm_data exynos5250_pm_data = {
301 .wkup_irq = exynos5250_wkup_irq,
302 .wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
303 .release_ret_regs = exynos_release_ret_regs,
304 .extra_save = exynos5_sys_save,
305 .num_extra_save = ARRAY_SIZE(exynos5_sys_save),
306 .pm_suspend = exynos_pm_suspend,
307 .pm_resume = exynos_pm_resume,
308 .pm_prepare = exynos_pm_prepare,
309 .cpu_suspend = exynos_cpu_suspend,
310};
311
312static struct of_device_id exynos_pmu_of_device_ids[] = {
313 {
314 .compatible = "samsung,exynos4210-pmu",
315 .data = &exynos4_pm_data,
316 }, {
317 .compatible = "samsung,exynos4212-pmu",
318 .data = &exynos4_pm_data,
319 }, {
320 .compatible = "samsung,exynos4412-pmu",
321 .data = &exynos4_pm_data,
322 }, {
323 .compatible = "samsung,exynos5250-pmu",
324 .data = &exynos5250_pm_data,
325 },
326 { /*sentinel*/ },
327};
328
329static struct syscore_ops exynos_pm_syscore_ops;
330
331void __init exynos_pm_init(void)
332{
333 const struct of_device_id *match;
334 u32 tmp;
335
336 of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
337 if (!match) {
338 pr_err("Failed to find PMU node\n");
339 return;
340 }
341 pm_data = (struct exynos_pm_data *) match->data;
342
343 /* Platform-specific GIC callback */
344 gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
345
346 /* All wakeup disable */
347 tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
348 tmp |= pm_data->wake_disable_mask;
349 pmu_raw_writel(tmp, S5P_WAKEUP_MASK);
350
351 exynos_pm_syscore_ops.suspend = pm_data->pm_suspend;
352 exynos_pm_syscore_ops.resume = pm_data->pm_resume;
353
354 register_syscore_ops(&exynos_pm_syscore_ops);
355 suspend_set_ops(&exynos_suspend_ops);
356}
diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile
index f0a008496993..87746c37f030 100644
--- a/arch/arm/plat-samsung/Makefile
+++ b/arch/arm/plat-samsung/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o
35# PM support 35# PM support
36 36
37obj-$(CONFIG_PM_SLEEP) += pm-common.o 37obj-$(CONFIG_PM_SLEEP) += pm-common.o
38obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm-common.o
38obj-$(CONFIG_SAMSUNG_PM) += pm.o 39obj-$(CONFIG_SAMSUNG_PM) += pm.o
39obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o 40obj-$(CONFIG_SAMSUNG_PM_GPIO) += pm-gpio.o
40obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o 41obj-$(CONFIG_SAMSUNG_PM_CHECK) += pm-check.o