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