root/arch/arm/mach-exynos/pm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exynos_boot_vector_addr
  2. exynos_boot_vector_flag
  3. exynos_cpu_save_register
  4. exynos_cpu_restore_register
  5. exynos_pm_central_suspend
  6. exynos_pm_central_resume
  7. exynos_set_wakeupmask
  8. exynos_cpu_set_boot_vector
  9. exynos_aftr_finisher
  10. exynos_enter_aftr
  11. exynos_cpu0_enter_aftr
  12. exynos_wfi_finisher
  13. exynos_cpu1_powerdown
  14. exynos_pre_enter_aftr
  15. exynos_post_enter_aftr

   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 
  27 static inline void __iomem *exynos_boot_vector_addr(void)
  28 {
  29         if (samsung_rev() == EXYNOS4210_REV_1_1)
  30                 return pmu_base_addr + S5P_INFORM7;
  31         else if (samsung_rev() == EXYNOS4210_REV_1_0)
  32                 return sysram_base_addr + 0x24;
  33         return pmu_base_addr + S5P_INFORM0;
  34 }
  35 
  36 static inline void __iomem *exynos_boot_vector_flag(void)
  37 {
  38         if (samsung_rev() == EXYNOS4210_REV_1_1)
  39                 return pmu_base_addr + S5P_INFORM6;
  40         else if (samsung_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 */
  48 static unsigned int save_arm_register[2];
  49 
  50 void 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 
  67 void 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 
  86 void 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 
  96 int 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 */
 120 static 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 
 127 static 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 
 134 static 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 
 153 void 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)
 187 static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
 188 
 189 static 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 
 232 abort:
 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         }
 275 fail:
 276         return ret;
 277 }
 278 
 279 static 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 
 288 static 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 
 310 cpu1_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 
 320 static 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 
 327 static void exynos_post_enter_aftr(void)
 328 {
 329         atomic_set(&cpu1_wakeup, 0);
 330 }
 331 
 332 struct 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 */

/* [<][>][^][v][top][bottom][index][help] */