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