Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
0004 //      http://www.samsung.com
0005 //
0006 // Exynos - Power Management support
0007 //
0008 // Based on arch/arm/mach-s3c2410/pm.c
0009 // Copyright (c) 2006 Simtec Electronics
0010 //  Ben Dooks <ben@simtec.co.uk>
0011 
0012 #include <linux/init.h>
0013 #include <linux/suspend.h>
0014 #include <linux/cpu_pm.h>
0015 #include <linux/io.h>
0016 #include <linux/of.h>
0017 #include <linux/soc/samsung/exynos-regs-pmu.h>
0018 #include <linux/soc/samsung/exynos-pmu.h>
0019 
0020 #include <asm/firmware.h>
0021 #include <asm/smp_scu.h>
0022 #include <asm/suspend.h>
0023 #include <asm/cacheflush.h>
0024 
0025 #include "common.h"
0026 
0027 static inline void __iomem *exynos_boot_vector_addr(void)
0028 {
0029     if (exynos_rev() == EXYNOS4210_REV_1_1)
0030         return pmu_base_addr + S5P_INFORM7;
0031     else if (exynos_rev() == EXYNOS4210_REV_1_0)
0032         return sysram_base_addr + 0x24;
0033     return pmu_base_addr + S5P_INFORM0;
0034 }
0035 
0036 static inline void __iomem *exynos_boot_vector_flag(void)
0037 {
0038     if (exynos_rev() == EXYNOS4210_REV_1_1)
0039         return pmu_base_addr + S5P_INFORM6;
0040     else if (exynos_rev() == EXYNOS4210_REV_1_0)
0041         return sysram_base_addr + 0x20;
0042     return pmu_base_addr + S5P_INFORM1;
0043 }
0044 
0045 #define S5P_CHECK_AFTR  0xFCBA0D10
0046 
0047 /* For Cortex-A9 Diagnostic and Power control register */
0048 static unsigned int save_arm_register[2];
0049 
0050 void exynos_cpu_save_register(void)
0051 {
0052     unsigned long tmp;
0053 
0054     /* Save Power control register */
0055     asm ("mrc p15, 0, %0, c15, c0, 0"
0056          : "=r" (tmp) : : "cc");
0057 
0058     save_arm_register[0] = tmp;
0059 
0060     /* Save Diagnostic register */
0061     asm ("mrc p15, 0, %0, c15, c0, 1"
0062          : "=r" (tmp) : : "cc");
0063 
0064     save_arm_register[1] = tmp;
0065 }
0066 
0067 void exynos_cpu_restore_register(void)
0068 {
0069     unsigned long tmp;
0070 
0071     /* Restore Power control register */
0072     tmp = save_arm_register[0];
0073 
0074     asm volatile ("mcr p15, 0, %0, c15, c0, 0"
0075               : : "r" (tmp)
0076               : "cc");
0077 
0078     /* Restore Diagnostic register */
0079     tmp = save_arm_register[1];
0080 
0081     asm volatile ("mcr p15, 0, %0, c15, c0, 1"
0082               : : "r" (tmp)
0083               : "cc");
0084 }
0085 
0086 void exynos_pm_central_suspend(void)
0087 {
0088     unsigned long tmp;
0089 
0090     /* Setting Central Sequence Register for power down mode */
0091     tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
0092     tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
0093     pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
0094 }
0095 
0096 int exynos_pm_central_resume(void)
0097 {
0098     unsigned long tmp;
0099 
0100     /*
0101      * If PMU failed while entering sleep mode, WFI will be
0102      * ignored by PMU and then exiting cpu_do_idle().
0103      * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
0104      * in this situation.
0105      */
0106     tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
0107     if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
0108         tmp |= S5P_CENTRAL_LOWPWR_CFG;
0109         pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
0110         /* clear the wakeup state register */
0111         pmu_raw_writel(0x0, S5P_WAKEUP_STAT);
0112         /* No need to perform below restore code */
0113         return -1;
0114     }
0115 
0116     return 0;
0117 }
0118 
0119 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
0120 static void exynos_set_wakeupmask(long mask)
0121 {
0122     pmu_raw_writel(mask, S5P_WAKEUP_MASK);
0123     if (soc_is_exynos3250())
0124         pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
0125 }
0126 
0127 static void exynos_cpu_set_boot_vector(long flags)
0128 {
0129     writel_relaxed(__pa_symbol(exynos_cpu_resume),
0130                exynos_boot_vector_addr());
0131     writel_relaxed(flags, exynos_boot_vector_flag());
0132 }
0133 
0134 static int exynos_aftr_finisher(unsigned long flags)
0135 {
0136     int ret;
0137 
0138     exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
0139     /* Set value of power down register for aftr mode */
0140     exynos_sys_powerdown_conf(SYS_AFTR);
0141 
0142     ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR);
0143     if (ret == -ENOSYS) {
0144         if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
0145             exynos_cpu_save_register();
0146         exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
0147         cpu_do_idle();
0148     }
0149 
0150     return 1;
0151 }
0152 
0153 void exynos_enter_aftr(void)
0154 {
0155     unsigned int cpuid = smp_processor_id();
0156 
0157     cpu_pm_enter();
0158 
0159     if (soc_is_exynos3250())
0160         exynos_set_boot_flag(cpuid, C2_STATE);
0161 
0162     exynos_pm_central_suspend();
0163 
0164     if (soc_is_exynos4412()) {
0165         /* Setting SEQ_OPTION register */
0166         pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
0167                    S5P_CENTRAL_SEQ_OPTION);
0168     }
0169 
0170     cpu_suspend(0, exynos_aftr_finisher);
0171 
0172     if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
0173         exynos_scu_enable();
0174         if (call_firmware_op(resume) == -ENOSYS)
0175             exynos_cpu_restore_register();
0176     }
0177 
0178     exynos_pm_central_resume();
0179 
0180     if (soc_is_exynos3250())
0181         exynos_clear_boot_flag(cpuid, C2_STATE);
0182 
0183     cpu_pm_exit();
0184 }
0185 
0186 #if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
0187 static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
0188 
0189 static int exynos_cpu0_enter_aftr(void)
0190 {
0191     int ret = -1;
0192 
0193     /*
0194      * If the other cpu is powered on, we have to power it off, because
0195      * the AFTR state won't work otherwise
0196      */
0197     if (cpu_online(1)) {
0198         /*
0199          * We reach a sync point with the coupled idle state, we know
0200          * the other cpu will power down itself or will abort the
0201          * sequence, let's wait for one of these to happen
0202          */
0203         while (exynos_cpu_power_state(1)) {
0204             unsigned long boot_addr;
0205 
0206             /*
0207              * The other cpu may skip idle and boot back
0208              * up again
0209              */
0210             if (atomic_read(&cpu1_wakeup))
0211                 goto abort;
0212 
0213             /*
0214              * The other cpu may bounce through idle and
0215              * boot back up again, getting stuck in the
0216              * boot rom code
0217              */
0218             ret = exynos_get_boot_addr(1, &boot_addr);
0219             if (ret)
0220                 goto fail;
0221             ret = -1;
0222             if (boot_addr == 0)
0223                 goto abort;
0224 
0225             cpu_relax();
0226         }
0227     }
0228 
0229     exynos_enter_aftr();
0230     ret = 0;
0231 
0232 abort:
0233     if (cpu_online(1)) {
0234         unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);
0235 
0236         /*
0237          * Set the boot vector to something non-zero
0238          */
0239         ret = exynos_set_boot_addr(1, boot_addr);
0240         if (ret)
0241             goto fail;
0242         dsb();
0243 
0244         /*
0245          * Turn on cpu1 and wait for it to be on
0246          */
0247         exynos_cpu_power_up(1);
0248         while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
0249             cpu_relax();
0250 
0251         if (soc_is_exynos3250()) {
0252             while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
0253                    !atomic_read(&cpu1_wakeup))
0254                 cpu_relax();
0255 
0256             if (!atomic_read(&cpu1_wakeup))
0257                 exynos_core_restart(1);
0258         }
0259 
0260         while (!atomic_read(&cpu1_wakeup)) {
0261             smp_rmb();
0262 
0263             /*
0264              * Poke cpu1 out of the boot rom
0265              */
0266 
0267             ret = exynos_set_boot_addr(1, boot_addr);
0268             if (ret)
0269                 goto fail;
0270 
0271             call_firmware_op(cpu_boot, 1);
0272             dsb_sev();
0273         }
0274     }
0275 fail:
0276     return ret;
0277 }
0278 
0279 static int exynos_wfi_finisher(unsigned long flags)
0280 {
0281     if (soc_is_exynos3250())
0282         flush_cache_all();
0283     cpu_do_idle();
0284 
0285     return -1;
0286 }
0287 
0288 static int exynos_cpu1_powerdown(void)
0289 {
0290     int ret = -1;
0291 
0292     /*
0293      * Idle sequence for cpu1
0294      */
0295     if (cpu_pm_enter())
0296         goto cpu1_aborted;
0297 
0298     /*
0299      * Turn off cpu 1
0300      */
0301     exynos_cpu_power_down(1);
0302 
0303     if (soc_is_exynos3250())
0304         pmu_raw_writel(0, S5P_PMU_SPARE2);
0305 
0306     ret = cpu_suspend(0, exynos_wfi_finisher);
0307 
0308     cpu_pm_exit();
0309 
0310 cpu1_aborted:
0311     dsb();
0312     /*
0313      * Notify cpu 0 that cpu 1 is awake
0314      */
0315     atomic_set(&cpu1_wakeup, 1);
0316 
0317     return ret;
0318 }
0319 
0320 static void exynos_pre_enter_aftr(void)
0321 {
0322     unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);
0323 
0324     (void)exynos_set_boot_addr(1, boot_addr);
0325 }
0326 
0327 static void exynos_post_enter_aftr(void)
0328 {
0329     atomic_set(&cpu1_wakeup, 0);
0330 }
0331 
0332 struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
0333     .cpu0_enter_aftr        = exynos_cpu0_enter_aftr,
0334     .cpu1_powerdown     = exynos_cpu1_powerdown,
0335     .pre_enter_aftr     = exynos_pre_enter_aftr,
0336     .post_enter_aftr        = exynos_post_enter_aftr,
0337 };
0338 #endif /* CONFIG_SMP && CONFIG_ARM_EXYNOS_CPUIDLE */