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