linux/arch/arm/mach-s5pv210/pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2010-2014 Samsung Electronics Co., Ltd.
   4//              http://www.samsung.com
   5//
   6// S5PV210 - Power Management support
   7//
   8// Based on arch/arm/mach-s3c2410/pm.c
   9// Copyright (c) 2006 Simtec Electronics
  10//      Ben Dooks <ben@simtec.co.uk>
  11
  12#include <linux/init.h>
  13#include <linux/suspend.h>
  14#include <linux/syscore_ops.h>
  15#include <linux/io.h>
  16#include <linux/soc/samsung/s3c-pm.h>
  17
  18#include <asm/cacheflush.h>
  19#include <asm/suspend.h>
  20
  21#include "common.h"
  22#include "regs-clock.h"
  23
  24/* helper functions to save and restore register state */
  25struct sleep_save {
  26        void __iomem    *reg;
  27        unsigned long   val;
  28};
  29
  30#define SAVE_ITEM(x) \
  31        { .reg = (x) }
  32
  33/**
  34 * s3c_pm_do_save() - save a set of registers for restoration on resume.
  35 * @ptr: Pointer to an array of registers.
  36 * @count: Size of the ptr array.
  37 *
  38 * Run through the list of registers given, saving their contents in the
  39 * array for later restoration when we wakeup.
  40 */
  41static void s3c_pm_do_save(struct sleep_save *ptr, int count)
  42{
  43        for (; count > 0; count--, ptr++) {
  44                ptr->val = readl_relaxed(ptr->reg);
  45                S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
  46        }
  47}
  48
  49/**
  50 * s3c_pm_do_restore() - restore register values from the save list.
  51 * @ptr: Pointer to an array of registers.
  52 * @count: Size of the ptr array.
  53 *
  54 * Restore the register values saved from s3c_pm_do_save().
  55 *
  56 * WARNING: Do not put any debug in here that may effect memory or use
  57 * peripherals, as things may be changing!
  58*/
  59
  60static void s3c_pm_do_restore_core(const struct sleep_save *ptr, int count)
  61{
  62        for (; count > 0; count--, ptr++)
  63                writel_relaxed(ptr->val, ptr->reg);
  64}
  65
  66static struct sleep_save s5pv210_core_save[] = {
  67        /* Clock ETC */
  68        SAVE_ITEM(S5P_MDNIE_SEL),
  69};
  70
  71/*
  72 * VIC wake-up support (TODO)
  73 */
  74static u32 s5pv210_irqwake_intmask = 0xffffffff;
  75
  76static u32 s5pv210_read_eint_wakeup_mask(void)
  77{
  78        return __raw_readl(S5P_EINT_WAKEUP_MASK);
  79}
  80
  81/*
  82 * Suspend helpers.
  83 */
  84static int s5pv210_cpu_suspend(unsigned long arg)
  85{
  86        unsigned long tmp;
  87
  88        /* issue the standby signal into the pm unit. Note, we
  89         * issue a write-buffer drain just in case */
  90
  91        tmp = 0;
  92
  93        asm("b 1f\n\t"
  94            ".align 5\n\t"
  95            "1:\n\t"
  96            "mcr p15, 0, %0, c7, c10, 5\n\t"
  97            "mcr p15, 0, %0, c7, c10, 4\n\t"
  98            "wfi" : : "r" (tmp));
  99
 100        pr_info("Failed to suspend the system\n");
 101        return 1; /* Aborting suspend */
 102}
 103
 104static void s5pv210_pm_prepare(void)
 105{
 106        unsigned int tmp;
 107
 108        /*
 109         * Set wake-up mask registers
 110         * S5P_EINT_WAKEUP_MASK is set by pinctrl driver in late suspend.
 111         */
 112        __raw_writel(s5pv210_irqwake_intmask, S5P_WAKEUP_MASK);
 113
 114        /* ensure at least INFORM0 has the resume address */
 115        __raw_writel(__pa_symbol(s5pv210_cpu_resume), S5P_INFORM0);
 116
 117        tmp = __raw_readl(S5P_SLEEP_CFG);
 118        tmp &= ~(S5P_SLEEP_CFG_OSC_EN | S5P_SLEEP_CFG_USBOSC_EN);
 119        __raw_writel(tmp, S5P_SLEEP_CFG);
 120
 121        /* WFI for SLEEP mode configuration by SYSCON */
 122        tmp = __raw_readl(S5P_PWR_CFG);
 123        tmp &= S5P_CFG_WFI_CLEAN;
 124        tmp |= S5P_CFG_WFI_SLEEP;
 125        __raw_writel(tmp, S5P_PWR_CFG);
 126
 127        /* SYSCON interrupt handling disable */
 128        tmp = __raw_readl(S5P_OTHERS);
 129        tmp |= S5P_OTHER_SYSC_INTOFF;
 130        __raw_writel(tmp, S5P_OTHERS);
 131
 132        s3c_pm_do_save(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save));
 133}
 134
 135/*
 136 * Suspend operations.
 137 */
 138static int s5pv210_suspend_enter(suspend_state_t state)
 139{
 140        u32 eint_wakeup_mask = s5pv210_read_eint_wakeup_mask();
 141        int ret;
 142
 143        S3C_PMDBG("%s: suspending the system...\n", __func__);
 144
 145        S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
 146                        s5pv210_irqwake_intmask, eint_wakeup_mask);
 147
 148        if (s5pv210_irqwake_intmask == -1U
 149            && eint_wakeup_mask == -1U) {
 150                pr_err("%s: No wake-up sources!\n", __func__);
 151                pr_err("%s: Aborting sleep\n", __func__);
 152                return -EINVAL;
 153        }
 154
 155        s3c_pm_save_uarts(false);
 156        s5pv210_pm_prepare();
 157        flush_cache_all();
 158        s3c_pm_check_store();
 159
 160        ret = cpu_suspend(0, s5pv210_cpu_suspend);
 161        if (ret)
 162                return ret;
 163
 164        s3c_pm_restore_uarts(false);
 165
 166        S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
 167                        __raw_readl(S5P_WAKEUP_STAT));
 168
 169        s3c_pm_check_restore();
 170
 171        S3C_PMDBG("%s: resuming the system...\n", __func__);
 172
 173        return 0;
 174}
 175
 176static int s5pv210_suspend_prepare(void)
 177{
 178        s3c_pm_check_prepare();
 179
 180        return 0;
 181}
 182
 183static void s5pv210_suspend_finish(void)
 184{
 185        s3c_pm_check_cleanup();
 186}
 187
 188static const struct platform_suspend_ops s5pv210_suspend_ops = {
 189        .enter          = s5pv210_suspend_enter,
 190        .prepare        = s5pv210_suspend_prepare,
 191        .finish         = s5pv210_suspend_finish,
 192        .valid          = suspend_valid_only_mem,
 193};
 194
 195/*
 196 * Syscore operations used to delay restore of certain registers.
 197 */
 198static void s5pv210_pm_resume(void)
 199{
 200        s3c_pm_do_restore_core(s5pv210_core_save, ARRAY_SIZE(s5pv210_core_save));
 201}
 202
 203static struct syscore_ops s5pv210_pm_syscore_ops = {
 204        .resume         = s5pv210_pm_resume,
 205};
 206
 207/*
 208 * Initialization entry point.
 209 */
 210void __init s5pv210_pm_init(void)
 211{
 212        register_syscore_ops(&s5pv210_pm_syscore_ops);
 213        suspend_set_ops(&s5pv210_suspend_ops);
 214}
 215