Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 22f36a48 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Sasha Levin
Browse files

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



[ Upstream commit 4d48edb3c3e1234d6b3fcdfb9ac24d7c6de449cb ]

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>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent ec56d2a6
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@
#include "iomap.h"
#include "irq.h"
#include "pm.h"
#include "reset.h"
#include "sleep.h"

#ifdef CONFIG_PM_SLEEP
@@ -72,15 +73,13 @@ static struct cpuidle_driver tegra_idle_driver = {

#ifdef CONFIG_PM_SLEEP
#ifdef CONFIG_SMP
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);

static int tegra20_reset_sleeping_cpu_1(void)
{
	int ret = 0;

	tegra_pen_lock();

	if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
	if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE)
		tegra20_cpu_shutdown(1);
	else
		ret = -EINVAL;
+7 −3
Original line number Diff line number Diff line
@@ -169,10 +169,10 @@ after_errata:
	cmp	r6, #TEGRA20
	bne	1f
	/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
	mov32	r5, TEGRA_PMC_BASE
	mov	r0, #0
	mov32	r5, TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET
	mov	r0, #CPU_NOT_RESETTABLE
	cmp	r10, #0
	strne	r0, [r5, #PMC_SCRATCH41]
	strneb	r0, [r5, #__tegra20_cpu1_resettable_status_offset]
1:
#endif

@@ -281,6 +281,10 @@ __tegra_cpu_reset_handler_data:
	.rept	TEGRA_RESET_DATA_SIZE
	.long	0
	.endr
	.globl	__tegra20_cpu1_resettable_status_offset
	.equ	__tegra20_cpu1_resettable_status_offset, \
					. - __tegra_cpu_reset_handler_start
	.byte	0
	.align L1_CACHE_SHIFT

ENTRY(__tegra_cpu_reset_handler_end)
+4 −0
Original line number Diff line number Diff line
@@ -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(void);
void __tegra20_cpu1_resettable_status_offset(void);
void __tegra_cpu_reset_handler_end(void);
void tegra_secondary_startup(void);

@@ -47,6 +48,9 @@ void tegra_secondary_startup(void);
	(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_start)))
#define tegra20_cpu1_resettable_status \
	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
	 (u32)__tegra20_cpu1_resettable_status_offset))
#endif

#define tegra_cpu_reset_handler_offset \
+21 −16
Original line number Diff line number Diff line
@@ -97,9 +97,10 @@ ENDPROC(tegra20_hotplug_shutdown)
ENTRY(tegra20_cpu_shutdown)
	cmp	r0, #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
	str	r12, [r1]
	strb	r12, [r1, r2]

	cpu_to_halt_reg r1, r0
	ldr	r3, =TEGRA_FLOW_CTRL_VIRT
@@ -182,38 +183,41 @@ ENDPROC(tegra_pen_unlock)
/*
 * 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.
 */
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
	str	r12, [r1]
	strb	r12, [r1, r2]
	ret	lr
ENDPROC(tegra20_cpu_clear_resettable)

/*
 * 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.
 */
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
	str	r12, [r1]
	strb	r12, [r1, r2]
	ret	lr
ENDPROC(tegra20_cpu_set_resettable_soon)

/*
 * 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.
 */
ENTRY(tegra20_cpu_is_resettable_soon)
	mov32	r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
	ldr	r12, [r1]
	mov32	r1, TEGRA_IRAM_RESET_BASE_VIRT
	ldr	r2, =__tegra20_cpu1_resettable_status_offset
	ldrb	r12, [r1, r2]
	cmp	r12, #CPU_RESETTABLE_SOON
	moveq	r0, #1
	movne	r0, #0
@@ -256,9 +260,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish)
	mov	r0, #TEGRA_FLUSH_CACHE_LOUIS
	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
	str	r3, [r0]
	strb	r3, [r0, r4]

	bl	tegra_cpu_do_idle

@@ -274,10 +279,10 @@ ENTRY(tegra20_sleep_cpu_secondary_finish)

	bl	tegra_pen_lock

	mov32	r3, TEGRA_PMC_VIRT
	add	r0, r3, #PMC_SCRATCH41
	mov32	r0, TEGRA_IRAM_RESET_BASE_VIRT
	ldr	r4, =__tegra20_cpu1_resettable_status_offset
	mov	r3, #CPU_NOT_RESETTABLE
	str	r3, [r0]
	strb	r3, [r0, r4]

	bl	tegra_pen_unlock

+4 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define __MACH_TEGRA_SLEEP_H

#include "iomap.h"
#include "irammap.h"

#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \
					+ IO_CPU_VIRT)
@@ -29,6 +30,9 @@
					+ 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 */
#define PMC_SCRATCH37	0x130
#define PMC_SCRATCH38	0x134