0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
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
0048 static unsigned int save_arm_register[2];
0049
0050 void exynos_cpu_save_register(void)
0051 {
0052 unsigned long tmp;
0053
0054
0055 asm ("mrc p15, 0, %0, c15, c0, 0"
0056 : "=r" (tmp) : : "cc");
0057
0058 save_arm_register[0] = tmp;
0059
0060
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
0072 tmp = save_arm_register[0];
0073
0074 asm volatile ("mcr p15, 0, %0, c15, c0, 0"
0075 : : "r" (tmp)
0076 : "cc");
0077
0078
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
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
0102
0103
0104
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
0111 pmu_raw_writel(0x0, S5P_WAKEUP_STAT);
0112
0113 return -1;
0114 }
0115
0116 return 0;
0117 }
0118
0119
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
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
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
0195
0196
0197 if (cpu_online(1)) {
0198
0199
0200
0201
0202
0203 while (exynos_cpu_power_state(1)) {
0204 unsigned long boot_addr;
0205
0206
0207
0208
0209
0210 if (atomic_read(&cpu1_wakeup))
0211 goto abort;
0212
0213
0214
0215
0216
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
0238
0239 ret = exynos_set_boot_addr(1, boot_addr);
0240 if (ret)
0241 goto fail;
0242 dsb();
0243
0244
0245
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
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
0294
0295 if (cpu_pm_enter())
0296 goto cpu1_aborted;
0297
0298
0299
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
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