diff options
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 172 |
1 files changed, 131 insertions, 41 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index ba18214c9aca..15af0ceb0a66 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c | |||
@@ -17,24 +17,35 @@ | |||
17 | #include <linux/suspend.h> | 17 | #include <linux/suspend.h> |
18 | #include <linux/syscore_ops.h> | 18 | #include <linux/syscore_ops.h> |
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | #include <linux/irqchip/arm-gic.h> | ||
20 | #include <linux/err.h> | 21 | #include <linux/err.h> |
21 | #include <linux/clk.h> | 22 | #include <linux/clk.h> |
22 | 23 | ||
23 | #include <asm/cacheflush.h> | 24 | #include <asm/cacheflush.h> |
24 | #include <asm/hardware/cache-l2x0.h> | 25 | #include <asm/hardware/cache-l2x0.h> |
25 | #include <asm/smp_scu.h> | 26 | #include <asm/smp_scu.h> |
27 | #include <asm/suspend.h> | ||
26 | 28 | ||
27 | #include <plat/cpu.h> | 29 | #include <plat/cpu.h> |
28 | #include <plat/pm.h> | 30 | #include <plat/pm-common.h> |
29 | #include <plat/pll.h> | 31 | #include <plat/pll.h> |
30 | #include <plat/regs-srom.h> | 32 | #include <plat/regs-srom.h> |
31 | 33 | ||
32 | #include <mach/map.h> | 34 | #include <mach/map.h> |
33 | #include <mach/pm-core.h> | ||
34 | 35 | ||
35 | #include "common.h" | 36 | #include "common.h" |
36 | #include "regs-pmu.h" | 37 | #include "regs-pmu.h" |
37 | 38 | ||
39 | /** | ||
40 | * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping | ||
41 | * @hwirq: Hardware IRQ signal of the GIC | ||
42 | * @mask: Mask in PMU wake-up mask register | ||
43 | */ | ||
44 | struct exynos_wkup_irq { | ||
45 | unsigned int hwirq; | ||
46 | u32 mask; | ||
47 | }; | ||
48 | |||
38 | static struct sleep_save exynos5_sys_save[] = { | 49 | static struct sleep_save exynos5_sys_save[] = { |
39 | SAVE_ITEM(EXYNOS5_SYS_I2C_CFG), | 50 | SAVE_ITEM(EXYNOS5_SYS_I2C_CFG), |
40 | }; | 51 | }; |
@@ -48,6 +59,46 @@ static struct sleep_save exynos_core_save[] = { | |||
48 | SAVE_ITEM(S5P_SROM_BC3), | 59 | SAVE_ITEM(S5P_SROM_BC3), |
49 | }; | 60 | }; |
50 | 61 | ||
62 | /* | ||
63 | * GIC wake-up support | ||
64 | */ | ||
65 | |||
66 | static u32 exynos_irqwake_intmask = 0xffffffff; | ||
67 | |||
68 | static const struct exynos_wkup_irq exynos4_wkup_irq[] = { | ||
69 | { 76, BIT(1) }, /* RTC alarm */ | ||
70 | { 77, BIT(2) }, /* RTC tick */ | ||
71 | { /* sentinel */ }, | ||
72 | }; | ||
73 | |||
74 | static const struct exynos_wkup_irq exynos5250_wkup_irq[] = { | ||
75 | { 75, BIT(1) }, /* RTC alarm */ | ||
76 | { 76, BIT(2) }, /* RTC tick */ | ||
77 | { /* sentinel */ }, | ||
78 | }; | ||
79 | |||
80 | static int exynos_irq_set_wake(struct irq_data *data, unsigned int state) | ||
81 | { | ||
82 | const struct exynos_wkup_irq *wkup_irq; | ||
83 | |||
84 | if (soc_is_exynos5250()) | ||
85 | wkup_irq = exynos5250_wkup_irq; | ||
86 | else | ||
87 | wkup_irq = exynos4_wkup_irq; | ||
88 | |||
89 | while (wkup_irq->mask) { | ||
90 | if (wkup_irq->hwirq == data->hwirq) { | ||
91 | if (!state) | ||
92 | exynos_irqwake_intmask |= wkup_irq->mask; | ||
93 | else | ||
94 | exynos_irqwake_intmask &= ~wkup_irq->mask; | ||
95 | return 0; | ||
96 | } | ||
97 | ++wkup_irq; | ||
98 | } | ||
99 | |||
100 | return -ENOENT; | ||
101 | } | ||
51 | 102 | ||
52 | /* For Cortex-A9 Diagnostic and Power control register */ | 103 | /* For Cortex-A9 Diagnostic and Power control register */ |
53 | static unsigned int save_arm_register[2]; | 104 | static unsigned int save_arm_register[2]; |
@@ -72,6 +123,10 @@ static void exynos_pm_prepare(void) | |||
72 | { | 123 | { |
73 | unsigned int tmp; | 124 | unsigned int tmp; |
74 | 125 | ||
126 | /* Set wake-up mask registers */ | ||
127 | __raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK); | ||
128 | __raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK); | ||
129 | |||
75 | s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); | 130 | s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); |
76 | 131 | ||
77 | if (soc_is_exynos5250()) { | 132 | if (soc_is_exynos5250()) { |
@@ -89,41 +144,8 @@ static void exynos_pm_prepare(void) | |||
89 | 144 | ||
90 | /* ensure at least INFORM0 has the resume address */ | 145 | /* ensure at least INFORM0 has the resume address */ |
91 | 146 | ||
92 | __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); | 147 | __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); |
93 | } | ||
94 | |||
95 | static int exynos_pm_add(struct device *dev, struct subsys_interface *sif) | ||
96 | { | ||
97 | pm_cpu_prep = exynos_pm_prepare; | ||
98 | pm_cpu_sleep = exynos_cpu_suspend; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static struct subsys_interface exynos_pm_interface = { | ||
104 | .name = "exynos_pm", | ||
105 | .subsys = &exynos_subsys, | ||
106 | .add_dev = exynos_pm_add, | ||
107 | }; | ||
108 | |||
109 | static __init int exynos_pm_drvinit(void) | ||
110 | { | ||
111 | unsigned int tmp; | ||
112 | |||
113 | if (soc_is_exynos5440()) | ||
114 | return 0; | ||
115 | |||
116 | s3c_pm_init(); | ||
117 | |||
118 | /* All wakeup disable */ | ||
119 | |||
120 | tmp = __raw_readl(S5P_WAKEUP_MASK); | ||
121 | tmp |= ((0xFF << 8) | (0x1F << 1)); | ||
122 | __raw_writel(tmp, S5P_WAKEUP_MASK); | ||
123 | |||
124 | return subsys_interface_register(&exynos_pm_interface); | ||
125 | } | 148 | } |
126 | arch_initcall(exynos_pm_drvinit); | ||
127 | 149 | ||
128 | static int exynos_pm_suspend(void) | 150 | static int exynos_pm_suspend(void) |
129 | { | 151 | { |
@@ -220,12 +242,80 @@ static struct syscore_ops exynos_pm_syscore_ops = { | |||
220 | .resume = exynos_pm_resume, | 242 | .resume = exynos_pm_resume, |
221 | }; | 243 | }; |
222 | 244 | ||
223 | static __init int exynos_pm_syscore_init(void) | 245 | /* |
246 | * Suspend Ops | ||
247 | */ | ||
248 | |||
249 | static int exynos_suspend_enter(suspend_state_t state) | ||
224 | { | 250 | { |
225 | if (soc_is_exynos5440()) | 251 | int ret; |
226 | return 0; | 252 | |
253 | s3c_pm_debug_init(); | ||
254 | |||
255 | S3C_PMDBG("%s: suspending the system...\n", __func__); | ||
256 | |||
257 | S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__, | ||
258 | exynos_irqwake_intmask, exynos_get_eint_wake_mask()); | ||
259 | |||
260 | if (exynos_irqwake_intmask == -1U | ||
261 | && exynos_get_eint_wake_mask() == -1U) { | ||
262 | pr_err("%s: No wake-up sources!\n", __func__); | ||
263 | pr_err("%s: Aborting sleep\n", __func__); | ||
264 | return -EINVAL; | ||
265 | } | ||
266 | |||
267 | s3c_pm_save_uarts(); | ||
268 | exynos_pm_prepare(); | ||
269 | flush_cache_all(); | ||
270 | s3c_pm_check_store(); | ||
271 | |||
272 | ret = cpu_suspend(0, exynos_cpu_suspend); | ||
273 | if (ret) | ||
274 | return ret; | ||
275 | |||
276 | s3c_pm_restore_uarts(); | ||
277 | |||
278 | S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, | ||
279 | __raw_readl(S5P_WAKEUP_STAT)); | ||
280 | |||
281 | s3c_pm_check_restore(); | ||
282 | |||
283 | S3C_PMDBG("%s: resuming the system...\n", __func__); | ||
227 | 284 | ||
228 | register_syscore_ops(&exynos_pm_syscore_ops); | ||
229 | return 0; | 285 | return 0; |
230 | } | 286 | } |
231 | arch_initcall(exynos_pm_syscore_init); | 287 | |
288 | static int exynos_suspend_prepare(void) | ||
289 | { | ||
290 | s3c_pm_check_prepare(); | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static void exynos_suspend_finish(void) | ||
296 | { | ||
297 | s3c_pm_check_cleanup(); | ||
298 | } | ||
299 | |||
300 | static const struct platform_suspend_ops exynos_suspend_ops = { | ||
301 | .enter = exynos_suspend_enter, | ||
302 | .prepare = exynos_suspend_prepare, | ||
303 | .finish = exynos_suspend_finish, | ||
304 | .valid = suspend_valid_only_mem, | ||
305 | }; | ||
306 | |||
307 | void __init exynos_pm_init(void) | ||
308 | { | ||
309 | u32 tmp; | ||
310 | |||
311 | /* Platform-specific GIC callback */ | ||
312 | gic_arch_extn.irq_set_wake = exynos_irq_set_wake; | ||
313 | |||
314 | /* All wakeup disable */ | ||
315 | tmp = __raw_readl(S5P_WAKEUP_MASK); | ||
316 | tmp |= ((0xFF << 8) | (0x1F << 1)); | ||
317 | __raw_writel(tmp, S5P_WAKEUP_MASK); | ||
318 | |||
319 | register_syscore_ops(&exynos_pm_syscore_ops); | ||
320 | suspend_set_ops(&exynos_suspend_ops); | ||
321 | } | ||