112bb3440SShawn Guo /*
212bb3440SShawn Guo  * Copyright (C) 2012 Freescale Semiconductor, Inc.
312bb3440SShawn Guo  *
412bb3440SShawn Guo  * This program is free software; you can redistribute it and/or modify
512bb3440SShawn Guo  * it under the terms of the GNU General Public License version 2 as
612bb3440SShawn Guo  * published by the Free Software Foundation.
712bb3440SShawn Guo  */
812bb3440SShawn Guo 
912bb3440SShawn Guo #include <linux/cpuidle.h>
1012bb3440SShawn Guo #include <linux/module.h>
1112bb3440SShawn Guo #include <asm/cpuidle.h>
12e5f9dec8SShawn Guo #include <asm/proc-fns.h>
1312bb3440SShawn Guo 
14e5f9dec8SShawn Guo #include "common.h"
1512bb3440SShawn Guo #include "cpuidle.h"
16a25d67a4SAnson Huang #include "hardware.h"
1712bb3440SShawn Guo 
18e5f9dec8SShawn Guo static atomic_t master = ATOMIC_INIT(0);
19e5f9dec8SShawn Guo static DEFINE_SPINLOCK(master_lock);
20e5f9dec8SShawn Guo 
21e5f9dec8SShawn Guo static int imx6q_enter_wait(struct cpuidle_device *dev,
22e5f9dec8SShawn Guo 			    struct cpuidle_driver *drv, int index)
23e5f9dec8SShawn Guo {
24e5f9dec8SShawn Guo 	if (atomic_inc_return(&master) == num_online_cpus()) {
25e5f9dec8SShawn Guo 		/*
26e5f9dec8SShawn Guo 		 * With this lock, we prevent other cpu to exit and enter
27e5f9dec8SShawn Guo 		 * this function again and become the master.
28e5f9dec8SShawn Guo 		 */
29e5f9dec8SShawn Guo 		if (!spin_trylock(&master_lock))
30e5f9dec8SShawn Guo 			goto idle;
31e5f9dec8SShawn Guo 		imx6q_set_lpm(WAIT_UNCLOCKED);
32e5f9dec8SShawn Guo 		cpu_do_idle();
33e5f9dec8SShawn Guo 		imx6q_set_lpm(WAIT_CLOCKED);
34e5f9dec8SShawn Guo 		spin_unlock(&master_lock);
35e5f9dec8SShawn Guo 		goto done;
36e5f9dec8SShawn Guo 	}
37e5f9dec8SShawn Guo 
38e5f9dec8SShawn Guo idle:
39e5f9dec8SShawn Guo 	cpu_do_idle();
40e5f9dec8SShawn Guo done:
41e5f9dec8SShawn Guo 	atomic_dec(&master);
42e5f9dec8SShawn Guo 
43e5f9dec8SShawn Guo 	return index;
44e5f9dec8SShawn Guo }
45e5f9dec8SShawn Guo 
4612bb3440SShawn Guo static struct cpuidle_driver imx6q_cpuidle_driver = {
4712bb3440SShawn Guo 	.name = "imx6q_cpuidle",
4812bb3440SShawn Guo 	.owner = THIS_MODULE,
49e5f9dec8SShawn Guo 	.states = {
50e5f9dec8SShawn Guo 		/* WFI */
51e5f9dec8SShawn Guo 		ARM_CPUIDLE_WFI_STATE,
52e5f9dec8SShawn Guo 		/* WAIT */
53e5f9dec8SShawn Guo 		{
54e5f9dec8SShawn Guo 			.exit_latency = 50,
55e5f9dec8SShawn Guo 			.target_residency = 75,
568de46effSDaniel Lezcano 			.flags = CPUIDLE_FLAG_TIME_VALID |
578de46effSDaniel Lezcano 			         CPUIDLE_FLAG_TIMER_STOP,
58e5f9dec8SShawn Guo 			.enter = imx6q_enter_wait,
59e5f9dec8SShawn Guo 			.name = "WAIT",
60e5f9dec8SShawn Guo 			.desc = "Clock off",
61e5f9dec8SShawn Guo 		},
62e5f9dec8SShawn Guo 	},
63e5f9dec8SShawn Guo 	.state_count = 2,
64e5f9dec8SShawn Guo 	.safe_state_index = 0,
6512bb3440SShawn Guo };
6612bb3440SShawn Guo 
6712bb3440SShawn Guo int __init imx6q_cpuidle_init(void)
6812bb3440SShawn Guo {
69e5f9dec8SShawn Guo 	/* Need to enable SCU standby for entering WAIT modes */
70a25d67a4SAnson Huang 	if (!cpu_is_imx6sx())
71e5f9dec8SShawn Guo 		imx_scu_standby_enable();
72e5f9dec8SShawn Guo 
73fa6be65eSFabio Estevam 	/* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
74dfea953aSAnson Huang 	imx6q_set_int_mem_clk_lpm(true);
75e5f9dec8SShawn Guo 
7654a4644bSDaniel Lezcano 	return cpuidle_register(&imx6q_cpuidle_driver, NULL);
7712bb3440SShawn Guo }
78