18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CPU complex suspend & resume functions for Tegra SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk/tegra.h> 98c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 108c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <linux/suspend.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/firmware/trusted_foundations.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <soc/tegra/flowctrl.h> 228c2ecf20Sopenharmony_ci#include <soc/tegra/fuse.h> 238c2ecf20Sopenharmony_ci#include <soc/tegra/pm.h> 248c2ecf20Sopenharmony_ci#include <soc/tegra/pmc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 278c2ecf20Sopenharmony_ci#include <asm/firmware.h> 288c2ecf20Sopenharmony_ci#include <asm/idmap.h> 298c2ecf20Sopenharmony_ci#include <asm/proc-fns.h> 308c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 318c2ecf20Sopenharmony_ci#include <asm/suspend.h> 328c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "iomap.h" 358c2ecf20Sopenharmony_ci#include "pm.h" 368c2ecf20Sopenharmony_ci#include "reset.h" 378c2ecf20Sopenharmony_ci#include "sleep.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 408c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(tegra_lp2_lock); 418c2ecf20Sopenharmony_cistatic u32 iram_save_size; 428c2ecf20Sopenharmony_cistatic void *iram_save_addr; 438c2ecf20Sopenharmony_cistruct tegra_lp1_iram tegra_lp1_iram; 448c2ecf20Sopenharmony_civoid (*tegra_tear_down_cpu)(void); 458c2ecf20Sopenharmony_civoid (*tegra_sleep_core_finish)(unsigned long v2p); 468c2ecf20Sopenharmony_cistatic int (*tegra_sleep_func)(unsigned long v2p); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void tegra_tear_down_cpu_init(void) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci switch (tegra_get_chip_id()) { 518c2ecf20Sopenharmony_ci case TEGRA20: 528c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) 538c2ecf20Sopenharmony_ci tegra_tear_down_cpu = tegra20_tear_down_cpu; 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci case TEGRA30: 568c2ecf20Sopenharmony_ci case TEGRA114: 578c2ecf20Sopenharmony_ci case TEGRA124: 588c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || 598c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || 608c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) 618c2ecf20Sopenharmony_ci tegra_tear_down_cpu = tegra30_tear_down_cpu; 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * restore_cpu_complex 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * restores cpu clock setting, clears flow controller 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Always called on CPU 0. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic void restore_cpu_complex(void) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci BUG_ON(cpu != 0); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 808c2ecf20Sopenharmony_ci cpu = cpu_logical_map(cpu); 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Restore the CPU clock settings */ 848c2ecf20Sopenharmony_ci tegra_cpu_clock_resume(); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci flowctrl_cpu_suspend_exit(cpu); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * suspend_cpu_complex 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * saves pll state for use by restart_plls, prepares flow controller for 938c2ecf20Sopenharmony_ci * transition to suspend state 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Must always be called on cpu 0. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic void suspend_cpu_complex(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int cpu = smp_processor_id(); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci BUG_ON(cpu != 0); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1048c2ecf20Sopenharmony_ci cpu = cpu_logical_map(cpu); 1058c2ecf20Sopenharmony_ci#endif 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Save the CPU clock settings */ 1088c2ecf20Sopenharmony_ci tegra_cpu_clock_suspend(); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci flowctrl_cpu_suspend_enter(cpu); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_civoid tegra_pm_clear_cpu_in_lp2(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int phy_cpu_id = cpu_logical_map(smp_processor_id()); 1168c2ecf20Sopenharmony_ci u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci spin_lock(&tegra_lp2_lock); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id))); 1218c2ecf20Sopenharmony_ci *cpu_in_lp2 &= ~BIT(phy_cpu_id); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_unlock(&tegra_lp2_lock); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_civoid tegra_pm_set_cpu_in_lp2(void) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int phy_cpu_id = cpu_logical_map(smp_processor_id()); 1298c2ecf20Sopenharmony_ci u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci spin_lock(&tegra_lp2_lock); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id))); 1348c2ecf20Sopenharmony_ci *cpu_in_lp2 |= BIT(phy_cpu_id); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_unlock(&tegra_lp2_lock); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int tegra_sleep_cpu(unsigned long v2p) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci if (tegra_cpu_car_ops->rail_off_ready && 1428c2ecf20Sopenharmony_ci WARN_ON(!tegra_cpu_rail_off_ready())) 1438c2ecf20Sopenharmony_ci return -EBUSY; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * L2 cache disabling using kernel API only allowed when all 1478c2ecf20Sopenharmony_ci * secondary CPU's are offline. Cache have to be disabled with 1488c2ecf20Sopenharmony_ci * MMU-on if cache maintenance is done via Trusted Foundations 1498c2ecf20Sopenharmony_ci * firmware. Note that CPUIDLE won't ever enter powergate on Tegra30 1508c2ecf20Sopenharmony_ci * if any of secondary CPU's is online and this is the LP2-idle 1518c2ecf20Sopenharmony_ci * code-path only for Tegra20/30. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci#ifdef CONFIG_OUTER_CACHE 1548c2ecf20Sopenharmony_ci if (trusted_foundations_registered() && outer_cache.disable) 1558c2ecf20Sopenharmony_ci outer_cache.disable(); 1568c2ecf20Sopenharmony_ci#endif 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Note that besides of setting up CPU reset vector this firmware 1598c2ecf20Sopenharmony_ci * call may also do the following, depending on the FW version: 1608c2ecf20Sopenharmony_ci * 1) Disable L2. But this doesn't matter since we already 1618c2ecf20Sopenharmony_ci * disabled the L2. 1628c2ecf20Sopenharmony_ci * 2) Disable D-cache. This need to be taken into account in 1638c2ecf20Sopenharmony_ci * particular by the tegra_disable_clean_inv_dcache() which 1648c2ecf20Sopenharmony_ci * shall avoid the re-disable. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci call_firmware_op(prepare_idle, TF_PM_MODE_LP2); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci setup_mm_for_reboot(); 1698c2ecf20Sopenharmony_ci tegra_sleep_cpu_finish(v2p); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* should never here */ 1728c2ecf20Sopenharmony_ci BUG(); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void tegra_pm_set(enum tegra_suspend_mode mode) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci u32 value; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci switch (tegra_get_chip_id()) { 1828c2ecf20Sopenharmony_ci case TEGRA20: 1838c2ecf20Sopenharmony_ci case TEGRA30: 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci default: 1868c2ecf20Sopenharmony_ci /* Turn off CRAIL */ 1878c2ecf20Sopenharmony_ci value = flowctrl_read_cpu_csr(0); 1888c2ecf20Sopenharmony_ci value &= ~FLOW_CTRL_CSR_ENABLE_EXT_MASK; 1898c2ecf20Sopenharmony_ci value |= FLOW_CTRL_CSR_ENABLE_EXT_CRAIL; 1908c2ecf20Sopenharmony_ci flowctrl_write_cpu_csr(0, value); 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci tegra_pmc_enter_suspend_mode(mode); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciint tegra_pm_enter_lp2(void) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int err; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci tegra_pm_set(TEGRA_SUSPEND_LP2); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci cpu_cluster_pm_enter(); 2048c2ecf20Sopenharmony_ci suspend_cpu_complex(); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci err = cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * Resume L2 cache if it wasn't re-enabled early during resume, 2108c2ecf20Sopenharmony_ci * which is the case for Tegra30 that has to re-enable the cache 2118c2ecf20Sopenharmony_ci * via firmware call. In other cases cache is already enabled and 2128c2ecf20Sopenharmony_ci * hence re-enabling is a no-op. This is always a no-op on Tegra114+. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci outer_resume(); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci restore_cpu_complex(); 2178c2ecf20Sopenharmony_ci cpu_cluster_pm_exit(); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci call_firmware_op(prepare_idle, TF_PM_MODE_NONE); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return err; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cienum tegra_suspend_mode tegra_pm_validate_suspend_mode( 2258c2ecf20Sopenharmony_ci enum tegra_suspend_mode mode) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * The Tegra devices support suspending to LP1 or lower currently. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci if (mode > TEGRA_SUSPEND_LP1) 2318c2ecf20Sopenharmony_ci return TEGRA_SUSPEND_LP1; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return mode; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int tegra_sleep_core(unsigned long v2p) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * Cache have to be disabled with MMU-on if cache maintenance is done 2408c2ecf20Sopenharmony_ci * via Trusted Foundations firmware. This is a no-op on Tegra114+. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci if (trusted_foundations_registered()) 2438c2ecf20Sopenharmony_ci outer_disable(); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci call_firmware_op(prepare_idle, TF_PM_MODE_LP1); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci setup_mm_for_reboot(); 2488c2ecf20Sopenharmony_ci tegra_sleep_core_finish(v2p); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* should never here */ 2518c2ecf20Sopenharmony_ci BUG(); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* 2578c2ecf20Sopenharmony_ci * tegra_lp1_iram_hook 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Hooking the address of LP1 reset vector and SDRAM self-refresh code in 2608c2ecf20Sopenharmony_ci * SDRAM. These codes not be copied to IRAM in this fuction. We need to 2618c2ecf20Sopenharmony_ci * copy these code to IRAM before LP0/LP1 suspend and restore the content 2628c2ecf20Sopenharmony_ci * of IRAM after resume. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_cistatic bool tegra_lp1_iram_hook(void) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci switch (tegra_get_chip_id()) { 2678c2ecf20Sopenharmony_ci case TEGRA20: 2688c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) 2698c2ecf20Sopenharmony_ci tegra20_lp1_iram_hook(); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case TEGRA30: 2728c2ecf20Sopenharmony_ci case TEGRA114: 2738c2ecf20Sopenharmony_ci case TEGRA124: 2748c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || 2758c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || 2768c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) 2778c2ecf20Sopenharmony_ci tegra30_lp1_iram_hook(); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci default: 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr) 2848c2ecf20Sopenharmony_ci return false; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci iram_save_size = tegra_lp1_iram.end_addr - tegra_lp1_iram.start_addr; 2878c2ecf20Sopenharmony_ci iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (!iram_save_addr) 2898c2ecf20Sopenharmony_ci return false; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return true; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic bool tegra_sleep_core_init(void) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci switch (tegra_get_chip_id()) { 2978c2ecf20Sopenharmony_ci case TEGRA20: 2988c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) 2998c2ecf20Sopenharmony_ci tegra20_sleep_core_init(); 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci case TEGRA30: 3028c2ecf20Sopenharmony_ci case TEGRA114: 3038c2ecf20Sopenharmony_ci case TEGRA124: 3048c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || 3058c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || 3068c2ecf20Sopenharmony_ci IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) 3078c2ecf20Sopenharmony_ci tegra30_sleep_core_init(); 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!tegra_sleep_core_finish) 3148c2ecf20Sopenharmony_ci return false; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return true; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void tegra_suspend_enter_lp1(void) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci /* copy the reset vector & SDRAM shutdown code into IRAM */ 3228c2ecf20Sopenharmony_ci memcpy(iram_save_addr, IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), 3238c2ecf20Sopenharmony_ci iram_save_size); 3248c2ecf20Sopenharmony_ci memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), 3258c2ecf20Sopenharmony_ci tegra_lp1_iram.start_addr, iram_save_size); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci *((u32 *)tegra_cpu_lp1_mask) = 1; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void tegra_suspend_exit_lp1(void) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci /* restore IRAM */ 3338c2ecf20Sopenharmony_ci memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), iram_save_addr, 3348c2ecf20Sopenharmony_ci iram_save_size); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci *(u32 *)tegra_cpu_lp1_mask = 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { 3408c2ecf20Sopenharmony_ci [TEGRA_SUSPEND_NONE] = "none", 3418c2ecf20Sopenharmony_ci [TEGRA_SUSPEND_LP2] = "LP2", 3428c2ecf20Sopenharmony_ci [TEGRA_SUSPEND_LP1] = "LP1", 3438c2ecf20Sopenharmony_ci [TEGRA_SUSPEND_LP0] = "LP0", 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int tegra_suspend_enter(suspend_state_t state) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (WARN_ON(mode < TEGRA_SUSPEND_NONE || 3518c2ecf20Sopenharmony_ci mode >= TEGRA_MAX_SUSPEND_MODE)) 3528c2ecf20Sopenharmony_ci return -EINVAL; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci pr_info("Entering suspend state %s\n", lp_state[mode]); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci tegra_pm_set(mode); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci local_fiq_disable(); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci suspend_cpu_complex(); 3618c2ecf20Sopenharmony_ci switch (mode) { 3628c2ecf20Sopenharmony_ci case TEGRA_SUSPEND_LP1: 3638c2ecf20Sopenharmony_ci tegra_suspend_enter_lp1(); 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci case TEGRA_SUSPEND_LP2: 3668c2ecf20Sopenharmony_ci tegra_pm_set_cpu_in_lp2(); 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci default: 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, tegra_sleep_func); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* 3758c2ecf20Sopenharmony_ci * Resume L2 cache if it wasn't re-enabled early during resume, 3768c2ecf20Sopenharmony_ci * which is the case for Tegra30 that has to re-enable the cache 3778c2ecf20Sopenharmony_ci * via firmware call. In other cases cache is already enabled and 3788c2ecf20Sopenharmony_ci * hence re-enabling is a no-op. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci outer_resume(); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci switch (mode) { 3838c2ecf20Sopenharmony_ci case TEGRA_SUSPEND_LP1: 3848c2ecf20Sopenharmony_ci tegra_suspend_exit_lp1(); 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case TEGRA_SUSPEND_LP2: 3878c2ecf20Sopenharmony_ci tegra_pm_clear_cpu_in_lp2(); 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci default: 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci restore_cpu_complex(); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci local_fiq_enable(); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci call_firmware_op(prepare_idle, TF_PM_MODE_NONE); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops tegra_suspend_ops = { 4028c2ecf20Sopenharmony_ci .valid = suspend_valid_only_mem, 4038c2ecf20Sopenharmony_ci .enter = tegra_suspend_enter, 4048c2ecf20Sopenharmony_ci}; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_civoid __init tegra_init_suspend(void) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (mode == TEGRA_SUSPEND_NONE) 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci tegra_tear_down_cpu_init(); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (mode >= TEGRA_SUSPEND_LP1) { 4168c2ecf20Sopenharmony_ci if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) { 4178c2ecf20Sopenharmony_ci pr_err("%s: unable to allocate memory for SDRAM" 4188c2ecf20Sopenharmony_ci "self-refresh -- LP0/LP1 unavailable\n", 4198c2ecf20Sopenharmony_ci __func__); 4208c2ecf20Sopenharmony_ci tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2); 4218c2ecf20Sopenharmony_ci mode = TEGRA_SUSPEND_LP2; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* set up sleep function for cpu_suspend */ 4268c2ecf20Sopenharmony_ci switch (mode) { 4278c2ecf20Sopenharmony_ci case TEGRA_SUSPEND_LP1: 4288c2ecf20Sopenharmony_ci tegra_sleep_func = tegra_sleep_core; 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci case TEGRA_SUSPEND_LP2: 4318c2ecf20Sopenharmony_ci tegra_sleep_func = tegra_sleep_cpu; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci suspend_set_ops(&tegra_suspend_ops); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ciint tegra_pm_park_secondary_cpu(unsigned long cpu) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci if (cpu > 0) { 4438c2ecf20Sopenharmony_ci tegra_disable_clean_inv_dcache(TEGRA_FLUSH_CACHE_LOUIS); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (tegra_get_chip_id() == TEGRA20) 4468c2ecf20Sopenharmony_ci tegra20_hotplug_shutdown(); 4478c2ecf20Sopenharmony_ci else 4488c2ecf20Sopenharmony_ci tegra30_hotplug_shutdown(); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return -EINVAL; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci#endif 454