0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/init.h>
0013 #include <linux/suspend.h>
0014 #include <linux/syscore_ops.h>
0015 #include <linux/io.h>
0016 #include <linux/soc/samsung/s3c-pm.h>
0017
0018 #include <asm/cacheflush.h>
0019 #include <asm/suspend.h>
0020
0021 #include "common.h"
0022 #include "regs-clock.h"
0023
0024
0025 struct sleep_save {
0026 void __iomem *reg;
0027 unsigned long val;
0028 };
0029
0030 #define SAVE_ITEM(x) \
0031 { .reg = (x) }
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 static void s3c_pm_do_save(struct sleep_save *ptr, int count)
0042 {
0043 for (; count > 0; count--, ptr++) {
0044 ptr->val = readl_relaxed(ptr->reg);
0045 S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
0046 }
0047 }
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 static void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count)
0061 {
0062 for (; count > 0; count--, ptr++)
0063 writel_relaxed(ptr->val, ptr->reg);
0064 }
0065
0066 static struct sleep_save s5pv210_core_save[] = {
0067
0068 SAVE_ITEM(S5P_MDNIE_SEL),
0069 };
0070
0071
0072
0073
0074 static u32 s5pv210_irqwake_intmask = 0xffffffff;
0075
0076 static u32 s5pv210_read_eint_wakeup_mask(void)
0077 {
0078 return __raw_readl(S5P_EINT_WAKEUP_MASK);
0079 }
0080
0081
0082
0083
0084 static int s5pv210_cpu_suspend(unsigned long arg)
0085 {
0086 unsigned long tmp;
0087
0088
0089
0090
0091 tmp = 0;
0092
0093 asm("b 1f\n\t"
0094 ".align 5\n\t"
0095 "1:\n\t"
0096 "mcr p15, 0, %0, c7, c10, 5\n\t"
0097 "mcr p15, 0, %0, c7, c10, 4\n\t"
0098 "wfi" : : "r" (tmp));
0099
0100 pr_info("Failed to suspend the system\n");
0101 return 1;
0102 }
0103
0104 static void s5pv210_pm_prepare(void)
0105 {
0106 unsigned int tmp;
0107
0108
0109
0110
0111
0112 __raw_writel(s5pv210_irqwake_intmask, S5P_WAKEUP_MASK);
0113
0114
0115 __raw_writel(__pa_symbol(s5pv210_cpu_resume), S5P_INFORM0);
0116
0117 tmp = __raw_readl(S5P_SLEEP_CFG);
0118 tmp &= ~(S5P_SLEEP_CFG_OSC_EN | S5P_SLEEP_CFG_USBOSC_EN);
0119 __raw_writel(tmp, S5P_SLEEP_CFG);
0120
0121
0122 tmp = __raw_readl(S5P_PWR_CFG);
0123 tmp &= S5P_CFG_WFI_CLEAN;
0124 tmp |= S5P_CFG_WFI_SLEEP;
0125 __raw_writel(tmp, S5P_PWR_CFG);
0126
0127
0128 tmp = __raw_readl(S5P_OTHERS);
0129 tmp |= S5P_OTHER_SYSC_INTOFF;
0130 __raw_writel(tmp, S5P_OTHERS);
0131
0132 s3c_pm_do_save(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save));
0133 }
0134
0135
0136
0137
0138 static int s5pv210_suspend_enter(suspend_state_t state)
0139 {
0140 u32 eint_wakeup_mask = s5pv210_read_eint_wakeup_mask();
0141 int ret;
0142
0143 S3C_PMDBG("%s: suspending the system...\n", __func__);
0144
0145 S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
0146 s5pv210_irqwake_intmask, eint_wakeup_mask);
0147
0148 if (s5pv210_irqwake_intmask == -1U
0149 && eint_wakeup_mask == -1U) {
0150 pr_err("%s: No wake-up sources!\n", __func__);
0151 pr_err("%s: Aborting sleep\n", __func__);
0152 return -EINVAL;
0153 }
0154
0155 s3c_pm_save_uarts(false);
0156 s5pv210_pm_prepare();
0157 flush_cache_all();
0158 s3c_pm_check_store();
0159
0160 ret = cpu_suspend(0, s5pv210_cpu_suspend);
0161 if (ret)
0162 return ret;
0163
0164 s3c_pm_restore_uarts(false);
0165
0166 S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
0167 __raw_readl(S5P_WAKEUP_STAT));
0168
0169 s3c_pm_check_restore();
0170
0171 S3C_PMDBG("%s: resuming the system...\n", __func__);
0172
0173 return 0;
0174 }
0175
0176 static int s5pv210_suspend_prepare(void)
0177 {
0178 s3c_pm_check_prepare();
0179
0180 return 0;
0181 }
0182
0183 static void s5pv210_suspend_finish(void)
0184 {
0185 s3c_pm_check_cleanup();
0186 }
0187
0188 static const struct platform_suspend_ops s5pv210_suspend_ops = {
0189 .enter = s5pv210_suspend_enter,
0190 .prepare = s5pv210_suspend_prepare,
0191 .finish = s5pv210_suspend_finish,
0192 .valid = suspend_valid_only_mem,
0193 };
0194
0195
0196
0197
0198 static void s5pv210_pm_resume(void)
0199 {
0200 s3c_pm_do_restore_core(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save));
0201 }
0202
0203 static struct syscore_ops s5pv210_pm_syscore_ops = {
0204 .resume = s5pv210_pm_resume,
0205 };
0206
0207
0208
0209
0210 void __init s5pv210_pm_init(void)
0211 {
0212 register_syscore_ops(&s5pv210_pm_syscore_ops);
0213 suspend_set_ops(&s5pv210_suspend_ops);
0214 }