pm.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /*
  2. * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
  3. * http://www.samsung.com
  4. *
  5. * EXYNOS - Power Management support
  6. *
  7. * Based on arch/arm/mach-s3c2410/pm.c
  8. * Copyright (c) 2006 Simtec Electronics
  9. * Ben Dooks <ben@simtec.co.uk>
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License version 2 as
  13. * published by the Free Software Foundation.
  14. */
  15. #include <linux/init.h>
  16. #include <linux/suspend.h>
  17. #include <linux/cpu_pm.h>
  18. #include <linux/io.h>
  19. #include <linux/err.h>
  20. #include <asm/firmware.h>
  21. #include <asm/smp_scu.h>
  22. #include <asm/suspend.h>
  23. #include <mach/map.h>
  24. #include <plat/pm-common.h>
  25. #include "common.h"
  26. #include "exynos-pmu.h"
  27. #include "regs-pmu.h"
  28. static inline void __iomem *exynos_boot_vector_addr(void)
  29. {
  30. if (samsung_rev() == EXYNOS4210_REV_1_1)
  31. return pmu_base_addr + S5P_INFORM7;
  32. else if (samsung_rev() == EXYNOS4210_REV_1_0)
  33. return sysram_base_addr + 0x24;
  34. return pmu_base_addr + S5P_INFORM0;
  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. #define S5P_CHECK_AFTR 0xFCBA0D10
  45. /* For Cortex-A9 Diagnostic and Power control register */
  46. static unsigned int save_arm_register[2];
  47. void exynos_cpu_save_register(void)
  48. {
  49. unsigned long tmp;
  50. /* Save Power control register */
  51. asm ("mrc p15, 0, %0, c15, c0, 0"
  52. : "=r" (tmp) : : "cc");
  53. save_arm_register[0] = tmp;
  54. /* Save Diagnostic register */
  55. asm ("mrc p15, 0, %0, c15, c0, 1"
  56. : "=r" (tmp) : : "cc");
  57. save_arm_register[1] = tmp;
  58. }
  59. void exynos_cpu_restore_register(void)
  60. {
  61. unsigned long tmp;
  62. /* Restore Power control register */
  63. tmp = save_arm_register[0];
  64. asm volatile ("mcr p15, 0, %0, c15, c0, 0"
  65. : : "r" (tmp)
  66. : "cc");
  67. /* Restore Diagnostic register */
  68. tmp = save_arm_register[1];
  69. asm volatile ("mcr p15, 0, %0, c15, c0, 1"
  70. : : "r" (tmp)
  71. : "cc");
  72. }
  73. void exynos_pm_central_suspend(void)
  74. {
  75. unsigned long tmp;
  76. /* Setting Central Sequence Register for power down mode */
  77. tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
  78. tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
  79. pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
  80. }
  81. int exynos_pm_central_resume(void)
  82. {
  83. unsigned long tmp;
  84. /*
  85. * If PMU failed while entering sleep mode, WFI will be
  86. * ignored by PMU and then exiting cpu_do_idle().
  87. * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
  88. * in this situation.
  89. */
  90. tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
  91. if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
  92. tmp |= S5P_CENTRAL_LOWPWR_CFG;
  93. pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
  94. /* clear the wakeup state register */
  95. pmu_raw_writel(0x0, S5P_WAKEUP_STAT);
  96. /* No need to perform below restore code */
  97. return -1;
  98. }
  99. return 0;
  100. }
  101. /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
  102. static void exynos_set_wakeupmask(long mask)
  103. {
  104. pmu_raw_writel(mask, S5P_WAKEUP_MASK);
  105. }
  106. static void exynos_cpu_set_boot_vector(long flags)
  107. {
  108. __raw_writel(virt_to_phys(exynos_cpu_resume),
  109. exynos_boot_vector_addr());
  110. __raw_writel(flags, exynos_boot_vector_flag());
  111. }
  112. static int exynos_aftr_finisher(unsigned long flags)
  113. {
  114. int ret;
  115. exynos_set_wakeupmask(0x0000ff3e);
  116. /* Set value of power down register for aftr mode */
  117. exynos_sys_powerdown_conf(SYS_AFTR);
  118. ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR);
  119. if (ret == -ENOSYS) {
  120. if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
  121. exynos_cpu_save_register();
  122. exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
  123. cpu_do_idle();
  124. }
  125. return 1;
  126. }
  127. void exynos_enter_aftr(void)
  128. {
  129. cpu_pm_enter();
  130. exynos_pm_central_suspend();
  131. if (of_machine_is_compatible("samsung,exynos4212") ||
  132. of_machine_is_compatible("samsung,exynos4412")) {
  133. /* Setting SEQ_OPTION register */
  134. pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
  135. S5P_CENTRAL_SEQ_OPTION);
  136. }
  137. cpu_suspend(0, exynos_aftr_finisher);
  138. if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
  139. scu_enable(S5P_VA_SCU);
  140. if (call_firmware_op(resume) == -ENOSYS)
  141. exynos_cpu_restore_register();
  142. }
  143. exynos_pm_central_resume();
  144. cpu_pm_exit();
  145. }
  146. #if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
  147. static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
  148. static int exynos_cpu0_enter_aftr(void)
  149. {
  150. int ret = -1;
  151. /*
  152. * If the other cpu is powered on, we have to power it off, because
  153. * the AFTR state won't work otherwise
  154. */
  155. if (cpu_online(1)) {
  156. /*
  157. * We reach a sync point with the coupled idle state, we know
  158. * the other cpu will power down itself or will abort the
  159. * sequence, let's wait for one of these to happen
  160. */
  161. while (exynos_cpu_power_state(1)) {
  162. /*
  163. * The other cpu may skip idle and boot back
  164. * up again
  165. */
  166. if (atomic_read(&cpu1_wakeup))
  167. goto abort;
  168. /*
  169. * The other cpu may bounce through idle and
  170. * boot back up again, getting stuck in the
  171. * boot rom code
  172. */
  173. if (__raw_readl(cpu_boot_reg_base()) == 0)
  174. goto abort;
  175. cpu_relax();
  176. }
  177. }
  178. exynos_enter_aftr();
  179. ret = 0;
  180. abort:
  181. if (cpu_online(1)) {
  182. /*
  183. * Set the boot vector to something non-zero
  184. */
  185. __raw_writel(virt_to_phys(exynos_cpu_resume),
  186. cpu_boot_reg_base());
  187. dsb();
  188. /*
  189. * Turn on cpu1 and wait for it to be on
  190. */
  191. exynos_cpu_power_up(1);
  192. while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
  193. cpu_relax();
  194. while (!atomic_read(&cpu1_wakeup)) {
  195. /*
  196. * Poke cpu1 out of the boot rom
  197. */
  198. __raw_writel(virt_to_phys(exynos_cpu_resume),
  199. cpu_boot_reg_base());
  200. arch_send_wakeup_ipi_mask(cpumask_of(1));
  201. }
  202. }
  203. return ret;
  204. }
  205. static int exynos_wfi_finisher(unsigned long flags)
  206. {
  207. cpu_do_idle();
  208. return -1;
  209. }
  210. static int exynos_cpu1_powerdown(void)
  211. {
  212. int ret = -1;
  213. /*
  214. * Idle sequence for cpu1
  215. */
  216. if (cpu_pm_enter())
  217. goto cpu1_aborted;
  218. /*
  219. * Turn off cpu 1
  220. */
  221. exynos_cpu_power_down(1);
  222. ret = cpu_suspend(0, exynos_wfi_finisher);
  223. cpu_pm_exit();
  224. cpu1_aborted:
  225. dsb();
  226. /*
  227. * Notify cpu 0 that cpu 1 is awake
  228. */
  229. atomic_set(&cpu1_wakeup, 1);
  230. return ret;
  231. }
  232. static void exynos_pre_enter_aftr(void)
  233. {
  234. __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base());
  235. }
  236. static void exynos_post_enter_aftr(void)
  237. {
  238. atomic_set(&cpu1_wakeup, 0);
  239. }
  240. struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
  241. .cpu0_enter_aftr = exynos_cpu0_enter_aftr,
  242. .cpu1_powerdown = exynos_cpu1_powerdown,
  243. .pre_enter_aftr = exynos_pre_enter_aftr,
  244. .post_enter_aftr = exynos_post_enter_aftr,
  245. };
  246. #endif /* CONFIG_SMP && CONFIG_ARM_EXYNOS_CPUIDLE */