aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
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
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')
-rw-r--r--arch/arm/mach-tegra/Kconfig1
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra20.c125
-rw-r--r--arch/arm/mach-tegra/sleep-tegra20.S53
-rw-r--r--arch/arm/mach-tegra/sleep.S19
-rw-r--r--arch/arm/mach-tegra/sleep.h3
5 files changed, 192 insertions, 9 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 1ec7f80e2af5..abc688fd4807 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -4,6 +4,7 @@ comment "NVIDIA Tegra options"
4 4
5config ARCH_TEGRA_2x_SOC 5config ARCH_TEGRA_2x_SOC
6 bool "Enable support for Tegra20 family" 6 bool "Enable support for Tegra20 family"
7 select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
7 select ARCH_REQUIRE_GPIOLIB 8 select ARCH_REQUIRE_GPIOLIB
8 select ARM_ERRATA_720789 9 select ARM_ERRATA_720789
9 select ARM_ERRATA_742230 if SMP 10 select ARM_ERRATA_742230 if SMP
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);
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S
index 1074364e77ee..9f6bfafdd512 100644
--- a/arch/arm/mach-tegra/sleep-tegra20.S
+++ b/arch/arm/mach-tegra/sleep-tegra20.S
@@ -57,6 +57,9 @@ ENDPROC(tegra20_hotplug_shutdown)
57ENTRY(tegra20_cpu_shutdown) 57ENTRY(tegra20_cpu_shutdown)
58 cmp r0, #0 58 cmp r0, #0
59 moveq pc, lr @ must not be called for CPU 0 59 moveq pc, lr @ must not be called for CPU 0
60 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
61 mov r12, #CPU_RESETTABLE
62 str r12, [r1]
60 63
61 cpu_to_halt_reg r1, r0 64 cpu_to_halt_reg r1, r0
62 ldr r3, =TEGRA_FLOW_CTRL_VIRT 65 ldr r3, =TEGRA_FLOW_CTRL_VIRT
@@ -163,6 +166,21 @@ ENTRY(tegra20_cpu_set_resettable_soon)
163ENDPROC(tegra20_cpu_set_resettable_soon) 166ENDPROC(tegra20_cpu_set_resettable_soon)
164 167
165/* 168/*
169 * tegra20_cpu_is_resettable_soon(void)
170 *
171 * Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been
172 * set because it is expected that the secondary CPU will be idle soon.
173 */
174ENTRY(tegra20_cpu_is_resettable_soon)
175 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
176 ldr r12, [r1]
177 cmp r12, #CPU_RESETTABLE_SOON
178 moveq r0, #1
179 movne r0, #0
180 mov pc, lr
181ENDPROC(tegra20_cpu_is_resettable_soon)
182
183/*
166 * tegra20_sleep_cpu_secondary_finish(unsigned long v2p) 184 * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
167 * 185 *
168 * Enters WFI on secondary CPU by exiting coherency. 186 * Enters WFI on secondary CPU by exiting coherency.
@@ -221,4 +239,39 @@ ENTRY(tegra20_sleep_cpu_secondary_finish)
221 239
222 ldmfd sp!, {r4 - r11, pc} 240 ldmfd sp!, {r4 - r11, pc}
223ENDPROC(tegra20_sleep_cpu_secondary_finish) 241ENDPROC(tegra20_sleep_cpu_secondary_finish)
242
243/*
244 * tegra20_tear_down_cpu
245 *
246 * Switches the CPU cluster to PLL-P and enters sleep.
247 */
248ENTRY(tegra20_tear_down_cpu)
249 bl tegra_switch_cpu_to_pllp
250 b tegra20_enter_sleep
251ENDPROC(tegra20_tear_down_cpu)
252
253/*
254 * tegra20_enter_sleep
255 *
256 * uses flow controller to enter sleep state
257 * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
258 * executes from SDRAM with target state is LP2
259 */
260tegra20_enter_sleep:
261 mov32 r6, TEGRA_FLOW_CTRL_BASE
262
263 mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
264 orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
265 cpu_id r1
266 cpu_to_halt_reg r1, r1
267 str r0, [r6, r1]
268 dsb
269 ldr r0, [r6, r1] /* memory barrier */
270
271halted:
272 dsb
273 wfe /* CPU should be power gated here */
274 isb
275 b halted
276
224#endif 277#endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index addae357da3f..364d84523fba 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -34,6 +34,9 @@
34#include "flowctrl.h" 34#include "flowctrl.h"
35#include "sleep.h" 35#include "sleep.h"
36 36
37#define CLK_RESET_CCLK_BURST 0x20
38#define CLK_RESET_CCLK_DIVIDER 0x24
39
37#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) 40#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
38/* 41/*
39 * tegra_disable_clean_inv_dcache 42 * tegra_disable_clean_inv_dcache
@@ -110,4 +113,20 @@ ENTRY(tegra_shut_off_mmu)
110 mov pc, r0 113 mov pc, r0
111ENDPROC(tegra_shut_off_mmu) 114ENDPROC(tegra_shut_off_mmu)
112 .popsection 115 .popsection
116
117/*
118 * tegra_switch_cpu_to_pllp
119 *
120 * In LP2 the normal cpu clock pllx will be turned off. Switch the CPU to pllp
121 */
122ENTRY(tegra_switch_cpu_to_pllp)
123 /* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */
124 mov32 r5, TEGRA_CLK_RESET_BASE
125 mov r0, #(2 << 28) @ burst policy = run mode
126 orr r0, r0, #(4 << 4) @ use PLLP in run mode burst
127 str r0, [r5, #CLK_RESET_CCLK_BURST]
128 mov r0, #0
129 str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
130 mov pc, lr
131ENDPROC(tegra_switch_cpu_to_pllp)
113#endif 132#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e39a56beb3a0..4ffae541726e 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -131,6 +131,8 @@ static inline void tegra20_hotplug_init(void) {}
131static inline void tegra30_hotplug_init(void) {} 131static inline void tegra30_hotplug_init(void) {}
132#endif 132#endif
133 133
134void tegra20_cpu_shutdown(int cpu);
135int tegra20_cpu_is_resettable_soon(void);
134void tegra20_cpu_clear_resettable(void); 136void tegra20_cpu_clear_resettable(void);
135#ifdef CONFIG_ARCH_TEGRA_2x_SOC 137#ifdef CONFIG_ARCH_TEGRA_2x_SOC
136void tegra20_cpu_set_resettable_soon(void); 138void tegra20_cpu_set_resettable_soon(void);
@@ -139,6 +141,7 @@ static inline void tegra20_cpu_set_resettable_soon(void) {}
139#endif 141#endif
140 142
141int tegra20_sleep_cpu_secondary_finish(unsigned long); 143int tegra20_sleep_cpu_secondary_finish(unsigned long);
144void tegra20_tear_down_cpu(void);
142int tegra30_sleep_cpu_secondary_finish(unsigned long); 145int tegra30_sleep_cpu_secondary_finish(unsigned long);
143void tegra30_tear_down_cpu(void); 146void tegra30_tear_down_cpu(void);
144 147