| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  |  * OMAP4+ CPU idle Routines | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  |  * Copyright (C) 2011-2013 Texas Instruments, Inc. | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  |  * Santosh Shilimkar <santosh.shilimkar@ti.com> | 
					
						
							|  |  |  |  * Rajendra Nayak <rnayak@ti.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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/sched.h>
 | 
					
						
							|  |  |  | #include <linux/cpuidle.h>
 | 
					
						
							|  |  |  | #include <linux/cpu_pm.h>
 | 
					
						
							|  |  |  | #include <linux/export.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-23 08:54:39 +00:00
										 |  |  | #include <asm/cpuidle.h>
 | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | #include <asm/proc-fns.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "common.h"
 | 
					
						
							|  |  |  | #include "pm.h"
 | 
					
						
							|  |  |  | #include "prm.h"
 | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | #include "clockdomain.h"
 | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:27 +02:00
										 |  |  | /* Machine specific information */ | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | struct idle_statedata { | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 	u32 cpu_state; | 
					
						
							|  |  |  | 	u32 mpu_logic_state; | 
					
						
							|  |  |  | 	u32 mpu_state; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | static struct idle_statedata omap4_idle_data[] = { | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:26 +02:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		.cpu_state = PWRDM_POWER_ON, | 
					
						
							|  |  |  | 		.mpu_state = PWRDM_POWER_ON, | 
					
						
							|  |  |  | 		.mpu_logic_state = PWRDM_POWER_RET, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.cpu_state = PWRDM_POWER_OFF, | 
					
						
							|  |  |  | 		.mpu_state = PWRDM_POWER_RET, | 
					
						
							|  |  |  | 		.mpu_logic_state = PWRDM_POWER_RET, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.cpu_state = PWRDM_POWER_OFF, | 
					
						
							|  |  |  | 		.mpu_state = PWRDM_POWER_RET, | 
					
						
							|  |  |  | 		.mpu_logic_state = PWRDM_POWER_OFF, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | static struct powerdomain *mpu_pd, *cpu_pd[NR_CPUS]; | 
					
						
							|  |  |  | static struct clockdomain *cpu_clkdm[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-14 17:26:17 -07:00
										 |  |  | static atomic_t abort_barrier; | 
					
						
							|  |  |  | static bool cpu_done[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | static struct idle_statedata *state_ptr = &omap4_idle_data[0]; | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-15 01:39:19 -07:00
										 |  |  | /* Private functions */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  |  * omap_enter_idle_[simple/coupled] - OMAP4PLUS cpuidle entry functions | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  |  * @dev: cpuidle device | 
					
						
							|  |  |  |  * @drv: cpuidle driver | 
					
						
							|  |  |  |  * @index: the index of state to be entered | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Called from the CPUidle framework to program the device to the | 
					
						
							|  |  |  |  * specified low power state selected by the governor. | 
					
						
							|  |  |  |  * Returns the amount of time spent in the low power state. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | static int omap_enter_idle_simple(struct cpuidle_device *dev, | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 			struct cpuidle_driver *drv, | 
					
						
							|  |  |  | 			int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	omap_do_wfi(); | 
					
						
							|  |  |  | 	return index; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | static int omap_enter_idle_coupled(struct cpuidle_device *dev, | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 			struct cpuidle_driver *drv, | 
					
						
							|  |  |  | 			int index) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | 	struct idle_statedata *cx = state_ptr + index; | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	 * CPU0 has to wait and stay ON until CPU1 is OFF state. | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 	 * This is necessary to honour hardware recommondation | 
					
						
							|  |  |  | 	 * of triggeing all the possible low power modes once CPU1 is | 
					
						
							|  |  |  | 	 * out of coherency and in OFF mode. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) { | 
					
						
							| 
									
										
										
										
											2012-03-14 17:26:17 -07:00
										 |  |  | 		while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF) { | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 			cpu_relax(); | 
					
						
							| 
									
										
										
										
											2012-03-14 17:26:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * CPU1 could have already entered & exited idle | 
					
						
							|  |  |  | 			 * without hitting off because of a wakeup | 
					
						
							|  |  |  | 			 * or a failed attempt to hit off mode.  Check for | 
					
						
							|  |  |  | 			 * that here, otherwise we could spin forever | 
					
						
							|  |  |  | 			 * waiting for CPU1 off. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (cpu_done[1]) | 
					
						
							|  |  |  | 			    goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Call idle CPU PM enter notifier chain so that | 
					
						
							|  |  |  | 	 * VFP and per CPU interrupt context is saved. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	cpu_pm_enter(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->cpu == 0) { | 
					
						
							|  |  |  | 		pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state); | 
					
						
							|  |  |  | 		omap_set_pwrdm_state(mpu_pd, cx->mpu_state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Call idle CPU cluster PM enter notifier chain | 
					
						
							|  |  |  | 		 * to save GIC and wakeupgen context. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if ((cx->mpu_state == PWRDM_POWER_RET) && | 
					
						
							|  |  |  | 			(cx->mpu_logic_state == PWRDM_POWER_OFF)) | 
					
						
							|  |  |  | 				cpu_cluster_pm_enter(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	omap4_enter_lowpower(dev->cpu, cx->cpu_state); | 
					
						
							| 
									
										
										
										
											2012-03-14 17:26:17 -07:00
										 |  |  | 	cpu_done[dev->cpu] = true; | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	/* Wakeup CPU1 only if it is not offlined */ | 
					
						
							|  |  |  | 	if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) { | 
					
						
							|  |  |  | 		clkdm_wakeup(cpu_clkdm[1]); | 
					
						
							| 
									
										
										
										
											2013-02-08 22:50:58 +05:30
										 |  |  | 		omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON); | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 		clkdm_allow_idle(cpu_clkdm[1]); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Call idle CPU PM exit notifier chain to restore | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	 * VFP and per CPU IRQ context. | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	cpu_pm_exit(); | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Call idle CPU cluster PM exit notifier chain | 
					
						
							|  |  |  | 	 * to restore GIC and wakeupgen context. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-03-25 15:35:08 +05:30
										 |  |  | 	if ((cx->mpu_state == PWRDM_POWER_RET) && | 
					
						
							|  |  |  | 		(cx->mpu_logic_state == PWRDM_POWER_OFF)) | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 		cpu_cluster_pm_exit(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-14 17:26:17 -07:00
										 |  |  | fail: | 
					
						
							|  |  |  | 	cpuidle_coupled_parallel_barrier(dev, &abort_barrier); | 
					
						
							|  |  |  | 	cpu_done[dev->cpu] = false; | 
					
						
							| 
									
										
										
										
											2011-01-16 00:42:31 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 	return index; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-15 01:39:19 -07:00
										 |  |  | static struct cpuidle_driver omap4_idle_driver = { | 
					
						
							| 
									
										
										
										
											2012-03-20 15:22:47 -05:00
										 |  |  | 	.name				= "omap4_idle", | 
					
						
							|  |  |  | 	.owner				= THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 	.states = { | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			/* C1 - CPU0 ON + CPU1 ON + MPU ON */ | 
					
						
							|  |  |  | 			.exit_latency = 2 + 2, | 
					
						
							|  |  |  | 			.target_residency = 5, | 
					
						
							|  |  |  | 			.flags = CPUIDLE_FLAG_TIME_VALID, | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | 			.enter = omap_enter_idle_simple, | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 			.name = "C1", | 
					
						
							| 
									
										
										
										
											2013-03-25 15:35:06 +05:30
										 |  |  | 			.desc = "CPUx ON, MPUSS ON" | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2012-12-15 01:39:19 -07:00
										 |  |  | 			/* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */ | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 			.exit_latency = 328 + 440, | 
					
						
							|  |  |  | 			.target_residency = 960, | 
					
						
							| 
									
										
										
										
											2013-03-21 12:21:32 +00:00
										 |  |  | 			.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED | | 
					
						
							|  |  |  | 			         CPUIDLE_FLAG_TIMER_STOP, | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | 			.enter = omap_enter_idle_coupled, | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 			.name = "C2", | 
					
						
							| 
									
										
										
										
											2013-03-25 15:35:06 +05:30
										 |  |  | 			.desc = "CPUx OFF, MPUSS CSWR", | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			/* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */ | 
					
						
							|  |  |  | 			.exit_latency = 460 + 518, | 
					
						
							|  |  |  | 			.target_residency = 1100, | 
					
						
							| 
									
										
										
										
											2013-03-21 12:21:32 +00:00
										 |  |  | 			.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED | | 
					
						
							|  |  |  | 			         CPUIDLE_FLAG_TIMER_STOP, | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  | 			.enter = omap_enter_idle_coupled, | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 			.name = "C3", | 
					
						
							| 
									
										
										
										
											2013-03-25 15:35:06 +05:30
										 |  |  | 			.desc = "CPUx OFF, MPUSS OSWR", | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:26 +02:00
										 |  |  | 	.state_count = ARRAY_SIZE(omap4_idle_data), | 
					
						
							| 
									
										
										
										
											2012-04-24 16:05:23 +02:00
										 |  |  | 	.safe_state_index = 0, | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-15 01:39:19 -07:00
										 |  |  | /* Public functions */ | 
					
						
							| 
									
										
										
										
											2012-04-17 15:09:20 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  |  * omap4_idle_init - Init routine for OMAP4+ idle | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-04-05 18:29:03 +05:30
										 |  |  |  * Registers the OMAP4+ specific cpuidle driver to the cpuidle | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  |  * framework with the valid set of states. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int __init omap4_idle_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	mpu_pd = pwrdm_lookup("mpu_pwrdm"); | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm"); | 
					
						
							|  |  |  | 	cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm"); | 
					
						
							|  |  |  | 	if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1])) | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-25 21:00:40 +05:30
										 |  |  | 	cpu_clkdm[0] = clkdm_lookup("mpu0_clkdm"); | 
					
						
							|  |  |  | 	cpu_clkdm[1] = clkdm_lookup("mpu1_clkdm"); | 
					
						
							|  |  |  | 	if (!cpu_clkdm[0] || !cpu_clkdm[1]) | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-23 08:54:39 +00:00
										 |  |  | 	return cpuidle_register(&omap4_idle_driver, cpu_online_mask); | 
					
						
							| 
									
										
										
										
											2011-08-16 17:31:40 +05:30
										 |  |  | } |