18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012, NVIDIA Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/linkage.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <soc/tegra/flowctrl.h> 108c2ecf20Sopenharmony_ci#include <soc/tegra/fuse.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/assembler.h> 138c2ecf20Sopenharmony_ci#include <asm/asm-offsets.h> 148c2ecf20Sopenharmony_ci#include <asm/cache.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "iomap.h" 178c2ecf20Sopenharmony_ci#include "reset.h" 188c2ecf20Sopenharmony_ci#include "sleep.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define PMC_SCRATCH41 0x140 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * tegra_resume 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * CPU boot vector when restarting the a CPU following 278c2ecf20Sopenharmony_ci * an LP2 transition. Also branched to by LP0 and LP1 resume after 288c2ecf20Sopenharmony_ci * re-enabling sdram. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * r6: SoC ID 318c2ecf20Sopenharmony_ci * r8: CPU part number 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ciENTRY(tegra_resume) 348c2ecf20Sopenharmony_ci check_cpu_part_num 0xc09, r8, r9 358c2ecf20Sopenharmony_ci bleq v7_invalidate_l1 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci cpu_id r0 388c2ecf20Sopenharmony_ci cmp r0, #0 @ CPU0? 398c2ecf20Sopenharmony_ci THUMB( it ne ) 408c2ecf20Sopenharmony_ci bne cpu_resume @ no 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci tegra_get_soc_id TEGRA_APB_MISC_BASE, r6 438c2ecf20Sopenharmony_ci /* Are we on Tegra20? */ 448c2ecf20Sopenharmony_ci cmp r6, #TEGRA20 458c2ecf20Sopenharmony_ci beq 1f @ Yes 468c2ecf20Sopenharmony_ci /* Clear the flow controller flags for this CPU. */ 478c2ecf20Sopenharmony_ci cpu_to_csr_reg r3, r0 488c2ecf20Sopenharmony_ci mov32 r2, TEGRA_FLOW_CTRL_BASE 498c2ecf20Sopenharmony_ci ldr r1, [r2, r3] 508c2ecf20Sopenharmony_ci /* Clear event & intr flag */ 518c2ecf20Sopenharmony_ci orr r1, r1, \ 528c2ecf20Sopenharmony_ci #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG 538c2ecf20Sopenharmony_ci movw r0, #0x3FFD @ enable, cluster_switch, immed, bitmaps 548c2ecf20Sopenharmony_ci @ & ext flags for CPU power mgnt 558c2ecf20Sopenharmony_ci bic r1, r1, r0 568c2ecf20Sopenharmony_ci str r1, [r2, r3] 578c2ecf20Sopenharmony_ci1: 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mov32 r9, 0xc09 608c2ecf20Sopenharmony_ci cmp r8, r9 618c2ecf20Sopenharmony_ci bne end_ca9_scu_l2_resume 628c2ecf20Sopenharmony_ci#ifdef CONFIG_HAVE_ARM_SCU 638c2ecf20Sopenharmony_ci /* enable SCU */ 648c2ecf20Sopenharmony_ci mov32 r0, TEGRA_ARM_PERIF_BASE 658c2ecf20Sopenharmony_ci ldr r1, [r0] 668c2ecf20Sopenharmony_ci orr r1, r1, #1 678c2ecf20Sopenharmony_ci str r1, [r0] 688c2ecf20Sopenharmony_ci#endif 698c2ecf20Sopenharmony_ci bl tegra_resume_trusted_foundations 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#ifdef CONFIG_CACHE_L2X0 728c2ecf20Sopenharmony_ci /* L2 cache resume & re-enable */ 738c2ecf20Sopenharmony_ci bl l2c310_early_resume 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ciend_ca9_scu_l2_resume: 768c2ecf20Sopenharmony_ci mov32 r9, 0xc0f 778c2ecf20Sopenharmony_ci cmp r8, r9 788c2ecf20Sopenharmony_ci bleq tegra_init_l2_for_a15 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci b cpu_resume 818c2ecf20Sopenharmony_ciENDPROC(tegra_resume) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * tegra_resume_trusted_foundations 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * Trusted Foundations firmware initialization. 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Doesn't return if firmware presents. 898c2ecf20Sopenharmony_ci * Corrupted registers: r1, r2 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ciENTRY(tegra_resume_trusted_foundations) 928c2ecf20Sopenharmony_ci /* Check whether Trusted Foundations firmware presents. */ 938c2ecf20Sopenharmony_ci mov32 r2, TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET 948c2ecf20Sopenharmony_ci ldr r1, =__tegra_cpu_reset_handler_data_offset + \ 958c2ecf20Sopenharmony_ci RESET_DATA(TF_PRESENT) 968c2ecf20Sopenharmony_ci ldr r1, [r2, r1] 978c2ecf20Sopenharmony_ci cmp r1, #0 988c2ecf20Sopenharmony_ci reteq lr 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci .arch_extension sec 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * First call after suspend wakes firmware. No arguments required 1038c2ecf20Sopenharmony_ci * for some firmware versions. Downstream kernel of ASUS TF300T uses 1048c2ecf20Sopenharmony_ci * r0=3 for the wake-up notification. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci mov r0, #3 1078c2ecf20Sopenharmony_ci smc #0 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci b cpu_resume 1108c2ecf20Sopenharmony_ciENDPROC(tegra_resume_trusted_foundations) 1118c2ecf20Sopenharmony_ci#endif 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci .align L1_CACHE_SHIFT 1148c2ecf20Sopenharmony_ciENTRY(__tegra_cpu_reset_handler_start) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * __tegra_cpu_reset_handler: 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Common handler for all CPU reset events. 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * Register usage within the reset handler: 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci * Others: scratch 1248c2ecf20Sopenharmony_ci * R6 = SoC ID 1258c2ecf20Sopenharmony_ci * R7 = CPU present (to the OS) mask 1268c2ecf20Sopenharmony_ci * R8 = CPU in LP1 state mask 1278c2ecf20Sopenharmony_ci * R9 = CPU in LP2 state mask 1288c2ecf20Sopenharmony_ci * R10 = CPU number 1298c2ecf20Sopenharmony_ci * R11 = CPU mask 1308c2ecf20Sopenharmony_ci * R12 = pointer to reset handler data 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * NOTE: This code is copied to IRAM. All code and data accesses 1338c2ecf20Sopenharmony_ci * must be position-independent. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci .arm 1378c2ecf20Sopenharmony_ci .align L1_CACHE_SHIFT 1388c2ecf20Sopenharmony_ciENTRY(__tegra_cpu_reset_handler) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci cpsid aif, 0x13 @ SVC mode, interrupts disabled 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci tegra_get_soc_id TEGRA_APB_MISC_BASE, r6 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci adr r12, __tegra_cpu_reset_handler_data 1458c2ecf20Sopenharmony_ci ldr r5, [r12, #RESET_DATA(TF_PRESENT)] 1468c2ecf20Sopenharmony_ci cmp r5, #0 1478c2ecf20Sopenharmony_ci bne after_errata 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_2x_SOC 1508c2ecf20Sopenharmony_cit20_check: 1518c2ecf20Sopenharmony_ci cmp r6, #TEGRA20 1528c2ecf20Sopenharmony_ci bne after_t20_check 1538c2ecf20Sopenharmony_cit20_errata: 1548c2ecf20Sopenharmony_ci # Tegra20 is a Cortex-A9 r1p1 1558c2ecf20Sopenharmony_ci mrc p15, 0, r0, c1, c0, 0 @ read system control register 1568c2ecf20Sopenharmony_ci orr r0, r0, #1 << 14 @ erratum 716044 1578c2ecf20Sopenharmony_ci mcr p15, 0, r0, c1, c0, 0 @ write system control register 1588c2ecf20Sopenharmony_ci mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register 1598c2ecf20Sopenharmony_ci orr r0, r0, #1 << 4 @ erratum 742230 1608c2ecf20Sopenharmony_ci orr r0, r0, #1 << 11 @ erratum 751472 1618c2ecf20Sopenharmony_ci mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register 1628c2ecf20Sopenharmony_ci b after_errata 1638c2ecf20Sopenharmony_ciafter_t20_check: 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_3x_SOC 1668c2ecf20Sopenharmony_cit30_check: 1678c2ecf20Sopenharmony_ci cmp r6, #TEGRA30 1688c2ecf20Sopenharmony_ci bne after_t30_check 1698c2ecf20Sopenharmony_cit30_errata: 1708c2ecf20Sopenharmony_ci # Tegra30 is a Cortex-A9 r2p9 1718c2ecf20Sopenharmony_ci mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register 1728c2ecf20Sopenharmony_ci orr r0, r0, #1 << 6 @ erratum 743622 1738c2ecf20Sopenharmony_ci orr r0, r0, #1 << 11 @ erratum 751472 1748c2ecf20Sopenharmony_ci mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register 1758c2ecf20Sopenharmony_ci b after_errata 1768c2ecf20Sopenharmony_ciafter_t30_check: 1778c2ecf20Sopenharmony_ci#endif 1788c2ecf20Sopenharmony_ciafter_errata: 1798c2ecf20Sopenharmony_ci mrc p15, 0, r10, c0, c0, 5 @ MPIDR 1808c2ecf20Sopenharmony_ci and r10, r10, #0x3 @ R10 = CPU number 1818c2ecf20Sopenharmony_ci mov r11, #1 1828c2ecf20Sopenharmony_ci mov r11, r11, lsl r10 @ R11 = CPU mask 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1858c2ecf20Sopenharmony_ci /* Does the OS know about this CPU? */ 1868c2ecf20Sopenharmony_ci ldr r7, [r12, #RESET_DATA(MASK_PRESENT)] 1878c2ecf20Sopenharmony_ci tst r7, r11 @ if !present 1888c2ecf20Sopenharmony_ci bleq __die @ CPU not present (to OS) 1898c2ecf20Sopenharmony_ci#endif 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Waking up from LP1? */ 1928c2ecf20Sopenharmony_ci ldr r8, [r12, #RESET_DATA(MASK_LP1)] 1938c2ecf20Sopenharmony_ci tst r8, r11 @ if in_lp1 1948c2ecf20Sopenharmony_ci beq __is_not_lp1 1958c2ecf20Sopenharmony_ci cmp r10, #0 1968c2ecf20Sopenharmony_ci bne __die @ only CPU0 can be here 1978c2ecf20Sopenharmony_ci ldr lr, [r12, #RESET_DATA(STARTUP_LP1)] 1988c2ecf20Sopenharmony_ci cmp lr, #0 1998c2ecf20Sopenharmony_ci bleq __die @ no LP1 startup handler 2008c2ecf20Sopenharmony_ci THUMB( add lr, lr, #1 ) @ switch to Thumb mode 2018c2ecf20Sopenharmony_ci bx lr 2028c2ecf20Sopenharmony_ci__is_not_lp1: 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Waking up from LP2? */ 2058c2ecf20Sopenharmony_ci ldr r9, [r12, #RESET_DATA(MASK_LP2)] 2068c2ecf20Sopenharmony_ci tst r9, r11 @ if in_lp2 2078c2ecf20Sopenharmony_ci beq __is_not_lp2 2088c2ecf20Sopenharmony_ci ldr lr, [r12, #RESET_DATA(STARTUP_LP2)] 2098c2ecf20Sopenharmony_ci cmp lr, #0 2108c2ecf20Sopenharmony_ci bleq __die @ no LP2 startup handler 2118c2ecf20Sopenharmony_ci bx lr 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci__is_not_lp2: 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * Can only be secondary boot (initial or hotplug) 2188c2ecf20Sopenharmony_ci * CPU0 can't be here for Tegra20/30 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci cmp r6, #TEGRA114 2218c2ecf20Sopenharmony_ci beq __no_cpu0_chk 2228c2ecf20Sopenharmony_ci cmp r10, #0 2238c2ecf20Sopenharmony_ci bleq __die @ CPU0 cannot be here 2248c2ecf20Sopenharmony_ci__no_cpu0_chk: 2258c2ecf20Sopenharmony_ci ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)] 2268c2ecf20Sopenharmony_ci cmp lr, #0 2278c2ecf20Sopenharmony_ci bleq __die @ no secondary startup handler 2288c2ecf20Sopenharmony_ci bx lr 2298c2ecf20Sopenharmony_ci#endif 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* 2328c2ecf20Sopenharmony_ci * We don't know why the CPU reset. Just kill it. 2338c2ecf20Sopenharmony_ci * The LR register will contain the address we died at + 4. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci__die: 2378c2ecf20Sopenharmony_ci sub lr, lr, #4 2388c2ecf20Sopenharmony_ci mov32 r7, TEGRA_PMC_BASE 2398c2ecf20Sopenharmony_ci str lr, [r7, #PMC_SCRATCH41] 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci mov32 r7, TEGRA_CLK_RESET_BASE 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Are we on Tegra20? */ 2448c2ecf20Sopenharmony_ci cmp r6, #TEGRA20 2458c2ecf20Sopenharmony_ci bne 1f 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_2x_SOC 2488c2ecf20Sopenharmony_ci mov32 r0, 0x1111 2498c2ecf20Sopenharmony_ci mov r1, r0, lsl r10 2508c2ecf20Sopenharmony_ci str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET 2518c2ecf20Sopenharmony_ci#endif 2528c2ecf20Sopenharmony_ci1: 2538c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_3x_SOC 2548c2ecf20Sopenharmony_ci mov32 r6, TEGRA_FLOW_CTRL_BASE 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci cmp r10, #0 2578c2ecf20Sopenharmony_ci moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS 2588c2ecf20Sopenharmony_ci moveq r2, #FLOW_CTRL_CPU0_CSR 2598c2ecf20Sopenharmony_ci movne r1, r10, lsl #3 2608c2ecf20Sopenharmony_ci addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8) 2618c2ecf20Sopenharmony_ci addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8) 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Clear CPU "event" and "interrupt" flags and power gate 2648c2ecf20Sopenharmony_ci it when halting but not before it is in the "WFI" state. */ 2658c2ecf20Sopenharmony_ci ldr r0, [r6, +r2] 2668c2ecf20Sopenharmony_ci orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG 2678c2ecf20Sopenharmony_ci orr r0, r0, #FLOW_CTRL_CSR_ENABLE 2688c2ecf20Sopenharmony_ci str r0, [r6, +r2] 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Unconditionally halt this CPU */ 2718c2ecf20Sopenharmony_ci mov r0, #FLOW_CTRL_WAITEVENT 2728c2ecf20Sopenharmony_ci str r0, [r6, +r1] 2738c2ecf20Sopenharmony_ci ldr r0, [r6, +r1] @ memory barrier 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci dsb 2768c2ecf20Sopenharmony_ci isb 2778c2ecf20Sopenharmony_ci wfi @ CPU should be power gated here 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* If the CPU didn't power gate above just kill it's clock. */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mov r0, r11, lsl #8 2828c2ecf20Sopenharmony_ci str r0, [r7, #348] @ CLK_CPU_CMPLX_SET 2838c2ecf20Sopenharmony_ci#endif 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* If the CPU still isn't dead, just spin here. */ 2868c2ecf20Sopenharmony_ci b . 2878c2ecf20Sopenharmony_ciENDPROC(__tegra_cpu_reset_handler) 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci .align L1_CACHE_SHIFT 2908c2ecf20Sopenharmony_ci .type __tegra_cpu_reset_handler_data, %object 2918c2ecf20Sopenharmony_ci .globl __tegra_cpu_reset_handler_data 2928c2ecf20Sopenharmony_ci .globl __tegra_cpu_reset_handler_data_offset 2938c2ecf20Sopenharmony_ci .equ __tegra_cpu_reset_handler_data_offset, \ 2948c2ecf20Sopenharmony_ci . - __tegra_cpu_reset_handler_start 2958c2ecf20Sopenharmony_ci__tegra_cpu_reset_handler_data: 2968c2ecf20Sopenharmony_ci .rept TEGRA_RESET_DATA_SIZE 2978c2ecf20Sopenharmony_ci .long 0 2988c2ecf20Sopenharmony_ci .endr 2998c2ecf20Sopenharmony_ci .align L1_CACHE_SHIFT 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciENTRY(__tegra_cpu_reset_handler_end) 302