Commit 4d48edb3 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding

ARM: tegra20: Store CPU "resettable" status in IRAM

Commit 7232398a ("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: default avatarDmitry Osipenko <digetx@gmail.com>
Fixes: 7232398a (ARM: tegra: Convert PMC to a driver)
Cc: <stable@vger.kernel.org> # v3.17+
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 039aa4d6
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "iomap.h" #include "iomap.h"
#include "irq.h" #include "irq.h"
#include "pm.h" #include "pm.h"
#include "reset.h"
#include "sleep.h" #include "sleep.h"
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -70,15 +71,13 @@ static struct cpuidle_driver tegra_idle_driver = { ...@@ -70,15 +71,13 @@ static struct cpuidle_driver tegra_idle_driver = {
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
static int tegra20_reset_sleeping_cpu_1(void) static int tegra20_reset_sleeping_cpu_1(void)
{ {
int ret = 0; int ret = 0;
tegra_pen_lock(); tegra_pen_lock();
if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE) if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE)
tegra20_cpu_shutdown(1); tegra20_cpu_shutdown(1);
else else
ret = -EINVAL; ret = -EINVAL;
......
...@@ -169,10 +169,10 @@ after_errata: ...@@ -169,10 +169,10 @@ after_errata:
cmp r6, #TEGRA20 cmp r6, #TEGRA20
bne 1f bne 1f
/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
mov32 r5, TEGRA_PMC_BASE mov32 r5, TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET
mov r0, #0 mov r0, #CPU_NOT_RESETTABLE
cmp r10, #0 cmp r10, #0
strne r0, [r5, #PMC_SCRATCH41] strneb r0, [r5, #__tegra20_cpu1_resettable_status_offset]
1: 1:
#endif #endif
...@@ -281,6 +281,10 @@ __tegra_cpu_reset_handler_data: ...@@ -281,6 +281,10 @@ __tegra_cpu_reset_handler_data:
.rept TEGRA_RESET_DATA_SIZE .rept TEGRA_RESET_DATA_SIZE
.long 0 .long 0
.endr .endr
.globl __tegra20_cpu1_resettable_status_offset
.equ __tegra20_cpu1_resettable_status_offset, \
. - __tegra_cpu_reset_handler_start
.byte 0
.align L1_CACHE_SHIFT .align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_end) ENTRY(__tegra_cpu_reset_handler_end)
...@@ -35,6 +35,7 @@ extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]; ...@@ -35,6 +35,7 @@ extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
void __tegra_cpu_reset_handler_start(void); void __tegra_cpu_reset_handler_start(void);
void __tegra_cpu_reset_handler(void); void __tegra_cpu_reset_handler(void);
void __tegra20_cpu1_resettable_status_offset(void);
void __tegra_cpu_reset_handler_end(void); void __tegra_cpu_reset_handler_end(void);
void tegra_secondary_startup(void); void tegra_secondary_startup(void);
...@@ -47,6 +48,9 @@ void tegra_secondary_startup(void); ...@@ -47,6 +48,9 @@ void tegra_secondary_startup(void);
(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
(u32)__tegra_cpu_reset_handler_start))) (u32)__tegra_cpu_reset_handler_start)))
#define tegra20_cpu1_resettable_status \
(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
(u32)__tegra20_cpu1_resettable_status_offset))
#endif #endif
#define tegra_cpu_reset_handler_offset \ #define tegra_cpu_reset_handler_offset \
......
...@@ -97,9 +97,10 @@ ENDPROC(tegra20_hotplug_shutdown) ...@@ -97,9 +97,10 @@ ENDPROC(tegra20_hotplug_shutdown)
ENTRY(tegra20_cpu_shutdown) ENTRY(tegra20_cpu_shutdown)
cmp r0, #0 cmp r0, #0
reteq lr @ must not be called for CPU 0 reteq lr @ must not be called for CPU 0
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
ldr r2, =__tegra20_cpu1_resettable_status_offset
mov r12, #CPU_RESETTABLE mov r12, #CPU_RESETTABLE
str r12, [r1] strb r12, [r1, r2]
cpu_to_halt_reg r1, r0 cpu_to_halt_reg r1, r0
ldr r3, =TEGRA_FLOW_CTRL_VIRT ldr r3, =TEGRA_FLOW_CTRL_VIRT
...@@ -182,38 +183,41 @@ ENDPROC(tegra_pen_unlock) ...@@ -182,38 +183,41 @@ ENDPROC(tegra_pen_unlock)
/* /*
* tegra20_cpu_clear_resettable(void) * tegra20_cpu_clear_resettable(void)
* *
* Called to clear the "resettable soon" flag in PMC_SCRATCH41 when * Called to clear the "resettable soon" flag in IRAM variable when
* it is expected that the secondary CPU will be idle soon. * it is expected that the secondary CPU will be idle soon.
*/ */
ENTRY(tegra20_cpu_clear_resettable) ENTRY(tegra20_cpu_clear_resettable)
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
ldr r2, =__tegra20_cpu1_resettable_status_offset
mov r12, #CPU_NOT_RESETTABLE mov r12, #CPU_NOT_RESETTABLE
str r12, [r1] strb r12, [r1, r2]
ret lr ret lr
ENDPROC(tegra20_cpu_clear_resettable) ENDPROC(tegra20_cpu_clear_resettable)
/* /*
* tegra20_cpu_set_resettable_soon(void) * tegra20_cpu_set_resettable_soon(void)
* *
* Called to set the "resettable soon" flag in PMC_SCRATCH41 when * Called to set the "resettable soon" flag in IRAM variable when
* it is expected that the secondary CPU will be idle soon. * it is expected that the secondary CPU will be idle soon.
*/ */
ENTRY(tegra20_cpu_set_resettable_soon) ENTRY(tegra20_cpu_set_resettable_soon)
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
ldr r2, =__tegra20_cpu1_resettable_status_offset
mov r12, #CPU_RESETTABLE_SOON mov r12, #CPU_RESETTABLE_SOON
str r12, [r1] strb r12, [r1, r2]
ret lr ret lr
ENDPROC(tegra20_cpu_set_resettable_soon) ENDPROC(tegra20_cpu_set_resettable_soon)
/* /*
* tegra20_cpu_is_resettable_soon(void) * tegra20_cpu_is_resettable_soon(void)
* *
* Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been * Returns true if the "resettable soon" flag in IRAM variable has been
* set because it is expected that the secondary CPU will be idle soon. * set because it is expected that the secondary CPU will be idle soon.
*/ */
ENTRY(tegra20_cpu_is_resettable_soon) ENTRY(tegra20_cpu_is_resettable_soon)
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41 mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
ldr r12, [r1] ldr r2, =__tegra20_cpu1_resettable_status_offset
ldrb r12, [r1, r2]
cmp r12, #CPU_RESETTABLE_SOON cmp r12, #CPU_RESETTABLE_SOON
moveq r0, #1 moveq r0, #1
movne r0, #0 movne r0, #0
...@@ -256,9 +260,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) ...@@ -256,9 +260,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish)
mov r0, #TEGRA_FLUSH_CACHE_LOUIS mov r0, #TEGRA_FLUSH_CACHE_LOUIS
bl tegra_disable_clean_inv_dcache bl tegra_disable_clean_inv_dcache
mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41 mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT
ldr r4, =__tegra20_cpu1_resettable_status_offset
mov r3, #CPU_RESETTABLE mov r3, #CPU_RESETTABLE
str r3, [r0] strb r3, [r0, r4]
bl tegra_cpu_do_idle bl tegra_cpu_do_idle
...@@ -274,10 +279,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) ...@@ -274,10 +279,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish)
bl tegra_pen_lock bl tegra_pen_lock
mov32 r3, TEGRA_PMC_VIRT mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT
add r0, r3, #PMC_SCRATCH41 ldr r4, =__tegra20_cpu1_resettable_status_offset
mov r3, #CPU_NOT_RESETTABLE mov r3, #CPU_NOT_RESETTABLE
str r3, [r0] strb r3, [r0, r4]
bl tegra_pen_unlock bl tegra_pen_unlock
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#define __MACH_TEGRA_SLEEP_H #define __MACH_TEGRA_SLEEP_H
#include "iomap.h" #include "iomap.h"
#include "irammap.h"
#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \ #define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \
+ IO_CPU_VIRT) + IO_CPU_VIRT)
...@@ -29,6 +30,9 @@ ...@@ -29,6 +30,9 @@
+ IO_APB_VIRT) + IO_APB_VIRT)
#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT) #define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
#define TEGRA_IRAM_RESET_BASE_VIRT (IO_IRAM_VIRT + \
TEGRA_IRAM_RESET_HANDLER_OFFSET)
/* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */ /* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */
#define PMC_SCRATCH37 0x130 #define PMC_SCRATCH37 0x130
#define PMC_SCRATCH38 0x134 #define PMC_SCRATCH38 0x134
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment