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