Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2010-2014 Samsung Electronics Co., Ltd.
0004 //      http://www.samsung.com
0005 //
0006 // S5PV210 - Power Management support
0007 //
0008 // Based on arch/arm/mach-s3c2410/pm.c
0009 // Copyright (c) 2006 Simtec Electronics
0010 //  Ben Dooks <ben@simtec.co.uk>
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 /* helper functions to save and restore register state */
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  * s3c_pm_do_save() - save a set of registers for restoration on resume.
0035  * @ptr: Pointer to an array of registers.
0036  * @count: Size of the ptr array.
0037  *
0038  * Run through the list of registers given, saving their contents in the
0039  * array for later restoration when we wakeup.
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  * s3c_pm_do_restore() - restore register values from the save list.
0051  * @ptr: Pointer to an array of registers.
0052  * @count: Size of the ptr array.
0053  *
0054  * Restore the register values saved from s3c_pm_do_save().
0055  *
0056  * WARNING: Do not put any debug in here that may effect memory or use
0057  * peripherals, as things may be changing!
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     /* Clock ETC */
0068     SAVE_ITEM(S5P_MDNIE_SEL),
0069 };
0070 
0071 /*
0072  * VIC wake-up support (TODO)
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  * Suspend helpers.
0083  */
0084 static int s5pv210_cpu_suspend(unsigned long arg)
0085 {
0086     unsigned long tmp;
0087 
0088     /* issue the standby signal into the pm unit. Note, we
0089      * issue a write-buffer drain just in case */
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; /* Aborting suspend */
0102 }
0103 
0104 static void s5pv210_pm_prepare(void)
0105 {
0106     unsigned int tmp;
0107 
0108     /*
0109      * Set wake-up mask registers
0110      * S5P_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend.
0111      */
0112     __raw_writel(s5pv210_irqwake_intmask, S5P_WAKEUP_MASK);
0113 
0114     /* ensure at least INFORM0 has the resume address */
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     /* WFI for SLEEP mode configuration by SYSCON */
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     /* SYSCON interrupt handling disable */
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  * Suspend operations.
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  * Syscore operations used to delay restore of certain registers.
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  * Initialization entry point.
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 }