1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // Copyright (c) 2010-2014 Samsung Electronics Co., Ltd.
4*4882a593Smuzhiyun // http://www.samsung.com
5*4882a593Smuzhiyun //
6*4882a593Smuzhiyun // S5PV210 - Power Management support
7*4882a593Smuzhiyun //
8*4882a593Smuzhiyun // Based on arch/arm/mach-s3c2410/pm.c
9*4882a593Smuzhiyun // Copyright (c) 2006 Simtec Electronics
10*4882a593Smuzhiyun // Ben Dooks <ben@simtec.co.uk>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/init.h>
13*4882a593Smuzhiyun #include <linux/suspend.h>
14*4882a593Smuzhiyun #include <linux/syscore_ops.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <linux/soc/samsung/s3c-pm.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include <asm/cacheflush.h>
19*4882a593Smuzhiyun #include <asm/suspend.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #include "common.h"
22*4882a593Smuzhiyun #include "regs-clock.h"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun /* helper functions to save and restore register state */
25*4882a593Smuzhiyun struct sleep_save {
26*4882a593Smuzhiyun void __iomem *reg;
27*4882a593Smuzhiyun unsigned long val;
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define SAVE_ITEM(x) \
31*4882a593Smuzhiyun { .reg = (x) }
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun /**
34*4882a593Smuzhiyun * s3c_pm_do_save() - save a set of registers for restoration on resume.
35*4882a593Smuzhiyun * @ptr: Pointer to an array of registers.
36*4882a593Smuzhiyun * @count: Size of the ptr array.
37*4882a593Smuzhiyun *
38*4882a593Smuzhiyun * Run through the list of registers given, saving their contents in the
39*4882a593Smuzhiyun * array for later restoration when we wakeup.
40*4882a593Smuzhiyun */
s3c_pm_do_save(struct sleep_save * ptr,int count)41*4882a593Smuzhiyun static void s3c_pm_do_save(struct sleep_save *ptr, int count)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun for (; count > 0; count--, ptr++) {
44*4882a593Smuzhiyun ptr->val = readl_relaxed(ptr->reg);
45*4882a593Smuzhiyun S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /**
50*4882a593Smuzhiyun * s3c_pm_do_restore() - restore register values from the save list.
51*4882a593Smuzhiyun * @ptr: Pointer to an array of registers.
52*4882a593Smuzhiyun * @count: Size of the ptr array.
53*4882a593Smuzhiyun *
54*4882a593Smuzhiyun * Restore the register values saved from s3c_pm_do_save().
55*4882a593Smuzhiyun *
56*4882a593Smuzhiyun * WARNING: Do not put any debug in here that may effect memory or use
57*4882a593Smuzhiyun * peripherals, as things may be changing!
58*4882a593Smuzhiyun */
59*4882a593Smuzhiyun
s3c_pm_do_restore_core(const struct sleep_save * ptr,int count)60*4882a593Smuzhiyun static void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun for (; count > 0; count--, ptr++)
63*4882a593Smuzhiyun writel_relaxed(ptr->val, ptr->reg);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static struct sleep_save s5pv210_core_save[] = {
67*4882a593Smuzhiyun /* Clock ETC */
68*4882a593Smuzhiyun SAVE_ITEM(S5P_MDNIE_SEL),
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun /*
72*4882a593Smuzhiyun * VIC wake-up support (TODO)
73*4882a593Smuzhiyun */
74*4882a593Smuzhiyun static u32 s5pv210_irqwake_intmask = 0xffffffff;
75*4882a593Smuzhiyun
s5pv210_read_eint_wakeup_mask(void)76*4882a593Smuzhiyun static u32 s5pv210_read_eint_wakeup_mask(void)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun return __raw_readl(S5P_EINT_WAKEUP_MASK);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /*
82*4882a593Smuzhiyun * Suspend helpers.
83*4882a593Smuzhiyun */
s5pv210_cpu_suspend(unsigned long arg)84*4882a593Smuzhiyun static int s5pv210_cpu_suspend(unsigned long arg)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun unsigned long tmp;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun /* issue the standby signal into the pm unit. Note, we
89*4882a593Smuzhiyun * issue a write-buffer drain just in case */
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun tmp = 0;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun asm("b 1f\n\t"
94*4882a593Smuzhiyun ".align 5\n\t"
95*4882a593Smuzhiyun "1:\n\t"
96*4882a593Smuzhiyun "mcr p15, 0, %0, c7, c10, 5\n\t"
97*4882a593Smuzhiyun "mcr p15, 0, %0, c7, c10, 4\n\t"
98*4882a593Smuzhiyun "wfi" : : "r" (tmp));
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun pr_info("Failed to suspend the system\n");
101*4882a593Smuzhiyun return 1; /* Aborting suspend */
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
s5pv210_pm_prepare(void)104*4882a593Smuzhiyun static void s5pv210_pm_prepare(void)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun unsigned int tmp;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /*
109*4882a593Smuzhiyun * Set wake-up mask registers
110*4882a593Smuzhiyun * S5P_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend.
111*4882a593Smuzhiyun */
112*4882a593Smuzhiyun __raw_writel(s5pv210_irqwake_intmask, S5P_WAKEUP_MASK);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* ensure at least INFORM0 has the resume address */
115*4882a593Smuzhiyun __raw_writel(__pa_symbol(s5pv210_cpu_resume), S5P_INFORM0);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun tmp = __raw_readl(S5P_SLEEP_CFG);
118*4882a593Smuzhiyun tmp &= ~(S5P_SLEEP_CFG_OSC_EN | S5P_SLEEP_CFG_USBOSC_EN);
119*4882a593Smuzhiyun __raw_writel(tmp, S5P_SLEEP_CFG);
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun /* WFI for SLEEP mode configuration by SYSCON */
122*4882a593Smuzhiyun tmp = __raw_readl(S5P_PWR_CFG);
123*4882a593Smuzhiyun tmp &= S5P_CFG_WFI_CLEAN;
124*4882a593Smuzhiyun tmp |= S5P_CFG_WFI_SLEEP;
125*4882a593Smuzhiyun __raw_writel(tmp, S5P_PWR_CFG);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* SYSCON interrupt handling disable */
128*4882a593Smuzhiyun tmp = __raw_readl(S5P_OTHERS);
129*4882a593Smuzhiyun tmp |= S5P_OTHER_SYSC_INTOFF;
130*4882a593Smuzhiyun __raw_writel(tmp, S5P_OTHERS);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun s3c_pm_do_save(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save));
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /*
136*4882a593Smuzhiyun * Suspend operations.
137*4882a593Smuzhiyun */
s5pv210_suspend_enter(suspend_state_t state)138*4882a593Smuzhiyun static int s5pv210_suspend_enter(suspend_state_t state)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun u32 eint_wakeup_mask = s5pv210_read_eint_wakeup_mask();
141*4882a593Smuzhiyun int ret;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun S3C_PMDBG("%s: suspending the system...\n", __func__);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
146*4882a593Smuzhiyun s5pv210_irqwake_intmask, eint_wakeup_mask);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (s5pv210_irqwake_intmask == -1U
149*4882a593Smuzhiyun && eint_wakeup_mask == -1U) {
150*4882a593Smuzhiyun pr_err("%s: No wake-up sources!\n", __func__);
151*4882a593Smuzhiyun pr_err("%s: Aborting sleep\n", __func__);
152*4882a593Smuzhiyun return -EINVAL;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun s3c_pm_save_uarts(false);
156*4882a593Smuzhiyun s5pv210_pm_prepare();
157*4882a593Smuzhiyun flush_cache_all();
158*4882a593Smuzhiyun s3c_pm_check_store();
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun ret = cpu_suspend(0, s5pv210_cpu_suspend);
161*4882a593Smuzhiyun if (ret)
162*4882a593Smuzhiyun return ret;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun s3c_pm_restore_uarts(false);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
167*4882a593Smuzhiyun __raw_readl(S5P_WAKEUP_STAT));
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun s3c_pm_check_restore();
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun S3C_PMDBG("%s: resuming the system...\n", __func__);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun return 0;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
s5pv210_suspend_prepare(void)176*4882a593Smuzhiyun static int s5pv210_suspend_prepare(void)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun s3c_pm_check_prepare();
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun return 0;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
s5pv210_suspend_finish(void)183*4882a593Smuzhiyun static void s5pv210_suspend_finish(void)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun s3c_pm_check_cleanup();
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun static const struct platform_suspend_ops s5pv210_suspend_ops = {
189*4882a593Smuzhiyun .enter = s5pv210_suspend_enter,
190*4882a593Smuzhiyun .prepare = s5pv210_suspend_prepare,
191*4882a593Smuzhiyun .finish = s5pv210_suspend_finish,
192*4882a593Smuzhiyun .valid = suspend_valid_only_mem,
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun /*
196*4882a593Smuzhiyun * Syscore operations used to delay restore of certain registers.
197*4882a593Smuzhiyun */
s5pv210_pm_resume(void)198*4882a593Smuzhiyun static void s5pv210_pm_resume(void)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun s3c_pm_do_restore_core(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save));
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun static struct syscore_ops s5pv210_pm_syscore_ops = {
204*4882a593Smuzhiyun .resume = s5pv210_pm_resume,
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /*
208*4882a593Smuzhiyun * Initialization entry point.
209*4882a593Smuzhiyun */
s5pv210_pm_init(void)210*4882a593Smuzhiyun void __init s5pv210_pm_init(void)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun register_syscore_ops(&s5pv210_pm_syscore_ops);
213*4882a593Smuzhiyun suspend_set_ops(&s5pv210_suspend_ops);
214*4882a593Smuzhiyun }
215