1/* linux/arch/arm/plat-s3c/pm.c 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 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13*/ 14 15#include <linux/init.h> 16#include <linux/suspend.h> 17#include <linux/errno.h> 18#include <linux/delay.h> 19#include <linux/serial_core.h> 20#include <linux/io.h> 21 22#include <asm/cacheflush.h> 23#include <asm/suspend.h> 24#include <mach/hardware.h> 25#include <mach/map.h> 26 27#include <plat/regs-serial.h> 28#include <mach/regs-clock.h> 29#include <mach/regs-irq.h> 30#include <asm/irq.h> 31 32#include <plat/pm.h> 33#include <mach/pm-core.h> 34 35/* for external use */ 36 37unsigned long s3c_pm_flags; 38 39/* Debug code: 40 * 41 * This code supports debug output to the low level UARTs for use on 42 * resume before the console layer is available. 43*/ 44 45#ifdef CONFIG_SAMSUNG_PM_DEBUG 46extern void printascii(const char *); 47 48void s3c_pm_dbg(const char *fmt, ...) 49{ 50 va_list va; 51 char buff[256]; 52 53 va_start(va, fmt); 54 vsprintf(buff, fmt, va); 55 va_end(va); 56 57 printascii(buff); 58} 59 60static inline void s3c_pm_debug_init(void) 61{ 62 /* restart uart clocks so we can use them to output */ 63 s3c_pm_debug_init_uart(); 64} 65 66#else 67#define s3c_pm_debug_init() do { } while(0) 68 69#endif /* CONFIG_SAMSUNG_PM_DEBUG */ 70 71/* Save the UART configurations if we are configured for debug. */ 72 73unsigned char pm_uart_udivslot; 74 75#ifdef CONFIG_SAMSUNG_PM_DEBUG 76 77struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS]; 78 79static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save) 80{ 81 void __iomem *regs = S3C_VA_UARTx(uart); 82 83 save->ulcon = __raw_readl(regs + S3C2410_ULCON); 84 save->ucon = __raw_readl(regs + S3C2410_UCON); 85 save->ufcon = __raw_readl(regs + S3C2410_UFCON); 86 save->umcon = __raw_readl(regs + S3C2410_UMCON); 87 save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV); 88 89 if (pm_uart_udivslot) 90 save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT); 91 92 S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n", 93 uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv); 94} 95 96static void s3c_pm_save_uarts(void) 97{ 98 struct pm_uart_save *save = uart_save; 99 unsigned int uart; 100 101 for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++) 102 s3c_pm_save_uart(uart, save); 103} 104 105static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save) 106{ 107 void __iomem *regs = S3C_VA_UARTx(uart); 108 109 s3c_pm_arch_update_uart(regs, save); 110 111 __raw_writel(save->ulcon, regs + S3C2410_ULCON); 112 __raw_writel(save->ucon, regs + S3C2410_UCON); 113 __raw_writel(save->ufcon, regs + S3C2410_UFCON); 114 __raw_writel(save->umcon, regs + S3C2410_UMCON); 115 __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV); 116 117 if (pm_uart_udivslot) 118 __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT); 119} 120 121static void s3c_pm_restore_uarts(void) 122{ 123 struct pm_uart_save *save = uart_save; 124 unsigned int uart; 125 126 for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++) 127 s3c_pm_restore_uart(uart, save); 128} 129#else 130static void s3c_pm_save_uarts(void) { } 131static void s3c_pm_restore_uarts(void) { } 132#endif 133 134/* The IRQ ext-int code goes here, it is too small to currently bother 135 * with its own file. */ 136 137unsigned long s3c_irqwake_intmask = 0xffffffffL; 138unsigned long s3c_irqwake_eintmask = 0xffffffffL; 139 140int s3c_irqext_wake(struct irq_data *data, unsigned int state) 141{ 142 unsigned long bit = 1L << IRQ_EINT_BIT(data->irq); 143 144 if (!(s3c_irqwake_eintallow & bit)) 145 return -ENOENT; 146 147 printk(KERN_INFO "wake %s for irq %d\n", 148 state ? "enabled" : "disabled", data->irq); 149 150 if (!state) 151 s3c_irqwake_eintmask |= bit; 152 else 153 s3c_irqwake_eintmask &= ~bit; 154 155 return 0; 156} 157 158/* helper functions to save and restore register state */ 159 160/** 161 * s3c_pm_do_save() - save a set of registers for restoration on resume. 162 * @ptr: Pointer to an array of registers. 163 * @count: Size of the ptr array. 164 * 165 * Run through the list of registers given, saving their contents in the 166 * array for later restoration when we wakeup. 167 */ 168void s3c_pm_do_save(struct sleep_save *ptr, int count) 169{ 170 for (; count > 0; count--, ptr++) { 171 ptr->val = __raw_readl(ptr->reg); 172 S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val); 173 } 174} 175 176/** 177 * s3c_pm_do_restore() - restore register values from the save list. 178 * @ptr: Pointer to an array of registers. 179 * @count: Size of the ptr array. 180 * 181 * Restore the register values saved from s3c_pm_do_save(). 182 * 183 * Note, we do not use S3C_PMDBG() in here, as the system may not have 184 * restore the UARTs state yet 185*/ 186 187void s3c_pm_do_restore(struct sleep_save *ptr, int count) 188{ 189 for (; count > 0; count--, ptr++) { 190 printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n", 191 ptr->reg, ptr->val, __raw_readl(ptr->reg)); 192 193 __raw_writel(ptr->val, ptr->reg); 194 } 195} 196 197/** 198 * s3c_pm_do_restore_core() - early restore register values from save list. 199 * 200 * This is similar to s3c_pm_do_restore() except we try and minimise the 201 * side effects of the function in case registers that hardware might need 202 * to work has been restored. 203 * 204 * WARNING: Do not put any debug in here that may effect memory or use 205 * peripherals, as things may be changing! 206*/ 207 208void s3c_pm_do_restore_core(struct sleep_save *ptr, int count) 209{ 210 for (; count > 0; count--, ptr++) 211 __raw_writel(ptr->val, ptr->reg); 212} 213 214/* s3c2410_pm_show_resume_irqs 215 * 216 * print any IRQs asserted at resume time (ie, we woke from) 217*/ 218static void __maybe_unused s3c_pm_show_resume_irqs(int start, 219 unsigned long which, 220 unsigned long mask) 221{ 222 int i; 223 224 which &= ~mask; 225 226 for (i = 0; i <= 31; i++) { 227 if (which & (1L<<i)) { 228 S3C_PMDBG("IRQ %d asserted at resume\n", start+i); 229 } 230 } 231} 232 233 234void (*pm_cpu_prep)(void); 235int (*pm_cpu_sleep)(unsigned long); 236 237#define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) 238 239/* s3c_pm_enter 240 * 241 * central control for sleep/resume process 242*/ 243 244static int s3c_pm_enter(suspend_state_t state) 245{ 246 /* ensure the debug is initialised (if enabled) */ 247 248 s3c_pm_debug_init(); 249 250 S3C_PMDBG("%s(%d)\n", __func__, state); 251 252 if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { 253 printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__); 254 return -EINVAL; 255 } 256 257 /* check if we have anything to wake-up with... bad things seem 258 * to happen if you suspend with no wakeup (system will often 259 * require a full power-cycle) 260 */ 261 262 if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) && 263 !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) { 264 printk(KERN_ERR "%s: No wake-up sources!\n", __func__); 265 printk(KERN_ERR "%s: Aborting sleep\n", __func__); 266 return -EINVAL; 267 } 268 269 /* save all necessary core registers not covered by the drivers */ 270 271 samsung_pm_save_gpios(); 272 samsung_pm_saved_gpios(); 273 s3c_pm_save_uarts(); 274 s3c_pm_save_core(); 275 276 /* set the irq configuration for wake */ 277 278 s3c_pm_configure_extint(); 279 280 S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n", 281 s3c_irqwake_intmask, s3c_irqwake_eintmask); 282 283 s3c_pm_arch_prepare_irqs(); 284 285 /* call cpu specific preparation */ 286 287 pm_cpu_prep(); 288 289 /* flush cache back to ram */ 290 291 flush_cache_all(); 292 293 s3c_pm_check_store(); 294 295 /* send the cpu to sleep... */ 296 297 s3c_pm_arch_stop_clocks(); 298 299 /* this will also act as our return point from when 300 * we resume as it saves its own register state and restores it 301 * during the resume. */ 302 303 cpu_suspend(0, pm_cpu_sleep); 304 305 /* restore the system state */ 306 307 s3c_pm_restore_core(); 308 s3c_pm_restore_uarts(); 309 samsung_pm_restore_gpios(); 310 s3c_pm_restored_gpios(); 311 312 s3c_pm_debug_init(); 313 314 /* check what irq (if any) restored the system */ 315 316 s3c_pm_arch_show_resume_irqs(); 317 318 S3C_PMDBG("%s: post sleep, preparing to return\n", __func__); 319 320 /* LEDs should now be 1110 */ 321 s3c_pm_debug_smdkled(1 << 1, 0); 322 323 s3c_pm_check_restore(); 324 325 /* ok, let's return from sleep */ 326 327 S3C_PMDBG("S3C PM Resume (post-restore)\n"); 328 return 0; 329} 330 331static int s3c_pm_prepare(void) 332{ 333 /* prepare check area if configured */ 334 335 s3c_pm_check_prepare(); 336 return 0; 337} 338 339static void s3c_pm_finish(void) 340{ 341 s3c_pm_check_cleanup(); 342} 343 344static const struct platform_suspend_ops s3c_pm_ops = { 345 .enter = s3c_pm_enter, 346 .prepare = s3c_pm_prepare, 347 .finish = s3c_pm_finish, 348 .valid = suspend_valid_only_mem, 349}; 350 351/* s3c_pm_init 352 * 353 * Attach the power management functions. This should be called 354 * from the board specific initialisation if the board supports 355 * it. 356*/ 357 358int __init s3c_pm_init(void) 359{ 360 printk("S3C Power Management, Copyright 2004 Simtec Electronics\n"); 361 362 suspend_set_ops(&s3c_pm_ops); 363 return 0; 364} 365