aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/pm.c
diff options
context:
space:
mode:
authorBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>2015-01-24 00:05:50 -0500
committerKukjin Kim <kgene@kernel.org>2015-01-29 18:39:15 -0500
commit712eddf70225ab5ae65e946e22d2dfe6b93e8dd1 (patch)
tree62477a0a4b364cd2e87786481ee6dfe03e3fd569 /arch/arm/mach-exynos/pm.c
parent865e8b76a04d018f23d809ebf735c52105e3adb2 (diff)
cpuidle: exynos: add coupled cpuidle support for exynos4210
The following patch adds coupled cpuidle support for Exynos4210 to an existing cpuidle-exynos driver. As a result it enables AFTR mode to be used by default on Exynos4210 without the need to hot unplug CPU1 first. The patch is heavily based on earlier cpuidle-exynos4210 driver from Daniel Lezcano: http://www.spinics.net/lists/linux-samsung-soc/msg28134.html Changes from Daniel's code include: - porting code to current kernels - fixing it to work on my setup (by using S5P_INFORM register instead of S5P_VA_SYSRAM one on Revison 1.1 and retrying poking CPU1 out of the BOOT ROM if necessary) - fixing rare lockup caused by waiting for CPU1 to get stuck in the BOOT ROM (CPU hotplug code in arch/arm/mach-exynos/platsmp.c doesn't require this and works fine) - moving Exynos specific code to arch/arm/mach-exynos/pm.c - using cpu_boot_reg_base() helper instead of BOOT_VECTOR macro - using exynos_cpu_*() helpers instead of accessing registers directly - using arch_send_wakeup_ipi_mask() instead of dsb_sev() (this matches CPU hotplug code in arch/arm/mach-exynos/platsmp.c) - integrating separate exynos4210-cpuidle driver into existing exynos-cpuidle one Cc: Colin Cross <ccross@google.com> Cc: Kukjin Kim <kgene.kim@samsung.com> Cc: Krzysztof Kozlowski <k.kozlowski@samsung.com> Cc: Tomasz Figa <tomasz.figa@gmail.com> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Kukjin Kim <kgene@kernel.org>
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r--arch/arm/mach-exynos/pm.c122
1 files changed, 122 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index 159eb4c9779e..1f9cbd434c60 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -179,3 +179,125 @@ void exynos_enter_aftr(void)
179 179
180 cpu_pm_exit(); 180 cpu_pm_exit();
181} 181}
182
183static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
184
185static int exynos_cpu0_enter_aftr(void)
186{
187 int ret = -1;
188
189 /*
190 * If the other cpu is powered on, we have to power it off, because
191 * the AFTR state won't work otherwise
192 */
193 if (cpu_online(1)) {
194 /*
195 * We reach a sync point with the coupled idle state, we know
196 * the other cpu will power down itself or will abort the
197 * sequence, let's wait for one of these to happen
198 */
199 while (exynos_cpu_power_state(1)) {
200 /*
201 * The other cpu may skip idle and boot back
202 * up again
203 */
204 if (atomic_read(&cpu1_wakeup))
205 goto abort;
206
207 /*
208 * The other cpu may bounce through idle and
209 * boot back up again, getting stuck in the
210 * boot rom code
211 */
212 if (__raw_readl(cpu_boot_reg_base()) == 0)
213 goto abort;
214
215 cpu_relax();
216 }
217 }
218
219 exynos_enter_aftr();
220 ret = 0;
221
222abort:
223 if (cpu_online(1)) {
224 /*
225 * Set the boot vector to something non-zero
226 */
227 __raw_writel(virt_to_phys(exynos_cpu_resume),
228 cpu_boot_reg_base());
229 dsb();
230
231 /*
232 * Turn on cpu1 and wait for it to be on
233 */
234 exynos_cpu_power_up(1);
235 while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
236 cpu_relax();
237
238 while (!atomic_read(&cpu1_wakeup)) {
239 /*
240 * Poke cpu1 out of the boot rom
241 */
242 __raw_writel(virt_to_phys(exynos_cpu_resume),
243 cpu_boot_reg_base());
244
245 arch_send_wakeup_ipi_mask(cpumask_of(1));
246 }
247 }
248
249 return ret;
250}
251
252static int exynos_wfi_finisher(unsigned long flags)
253{
254 cpu_do_idle();
255
256 return -1;
257}
258
259static int exynos_cpu1_powerdown(void)
260{
261 int ret = -1;
262
263 /*
264 * Idle sequence for cpu1
265 */
266 if (cpu_pm_enter())
267 goto cpu1_aborted;
268
269 /*
270 * Turn off cpu 1
271 */
272 exynos_cpu_power_down(1);
273
274 ret = cpu_suspend(0, exynos_wfi_finisher);
275
276 cpu_pm_exit();
277
278cpu1_aborted:
279 dsb();
280 /*
281 * Notify cpu 0 that cpu 1 is awake
282 */
283 atomic_set(&cpu1_wakeup, 1);
284
285 return ret;
286}
287
288static void exynos_pre_enter_aftr(void)
289{
290 __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base());
291}
292
293static void exynos_post_enter_aftr(void)
294{
295 atomic_set(&cpu1_wakeup, 0);
296}
297
298struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
299 .cpu0_enter_aftr = exynos_cpu0_enter_aftr,
300 .cpu1_powerdown = exynos_cpu1_powerdown,
301 .pre_enter_aftr = exynos_pre_enter_aftr,
302 .post_enter_aftr = exynos_post_enter_aftr,
303};