1*71b9114dSArnd Bergmann // SPDX-License-Identifier: GPL-2.0 2*71b9114dSArnd Bergmann // 3*71b9114dSArnd Bergmann // Copyright 2008 Openmoko, Inc. 4*71b9114dSArnd Bergmann // Copyright 2004-2008 Simtec Electronics 5*71b9114dSArnd Bergmann // Ben Dooks <ben@simtec.co.uk> 6*71b9114dSArnd Bergmann // http://armlinux.simtec.co.uk/ 7*71b9114dSArnd Bergmann // 8*71b9114dSArnd Bergmann // S3C common power management (suspend to ram) support. 9*71b9114dSArnd Bergmann 10*71b9114dSArnd Bergmann #include <linux/init.h> 11*71b9114dSArnd Bergmann #include <linux/suspend.h> 12*71b9114dSArnd Bergmann #include <linux/errno.h> 13*71b9114dSArnd Bergmann #include <linux/delay.h> 14*71b9114dSArnd Bergmann #include <linux/of.h> 15*71b9114dSArnd Bergmann #include <linux/serial_s3c.h> 16*71b9114dSArnd Bergmann #include <linux/io.h> 17*71b9114dSArnd Bergmann 18*71b9114dSArnd Bergmann #include <asm/cacheflush.h> 19*71b9114dSArnd Bergmann #include <asm/suspend.h> 20*71b9114dSArnd Bergmann 21*71b9114dSArnd Bergmann #include <mach/map.h> 22*71b9114dSArnd Bergmann #include <mach/regs-clock.h> 23*71b9114dSArnd Bergmann #include <mach/regs-irq.h> 24*71b9114dSArnd Bergmann #include <mach/irqs.h> 25*71b9114dSArnd Bergmann 26*71b9114dSArnd Bergmann #include <asm/irq.h> 27*71b9114dSArnd Bergmann 28*71b9114dSArnd Bergmann #include <plat/cpu.h> 29*71b9114dSArnd Bergmann #include <plat/pm.h> 30*71b9114dSArnd Bergmann #include <mach/pm-core.h> 31*71b9114dSArnd Bergmann 32*71b9114dSArnd Bergmann /* for external use */ 33*71b9114dSArnd Bergmann 34*71b9114dSArnd Bergmann unsigned long s3c_pm_flags; 35*71b9114dSArnd Bergmann 36*71b9114dSArnd Bergmann /* The IRQ ext-int code goes here, it is too small to currently bother 37*71b9114dSArnd Bergmann * with its own file. */ 38*71b9114dSArnd Bergmann 39*71b9114dSArnd Bergmann unsigned long s3c_irqwake_intmask = 0xffffffffL; 40*71b9114dSArnd Bergmann unsigned long s3c_irqwake_eintmask = 0xffffffffL; 41*71b9114dSArnd Bergmann 42*71b9114dSArnd Bergmann int s3c_irqext_wake(struct irq_data *data, unsigned int state) 43*71b9114dSArnd Bergmann { 44*71b9114dSArnd Bergmann unsigned long bit = 1L << IRQ_EINT_BIT(data->irq); 45*71b9114dSArnd Bergmann 46*71b9114dSArnd Bergmann if (!(s3c_irqwake_eintallow & bit)) 47*71b9114dSArnd Bergmann return -ENOENT; 48*71b9114dSArnd Bergmann 49*71b9114dSArnd Bergmann printk(KERN_INFO "wake %s for irq %d\n", 50*71b9114dSArnd Bergmann state ? "enabled" : "disabled", data->irq); 51*71b9114dSArnd Bergmann 52*71b9114dSArnd Bergmann if (!state) 53*71b9114dSArnd Bergmann s3c_irqwake_eintmask |= bit; 54*71b9114dSArnd Bergmann else 55*71b9114dSArnd Bergmann s3c_irqwake_eintmask &= ~bit; 56*71b9114dSArnd Bergmann 57*71b9114dSArnd Bergmann return 0; 58*71b9114dSArnd Bergmann } 59*71b9114dSArnd Bergmann 60*71b9114dSArnd Bergmann void (*pm_cpu_prep)(void); 61*71b9114dSArnd Bergmann int (*pm_cpu_sleep)(unsigned long); 62*71b9114dSArnd Bergmann 63*71b9114dSArnd Bergmann #define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) 64*71b9114dSArnd Bergmann 65*71b9114dSArnd Bergmann /* s3c_pm_enter 66*71b9114dSArnd Bergmann * 67*71b9114dSArnd Bergmann * central control for sleep/resume process 68*71b9114dSArnd Bergmann */ 69*71b9114dSArnd Bergmann 70*71b9114dSArnd Bergmann static int s3c_pm_enter(suspend_state_t state) 71*71b9114dSArnd Bergmann { 72*71b9114dSArnd Bergmann int ret; 73*71b9114dSArnd Bergmann /* ensure the debug is initialised (if enabled) */ 74*71b9114dSArnd Bergmann s3c_pm_debug_init_uart(); 75*71b9114dSArnd Bergmann 76*71b9114dSArnd Bergmann S3C_PMDBG("%s(%d)\n", __func__, state); 77*71b9114dSArnd Bergmann 78*71b9114dSArnd Bergmann if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { 79*71b9114dSArnd Bergmann printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__); 80*71b9114dSArnd Bergmann return -EINVAL; 81*71b9114dSArnd Bergmann } 82*71b9114dSArnd Bergmann 83*71b9114dSArnd Bergmann /* check if we have anything to wake-up with... bad things seem 84*71b9114dSArnd Bergmann * to happen if you suspend with no wakeup (system will often 85*71b9114dSArnd Bergmann * require a full power-cycle) 86*71b9114dSArnd Bergmann */ 87*71b9114dSArnd Bergmann 88*71b9114dSArnd Bergmann if (!of_have_populated_dt() && 89*71b9114dSArnd Bergmann !any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && 90*71b9114dSArnd Bergmann !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { 91*71b9114dSArnd Bergmann printk(KERN_ERR "%s: No wake-up sources!\n", __func__); 92*71b9114dSArnd Bergmann printk(KERN_ERR "%s: Aborting sleep\n", __func__); 93*71b9114dSArnd Bergmann return -EINVAL; 94*71b9114dSArnd Bergmann } 95*71b9114dSArnd Bergmann 96*71b9114dSArnd Bergmann /* save all necessary core registers not covered by the drivers */ 97*71b9114dSArnd Bergmann 98*71b9114dSArnd Bergmann if (!of_have_populated_dt()) { 99*71b9114dSArnd Bergmann samsung_pm_save_gpios(); 100*71b9114dSArnd Bergmann samsung_pm_saved_gpios(); 101*71b9114dSArnd Bergmann } 102*71b9114dSArnd Bergmann 103*71b9114dSArnd Bergmann s3c_pm_save_uarts(soc_is_s3c2410()); 104*71b9114dSArnd Bergmann s3c_pm_save_core(); 105*71b9114dSArnd Bergmann 106*71b9114dSArnd Bergmann /* set the irq configuration for wake */ 107*71b9114dSArnd Bergmann 108*71b9114dSArnd Bergmann s3c_pm_configure_extint(); 109*71b9114dSArnd Bergmann 110*71b9114dSArnd Bergmann S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n", 111*71b9114dSArnd Bergmann s3c_irqwake_intmask, s3c_irqwake_eintmask); 112*71b9114dSArnd Bergmann 113*71b9114dSArnd Bergmann s3c_pm_arch_prepare_irqs(); 114*71b9114dSArnd Bergmann 115*71b9114dSArnd Bergmann /* call cpu specific preparation */ 116*71b9114dSArnd Bergmann 117*71b9114dSArnd Bergmann pm_cpu_prep(); 118*71b9114dSArnd Bergmann 119*71b9114dSArnd Bergmann /* flush cache back to ram */ 120*71b9114dSArnd Bergmann 121*71b9114dSArnd Bergmann flush_cache_all(); 122*71b9114dSArnd Bergmann 123*71b9114dSArnd Bergmann s3c_pm_check_store(); 124*71b9114dSArnd Bergmann 125*71b9114dSArnd Bergmann /* send the cpu to sleep... */ 126*71b9114dSArnd Bergmann 127*71b9114dSArnd Bergmann s3c_pm_arch_stop_clocks(); 128*71b9114dSArnd Bergmann 129*71b9114dSArnd Bergmann /* this will also act as our return point from when 130*71b9114dSArnd Bergmann * we resume as it saves its own register state and restores it 131*71b9114dSArnd Bergmann * during the resume. */ 132*71b9114dSArnd Bergmann 133*71b9114dSArnd Bergmann ret = cpu_suspend(0, pm_cpu_sleep); 134*71b9114dSArnd Bergmann if (ret) 135*71b9114dSArnd Bergmann return ret; 136*71b9114dSArnd Bergmann 137*71b9114dSArnd Bergmann /* restore the system state */ 138*71b9114dSArnd Bergmann 139*71b9114dSArnd Bergmann s3c_pm_restore_core(); 140*71b9114dSArnd Bergmann s3c_pm_restore_uarts(soc_is_s3c2410()); 141*71b9114dSArnd Bergmann 142*71b9114dSArnd Bergmann if (!of_have_populated_dt()) { 143*71b9114dSArnd Bergmann samsung_pm_restore_gpios(); 144*71b9114dSArnd Bergmann s3c_pm_restored_gpios(); 145*71b9114dSArnd Bergmann } 146*71b9114dSArnd Bergmann 147*71b9114dSArnd Bergmann s3c_pm_debug_init_uart(); 148*71b9114dSArnd Bergmann 149*71b9114dSArnd Bergmann /* check what irq (if any) restored the system */ 150*71b9114dSArnd Bergmann 151*71b9114dSArnd Bergmann s3c_pm_arch_show_resume_irqs(); 152*71b9114dSArnd Bergmann 153*71b9114dSArnd Bergmann S3C_PMDBG("%s: post sleep, preparing to return\n", __func__); 154*71b9114dSArnd Bergmann 155*71b9114dSArnd Bergmann /* LEDs should now be 1110 */ 156*71b9114dSArnd Bergmann s3c_pm_debug_smdkled(1 << 1, 0); 157*71b9114dSArnd Bergmann 158*71b9114dSArnd Bergmann s3c_pm_check_restore(); 159*71b9114dSArnd Bergmann 160*71b9114dSArnd Bergmann /* ok, let's return from sleep */ 161*71b9114dSArnd Bergmann 162*71b9114dSArnd Bergmann S3C_PMDBG("S3C PM Resume (post-restore)\n"); 163*71b9114dSArnd Bergmann return 0; 164*71b9114dSArnd Bergmann } 165*71b9114dSArnd Bergmann 166*71b9114dSArnd Bergmann static int s3c_pm_prepare(void) 167*71b9114dSArnd Bergmann { 168*71b9114dSArnd Bergmann /* prepare check area if configured */ 169*71b9114dSArnd Bergmann 170*71b9114dSArnd Bergmann s3c_pm_check_prepare(); 171*71b9114dSArnd Bergmann return 0; 172*71b9114dSArnd Bergmann } 173*71b9114dSArnd Bergmann 174*71b9114dSArnd Bergmann static void s3c_pm_finish(void) 175*71b9114dSArnd Bergmann { 176*71b9114dSArnd Bergmann s3c_pm_check_cleanup(); 177*71b9114dSArnd Bergmann } 178*71b9114dSArnd Bergmann 179*71b9114dSArnd Bergmann static const struct platform_suspend_ops s3c_pm_ops = { 180*71b9114dSArnd Bergmann .enter = s3c_pm_enter, 181*71b9114dSArnd Bergmann .prepare = s3c_pm_prepare, 182*71b9114dSArnd Bergmann .finish = s3c_pm_finish, 183*71b9114dSArnd Bergmann .valid = suspend_valid_only_mem, 184*71b9114dSArnd Bergmann }; 185*71b9114dSArnd Bergmann 186*71b9114dSArnd Bergmann /* s3c_pm_init 187*71b9114dSArnd Bergmann * 188*71b9114dSArnd Bergmann * Attach the power management functions. This should be called 189*71b9114dSArnd Bergmann * from the board specific initialisation if the board supports 190*71b9114dSArnd Bergmann * it. 191*71b9114dSArnd Bergmann */ 192*71b9114dSArnd Bergmann 193*71b9114dSArnd Bergmann int __init s3c_pm_init(void) 194*71b9114dSArnd Bergmann { 195*71b9114dSArnd Bergmann printk("S3C Power Management, Copyright 2004 Simtec Electronics\n"); 196*71b9114dSArnd Bergmann 197*71b9114dSArnd Bergmann suspend_set_ops(&s3c_pm_ops); 198*71b9114dSArnd Bergmann return 0; 199*71b9114dSArnd Bergmann } 200