ARM: EXYNOS: Fix build with PM_SLEEP=n and ARM_EXYNOS_CPUIDLE=y
[cascardo/linux.git] / arch / arm / mach-exynos / suspend.c
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  */
46 struct exynos_wkup_irq {
47         unsigned int hwirq;
48         u32 mask;
49 };
50
51 static struct sleep_save exynos5_sys_save[] = {
52         SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
53 };
54
55 static 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
64 struct 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
77 struct exynos_pm_data *pm_data;
78
79 /*
80  * GIC wake-up support
81  */
82
83 static u32 exynos_irqwake_intmask = 0xffffffff;
84
85 static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
86         { 76, BIT(1) }, /* RTC alarm */
87         { 77, BIT(2) }, /* RTC tick */
88         { /* sentinel */ },
89 };
90
91 static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
92         { 75, BIT(1) }, /* RTC alarm */
93         { 76, BIT(2) }, /* RTC tick */
94         { /* sentinel */ },
95 };
96
97 unsigned 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
108 static 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
130 static 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
139 static int exynos_cpu_suspend(unsigned long arg)
140 {
141         flush_cache_all();
142         outer_flush_all();
143         return exynos_cpu_do_idle();
144 }
145
146 static 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
153 static 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
163 static 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
177 static 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
187 static 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
196 static 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
219 early_wakeup:
220
221         /* Clear SLEEP mode set in INFORM1 */
222         pmu_raw_writel(0x0, S5P_INFORM1);
223 }
224
225 /*
226  * Suspend Ops
227  */
228
229 static 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
271 static int exynos_suspend_prepare(void)
272 {
273         s3c_pm_check_prepare();
274
275         return 0;
276 }
277
278 static void exynos_suspend_finish(void)
279 {
280         s3c_pm_check_cleanup();
281 }
282
283 static 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
290 static 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
300 static 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
312 static 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
329 static struct syscore_ops exynos_pm_syscore_ops;
330
331 void __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 }