linux/arch/arm/mach-exynos/pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2//
   3// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
   4//              http://www.samsung.com
   5//
   6// Exynos - 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/cpu_pm.h>
  15#include <linux/io.h>
  16#include <linux/of.h>
  17#include <linux/soc/samsung/exynos-regs-pmu.h>
  18#include <linux/soc/samsung/exynos-pmu.h>
  19
  20#include <asm/firmware.h>
  21#include <asm/smp_scu.h>
  22#include <asm/suspend.h>
  23#include <asm/cacheflush.h>
  24
  25#include "common.h"
  26
  27static inline void __iomem *exynos_boot_vector_addr(void)
  28{
  29        if (exynos_rev() == EXYNOS4210_REV_1_1)
  30                return pmu_base_addr + S5P_INFORM7;
  31        else if (exynos_rev() == EXYNOS4210_REV_1_0)
  32                return sysram_base_addr + 0x24;
  33        return pmu_base_addr + S5P_INFORM0;
  34}
  35
  36static inline void __iomem *exynos_boot_vector_flag(void)
  37{
  38        if (exynos_rev() == EXYNOS4210_REV_1_1)
  39                return pmu_base_addr + S5P_INFORM6;
  40        else if (exynos_rev() == EXYNOS4210_REV_1_0)
  41                return sysram_base_addr + 0x20;
  42        return pmu_base_addr + S5P_INFORM1;
  43}
  44
  45#define S5P_CHECK_AFTR  0xFCBA0D10
  46
  47/* For Cortex-A9 Diagnostic and Power control register */
  48static unsigned int save_arm_register[2];
  49
  50void exynos_cpu_save_register(void)
  51{
  52        unsigned long tmp;
  53
  54        /* Save Power control register */
  55        asm ("mrc p15, 0, %0, c15, c0, 0"
  56             : "=r" (tmp) : : "cc");
  57
  58        save_arm_register[0] = tmp;
  59
  60        /* Save Diagnostic register */
  61        asm ("mrc p15, 0, %0, c15, c0, 1"
  62             : "=r" (tmp) : : "cc");
  63
  64        save_arm_register[1] = tmp;
  65}
  66
  67void exynos_cpu_restore_register(void)
  68{
  69        unsigned long tmp;
  70
  71        /* Restore Power control register */
  72        tmp = save_arm_register[0];
  73
  74        asm volatile ("mcr p15, 0, %0, c15, c0, 0"
  75                      : : "r" (tmp)
  76                      : "cc");
  77
  78        /* Restore Diagnostic register */
  79        tmp = save_arm_register[1];
  80
  81        asm volatile ("mcr p15, 0, %0, c15, c0, 1"
  82                      : : "r" (tmp)
  83                      : "cc");
  84}
  85
  86void exynos_pm_central_suspend(void)
  87{
  88        unsigned long tmp;
  89
  90        /* Setting Central Sequence Register for power down mode */
  91        tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
  92        tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
  93        pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
  94}
  95
  96int exynos_pm_central_resume(void)
  97{
  98        unsigned long tmp;
  99
 100        /*
 101         * If PMU failed while entering sleep mode, WFI will be
 102         * ignored by PMU and then exiting cpu_do_idle().
 103         * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
 104         * in this situation.
 105         */
 106        tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
 107        if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
 108                tmp |= S5P_CENTRAL_LOWPWR_CFG;
 109                pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
 110                /* clear the wakeup state register */
 111                pmu_raw_writel(0x0, S5P_WAKEUP_STAT);
 112                /* No need to perform below restore code */
 113                return -1;
 114        }
 115
 116        return 0;
 117}
 118
 119/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
 120static void exynos_set_wakeupmask(long mask)
 121{
 122        pmu_raw_writel(mask, S5P_WAKEUP_MASK);
 123        if (soc_is_exynos3250())
 124                pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
 125}
 126
 127static void exynos_cpu_set_boot_vector(long flags)
 128{
 129        writel_relaxed(__pa_symbol(exynos_cpu_resume),
 130                       exynos_boot_vector_addr());
 131        writel_relaxed(flags, exynos_boot_vector_flag());
 132}
 133
 134static int exynos_aftr_finisher(unsigned long flags)
 135{
 136        int ret;
 137
 138        exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
 139        /* Set value of power down register for aftr mode */
 140        exynos_sys_powerdown_conf(SYS_AFTR);
 141
 142        ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR);
 143        if (ret == -ENOSYS) {
 144                if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
 145                        exynos_cpu_save_register();
 146                exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
 147                cpu_do_idle();
 148        }
 149
 150        return 1;
 151}
 152
 153void exynos_enter_aftr(void)
 154{
 155        unsigned int cpuid = smp_processor_id();
 156
 157        cpu_pm_enter();
 158
 159        if (soc_is_exynos3250())
 160                exynos_set_boot_flag(cpuid, C2_STATE);
 161
 162        exynos_pm_central_suspend();
 163
 164        if (soc_is_exynos4412()) {
 165                /* Setting SEQ_OPTION register */
 166                pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
 167                               S5P_CENTRAL_SEQ_OPTION);
 168        }
 169
 170        cpu_suspend(0, exynos_aftr_finisher);
 171
 172        if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
 173                exynos_scu_enable();
 174                if (call_firmware_op(resume) == -ENOSYS)
 175                        exynos_cpu_restore_register();
 176        }
 177
 178        exynos_pm_central_resume();
 179
 180        if (soc_is_exynos3250())
 181                exynos_clear_boot_flag(cpuid, C2_STATE);
 182
 183        cpu_pm_exit();
 184}
 185
 186#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
 187static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
 188
 189static int exynos_cpu0_enter_aftr(void)
 190{
 191        int ret = -1;
 192
 193        /*
 194         * If the other cpu is powered on, we have to power it off, because
 195         * the AFTR state won't work otherwise
 196         */
 197        if (cpu_online(1)) {
 198                /*
 199                 * We reach a sync point with the coupled idle state, we know
 200                 * the other cpu will power down itself or will abort the
 201                 * sequence, let's wait for one of these to happen
 202                 */
 203                while (exynos_cpu_power_state(1)) {
 204                        unsigned long boot_addr;
 205
 206                        /*
 207                         * The other cpu may skip idle and boot back
 208                         * up again
 209                         */
 210                        if (atomic_read(&cpu1_wakeup))
 211                                goto abort;
 212
 213                        /*
 214                         * The other cpu may bounce through idle and
 215                         * boot back up again, getting stuck in the
 216                         * boot rom code
 217                         */
 218                        ret = exynos_get_boot_addr(1, &boot_addr);
 219                        if (ret)
 220                                goto fail;
 221                        ret = -1;
 222                        if (boot_addr == 0)
 223                                goto abort;
 224
 225                        cpu_relax();
 226                }
 227        }
 228
 229        exynos_enter_aftr();
 230        ret = 0;
 231
 232abort:
 233        if (cpu_online(1)) {
 234                unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);
 235
 236                /*
 237                 * Set the boot vector to something non-zero
 238                 */
 239                ret = exynos_set_boot_addr(1, boot_addr);
 240                if (ret)
 241                        goto fail;
 242                dsb();
 243
 244                /*
 245                 * Turn on cpu1 and wait for it to be on
 246                 */
 247                exynos_cpu_power_up(1);
 248                while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
 249                        cpu_relax();
 250
 251                if (soc_is_exynos3250()) {
 252                        while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
 253                               !atomic_read(&cpu1_wakeup))
 254                                cpu_relax();
 255
 256                        if (!atomic_read(&cpu1_wakeup))
 257                                exynos_core_restart(1);
 258                }
 259
 260                while (!atomic_read(&cpu1_wakeup)) {
 261                        smp_rmb();
 262
 263                        /*
 264                         * Poke cpu1 out of the boot rom
 265                         */
 266
 267                        ret = exynos_set_boot_addr(1, boot_addr);
 268                        if (ret)
 269                                goto fail;
 270
 271                        call_firmware_op(cpu_boot, 1);
 272                        dsb_sev();
 273                }
 274        }
 275fail:
 276        return ret;
 277}
 278
 279static int exynos_wfi_finisher(unsigned long flags)
 280{
 281        if (soc_is_exynos3250())
 282                flush_cache_all();
 283        cpu_do_idle();
 284
 285        return -1;
 286}
 287
 288static int exynos_cpu1_powerdown(void)
 289{
 290        int ret = -1;
 291
 292        /*
 293         * Idle sequence for cpu1
 294         */
 295        if (cpu_pm_enter())
 296                goto cpu1_aborted;
 297
 298        /*
 299         * Turn off cpu 1
 300         */
 301        exynos_cpu_power_down(1);
 302
 303        if (soc_is_exynos3250())
 304                pmu_raw_writel(0, S5P_PMU_SPARE2);
 305
 306        ret = cpu_suspend(0, exynos_wfi_finisher);
 307
 308        cpu_pm_exit();
 309
 310cpu1_aborted:
 311        dsb();
 312        /*
 313         * Notify cpu 0 that cpu 1 is awake
 314         */
 315        atomic_set(&cpu1_wakeup, 1);
 316
 317        return ret;
 318}
 319
 320static void exynos_pre_enter_aftr(void)
 321{
 322        unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);
 323
 324        (void)exynos_set_boot_addr(1, boot_addr);
 325}
 326
 327static void exynos_post_enter_aftr(void)
 328{
 329        atomic_set(&cpu1_wakeup, 0);
 330}
 331
 332struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
 333        .cpu0_enter_aftr                = exynos_cpu0_enter_aftr,
 334        .cpu1_powerdown         = exynos_cpu1_powerdown,
 335        .pre_enter_aftr         = exynos_pre_enter_aftr,
 336        .post_enter_aftr                = exynos_post_enter_aftr,
 337};
 338#endif /* CONFIG_SMP && CONFIG_ARM_EXYNOS_CPUIDLE */
 339