diff options
author | Dmitry Osipenko <digetx@gmail.com> | 2015-01-15 05:58:57 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-07-10 12:49:34 -0400 |
commit | 3544f27efa25890516158c8e13d21e878969d125 (patch) | |
tree | ec76dc958d12241534c2b35cd5ac83b4bfb74f06 | |
parent | 3f3587c4ff8c828aac436237aeca8694a26defd3 (diff) |
ARM: tegra20: Store CPU "resettable" status in IRAM
commit 4d48edb3c3e1234d6b3fcdfb9ac24d7c6de449cb upstream.
Commit 7232398abc6a ("ARM: tegra: Convert PMC to a driver") changed tegra_resume()
location storing from late to early and, as a result, broke suspend on Tegra20.
PMC scratch register 41 is used by tegra LP1 resume code for retrieving stored
physical memory address of common resume function and in the same time used by
tegra20_cpu_shutdown() (shared by Tegra20 cpuidle driver and platform SMP code),
which is storing CPU1 "resettable" status. It implies strict order of scratch
register usage, otherwise resume function address is lost on Tegra20 after
disabling non-boot CPU's on suspend. Fix it by storing "resettable" status in
IRAM instead of PMC scratch register.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Fixes: 7232398abc6a (ARM: tegra: Convert PMC to a driver)
Signed-off-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | arch/arm/mach-tegra/cpuidle-tegra20.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset-handler.S | 10 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-tegra20.S | 37 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 4 |
5 files changed, 38 insertions, 22 deletions
diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 88de2dce2e87..7469347b1749 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include "iomap.h" | 34 | #include "iomap.h" |
35 | #include "irq.h" | 35 | #include "irq.h" |
36 | #include "pm.h" | 36 | #include "pm.h" |
37 | #include "reset.h" | ||
37 | #include "sleep.h" | 38 | #include "sleep.h" |
38 | 39 | ||
39 | #ifdef CONFIG_PM_SLEEP | 40 | #ifdef CONFIG_PM_SLEEP |
@@ -70,15 +71,13 @@ static struct cpuidle_driver tegra_idle_driver = { | |||
70 | 71 | ||
71 | #ifdef CONFIG_PM_SLEEP | 72 | #ifdef CONFIG_PM_SLEEP |
72 | #ifdef CONFIG_SMP | 73 | #ifdef CONFIG_SMP |
73 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); | ||
74 | |||
75 | static int tegra20_reset_sleeping_cpu_1(void) | 74 | static int tegra20_reset_sleeping_cpu_1(void) |
76 | { | 75 | { |
77 | int ret = 0; | 76 | int ret = 0; |
78 | 77 | ||
79 | tegra_pen_lock(); | 78 | tegra_pen_lock(); |
80 | 79 | ||
81 | if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE) | 80 | if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE) |
82 | tegra20_cpu_shutdown(1); | 81 | tegra20_cpu_shutdown(1); |
83 | else | 82 | else |
84 | ret = -EINVAL; | 83 | ret = -EINVAL; |
diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index 71be4af5e975..e3070fdab80b 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S | |||
@@ -169,10 +169,10 @@ after_errata: | |||
169 | cmp r6, #TEGRA20 | 169 | cmp r6, #TEGRA20 |
170 | bne 1f | 170 | bne 1f |
171 | /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ | 171 | /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ |
172 | mov32 r5, TEGRA_PMC_BASE | 172 | mov32 r5, TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET |
173 | mov r0, #0 | 173 | mov r0, #CPU_NOT_RESETTABLE |
174 | cmp r10, #0 | 174 | cmp r10, #0 |
175 | strne r0, [r5, #PMC_SCRATCH41] | 175 | strneb r0, [r5, #__tegra20_cpu1_resettable_status_offset] |
176 | 1: | 176 | 1: |
177 | #endif | 177 | #endif |
178 | 178 | ||
@@ -281,6 +281,10 @@ __tegra_cpu_reset_handler_data: | |||
281 | .rept TEGRA_RESET_DATA_SIZE | 281 | .rept TEGRA_RESET_DATA_SIZE |
282 | .long 0 | 282 | .long 0 |
283 | .endr | 283 | .endr |
284 | .globl __tegra20_cpu1_resettable_status_offset | ||
285 | .equ __tegra20_cpu1_resettable_status_offset, \ | ||
286 | . - __tegra_cpu_reset_handler_start | ||
287 | .byte 0 | ||
284 | .align L1_CACHE_SHIFT | 288 | .align L1_CACHE_SHIFT |
285 | 289 | ||
286 | ENTRY(__tegra_cpu_reset_handler_end) | 290 | ENTRY(__tegra_cpu_reset_handler_end) |
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h index 76a93434c6ee..29c3dec0126a 100644 --- a/arch/arm/mach-tegra/reset.h +++ b/arch/arm/mach-tegra/reset.h | |||
@@ -35,6 +35,7 @@ extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]; | |||
35 | 35 | ||
36 | void __tegra_cpu_reset_handler_start(void); | 36 | void __tegra_cpu_reset_handler_start(void); |
37 | void __tegra_cpu_reset_handler(void); | 37 | void __tegra_cpu_reset_handler(void); |
38 | void __tegra20_cpu1_resettable_status_offset(void); | ||
38 | void __tegra_cpu_reset_handler_end(void); | 39 | void __tegra_cpu_reset_handler_end(void); |
39 | void tegra_secondary_startup(void); | 40 | void tegra_secondary_startup(void); |
40 | 41 | ||
@@ -47,6 +48,9 @@ void tegra_secondary_startup(void); | |||
47 | (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ | 48 | (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ |
48 | ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \ | 49 | ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \ |
49 | (u32)__tegra_cpu_reset_handler_start))) | 50 | (u32)__tegra_cpu_reset_handler_start))) |
51 | #define tegra20_cpu1_resettable_status \ | ||
52 | (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ | ||
53 | (u32)__tegra20_cpu1_resettable_status_offset)) | ||
50 | #endif | 54 | #endif |
51 | 55 | ||
52 | #define tegra_cpu_reset_handler_offset \ | 56 | #define tegra_cpu_reset_handler_offset \ |
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S index be4bc5f853f5..e6b684e14322 100644 --- a/arch/arm/mach-tegra/sleep-tegra20.S +++ b/arch/arm/mach-tegra/sleep-tegra20.S | |||
@@ -97,9 +97,10 @@ ENDPROC(tegra20_hotplug_shutdown) | |||
97 | ENTRY(tegra20_cpu_shutdown) | 97 | ENTRY(tegra20_cpu_shutdown) |
98 | cmp r0, #0 | 98 | cmp r0, #0 |
99 | reteq lr @ must not be called for CPU 0 | 99 | reteq lr @ must not be called for CPU 0 |
100 | mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 | 100 | mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT |
101 | ldr r2, =__tegra20_cpu1_resettable_status_offset | ||
101 | mov r12, #CPU_RESETTABLE | 102 | mov r12, #CPU_RESETTABLE |
102 | str r12, [r1] | 103 | strb r12, [r1, r2] |
103 | 104 | ||
104 | cpu_to_halt_reg r1, r0 | 105 | cpu_to_halt_reg r1, r0 |
105 | ldr r3, =TEGRA_FLOW_CTRL_VIRT | 106 | ldr r3, =TEGRA_FLOW_CTRL_VIRT |
@@ -182,38 +183,41 @@ ENDPROC(tegra_pen_unlock) | |||
182 | /* | 183 | /* |
183 | * tegra20_cpu_clear_resettable(void) | 184 | * tegra20_cpu_clear_resettable(void) |
184 | * | 185 | * |
185 | * Called to clear the "resettable soon" flag in PMC_SCRATCH41 when | 186 | * Called to clear the "resettable soon" flag in IRAM variable when |
186 | * it is expected that the secondary CPU will be idle soon. | 187 | * it is expected that the secondary CPU will be idle soon. |
187 | */ | 188 | */ |
188 | ENTRY(tegra20_cpu_clear_resettable) | 189 | ENTRY(tegra20_cpu_clear_resettable) |
189 | mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 | 190 | mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT |
191 | ldr r2, =__tegra20_cpu1_resettable_status_offset | ||
190 | mov r12, #CPU_NOT_RESETTABLE | 192 | mov r12, #CPU_NOT_RESETTABLE |
191 | str r12, [r1] | 193 | strb r12, [r1, r2] |
192 | ret lr | 194 | ret lr |
193 | ENDPROC(tegra20_cpu_clear_resettable) | 195 | ENDPROC(tegra20_cpu_clear_resettable) |
194 | 196 | ||
195 | /* | 197 | /* |
196 | * tegra20_cpu_set_resettable_soon(void) | 198 | * tegra20_cpu_set_resettable_soon(void) |
197 | * | 199 | * |
198 | * Called to set the "resettable soon" flag in PMC_SCRATCH41 when | 200 | * Called to set the "resettable soon" flag in IRAM variable when |
199 | * it is expected that the secondary CPU will be idle soon. | 201 | * it is expected that the secondary CPU will be idle soon. |
200 | */ | 202 | */ |
201 | ENTRY(tegra20_cpu_set_resettable_soon) | 203 | ENTRY(tegra20_cpu_set_resettable_soon) |
202 | mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 | 204 | mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT |
205 | ldr r2, =__tegra20_cpu1_resettable_status_offset | ||
203 | mov r12, #CPU_RESETTABLE_SOON | 206 | mov r12, #CPU_RESETTABLE_SOON |
204 | str r12, [r1] | 207 | strb r12, [r1, r2] |
205 | ret lr | 208 | ret lr |
206 | ENDPROC(tegra20_cpu_set_resettable_soon) | 209 | ENDPROC(tegra20_cpu_set_resettable_soon) |
207 | 210 | ||
208 | /* | 211 | /* |
209 | * tegra20_cpu_is_resettable_soon(void) | 212 | * tegra20_cpu_is_resettable_soon(void) |
210 | * | 213 | * |
211 | * Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been | 214 | * Returns true if the "resettable soon" flag in IRAM variable has been |
212 | * set because it is expected that the secondary CPU will be idle soon. | 215 | * set because it is expected that the secondary CPU will be idle soon. |
213 | */ | 216 | */ |
214 | ENTRY(tegra20_cpu_is_resettable_soon) | 217 | ENTRY(tegra20_cpu_is_resettable_soon) |
215 | mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 | 218 | mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT |
216 | ldr r12, [r1] | 219 | ldr r2, =__tegra20_cpu1_resettable_status_offset |
220 | ldrb r12, [r1, r2] | ||
217 | cmp r12, #CPU_RESETTABLE_SOON | 221 | cmp r12, #CPU_RESETTABLE_SOON |
218 | moveq r0, #1 | 222 | moveq r0, #1 |
219 | movne r0, #0 | 223 | movne r0, #0 |
@@ -256,9 +260,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) | |||
256 | mov r0, #TEGRA_FLUSH_CACHE_LOUIS | 260 | mov r0, #TEGRA_FLUSH_CACHE_LOUIS |
257 | bl tegra_disable_clean_inv_dcache | 261 | bl tegra_disable_clean_inv_dcache |
258 | 262 | ||
259 | mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41 | 263 | mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT |
264 | ldr r4, =__tegra20_cpu1_resettable_status_offset | ||
260 | mov r3, #CPU_RESETTABLE | 265 | mov r3, #CPU_RESETTABLE |
261 | str r3, [r0] | 266 | strb r3, [r0, r4] |
262 | 267 | ||
263 | bl tegra_cpu_do_idle | 268 | bl tegra_cpu_do_idle |
264 | 269 | ||
@@ -274,10 +279,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) | |||
274 | 279 | ||
275 | bl tegra_pen_lock | 280 | bl tegra_pen_lock |
276 | 281 | ||
277 | mov32 r3, TEGRA_PMC_VIRT | 282 | mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT |
278 | add r0, r3, #PMC_SCRATCH41 | 283 | ldr r4, =__tegra20_cpu1_resettable_status_offset |
279 | mov r3, #CPU_NOT_RESETTABLE | 284 | mov r3, #CPU_NOT_RESETTABLE |
280 | str r3, [r0] | 285 | strb r3, [r0, r4] |
281 | 286 | ||
282 | bl tegra_pen_unlock | 287 | bl tegra_pen_unlock |
283 | 288 | ||
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 92d46ec1361a..0d59360d891d 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #define __MACH_TEGRA_SLEEP_H | 18 | #define __MACH_TEGRA_SLEEP_H |
19 | 19 | ||
20 | #include "iomap.h" | 20 | #include "iomap.h" |
21 | #include "irammap.h" | ||
21 | 22 | ||
22 | #define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \ | 23 | #define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \ |
23 | + IO_CPU_VIRT) | 24 | + IO_CPU_VIRT) |
@@ -29,6 +30,9 @@ | |||
29 | + IO_APB_VIRT) | 30 | + IO_APB_VIRT) |
30 | #define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT) | 31 | #define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT) |
31 | 32 | ||
33 | #define TEGRA_IRAM_RESET_BASE_VIRT (IO_IRAM_VIRT + \ | ||
34 | TEGRA_IRAM_RESET_HANDLER_OFFSET) | ||
35 | |||
32 | /* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */ | 36 | /* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */ |
33 | #define PMC_SCRATCH37 0x130 | 37 | #define PMC_SCRATCH37 0x130 |
34 | #define PMC_SCRATCH38 0x134 | 38 | #define PMC_SCRATCH38 0x134 |