aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/cpuidle-tegra20.c
diff options
context:
space:
mode:
authorJoseph Lo <josephl@nvidia.com>2013-01-16 12:33:55 -0500
committerStephen Warren <swarren@nvidia.com>2013-01-28 13:20:38 -0500
commit1d328606c66b9bb1c0552f585943d596f37ae3b9 (patch)
treeebb8a6e60de7b5b96100ffbcda57445116a906ef /arch/arm/mach-tegra/cpuidle-tegra20.c
parentafec581c4b53e03a97d9ef1b7a746a67967573cf (diff)
ARM: tegra20: cpuidle: apply coupled cpuidle for powered-down mode
The "powered-down" cpuidle mode of Tegra20 needs the CPU0 be the last one core to go into this mode before other core. The coupled cpuidle framework can help to sync the MPCore to coupled state then go into "powered-down" idle mode together. The driver can just assume the MPCore come into "powered-down" mode at the same time. No need to take care if the CPU_0 goes into this mode along and only can put it into safe idle mode (WFI). The powered-down state of Tegra20 requires power gating both CPU cores. When the secondary CPU requests to enter powered-down state, it saves its own contexts and then enters WFI for waiting CPU0 in the same state. When the CPU0 requests powered-down state, it attempts to put the secondary CPU into reset to prevent it from waking up. Then power down both CPUs together and power off the cpu rail. Be aware of that, you may see the legacy power state "LP2" in the code which is exactly the same meaning of "CPU power down". Based on the work by: Colin Cross <ccross@android.com> Gary King <gking@nvidia.com> Signed-off-by: Joseph Lo <josephl@nvidia.com> Acked-by: Colin Cross <ccross@android.com> Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/cpuidle-tegra20.c')
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra20.c125
1 files changed, 116 insertions, 9 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
index 50f984d28faf..825ced4f7a40 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra20.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra20.c
@@ -24,6 +24,7 @@
24#include <linux/cpuidle.h> 24#include <linux/cpuidle.h>
25#include <linux/cpu_pm.h> 25#include <linux/cpu_pm.h>
26#include <linux/clockchips.h> 26#include <linux/clockchips.h>
27#include <linux/clk/tegra.h>
27 28
28#include <asm/cpuidle.h> 29#include <asm/cpuidle.h>
29#include <asm/proc-fns.h> 30#include <asm/proc-fns.h>
@@ -32,22 +33,28 @@
32 33
33#include "pm.h" 34#include "pm.h"
34#include "sleep.h" 35#include "sleep.h"
36#include "iomap.h"
37#include "irq.h"
38#include "flowctrl.h"
35 39
36#ifdef CONFIG_PM_SLEEP 40#ifdef CONFIG_PM_SLEEP
37static int tegra20_idle_lp2(struct cpuidle_device *dev, 41static bool abort_flag;
38 struct cpuidle_driver *drv, 42static atomic_t abort_barrier;
39 int index); 43static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
44 struct cpuidle_driver *drv,
45 int index);
40#endif 46#endif
41 47
42static struct cpuidle_state tegra_idle_states[] = { 48static struct cpuidle_state tegra_idle_states[] = {
43 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), 49 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
44#ifdef CONFIG_PM_SLEEP 50#ifdef CONFIG_PM_SLEEP
45 [1] = { 51 [1] = {
46 .enter = tegra20_idle_lp2, 52 .enter = tegra20_idle_lp2_coupled,
47 .exit_latency = 5000, 53 .exit_latency = 5000,
48 .target_residency = 10000, 54 .target_residency = 10000,
49 .power_usage = 0, 55 .power_usage = 0,
50 .flags = CPUIDLE_FLAG_TIME_VALID, 56 .flags = CPUIDLE_FLAG_TIME_VALID |
57 CPUIDLE_FLAG_COUPLED,
51 .name = "powered-down", 58 .name = "powered-down",
52 .desc = "CPU power gated", 59 .desc = "CPU power gated",
53 }, 60 },
@@ -64,6 +71,88 @@ static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
64 71
65#ifdef CONFIG_PM_SLEEP 72#ifdef CONFIG_PM_SLEEP
66#ifdef CONFIG_SMP 73#ifdef CONFIG_SMP
74static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
75
76static int tegra20_reset_sleeping_cpu_1(void)
77{
78 int ret = 0;
79
80 tegra_pen_lock();
81
82 if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
83 tegra20_cpu_shutdown(1);
84 else
85 ret = -EINVAL;
86
87 tegra_pen_unlock();
88
89 return ret;
90}
91
92static void tegra20_wake_cpu1_from_reset(void)
93{
94 tegra_pen_lock();
95
96 tegra20_cpu_clear_resettable();
97
98 /* enable cpu clock on cpu */
99 tegra_enable_cpu_clock(1);
100
101 /* take the CPU out of reset */
102 tegra_cpu_out_of_reset(1);
103
104 /* unhalt the cpu */
105 flowctrl_write_cpu_halt(1, 0);
106
107 tegra_pen_unlock();
108}
109
110static int tegra20_reset_cpu_1(void)
111{
112 if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
113 return 0;
114
115 tegra20_wake_cpu1_from_reset();
116 return -EBUSY;
117}
118#else
119static inline void tegra20_wake_cpu1_from_reset(void)
120{
121}
122
123static inline int tegra20_reset_cpu_1(void)
124{
125 return 0;
126}
127#endif
128
129static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
130 struct cpuidle_driver *drv,
131 int index)
132{
133 struct cpuidle_state *state = &drv->states[index];
134 u32 cpu_on_time = state->exit_latency;
135 u32 cpu_off_time = state->target_residency - state->exit_latency;
136
137 while (tegra20_cpu_is_resettable_soon())
138 cpu_relax();
139
140 if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
141 return false;
142
143 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
144
145 tegra_idle_lp2_last(cpu_on_time, cpu_off_time);
146
147 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
148
149 if (cpu_online(1))
150 tegra20_wake_cpu1_from_reset();
151
152 return true;
153}
154
155#ifdef CONFIG_SMP
67static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev, 156static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
68 struct cpuidle_driver *drv, 157 struct cpuidle_driver *drv,
69 int index) 158 int index)
@@ -87,20 +176,31 @@ static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
87} 176}
88#endif 177#endif
89 178
90static int tegra20_idle_lp2(struct cpuidle_device *dev, 179static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
91 struct cpuidle_driver *drv, 180 struct cpuidle_driver *drv,
92 int index) 181 int index)
93{ 182{
94 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; 183 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
95 bool entered_lp2 = false; 184 bool entered_lp2 = false;
96 185
186 if (tegra_pending_sgi())
187 ACCESS_ONCE(abort_flag) = true;
188
189 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
190
191 if (abort_flag) {
192 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
193 abort_flag = false; /* clean flag for next coming */
194 return -EINTR;
195 }
196
97 local_fiq_disable(); 197 local_fiq_disable();
98 198
99 tegra_set_cpu_in_lp2(cpu); 199 tegra_set_cpu_in_lp2(cpu);
100 cpu_pm_enter(); 200 cpu_pm_enter();
101 201
102 if (cpu == 0) 202 if (cpu == 0)
103 cpu_do_idle(); 203 entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index);
104 else 204 else
105 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index); 205 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
106 206
@@ -122,6 +222,10 @@ int __init tegra20_cpuidle_init(void)
122 struct cpuidle_device *dev; 222 struct cpuidle_device *dev;
123 struct cpuidle_driver *drv = &tegra_idle_driver; 223 struct cpuidle_driver *drv = &tegra_idle_driver;
124 224
225#ifdef CONFIG_PM_SLEEP
226 tegra_tear_down_cpu = tegra20_tear_down_cpu;
227#endif
228
125 drv->state_count = ARRAY_SIZE(tegra_idle_states); 229 drv->state_count = ARRAY_SIZE(tegra_idle_states);
126 memcpy(drv->states, tegra_idle_states, 230 memcpy(drv->states, tegra_idle_states,
127 drv->state_count * sizeof(drv->states[0])); 231 drv->state_count * sizeof(drv->states[0]));
@@ -135,6 +239,9 @@ int __init tegra20_cpuidle_init(void)
135 for_each_possible_cpu(cpu) { 239 for_each_possible_cpu(cpu) {
136 dev = &per_cpu(tegra_idle_device, cpu); 240 dev = &per_cpu(tegra_idle_device, cpu);
137 dev->cpu = cpu; 241 dev->cpu = cpu;
242#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
243 dev->coupled_cpus = *cpu_possible_mask;
244#endif
138 245
139 dev->state_count = drv->state_count; 246 dev->state_count = drv->state_count;
140 ret = cpuidle_register_device(dev); 247 ret = cpuidle_register_device(dev);