linux/arch/arm/mach-s3c64xx/pm.c
<<
>>
Prefs
   1/* linux/arch/arm/plat-s3c64xx/pm.c
   2 *
   3 * Copyright 2008 Openmoko, Inc.
   4 * Copyright 2008 Simtec Electronics
   5 *      Ben Dooks <ben@simtec.co.uk>
   6 *      http://armlinux.simtec.co.uk/
   7 *
   8 * S3C64XX CPU PM 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/serial_core.h>
  18#include <linux/io.h>
  19#include <linux/gpio.h>
  20#include <linux/pm_domain.h>
  21
  22#include <mach/map.h>
  23#include <mach/irqs.h>
  24
  25#include <plat/devs.h>
  26#include <plat/pm.h>
  27#include <plat/wakeup-mask.h>
  28
  29#include <mach/regs-gpio.h>
  30#include <mach/regs-clock.h>
  31
  32#include "regs-gpio-memport.h"
  33#include "regs-modem.h"
  34#include "regs-sys.h"
  35#include "regs-syscon-power.h"
  36
  37struct s3c64xx_pm_domain {
  38        char *const name;
  39        u32 ena;
  40        u32 pwr_stat;
  41        struct generic_pm_domain pd;
  42};
  43
  44static int s3c64xx_pd_off(struct generic_pm_domain *domain)
  45{
  46        struct s3c64xx_pm_domain *pd;
  47        u32 val;
  48
  49        pd = container_of(domain, struct s3c64xx_pm_domain, pd);
  50
  51        val = __raw_readl(S3C64XX_NORMAL_CFG);
  52        val &= ~(pd->ena);
  53        __raw_writel(val, S3C64XX_NORMAL_CFG);
  54
  55        return 0;
  56}
  57
  58static int s3c64xx_pd_on(struct generic_pm_domain *domain)
  59{
  60        struct s3c64xx_pm_domain *pd;
  61        u32 val;
  62        long retry = 1000000L;
  63
  64        pd = container_of(domain, struct s3c64xx_pm_domain, pd);
  65
  66        val = __raw_readl(S3C64XX_NORMAL_CFG);
  67        val |= pd->ena;
  68        __raw_writel(val, S3C64XX_NORMAL_CFG);
  69
  70        /* Not all domains provide power status readback */
  71        if (pd->pwr_stat) {
  72                do {
  73                        cpu_relax();
  74                        if (__raw_readl(S3C64XX_BLK_PWR_STAT) & pd->pwr_stat)
  75                                break;
  76                } while (retry--);
  77
  78                if (!retry) {
  79                        pr_err("Failed to start domain %s\n", pd->name);
  80                        return -EBUSY;
  81                }
  82        }
  83
  84        return 0;
  85}
  86
  87static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
  88        .name = "IROM",
  89        .ena = S3C64XX_NORMALCFG_IROM_ON,
  90        .pd = {
  91                .power_off = s3c64xx_pd_off,
  92                .power_on = s3c64xx_pd_on,
  93        },
  94};
  95
  96static struct s3c64xx_pm_domain s3c64xx_pm_etm = {
  97        .name = "ETM",
  98        .ena = S3C64XX_NORMALCFG_DOMAIN_ETM_ON,
  99        .pwr_stat = S3C64XX_BLKPWRSTAT_ETM,
 100        .pd = {
 101                .power_off = s3c64xx_pd_off,
 102                .power_on = s3c64xx_pd_on,
 103        },
 104};
 105
 106static struct s3c64xx_pm_domain s3c64xx_pm_s = {
 107        .name = "S",
 108        .ena = S3C64XX_NORMALCFG_DOMAIN_S_ON,
 109        .pwr_stat = S3C64XX_BLKPWRSTAT_S,
 110        .pd = {
 111                .power_off = s3c64xx_pd_off,
 112                .power_on = s3c64xx_pd_on,
 113        },
 114};
 115
 116static struct s3c64xx_pm_domain s3c64xx_pm_f = {
 117        .name = "F",
 118        .ena = S3C64XX_NORMALCFG_DOMAIN_F_ON,
 119        .pwr_stat = S3C64XX_BLKPWRSTAT_F,
 120        .pd = {
 121                .power_off = s3c64xx_pd_off,
 122                .power_on = s3c64xx_pd_on,
 123        },
 124};
 125
 126static struct s3c64xx_pm_domain s3c64xx_pm_p = {
 127        .name = "P",
 128        .ena = S3C64XX_NORMALCFG_DOMAIN_P_ON,
 129        .pwr_stat = S3C64XX_BLKPWRSTAT_P,
 130        .pd = {
 131                .power_off = s3c64xx_pd_off,
 132                .power_on = s3c64xx_pd_on,
 133        },
 134};
 135
 136static struct s3c64xx_pm_domain s3c64xx_pm_i = {
 137        .name = "I",
 138        .ena = S3C64XX_NORMALCFG_DOMAIN_I_ON,
 139        .pwr_stat = S3C64XX_BLKPWRSTAT_I,
 140        .pd = {
 141                .power_off = s3c64xx_pd_off,
 142                .power_on = s3c64xx_pd_on,
 143        },
 144};
 145
 146static struct s3c64xx_pm_domain s3c64xx_pm_g = {
 147        .name = "G",
 148        .ena = S3C64XX_NORMALCFG_DOMAIN_G_ON,
 149        .pd = {
 150                .power_off = s3c64xx_pd_off,
 151                .power_on = s3c64xx_pd_on,
 152        },
 153};
 154
 155static struct s3c64xx_pm_domain s3c64xx_pm_v = {
 156        .name = "V",
 157        .ena = S3C64XX_NORMALCFG_DOMAIN_V_ON,
 158        .pwr_stat = S3C64XX_BLKPWRSTAT_V,
 159        .pd = {
 160                .power_off = s3c64xx_pd_off,
 161                .power_on = s3c64xx_pd_on,
 162        },
 163};
 164
 165static struct s3c64xx_pm_domain *s3c64xx_always_on_pm_domains[] = {
 166        &s3c64xx_pm_irom,
 167};
 168
 169static struct s3c64xx_pm_domain *s3c64xx_pm_domains[] = {
 170        &s3c64xx_pm_etm,
 171        &s3c64xx_pm_g,
 172        &s3c64xx_pm_v,
 173        &s3c64xx_pm_i,
 174        &s3c64xx_pm_p,
 175        &s3c64xx_pm_s,
 176        &s3c64xx_pm_f,
 177};
 178
 179#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
 180void s3c_pm_debug_smdkled(u32 set, u32 clear)
 181{
 182        unsigned long flags;
 183        int i;
 184
 185        local_irq_save(flags);
 186        for (i = 0; i < 4; i++) {
 187                if (clear & (1 << i))
 188                        gpio_set_value(S3C64XX_GPN(12 + i), 0);
 189                if (set & (1 << i))
 190                        gpio_set_value(S3C64XX_GPN(12 + i), 1);
 191        }
 192        local_irq_restore(flags);
 193}
 194#endif
 195
 196static struct sleep_save core_save[] = {
 197        SAVE_ITEM(S3C_APLL_LOCK),
 198        SAVE_ITEM(S3C_MPLL_LOCK),
 199        SAVE_ITEM(S3C_EPLL_LOCK),
 200        SAVE_ITEM(S3C_CLK_SRC),
 201        SAVE_ITEM(S3C_CLK_DIV0),
 202        SAVE_ITEM(S3C_CLK_DIV1),
 203        SAVE_ITEM(S3C_CLK_DIV2),
 204        SAVE_ITEM(S3C_CLK_OUT),
 205        SAVE_ITEM(S3C_HCLK_GATE),
 206        SAVE_ITEM(S3C_PCLK_GATE),
 207        SAVE_ITEM(S3C_SCLK_GATE),
 208        SAVE_ITEM(S3C_MEM0_GATE),
 209
 210        SAVE_ITEM(S3C_EPLL_CON1),
 211        SAVE_ITEM(S3C_EPLL_CON0),
 212
 213        SAVE_ITEM(S3C64XX_MEM0DRVCON),
 214        SAVE_ITEM(S3C64XX_MEM1DRVCON),
 215
 216#ifndef CONFIG_CPU_FREQ
 217        SAVE_ITEM(S3C_APLL_CON),
 218        SAVE_ITEM(S3C_MPLL_CON),
 219#endif
 220};
 221
 222static struct sleep_save misc_save[] = {
 223        SAVE_ITEM(S3C64XX_AHB_CON0),
 224        SAVE_ITEM(S3C64XX_AHB_CON1),
 225        SAVE_ITEM(S3C64XX_AHB_CON2),
 226        
 227        SAVE_ITEM(S3C64XX_SPCON),
 228
 229        SAVE_ITEM(S3C64XX_MEM0CONSTOP),
 230        SAVE_ITEM(S3C64XX_MEM1CONSTOP),
 231        SAVE_ITEM(S3C64XX_MEM0CONSLP0),
 232        SAVE_ITEM(S3C64XX_MEM0CONSLP1),
 233        SAVE_ITEM(S3C64XX_MEM1CONSLP),
 234
 235        SAVE_ITEM(S3C64XX_SDMA_SEL),
 236        SAVE_ITEM(S3C64XX_MODEM_MIFPCON),
 237
 238        SAVE_ITEM(S3C64XX_NORMAL_CFG),
 239};
 240
 241void s3c_pm_configure_extint(void)
 242{
 243        __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK);
 244}
 245
 246void s3c_pm_restore_core(void)
 247{
 248        __raw_writel(0, S3C64XX_EINT_MASK);
 249
 250        s3c_pm_debug_smdkled(1 << 2, 0);
 251
 252        s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
 253        s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
 254}
 255
 256void s3c_pm_save_core(void)
 257{
 258        s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
 259        s3c_pm_do_save(core_save, ARRAY_SIZE(core_save));
 260}
 261
 262/* since both s3c6400 and s3c6410 share the same sleep pm calls, we
 263 * put the per-cpu code in here until any new cpu comes along and changes
 264 * this.
 265 */
 266
 267static int s3c64xx_cpu_suspend(unsigned long arg)
 268{
 269        unsigned long tmp;
 270
 271        /* set our standby method to sleep */
 272
 273        tmp = __raw_readl(S3C64XX_PWR_CFG);
 274        tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK;
 275        tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP;
 276        __raw_writel(tmp, S3C64XX_PWR_CFG);
 277
 278        /* clear any old wakeup */
 279
 280        __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT),
 281                     S3C64XX_WAKEUP_STAT);
 282
 283        /* set the LED state to 0110 over sleep */
 284        s3c_pm_debug_smdkled(3 << 1, 0xf);
 285
 286        /* issue the standby signal into the pm unit. Note, we
 287         * issue a write-buffer drain just in case */
 288
 289        tmp = 0;
 290
 291        asm("b 1f\n\t"
 292            ".align 5\n\t"
 293            "1:\n\t"
 294            "mcr p15, 0, %0, c7, c10, 5\n\t"
 295            "mcr p15, 0, %0, c7, c10, 4\n\t"
 296            "mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp));
 297
 298        /* we should never get past here */
 299
 300        pr_info("Failed to suspend the system\n");
 301        return 1; /* Aborting suspend */
 302}
 303
 304/* mapping of interrupts to parts of the wakeup mask */
 305static struct samsung_wakeup_mask wake_irqs[] = {
 306        { .irq = IRQ_RTC_ALARM, .bit = S3C64XX_PWRCFG_RTC_ALARM_DISABLE, },
 307        { .irq = IRQ_RTC_TIC,   .bit = S3C64XX_PWRCFG_RTC_TICK_DISABLE, },
 308        { .irq = IRQ_PENDN,     .bit = S3C64XX_PWRCFG_TS_DISABLE, },
 309        { .irq = IRQ_HSMMC0,    .bit = S3C64XX_PWRCFG_MMC0_DISABLE, },
 310        { .irq = IRQ_HSMMC1,    .bit = S3C64XX_PWRCFG_MMC1_DISABLE, },
 311        { .irq = IRQ_HSMMC2,    .bit = S3C64XX_PWRCFG_MMC2_DISABLE, },
 312        { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_BATF_DISABLE},
 313        { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_MSM_DISABLE },
 314        { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_HSI_DISABLE },
 315        { .irq = NO_WAKEUP_IRQ, .bit = S3C64XX_PWRCFG_MSM_DISABLE },
 316};
 317
 318static void s3c64xx_pm_prepare(void)
 319{
 320        samsung_sync_wakemask(S3C64XX_PWR_CFG,
 321                              wake_irqs, ARRAY_SIZE(wake_irqs));
 322
 323        /* store address of resume. */
 324        __raw_writel(virt_to_phys(s3c_cpu_resume), S3C64XX_INFORM0);
 325
 326        /* ensure previous wakeup state is cleared before sleeping */
 327        __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT);
 328}
 329
 330int __init s3c64xx_pm_init(void)
 331{
 332        int i;
 333
 334        s3c_pm_init();
 335
 336        for (i = 0; i < ARRAY_SIZE(s3c64xx_always_on_pm_domains); i++)
 337                pm_genpd_init(&s3c64xx_always_on_pm_domains[i]->pd,
 338                              &pm_domain_always_on_gov, false);
 339
 340        for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++)
 341                pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false);
 342
 343#ifdef CONFIG_S3C_DEV_FB
 344        if (dev_get_platdata(&s3c_device_fb.dev))
 345                pm_genpd_add_device(&s3c64xx_pm_f.pd, &s3c_device_fb.dev);
 346#endif
 347
 348        return 0;
 349}
 350
 351static __init int s3c64xx_pm_initcall(void)
 352{
 353        pm_cpu_prep = s3c64xx_pm_prepare;
 354        pm_cpu_sleep = s3c64xx_cpu_suspend;
 355        pm_uart_udivslot = 1;
 356
 357#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
 358        gpio_request(S3C64XX_GPN(12), "DEBUG_LED0");
 359        gpio_request(S3C64XX_GPN(13), "DEBUG_LED1");
 360        gpio_request(S3C64XX_GPN(14), "DEBUG_LED2");
 361        gpio_request(S3C64XX_GPN(15), "DEBUG_LED3");
 362        gpio_direction_output(S3C64XX_GPN(12), 0);
 363        gpio_direction_output(S3C64XX_GPN(13), 0);
 364        gpio_direction_output(S3C64XX_GPN(14), 0);
 365        gpio_direction_output(S3C64XX_GPN(15), 0);
 366#endif
 367
 368        return 0;
 369}
 370arch_initcall(s3c64xx_pm_initcall);
 371
 372int __init s3c64xx_pm_late_initcall(void)
 373{
 374        pm_genpd_poweroff_unused();
 375
 376        return 0;
 377}
 378