summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/reset-handler.S
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/reset-handler.S')
-rw-r--r--arch/arm/mach-tegra/reset-handler.S303
1 files changed, 303 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S
new file mode 100644
index 0000000000..0ea456264f
--- /dev/null
+++ b/arch/arm/mach-tegra/reset-handler.S
@@ -0,0 +1,303 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+
+#include <soc/tegra/flowctrl.h>
+#include <soc/tegra/fuse.h>
+
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/cache.h>
+
+#include "iomap.h"
+#include "reset.h"
+#include "sleep.h"
+
+#define PMC_SCRATCH41 0x140
+
+.arch armv7-a
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tegra_resume
+ *
+ * CPU boot vector when restarting the a CPU following
+ * an LP2 transition. Also branched to by LP0 and LP1 resume after
+ * re-enabling sdram.
+ *
+ * r6: SoC ID
+ * r8: CPU part number
+ */
+ENTRY(tegra_resume)
+ check_cpu_part_num 0xc09, r8, r9
+ bleq v7_invalidate_l1
+
+ cpu_id r0
+ cmp r0, #0 @ CPU0?
+ THUMB( it ne )
+ bne cpu_resume @ no
+
+ tegra_get_soc_id TEGRA_APB_MISC_BASE, r6
+ /* Are we on Tegra20? */
+ cmp r6, #TEGRA20
+ beq 1f @ Yes
+ /* Clear the flow controller flags for this CPU. */
+ cpu_to_csr_reg r3, r0
+ mov32 r2, TEGRA_FLOW_CTRL_BASE
+ ldr r1, [r2, r3]
+ /* Clear event & intr flag */
+ orr r1, r1, \
+ #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ movw r0, #0x3FFD @ enable, cluster_switch, immed, bitmaps
+ @ & ext flags for CPU power mgnt
+ bic r1, r1, r0
+ str r1, [r2, r3]
+1:
+
+ mov32 r9, 0xc09
+ cmp r8, r9
+ bne end_ca9_scu_l2_resume
+#ifdef CONFIG_HAVE_ARM_SCU
+ /* enable SCU */
+ mov32 r0, TEGRA_ARM_PERIF_BASE
+ ldr r1, [r0]
+ orr r1, r1, #1
+ str r1, [r0]
+#endif
+ bl tegra_resume_trusted_foundations
+
+#ifdef CONFIG_CACHE_L2X0
+ /* L2 cache resume & re-enable */
+ bl l2c310_early_resume
+#endif
+end_ca9_scu_l2_resume:
+ mov32 r9, 0xc0f
+ cmp r8, r9
+ bleq tegra_init_l2_for_a15
+
+ b cpu_resume
+ENDPROC(tegra_resume)
+
+/*
+ * tegra_resume_trusted_foundations
+ *
+ * Trusted Foundations firmware initialization.
+ *
+ * Doesn't return if firmware presents.
+ * Corrupted registers: r1, r2
+ */
+ENTRY(tegra_resume_trusted_foundations)
+ /* Check whether Trusted Foundations firmware presents. */
+ mov32 r2, TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET
+ ldr r1, =__tegra_cpu_reset_handler_data_offset + \
+ RESET_DATA(TF_PRESENT)
+ ldr r1, [r2, r1]
+ cmp r1, #0
+ reteq lr
+
+ .arch_extension sec
+ /*
+ * First call after suspend wakes firmware. No arguments required
+ * for some firmware versions. Downstream kernel of ASUS TF300T uses
+ * r0=3 for the wake-up notification.
+ */
+ mov r0, #3
+ smc #0
+
+ b cpu_resume
+ENDPROC(tegra_resume_trusted_foundations)
+#endif
+
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_cpu_reset_handler_start)
+
+/*
+ * __tegra_cpu_reset_handler:
+ *
+ * Common handler for all CPU reset events.
+ *
+ * Register usage within the reset handler:
+ *
+ * Others: scratch
+ * R6 = SoC ID
+ * R7 = CPU present (to the OS) mask
+ * R8 = CPU in LP1 state mask
+ * R9 = CPU in LP2 state mask
+ * R10 = CPU number
+ * R11 = CPU mask
+ * R12 = pointer to reset handler data
+ *
+ * NOTE: This code is copied to IRAM. All code and data accesses
+ * must be position-independent.
+ */
+
+ .arm
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_cpu_reset_handler)
+
+ cpsid aif, 0x13 @ SVC mode, interrupts disabled
+
+ tegra_get_soc_id TEGRA_APB_MISC_BASE, r6
+
+ adr r12, __tegra_cpu_reset_handler_data
+ ldr r5, [r12, #RESET_DATA(TF_PRESENT)]
+ cmp r5, #0
+ bne after_errata
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+t20_check:
+ cmp r6, #TEGRA20
+ bne after_t20_check
+t20_errata:
+ # Tegra20 is a Cortex-A9 r1p1
+ mrc p15, 0, r0, c1, c0, 0 @ read system control register
+ orr r0, r0, #1 << 14 @ erratum 716044
+ mcr p15, 0, r0, c1, c0, 0 @ write system control register
+ mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
+ orr r0, r0, #1 << 4 @ erratum 742230
+ orr r0, r0, #1 << 11 @ erratum 751472
+ mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
+ b after_errata
+after_t20_check:
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+t30_check:
+ cmp r6, #TEGRA30
+ bne after_t30_check
+t30_errata:
+ # Tegra30 is a Cortex-A9 r2p9
+ mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
+ orr r0, r0, #1 << 6 @ erratum 743622
+ orr r0, r0, #1 << 11 @ erratum 751472
+ mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
+ b after_errata
+after_t30_check:
+#endif
+after_errata:
+ mrc p15, 0, r10, c0, c0, 5 @ MPIDR
+ and r10, r10, #0x3 @ R10 = CPU number
+ mov r11, #1
+ mov r11, r11, lsl r10 @ R11 = CPU mask
+
+#ifdef CONFIG_SMP
+ /* Does the OS know about this CPU? */
+ ldr r7, [r12, #RESET_DATA(MASK_PRESENT)]
+ tst r7, r11 @ if !present
+ bleq __die @ CPU not present (to OS)
+#endif
+
+ /* Waking up from LP1? */
+ ldr r8, [r12, #RESET_DATA(MASK_LP1)]
+ tst r8, r11 @ if in_lp1
+ beq __is_not_lp1
+ cmp r10, #0
+ bne __die @ only CPU0 can be here
+ ldr lr, [r12, #RESET_DATA(STARTUP_LP1)]
+ cmp lr, #0
+ bleq __die @ no LP1 startup handler
+ THUMB( add lr, lr, #1 ) @ switch to Thumb mode
+ bx lr
+__is_not_lp1:
+
+ /* Waking up from LP2? */
+ ldr r9, [r12, #RESET_DATA(MASK_LP2)]
+ tst r9, r11 @ if in_lp2
+ beq __is_not_lp2
+ ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
+ cmp lr, #0
+ bleq __die @ no LP2 startup handler
+ bx lr
+
+__is_not_lp2:
+
+#ifdef CONFIG_SMP
+ /*
+ * Can only be secondary boot (initial or hotplug)
+ * CPU0 can't be here for Tegra20/30
+ */
+ cmp r6, #TEGRA114
+ beq __no_cpu0_chk
+ cmp r10, #0
+ bleq __die @ CPU0 cannot be here
+__no_cpu0_chk:
+ ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
+ cmp lr, #0
+ bleq __die @ no secondary startup handler
+ bx lr
+#endif
+
+/*
+ * We don't know why the CPU reset. Just kill it.
+ * The LR register will contain the address we died at + 4.
+ */
+
+__die:
+ sub lr, lr, #4
+ mov32 r7, TEGRA_PMC_BASE
+ str lr, [r7, #PMC_SCRATCH41]
+
+ mov32 r7, TEGRA_CLK_RESET_BASE
+
+ /* Are we on Tegra20? */
+ cmp r6, #TEGRA20
+ bne 1f
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ mov32 r0, 0x1111
+ mov r1, r0, lsl r10
+ str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET
+#endif
+1:
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+
+ cmp r10, #0
+ moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS
+ moveq r2, #FLOW_CTRL_CPU0_CSR
+ movne r1, r10, lsl #3
+ addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8)
+ addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8)
+
+ /* Clear CPU "event" and "interrupt" flags and power gate
+ it when halting but not before it is in the "WFI" state. */
+ ldr r0, [r6, +r2]
+ orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
+ orr r0, r0, #FLOW_CTRL_CSR_ENABLE
+ str r0, [r6, +r2]
+
+ /* Unconditionally halt this CPU */
+ mov r0, #FLOW_CTRL_WAITEVENT
+ str r0, [r6, +r1]
+ ldr r0, [r6, +r1] @ memory barrier
+
+ dsb
+ isb
+ wfi @ CPU should be power gated here
+
+ /* If the CPU didn't power gate above just kill it's clock. */
+
+ mov r0, r11, lsl #8
+ str r0, [r7, #348] @ CLK_CPU_CMPLX_SET
+#endif
+
+ /* If the CPU still isn't dead, just spin here. */
+ b .
+ENDPROC(__tegra_cpu_reset_handler)
+
+ .align L1_CACHE_SHIFT
+ .type __tegra_cpu_reset_handler_data, %object
+ .globl __tegra_cpu_reset_handler_data
+ .globl __tegra_cpu_reset_handler_data_offset
+ .equ __tegra_cpu_reset_handler_data_offset, \
+ . - __tegra_cpu_reset_handler_start
+__tegra_cpu_reset_handler_data:
+ .rept TEGRA_RESET_DATA_SIZE
+ .long 0
+ .endr
+ .align L1_CACHE_SHIFT
+
+ENTRY(__tegra_cpu_reset_handler_end)