18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2010-2014 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci// http://www.samsung.com 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// S5PV210 - Power Management support 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Based on arch/arm/mach-s3c2410/pm.c 98c2ecf20Sopenharmony_ci// Copyright (c) 2006 Simtec Electronics 108c2ecf20Sopenharmony_ci// Ben Dooks <ben@simtec.co.uk> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/suspend.h> 148c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/soc/samsung/s3c-pm.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 198c2ecf20Sopenharmony_ci#include <asm/suspend.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "common.h" 228c2ecf20Sopenharmony_ci#include "regs-clock.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* helper functions to save and restore register state */ 258c2ecf20Sopenharmony_cistruct sleep_save { 268c2ecf20Sopenharmony_ci void __iomem *reg; 278c2ecf20Sopenharmony_ci unsigned long val; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define SAVE_ITEM(x) \ 318c2ecf20Sopenharmony_ci { .reg = (x) } 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/** 348c2ecf20Sopenharmony_ci * s3c_pm_do_save() - save a set of registers for restoration on resume. 358c2ecf20Sopenharmony_ci * @ptr: Pointer to an array of registers. 368c2ecf20Sopenharmony_ci * @count: Size of the ptr array. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Run through the list of registers given, saving their contents in the 398c2ecf20Sopenharmony_ci * array for later restoration when we wakeup. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistatic void s3c_pm_do_save(struct sleep_save *ptr, int count) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci for (; count > 0; count--, ptr++) { 448c2ecf20Sopenharmony_ci ptr->val = readl_relaxed(ptr->reg); 458c2ecf20Sopenharmony_ci S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/** 508c2ecf20Sopenharmony_ci * s3c_pm_do_restore() - restore register values from the save list. 518c2ecf20Sopenharmony_ci * @ptr: Pointer to an array of registers. 528c2ecf20Sopenharmony_ci * @count: Size of the ptr array. 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * Restore the register values saved from s3c_pm_do_save(). 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * WARNING: Do not put any debug in here that may effect memory or use 578c2ecf20Sopenharmony_ci * peripherals, as things may be changing! 588c2ecf20Sopenharmony_ci*/ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci for (; count > 0; count--, ptr++) 638c2ecf20Sopenharmony_ci writel_relaxed(ptr->val, ptr->reg); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct sleep_save s5pv210_core_save[] = { 678c2ecf20Sopenharmony_ci /* Clock ETC */ 688c2ecf20Sopenharmony_ci SAVE_ITEM(S5P_MDNIE_SEL), 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * VIC wake-up support (TODO) 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic u32 s5pv210_irqwake_intmask = 0xffffffff; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic u32 s5pv210_read_eint_wakeup_mask(void) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci return __raw_readl(S5P_EINT_WAKEUP_MASK); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * Suspend helpers. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int s5pv210_cpu_suspend(unsigned long arg) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci unsigned long tmp; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* issue the standby signal into the pm unit. Note, we 898c2ecf20Sopenharmony_ci * issue a write-buffer drain just in case */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci tmp = 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci asm("b 1f\n\t" 948c2ecf20Sopenharmony_ci ".align 5\n\t" 958c2ecf20Sopenharmony_ci "1:\n\t" 968c2ecf20Sopenharmony_ci "mcr p15, 0, %0, c7, c10, 5\n\t" 978c2ecf20Sopenharmony_ci "mcr p15, 0, %0, c7, c10, 4\n\t" 988c2ecf20Sopenharmony_ci "wfi" : : "r" (tmp)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci pr_info("Failed to suspend the system\n"); 1018c2ecf20Sopenharmony_ci return 1; /* Aborting suspend */ 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void s5pv210_pm_prepare(void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci unsigned int tmp; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Set wake-up mask registers 1108c2ecf20Sopenharmony_ci * S5P_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci __raw_writel(s5pv210_irqwake_intmask, S5P_WAKEUP_MASK); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* ensure at least INFORM0 has the resume address */ 1158c2ecf20Sopenharmony_ci __raw_writel(__pa_symbol(s5pv210_cpu_resume), S5P_INFORM0); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci tmp = __raw_readl(S5P_SLEEP_CFG); 1188c2ecf20Sopenharmony_ci tmp &= ~(S5P_SLEEP_CFG_OSC_EN | S5P_SLEEP_CFG_USBOSC_EN); 1198c2ecf20Sopenharmony_ci __raw_writel(tmp, S5P_SLEEP_CFG); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* WFI for SLEEP mode configuration by SYSCON */ 1228c2ecf20Sopenharmony_ci tmp = __raw_readl(S5P_PWR_CFG); 1238c2ecf20Sopenharmony_ci tmp &= S5P_CFG_WFI_CLEAN; 1248c2ecf20Sopenharmony_ci tmp |= S5P_CFG_WFI_SLEEP; 1258c2ecf20Sopenharmony_ci __raw_writel(tmp, S5P_PWR_CFG); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* SYSCON interrupt handling disable */ 1288c2ecf20Sopenharmony_ci tmp = __raw_readl(S5P_OTHERS); 1298c2ecf20Sopenharmony_ci tmp |= S5P_OTHER_SYSC_INTOFF; 1308c2ecf20Sopenharmony_ci __raw_writel(tmp, S5P_OTHERS); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci s3c_pm_do_save(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save)); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * Suspend operations. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int s5pv210_suspend_enter(suspend_state_t state) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u32 eint_wakeup_mask = s5pv210_read_eint_wakeup_mask(); 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci S3C_PMDBG("%s: suspending the system...\n", __func__); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__, 1468c2ecf20Sopenharmony_ci s5pv210_irqwake_intmask, eint_wakeup_mask); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (s5pv210_irqwake_intmask == -1U 1498c2ecf20Sopenharmony_ci && eint_wakeup_mask == -1U) { 1508c2ecf20Sopenharmony_ci pr_err("%s: No wake-up sources!\n", __func__); 1518c2ecf20Sopenharmony_ci pr_err("%s: Aborting sleep\n", __func__); 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci s3c_pm_save_uarts(false); 1568c2ecf20Sopenharmony_ci s5pv210_pm_prepare(); 1578c2ecf20Sopenharmony_ci flush_cache_all(); 1588c2ecf20Sopenharmony_ci s3c_pm_check_store(); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = cpu_suspend(0, s5pv210_cpu_suspend); 1618c2ecf20Sopenharmony_ci if (ret) 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci s3c_pm_restore_uarts(false); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, 1678c2ecf20Sopenharmony_ci __raw_readl(S5P_WAKEUP_STAT)); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci s3c_pm_check_restore(); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci S3C_PMDBG("%s: resuming the system...\n", __func__); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int s5pv210_suspend_prepare(void) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci s3c_pm_check_prepare(); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void s5pv210_suspend_finish(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci s3c_pm_check_cleanup(); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops s5pv210_suspend_ops = { 1898c2ecf20Sopenharmony_ci .enter = s5pv210_suspend_enter, 1908c2ecf20Sopenharmony_ci .prepare = s5pv210_suspend_prepare, 1918c2ecf20Sopenharmony_ci .finish = s5pv210_suspend_finish, 1928c2ecf20Sopenharmony_ci .valid = suspend_valid_only_mem, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * Syscore operations used to delay restore of certain registers. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic void s5pv210_pm_resume(void) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci s3c_pm_do_restore_core(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save)); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic struct syscore_ops s5pv210_pm_syscore_ops = { 2048c2ecf20Sopenharmony_ci .resume = s5pv210_pm_resume, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* 2088c2ecf20Sopenharmony_ci * Initialization entry point. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_civoid __init s5pv210_pm_init(void) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci register_syscore_ops(&s5pv210_pm_syscore_ops); 2138c2ecf20Sopenharmony_ci suspend_set_ops(&s5pv210_suspend_ops); 2148c2ecf20Sopenharmony_ci} 215