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