pm.c 7.1 KB

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