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