1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
212bb3440SShawn Guo /*
312bb3440SShawn Guo  * Copyright (C) 2012 Freescale Semiconductor, Inc.
412bb3440SShawn Guo  */
512bb3440SShawn Guo 
6e67198ccSFrederic Weisbecker #include <linux/context_tracking.h>
712bb3440SShawn Guo #include <linux/cpuidle.h>
812bb3440SShawn Guo #include <linux/module.h>
912bb3440SShawn Guo #include <asm/cpuidle.h>
1012bb3440SShawn Guo 
1147096103SBen Dooks #include <soc/imx/cpuidle.h>
1247096103SBen Dooks 
13e5f9dec8SShawn Guo #include "common.h"
1412bb3440SShawn Guo #include "cpuidle.h"
15a25d67a4SAnson Huang #include "hardware.h"
1612bb3440SShawn Guo 
1791740fc8SKohji Okuno static int num_idle_cpus = 0;
1899ae52edSSebastian Andrzej Siewior static DEFINE_RAW_SPINLOCK(cpuidle_lock);
19e5f9dec8SShawn Guo 
imx6q_enter_wait(struct cpuidle_device * dev,struct cpuidle_driver * drv,int index)20*69e26b4fSPeter Zijlstra static __cpuidle int imx6q_enter_wait(struct cpuidle_device *dev,
21e5f9dec8SShawn Guo 				      struct cpuidle_driver *drv, int index)
22e5f9dec8SShawn Guo {
2399ae52edSSebastian Andrzej Siewior 	raw_spin_lock(&cpuidle_lock);
2491740fc8SKohji Okuno 	if (++num_idle_cpus == num_online_cpus())
258fb76a07SShawn Guo 		imx6_set_lpm(WAIT_UNCLOCKED);
2699ae52edSSebastian Andrzej Siewior 	raw_spin_unlock(&cpuidle_lock);
27e5f9dec8SShawn Guo 
28a01353cfSPeter Zijlstra 	ct_cpuidle_enter();
29e5f9dec8SShawn Guo 	cpu_do_idle();
30a01353cfSPeter Zijlstra 	ct_cpuidle_exit();
3191740fc8SKohji Okuno 
3299ae52edSSebastian Andrzej Siewior 	raw_spin_lock(&cpuidle_lock);
3391740fc8SKohji Okuno 	if (num_idle_cpus-- == num_online_cpus())
3491740fc8SKohji Okuno 		imx6_set_lpm(WAIT_CLOCKED);
3599ae52edSSebastian Andrzej Siewior 	raw_spin_unlock(&cpuidle_lock);
36e5f9dec8SShawn Guo 
37e5f9dec8SShawn Guo 	return index;
38e5f9dec8SShawn Guo }
39e5f9dec8SShawn Guo 
4012bb3440SShawn Guo static struct cpuidle_driver imx6q_cpuidle_driver = {
4112bb3440SShawn Guo 	.name = "imx6q_cpuidle",
4212bb3440SShawn Guo 	.owner = THIS_MODULE,
43e5f9dec8SShawn Guo 	.states = {
44e5f9dec8SShawn Guo 		/* WFI */
45e5f9dec8SShawn Guo 		ARM_CPUIDLE_WFI_STATE,
46e5f9dec8SShawn Guo 		/* WAIT */
47e5f9dec8SShawn Guo 		{
48e5f9dec8SShawn Guo 			.exit_latency = 50,
49e5f9dec8SShawn Guo 			.target_residency = 75,
501a67b926SUlf Hansson 			.flags = CPUIDLE_FLAG_TIMER_STOP | CPUIDLE_FLAG_RCU_IDLE,
51e5f9dec8SShawn Guo 			.enter = imx6q_enter_wait,
52e5f9dec8SShawn Guo 			.name = "WAIT",
53e5f9dec8SShawn Guo 			.desc = "Clock off",
54e5f9dec8SShawn Guo 		},
55e5f9dec8SShawn Guo 	},
56e5f9dec8SShawn Guo 	.state_count = 2,
57e5f9dec8SShawn Guo 	.safe_state_index = 0,
5812bb3440SShawn Guo };
5912bb3440SShawn Guo 
6029380905SLucas Stach /*
6129380905SLucas Stach  * i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from waking the
6229380905SLucas Stach  * CPUs when they are in wait(unclocked) state. As the hardware workaround isn't
6329380905SLucas Stach  * applicable to all boards, disable the deeper idle state when the workaround
6429380905SLucas Stach  * isn't present and the FEC is in use.
6529380905SLucas Stach  */
imx6q_cpuidle_fec_irqs_used(void)6629380905SLucas Stach void imx6q_cpuidle_fec_irqs_used(void)
6729380905SLucas Stach {
68cbda56d5SRafael J. Wysocki 	cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, true);
6929380905SLucas Stach }
702cb9caa4SShawn Guo EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_used);
7129380905SLucas Stach 
imx6q_cpuidle_fec_irqs_unused(void)7229380905SLucas Stach void imx6q_cpuidle_fec_irqs_unused(void)
7329380905SLucas Stach {
74cbda56d5SRafael J. Wysocki 	cpuidle_driver_state_disabled(&imx6q_cpuidle_driver, 1, false);
7529380905SLucas Stach }
762cb9caa4SShawn Guo EXPORT_SYMBOL_GPL(imx6q_cpuidle_fec_irqs_unused);
7729380905SLucas Stach 
imx6q_cpuidle_init(void)7812bb3440SShawn Guo int __init imx6q_cpuidle_init(void)
7912bb3440SShawn Guo {
80fa6be65eSFabio Estevam 	/* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
818765caa5SAnson Huang 	imx6_set_int_mem_clk_lpm(true);
82e5f9dec8SShawn Guo 
8354a4644bSDaniel Lezcano 	return cpuidle_register(&imx6q_cpuidle_driver, NULL);
8412bb3440SShawn Guo }
85