aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoseph Lo <josephl@nvidia.com>2013-01-15 17:10:38 -0500
committerStephen Warren <swarren@nvidia.com>2013-01-28 13:20:38 -0500
commit5c1350bdfcebf47b3b6f83d62e5860259858a54a (patch)
treea44b2ec6cf784e7979a0be813205a1e43d6ec749
parentd4b92fb2535a5b35cab9713d6793f1674cc45ba7 (diff)
ARM: tegra20: cpuidle: add powered-down state for secondary CPU
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. The Tegra20 had a limition to power down both CPU cores. The secondary CPU must waits for CPU0 in powered-down state too. If the secondary CPU be woken up before CPU0 entering powered-down state, then it needs to restore its CPU states and waits for next chance. 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> Signed-off-by: Stephen Warren <swarren@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/cpuidle-tegra20.c90
-rw-r--r--arch/arm/mach-tegra/pm.c3
-rw-r--r--arch/arm/mach-tegra/sleep-tegra20.S147
-rw-r--r--arch/arm/mach-tegra/sleep.h23
4 files changed, 259 insertions, 4 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c
index d32e8b0dbd4f..50f984d28faf 100644
--- a/arch/arm/mach-tegra/cpuidle-tegra20.c
+++ b/arch/arm/mach-tegra/cpuidle-tegra20.c
@@ -22,21 +22,99 @@
22#include <linux/kernel.h> 22#include <linux/kernel.h>
23#include <linux/module.h> 23#include <linux/module.h>
24#include <linux/cpuidle.h> 24#include <linux/cpuidle.h>
25#include <linux/cpu_pm.h>
26#include <linux/clockchips.h>
25 27
26#include <asm/cpuidle.h> 28#include <asm/cpuidle.h>
29#include <asm/proc-fns.h>
30#include <asm/suspend.h>
31#include <asm/smp_plat.h>
32
33#include "pm.h"
34#include "sleep.h"
35
36#ifdef CONFIG_PM_SLEEP
37static int tegra20_idle_lp2(struct cpuidle_device *dev,
38 struct cpuidle_driver *drv,
39 int index);
40#endif
41
42static struct cpuidle_state tegra_idle_states[] = {
43 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
44#ifdef CONFIG_PM_SLEEP
45 [1] = {
46 .enter = tegra20_idle_lp2,
47 .exit_latency = 5000,
48 .target_residency = 10000,
49 .power_usage = 0,
50 .flags = CPUIDLE_FLAG_TIME_VALID,
51 .name = "powered-down",
52 .desc = "CPU power gated",
53 },
54#endif
55};
27 56
28static struct cpuidle_driver tegra_idle_driver = { 57static struct cpuidle_driver tegra_idle_driver = {
29 .name = "tegra_idle", 58 .name = "tegra_idle",
30 .owner = THIS_MODULE, 59 .owner = THIS_MODULE,
31 .en_core_tk_irqen = 1, 60 .en_core_tk_irqen = 1,
32 .state_count = 1,
33 .states = {
34 [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
35 },
36}; 61};
37 62
38static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); 63static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
39 64
65#ifdef CONFIG_PM_SLEEP
66#ifdef CONFIG_SMP
67static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
68 struct cpuidle_driver *drv,
69 int index)
70{
71 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
72
73 cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
74
75 tegra20_cpu_clear_resettable();
76
77 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
78
79 return true;
80}
81#else
82static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
83 struct cpuidle_driver *drv,
84 int index)
85{
86 return true;
87}
88#endif
89
90static int tegra20_idle_lp2(struct cpuidle_device *dev,
91 struct cpuidle_driver *drv,
92 int index)
93{
94 u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
95 bool entered_lp2 = false;
96
97 local_fiq_disable();
98
99 tegra_set_cpu_in_lp2(cpu);
100 cpu_pm_enter();
101
102 if (cpu == 0)
103 cpu_do_idle();
104 else
105 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
106
107 cpu_pm_exit();
108 tegra_clear_cpu_in_lp2(cpu);
109
110 local_fiq_enable();
111
112 smp_rmb();
113
114 return entered_lp2 ? index : 0;
115}
116#endif
117
40int __init tegra20_cpuidle_init(void) 118int __init tegra20_cpuidle_init(void)
41{ 119{
42 int ret; 120 int ret;
@@ -44,6 +122,10 @@ int __init tegra20_cpuidle_init(void)
44 struct cpuidle_device *dev; 122 struct cpuidle_device *dev;
45 struct cpuidle_driver *drv = &tegra_idle_driver; 123 struct cpuidle_driver *drv = &tegra_idle_driver;
46 124
125 drv->state_count = ARRAY_SIZE(tegra_idle_states);
126 memcpy(drv->states, tegra_idle_states,
127 drv->state_count * sizeof(drv->states[0]));
128
47 ret = cpuidle_register_driver(&tegra_idle_driver); 129 ret = cpuidle_register_driver(&tegra_idle_driver);
48 if (ret) { 130 if (ret) {
49 pr_err("CPUidle driver registration failed\n"); 131 pr_err("CPUidle driver registration failed\n");
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index abfe9b93cc0c..523604de666f 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -36,6 +36,7 @@
36#include "iomap.h" 36#include "iomap.h"
37#include "reset.h" 37#include "reset.h"
38#include "flowctrl.h" 38#include "flowctrl.h"
39#include "fuse.h"
39#include "sleep.h" 40#include "sleep.h"
40 41
41#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ 42#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
@@ -173,6 +174,8 @@ bool tegra_set_cpu_in_lp2(int phy_cpu_id)
173 174
174 if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) 175 if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
175 last_cpu = true; 176 last_cpu = true;
177 else if (tegra_chip_id == TEGRA20 && phy_cpu_id == 1)
178 tegra20_cpu_set_resettable_soon();
176 179
177 spin_unlock(&tegra_lp2_lock); 180 spin_unlock(&tegra_lp2_lock);
178 return last_cpu; 181 return last_cpu;
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S
index ad2ca07d0578..1074364e77ee 100644
--- a/arch/arm/mach-tegra/sleep-tegra20.S
+++ b/arch/arm/mach-tegra/sleep-tegra20.S
@@ -21,6 +21,8 @@
21#include <linux/linkage.h> 21#include <linux/linkage.h>
22 22
23#include <asm/assembler.h> 23#include <asm/assembler.h>
24#include <asm/proc-fns.h>
25#include <asm/cp15.h>
24 26
25#include "sleep.h" 27#include "sleep.h"
26#include "flowctrl.h" 28#include "flowctrl.h"
@@ -75,3 +77,148 @@ ENTRY(tegra20_cpu_shutdown)
75 mov pc, lr 77 mov pc, lr
76ENDPROC(tegra20_cpu_shutdown) 78ENDPROC(tegra20_cpu_shutdown)
77#endif 79#endif
80
81#ifdef CONFIG_PM_SLEEP
82/*
83 * tegra_pen_lock
84 *
85 * spinlock implementation with no atomic test-and-set and no coherence
86 * using Peterson's algorithm on strongly-ordered registers
87 * used to synchronize a cpu waking up from wfi with entering lp2 on idle
88 *
89 * The reference link of Peterson's algorithm:
90 * http://en.wikipedia.org/wiki/Peterson's_algorithm
91 *
92 * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
93 * on cpu 0:
94 * r2 = flag[0] (in SCRATCH38)
95 * r3 = flag[1] (in SCRATCH39)
96 * on cpu1:
97 * r2 = flag[1] (in SCRATCH39)
98 * r3 = flag[0] (in SCRATCH38)
99 *
100 * must be called with MMU on
101 * corrupts r0-r3, r12
102 */
103ENTRY(tegra_pen_lock)
104 mov32 r3, TEGRA_PMC_VIRT
105 cpu_id r0
106 add r1, r3, #PMC_SCRATCH37
107 cmp r0, #0
108 addeq r2, r3, #PMC_SCRATCH38
109 addeq r3, r3, #PMC_SCRATCH39
110 addne r2, r3, #PMC_SCRATCH39
111 addne r3, r3, #PMC_SCRATCH38
112
113 mov r12, #1
114 str r12, [r2] @ flag[cpu] = 1
115 dsb
116 str r12, [r1] @ !turn = cpu
1171: dsb
118 ldr r12, [r3]
119 cmp r12, #1 @ flag[!cpu] == 1?
120 ldreq r12, [r1]
121 cmpeq r12, r0 @ !turn == cpu?
122 beq 1b @ while !turn == cpu && flag[!cpu] == 1
123
124 mov pc, lr @ locked
125ENDPROC(tegra_pen_lock)
126
127ENTRY(tegra_pen_unlock)
128 dsb
129 mov32 r3, TEGRA_PMC_VIRT
130 cpu_id r0
131 cmp r0, #0
132 addeq r2, r3, #PMC_SCRATCH38
133 addne r2, r3, #PMC_SCRATCH39
134 mov r12, #0
135 str r12, [r2]
136 mov pc, lr
137ENDPROC(tegra_pen_unlock)
138
139/*
140 * tegra20_cpu_clear_resettable(void)
141 *
142 * Called to clear the "resettable soon" flag in PMC_SCRATCH41 when
143 * it is expected that the secondary CPU will be idle soon.
144 */
145ENTRY(tegra20_cpu_clear_resettable)
146 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
147 mov r12, #CPU_NOT_RESETTABLE
148 str r12, [r1]
149 mov pc, lr
150ENDPROC(tegra20_cpu_clear_resettable)
151
152/*
153 * tegra20_cpu_set_resettable_soon(void)
154 *
155 * Called to set the "resettable soon" flag in PMC_SCRATCH41 when
156 * it is expected that the secondary CPU will be idle soon.
157 */
158ENTRY(tegra20_cpu_set_resettable_soon)
159 mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
160 mov r12, #CPU_RESETTABLE_SOON
161 str r12, [r1]
162 mov pc, lr
163ENDPROC(tegra20_cpu_set_resettable_soon)
164
165/*
166 * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
167 *
168 * Enters WFI on secondary CPU by exiting coherency.
169 */
170ENTRY(tegra20_sleep_cpu_secondary_finish)
171 stmfd sp!, {r4-r11, lr}
172
173 mrc p15, 0, r11, c1, c0, 1 @ save actlr before exiting coherency
174
175 /* Flush and disable the L1 data cache */
176 bl tegra_disable_clean_inv_dcache
177
178 mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41
179 mov r3, #CPU_RESETTABLE
180 str r3, [r0]
181
182 bl cpu_do_idle
183
184 /*
185 * cpu may be reset while in wfi, which will return through
186 * tegra_resume to cpu_resume
187 * or interrupt may wake wfi, which will return here
188 * cpu state is unchanged - MMU is on, cache is on, coherency
189 * is off, and the data cache is off
190 *
191 * r11 contains the original actlr
192 */
193
194 bl tegra_pen_lock
195
196 mov32 r3, TEGRA_PMC_VIRT
197 add r0, r3, #PMC_SCRATCH41
198 mov r3, #CPU_NOT_RESETTABLE
199 str r3, [r0]
200
201 bl tegra_pen_unlock
202
203 /* Re-enable the data cache */
204 mrc p15, 0, r10, c1, c0, 0
205 orr r10, r10, #CR_C
206 mcr p15, 0, r10, c1, c0, 0
207 isb
208
209 mcr p15, 0, r11, c1, c0, 1 @ reenable coherency
210
211 /* Invalidate the TLBs & BTAC */
212 mov r1, #0
213 mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs
214 mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC
215 dsb
216 isb
217
218 /* the cpu was running with coherency disabled,
219 * caches may be out of date */
220 bl v7_flush_kern_cache_louis
221
222 ldmfd sp!, {r4 - r11, pc}
223ENDPROC(tegra20_sleep_cpu_secondary_finish)
224#endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index 56505c381ea8..e39a56beb3a0 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -25,6 +25,19 @@
25 + IO_PPSB_VIRT) 25 + IO_PPSB_VIRT)
26#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS \ 26#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS \
27 + IO_PPSB_VIRT) 27 + IO_PPSB_VIRT)
28#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
29
30/* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */
31#define PMC_SCRATCH37 0x130
32#define PMC_SCRATCH38 0x134
33#define PMC_SCRATCH39 0x138
34#define PMC_SCRATCH41 0x140
35
36#ifdef CONFIG_ARCH_TEGRA_2x_SOC
37#define CPU_RESETTABLE 2
38#define CPU_RESETTABLE_SOON 1
39#define CPU_NOT_RESETTABLE 0
40#endif
28 41
29#ifdef __ASSEMBLY__ 42#ifdef __ASSEMBLY__
30/* returns the offset of the flow controller halt register for a cpu */ 43/* returns the offset of the flow controller halt register for a cpu */
@@ -104,6 +117,8 @@ exit_l2_resume:
104.endm 117.endm
105#endif /* CONFIG_CACHE_L2X0 */ 118#endif /* CONFIG_CACHE_L2X0 */
106#else 119#else
120void tegra_pen_lock(void);
121void tegra_pen_unlock(void);
107void tegra_resume(void); 122void tegra_resume(void);
108int tegra_sleep_cpu_finish(unsigned long); 123int tegra_sleep_cpu_finish(unsigned long);
109void tegra_disable_clean_inv_dcache(void); 124void tegra_disable_clean_inv_dcache(void);
@@ -116,6 +131,14 @@ static inline void tegra20_hotplug_init(void) {}
116static inline void tegra30_hotplug_init(void) {} 131static inline void tegra30_hotplug_init(void) {}
117#endif 132#endif
118 133
134void tegra20_cpu_clear_resettable(void);
135#ifdef CONFIG_ARCH_TEGRA_2x_SOC
136void tegra20_cpu_set_resettable_soon(void);
137#else
138static inline void tegra20_cpu_set_resettable_soon(void) {}
139#endif
140
141int tegra20_sleep_cpu_secondary_finish(unsigned long);
119int tegra30_sleep_cpu_secondary_finish(unsigned long); 142int tegra30_sleep_cpu_secondary_finish(unsigned long);
120void tegra30_tear_down_cpu(void); 143void tegra30_tear_down_cpu(void);
121 144