aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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