171b9114dSArnd Bergmann // SPDX-License-Identifier: GPL-2.0 271b9114dSArnd Bergmann // 371b9114dSArnd Bergmann // Copyright 2008 Openmoko, Inc. 471b9114dSArnd Bergmann // Copyright 2004-2008 Simtec Electronics 571b9114dSArnd Bergmann // Ben Dooks <ben@simtec.co.uk> 671b9114dSArnd Bergmann // http://armlinux.simtec.co.uk/ 771b9114dSArnd Bergmann // 871b9114dSArnd Bergmann // S3C common power management (suspend to ram) support. 971b9114dSArnd Bergmann 1071b9114dSArnd Bergmann #include <linux/init.h> 1171b9114dSArnd Bergmann #include <linux/suspend.h> 1271b9114dSArnd Bergmann #include <linux/errno.h> 1371b9114dSArnd Bergmann #include <linux/delay.h> 1471b9114dSArnd Bergmann #include <linux/of.h> 1571b9114dSArnd Bergmann #include <linux/serial_s3c.h> 1671b9114dSArnd Bergmann #include <linux/io.h> 1771b9114dSArnd Bergmann 1871b9114dSArnd Bergmann #include <asm/cacheflush.h> 1971b9114dSArnd Bergmann #include <asm/suspend.h> 2071b9114dSArnd Bergmann 21c6ff132dSArnd Bergmann #include "map.h" 22c6ff132dSArnd Bergmann #include "regs-clock.h" 23c6ff132dSArnd Bergmann #include "regs-irq.h" 24*c78a41fcSArnd Bergmann #include "irqs.h" 2571b9114dSArnd Bergmann 2671b9114dSArnd Bergmann #include <asm/irq.h> 2771b9114dSArnd Bergmann 28c6ff132dSArnd Bergmann #include "cpu.h" 29c6ff132dSArnd Bergmann #include "pm.h" 30c6ff132dSArnd Bergmann #include "pm-core.h" 3171b9114dSArnd Bergmann 3271b9114dSArnd Bergmann /* for external use */ 3371b9114dSArnd Bergmann 3471b9114dSArnd Bergmann unsigned long s3c_pm_flags; 3571b9114dSArnd Bergmann 3671b9114dSArnd Bergmann /* The IRQ ext-int code goes here, it is too small to currently bother 3771b9114dSArnd Bergmann * with its own file. */ 3871b9114dSArnd Bergmann 3971b9114dSArnd Bergmann unsigned long s3c_irqwake_intmask = 0xffffffffL; 4071b9114dSArnd Bergmann unsigned long s3c_irqwake_eintmask = 0xffffffffL; 4171b9114dSArnd Bergmann 4271b9114dSArnd Bergmann int s3c_irqext_wake(struct irq_data *data, unsigned int state) 4371b9114dSArnd Bergmann { 4471b9114dSArnd Bergmann unsigned long bit = 1L << IRQ_EINT_BIT(data->irq); 4571b9114dSArnd Bergmann 4671b9114dSArnd Bergmann if (!(s3c_irqwake_eintallow & bit)) 4771b9114dSArnd Bergmann return -ENOENT; 4871b9114dSArnd Bergmann 4971b9114dSArnd Bergmann printk(KERN_INFO "wake %s for irq %d\n", 5071b9114dSArnd Bergmann state ? "enabled" : "disabled", data->irq); 5171b9114dSArnd Bergmann 5271b9114dSArnd Bergmann if (!state) 5371b9114dSArnd Bergmann s3c_irqwake_eintmask |= bit; 5471b9114dSArnd Bergmann else 5571b9114dSArnd Bergmann s3c_irqwake_eintmask &= ~bit; 5671b9114dSArnd Bergmann 5771b9114dSArnd Bergmann return 0; 5871b9114dSArnd Bergmann } 5971b9114dSArnd Bergmann 6071b9114dSArnd Bergmann void (*pm_cpu_prep)(void); 6171b9114dSArnd Bergmann int (*pm_cpu_sleep)(unsigned long); 6271b9114dSArnd Bergmann 6371b9114dSArnd Bergmann #define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) 6471b9114dSArnd Bergmann 6571b9114dSArnd Bergmann /* s3c_pm_enter 6671b9114dSArnd Bergmann * 6771b9114dSArnd Bergmann * central control for sleep/resume process 6871b9114dSArnd Bergmann */ 6971b9114dSArnd Bergmann 7071b9114dSArnd Bergmann static int s3c_pm_enter(suspend_state_t state) 7171b9114dSArnd Bergmann { 7271b9114dSArnd Bergmann int ret; 7371b9114dSArnd Bergmann /* ensure the debug is initialised (if enabled) */ 7471b9114dSArnd Bergmann s3c_pm_debug_init_uart(); 7571b9114dSArnd Bergmann 7671b9114dSArnd Bergmann S3C_PMDBG("%s(%d)\n", __func__, state); 7771b9114dSArnd Bergmann 7871b9114dSArnd Bergmann if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { 7971b9114dSArnd Bergmann printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__); 8071b9114dSArnd Bergmann return -EINVAL; 8171b9114dSArnd Bergmann } 8271b9114dSArnd Bergmann 8371b9114dSArnd Bergmann /* check if we have anything to wake-up with... bad things seem 8471b9114dSArnd Bergmann * to happen if you suspend with no wakeup (system will often 8571b9114dSArnd Bergmann * require a full power-cycle) 8671b9114dSArnd Bergmann */ 8771b9114dSArnd Bergmann 8871b9114dSArnd Bergmann if (!of_have_populated_dt() && 8971b9114dSArnd Bergmann !any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && 9071b9114dSArnd Bergmann !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { 9171b9114dSArnd Bergmann printk(KERN_ERR "%s: No wake-up sources!\n", __func__); 9271b9114dSArnd Bergmann printk(KERN_ERR "%s: Aborting sleep\n", __func__); 9371b9114dSArnd Bergmann return -EINVAL; 9471b9114dSArnd Bergmann } 9571b9114dSArnd Bergmann 9671b9114dSArnd Bergmann /* save all necessary core registers not covered by the drivers */ 9771b9114dSArnd Bergmann 9871b9114dSArnd Bergmann if (!of_have_populated_dt()) { 9971b9114dSArnd Bergmann samsung_pm_save_gpios(); 10071b9114dSArnd Bergmann samsung_pm_saved_gpios(); 10171b9114dSArnd Bergmann } 10271b9114dSArnd Bergmann 10371b9114dSArnd Bergmann s3c_pm_save_uarts(soc_is_s3c2410()); 10471b9114dSArnd Bergmann s3c_pm_save_core(); 10571b9114dSArnd Bergmann 10671b9114dSArnd Bergmann /* set the irq configuration for wake */ 10771b9114dSArnd Bergmann 10871b9114dSArnd Bergmann s3c_pm_configure_extint(); 10971b9114dSArnd Bergmann 11071b9114dSArnd Bergmann S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n", 11171b9114dSArnd Bergmann s3c_irqwake_intmask, s3c_irqwake_eintmask); 11271b9114dSArnd Bergmann 11371b9114dSArnd Bergmann s3c_pm_arch_prepare_irqs(); 11471b9114dSArnd Bergmann 11571b9114dSArnd Bergmann /* call cpu specific preparation */ 11671b9114dSArnd Bergmann 11771b9114dSArnd Bergmann pm_cpu_prep(); 11871b9114dSArnd Bergmann 11971b9114dSArnd Bergmann /* flush cache back to ram */ 12071b9114dSArnd Bergmann 12171b9114dSArnd Bergmann flush_cache_all(); 12271b9114dSArnd Bergmann 12371b9114dSArnd Bergmann s3c_pm_check_store(); 12471b9114dSArnd Bergmann 12571b9114dSArnd Bergmann /* send the cpu to sleep... */ 12671b9114dSArnd Bergmann 12771b9114dSArnd Bergmann s3c_pm_arch_stop_clocks(); 12871b9114dSArnd Bergmann 12971b9114dSArnd Bergmann /* this will also act as our return point from when 13071b9114dSArnd Bergmann * we resume as it saves its own register state and restores it 13171b9114dSArnd Bergmann * during the resume. */ 13271b9114dSArnd Bergmann 13371b9114dSArnd Bergmann ret = cpu_suspend(0, pm_cpu_sleep); 13471b9114dSArnd Bergmann if (ret) 13571b9114dSArnd Bergmann return ret; 13671b9114dSArnd Bergmann 13771b9114dSArnd Bergmann /* restore the system state */ 13871b9114dSArnd Bergmann 13971b9114dSArnd Bergmann s3c_pm_restore_core(); 14071b9114dSArnd Bergmann s3c_pm_restore_uarts(soc_is_s3c2410()); 14171b9114dSArnd Bergmann 14271b9114dSArnd Bergmann if (!of_have_populated_dt()) { 14371b9114dSArnd Bergmann samsung_pm_restore_gpios(); 14471b9114dSArnd Bergmann s3c_pm_restored_gpios(); 14571b9114dSArnd Bergmann } 14671b9114dSArnd Bergmann 14771b9114dSArnd Bergmann s3c_pm_debug_init_uart(); 14871b9114dSArnd Bergmann 14971b9114dSArnd Bergmann /* check what irq (if any) restored the system */ 15071b9114dSArnd Bergmann 15171b9114dSArnd Bergmann s3c_pm_arch_show_resume_irqs(); 15271b9114dSArnd Bergmann 15371b9114dSArnd Bergmann S3C_PMDBG("%s: post sleep, preparing to return\n", __func__); 15471b9114dSArnd Bergmann 15571b9114dSArnd Bergmann /* LEDs should now be 1110 */ 15671b9114dSArnd Bergmann s3c_pm_debug_smdkled(1 << 1, 0); 15771b9114dSArnd Bergmann 15871b9114dSArnd Bergmann s3c_pm_check_restore(); 15971b9114dSArnd Bergmann 16071b9114dSArnd Bergmann /* ok, let's return from sleep */ 16171b9114dSArnd Bergmann 16271b9114dSArnd Bergmann S3C_PMDBG("S3C PM Resume (post-restore)\n"); 16371b9114dSArnd Bergmann return 0; 16471b9114dSArnd Bergmann } 16571b9114dSArnd Bergmann 16671b9114dSArnd Bergmann static int s3c_pm_prepare(void) 16771b9114dSArnd Bergmann { 16871b9114dSArnd Bergmann /* prepare check area if configured */ 16971b9114dSArnd Bergmann 17071b9114dSArnd Bergmann s3c_pm_check_prepare(); 17171b9114dSArnd Bergmann return 0; 17271b9114dSArnd Bergmann } 17371b9114dSArnd Bergmann 17471b9114dSArnd Bergmann static void s3c_pm_finish(void) 17571b9114dSArnd Bergmann { 17671b9114dSArnd Bergmann s3c_pm_check_cleanup(); 17771b9114dSArnd Bergmann } 17871b9114dSArnd Bergmann 17971b9114dSArnd Bergmann static const struct platform_suspend_ops s3c_pm_ops = { 18071b9114dSArnd Bergmann .enter = s3c_pm_enter, 18171b9114dSArnd Bergmann .prepare = s3c_pm_prepare, 18271b9114dSArnd Bergmann .finish = s3c_pm_finish, 18371b9114dSArnd Bergmann .valid = suspend_valid_only_mem, 18471b9114dSArnd Bergmann }; 18571b9114dSArnd Bergmann 18671b9114dSArnd Bergmann /* s3c_pm_init 18771b9114dSArnd Bergmann * 18871b9114dSArnd Bergmann * Attach the power management functions. This should be called 18971b9114dSArnd Bergmann * from the board specific initialisation if the board supports 19071b9114dSArnd Bergmann * it. 19171b9114dSArnd Bergmann */ 19271b9114dSArnd Bergmann 19371b9114dSArnd Bergmann int __init s3c_pm_init(void) 19471b9114dSArnd Bergmann { 19571b9114dSArnd Bergmann printk("S3C Power Management, Copyright 2004 Simtec Electronics\n"); 19671b9114dSArnd Bergmann 19771b9114dSArnd Bergmann suspend_set_ops(&s3c_pm_ops); 19871b9114dSArnd Bergmann return 0; 19971b9114dSArnd Bergmann } 200