| 
									
										
										
										
											2012-12-04 22:55:14 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (C) 2012 Freescale Semiconductor, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/cpuidle.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <asm/cpuidle.h>
 | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:15 +08:00
										 |  |  | #include <asm/proc-fns.h>
 | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:15 +08:00
										 |  |  | #include "common.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:14 +08:00
										 |  |  | #include "cpuidle.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:15 +08:00
										 |  |  | static atomic_t master = ATOMIC_INIT(0); | 
					
						
							|  |  |  | static DEFINE_SPINLOCK(master_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int imx6q_enter_wait(struct cpuidle_device *dev, | 
					
						
							|  |  |  | 			    struct cpuidle_driver *drv, int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (atomic_inc_return(&master) == num_online_cpus()) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * With this lock, we prevent other cpu to exit and enter | 
					
						
							|  |  |  | 		 * this function again and become the master. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (!spin_trylock(&master_lock)) | 
					
						
							|  |  |  | 			goto idle; | 
					
						
							|  |  |  | 		imx6q_set_lpm(WAIT_UNCLOCKED); | 
					
						
							|  |  |  | 		cpu_do_idle(); | 
					
						
							|  |  |  | 		imx6q_set_lpm(WAIT_CLOCKED); | 
					
						
							|  |  |  | 		spin_unlock(&master_lock); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | idle: | 
					
						
							|  |  |  | 	cpu_do_idle(); | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  | 	atomic_dec(&master); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return index; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:14 +08:00
										 |  |  | static struct cpuidle_driver imx6q_cpuidle_driver = { | 
					
						
							|  |  |  | 	.name = "imx6q_cpuidle", | 
					
						
							|  |  |  | 	.owner = THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:15 +08:00
										 |  |  | 	.states = { | 
					
						
							|  |  |  | 		/* WFI */ | 
					
						
							|  |  |  | 		ARM_CPUIDLE_WFI_STATE, | 
					
						
							|  |  |  | 		/* WAIT */ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			.exit_latency = 50, | 
					
						
							|  |  |  | 			.target_residency = 75, | 
					
						
							| 
									
										
										
										
											2013-03-21 12:21:33 +00:00
										 |  |  | 			.flags = CPUIDLE_FLAG_TIME_VALID | | 
					
						
							|  |  |  | 			         CPUIDLE_FLAG_TIMER_STOP, | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:15 +08:00
										 |  |  | 			.enter = imx6q_enter_wait, | 
					
						
							|  |  |  | 			.name = "WAIT", | 
					
						
							|  |  |  | 			.desc = "Clock off", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	.state_count = 2, | 
					
						
							|  |  |  | 	.safe_state_index = 0, | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:14 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __init imx6q_cpuidle_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:15 +08:00
										 |  |  | 	/* Need to enable SCU standby for entering WAIT modes */ | 
					
						
							|  |  |  | 	imx_scu_standby_enable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set chicken bit to get a reliable WAIT mode support */ | 
					
						
							|  |  |  | 	imx6q_set_chicken_bit(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-23 08:54:45 +00:00
										 |  |  | 	return cpuidle_register(&imx6q_cpuidle_driver, NULL); | 
					
						
							| 
									
										
										
										
											2012-12-04 22:55:14 +08:00
										 |  |  | } |