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