1d457ef35SJoseph Lo /* 2d457ef35SJoseph Lo * CPU complex suspend & resume functions for Tegra SoCs 3d457ef35SJoseph Lo * 4d457ef35SJoseph Lo * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved. 5d457ef35SJoseph Lo * 6d457ef35SJoseph Lo * This program is free software; you can redistribute it and/or modify it 7d457ef35SJoseph Lo * under the terms and conditions of the GNU General Public License, 8d457ef35SJoseph Lo * version 2, as published by the Free Software Foundation. 9d457ef35SJoseph Lo * 10d457ef35SJoseph Lo * This program is distributed in the hope it will be useful, but WITHOUT 11d457ef35SJoseph Lo * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12d457ef35SJoseph Lo * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13d457ef35SJoseph Lo * more details. 14d457ef35SJoseph Lo * 15d457ef35SJoseph Lo * You should have received a copy of the GNU General Public License 16d457ef35SJoseph Lo * along with this program. If not, see <http://www.gnu.org/licenses/>. 17d457ef35SJoseph Lo */ 18d457ef35SJoseph Lo 19d457ef35SJoseph Lo #include <linux/kernel.h> 20d457ef35SJoseph Lo #include <linux/spinlock.h> 21d457ef35SJoseph Lo #include <linux/io.h> 22d457ef35SJoseph Lo #include <linux/cpumask.h> 23d552920aSJoseph Lo #include <linux/delay.h> 24d552920aSJoseph Lo #include <linux/cpu_pm.h> 25c8c2e606SJoseph Lo #include <linux/suspend.h> 26d552920aSJoseph Lo #include <linux/err.h> 2789572c77SPrashant Gaikwad #include <linux/clk/tegra.h> 28d552920aSJoseph Lo 29d552920aSJoseph Lo #include <asm/smp_plat.h> 30d552920aSJoseph Lo #include <asm/cacheflush.h> 31d552920aSJoseph Lo #include <asm/suspend.h> 32d552920aSJoseph Lo #include <asm/idmap.h> 33d552920aSJoseph Lo #include <asm/proc-fns.h> 34d552920aSJoseph Lo #include <asm/tlbflush.h> 35d457ef35SJoseph Lo 36d457ef35SJoseph Lo #include "iomap.h" 37d457ef35SJoseph Lo #include "reset.h" 38d552920aSJoseph Lo #include "flowctrl.h" 395c1350bdSJoseph Lo #include "fuse.h" 4095872f42SJoseph Lo #include "pm.h" 410337c3e0SJoseph Lo #include "pmc.h" 42d552920aSJoseph Lo #include "sleep.h" 43d552920aSJoseph Lo 44d457ef35SJoseph Lo #ifdef CONFIG_PM_SLEEP 45d457ef35SJoseph Lo static DEFINE_SPINLOCK(tegra_lp2_lock); 4695872f42SJoseph Lo static u32 iram_save_size; 4795872f42SJoseph Lo static void *iram_save_addr; 4895872f42SJoseph Lo struct tegra_lp1_iram tegra_lp1_iram; 49d552920aSJoseph Lo void (*tegra_tear_down_cpu)(void); 5095872f42SJoseph Lo void (*tegra_sleep_core_finish)(unsigned long v2p); 5195872f42SJoseph Lo static int (*tegra_sleep_func)(unsigned long v2p); 52d457ef35SJoseph Lo 53bf91add4SJoseph Lo static void tegra_tear_down_cpu_init(void) 54bf91add4SJoseph Lo { 55bf91add4SJoseph Lo switch (tegra_chip_id) { 56bf91add4SJoseph Lo case TEGRA20: 57bf91add4SJoseph Lo if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) 58bf91add4SJoseph Lo tegra_tear_down_cpu = tegra20_tear_down_cpu; 59bf91add4SJoseph Lo break; 60bf91add4SJoseph Lo case TEGRA30: 61b573ad9fSJoseph Lo case TEGRA114: 62b573ad9fSJoseph Lo if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || 63b573ad9fSJoseph Lo IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) 64bf91add4SJoseph Lo tegra_tear_down_cpu = tegra30_tear_down_cpu; 65bf91add4SJoseph Lo break; 66bf91add4SJoseph Lo } 67bf91add4SJoseph Lo } 68bf91add4SJoseph Lo 69d552920aSJoseph Lo /* 70d552920aSJoseph Lo * restore_cpu_complex 71d552920aSJoseph Lo * 72d552920aSJoseph Lo * restores cpu clock setting, clears flow controller 73d552920aSJoseph Lo * 74d552920aSJoseph Lo * Always called on CPU 0. 75d552920aSJoseph Lo */ 76d552920aSJoseph Lo static void restore_cpu_complex(void) 77d552920aSJoseph Lo { 78d552920aSJoseph Lo int cpu = smp_processor_id(); 79d552920aSJoseph Lo 80d552920aSJoseph Lo BUG_ON(cpu != 0); 81d552920aSJoseph Lo 82d552920aSJoseph Lo #ifdef CONFIG_SMP 83d552920aSJoseph Lo cpu = cpu_logical_map(cpu); 84d552920aSJoseph Lo #endif 85d552920aSJoseph Lo 86d552920aSJoseph Lo /* Restore the CPU clock settings */ 87d552920aSJoseph Lo tegra_cpu_clock_resume(); 88d552920aSJoseph Lo 89d552920aSJoseph Lo flowctrl_cpu_suspend_exit(cpu); 90d552920aSJoseph Lo } 91d552920aSJoseph Lo 92d552920aSJoseph Lo /* 93d552920aSJoseph Lo * suspend_cpu_complex 94d552920aSJoseph Lo * 95d552920aSJoseph Lo * saves pll state for use by restart_plls, prepares flow controller for 96d552920aSJoseph Lo * transition to suspend state 97d552920aSJoseph Lo * 98d552920aSJoseph Lo * Must always be called on cpu 0. 99d552920aSJoseph Lo */ 100d552920aSJoseph Lo static void suspend_cpu_complex(void) 101d552920aSJoseph Lo { 102d552920aSJoseph Lo int cpu = smp_processor_id(); 103d552920aSJoseph Lo 104d552920aSJoseph Lo BUG_ON(cpu != 0); 105d552920aSJoseph Lo 106d552920aSJoseph Lo #ifdef CONFIG_SMP 107d552920aSJoseph Lo cpu = cpu_logical_map(cpu); 108d552920aSJoseph Lo #endif 109d552920aSJoseph Lo 110d552920aSJoseph Lo /* Save the CPU clock settings */ 111d552920aSJoseph Lo tegra_cpu_clock_suspend(); 112d552920aSJoseph Lo 113d552920aSJoseph Lo flowctrl_cpu_suspend_enter(cpu); 114d552920aSJoseph Lo } 115d552920aSJoseph Lo 1168f6a0b65SJoseph Lo void tegra_clear_cpu_in_lp2(void) 117d457ef35SJoseph Lo { 1188f6a0b65SJoseph Lo int phy_cpu_id = cpu_logical_map(smp_processor_id()); 119d457ef35SJoseph Lo u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; 120d457ef35SJoseph Lo 121d457ef35SJoseph Lo spin_lock(&tegra_lp2_lock); 122d457ef35SJoseph Lo 123d457ef35SJoseph Lo BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id))); 124d457ef35SJoseph Lo *cpu_in_lp2 &= ~BIT(phy_cpu_id); 125d457ef35SJoseph Lo 126d457ef35SJoseph Lo spin_unlock(&tegra_lp2_lock); 127d457ef35SJoseph Lo } 128d457ef35SJoseph Lo 1298f6a0b65SJoseph Lo bool tegra_set_cpu_in_lp2(void) 130d457ef35SJoseph Lo { 1318f6a0b65SJoseph Lo int phy_cpu_id = cpu_logical_map(smp_processor_id()); 132d457ef35SJoseph Lo bool last_cpu = false; 133d457ef35SJoseph Lo cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask; 134d457ef35SJoseph Lo u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; 135d457ef35SJoseph Lo 136d457ef35SJoseph Lo spin_lock(&tegra_lp2_lock); 137d457ef35SJoseph Lo 138d457ef35SJoseph Lo BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id))); 139d457ef35SJoseph Lo *cpu_in_lp2 |= BIT(phy_cpu_id); 140d457ef35SJoseph Lo 141d457ef35SJoseph Lo if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) 142d457ef35SJoseph Lo last_cpu = true; 1435c1350bdSJoseph Lo else if (tegra_chip_id == TEGRA20 && phy_cpu_id == 1) 1445c1350bdSJoseph Lo tegra20_cpu_set_resettable_soon(); 145d457ef35SJoseph Lo 146d457ef35SJoseph Lo spin_unlock(&tegra_lp2_lock); 147d457ef35SJoseph Lo return last_cpu; 148d457ef35SJoseph Lo } 149d552920aSJoseph Lo 1502058842eSArnd Bergmann int tegra_cpu_do_idle(void) 1512058842eSArnd Bergmann { 1522058842eSArnd Bergmann return cpu_do_idle(); 1532058842eSArnd Bergmann } 1542058842eSArnd Bergmann 155d552920aSJoseph Lo static int tegra_sleep_cpu(unsigned long v2p) 156d552920aSJoseph Lo { 1576affb482SWill Deacon setup_mm_for_reboot(); 158d552920aSJoseph Lo tegra_sleep_cpu_finish(v2p); 159d552920aSJoseph Lo 160d552920aSJoseph Lo /* should never here */ 161d552920aSJoseph Lo BUG(); 162d552920aSJoseph Lo 163d552920aSJoseph Lo return 0; 164d552920aSJoseph Lo } 165d552920aSJoseph Lo 1664d82d058SJoseph Lo void tegra_idle_lp2_last(void) 167d552920aSJoseph Lo { 168c8c2e606SJoseph Lo tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); 169d552920aSJoseph Lo 170d552920aSJoseph Lo cpu_cluster_pm_enter(); 171d552920aSJoseph Lo suspend_cpu_complex(); 172d552920aSJoseph Lo 173d552920aSJoseph Lo cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); 174d552920aSJoseph Lo 175d552920aSJoseph Lo restore_cpu_complex(); 176d552920aSJoseph Lo cpu_cluster_pm_exit(); 177d552920aSJoseph Lo } 178c8c2e606SJoseph Lo 179c8c2e606SJoseph Lo enum tegra_suspend_mode tegra_pm_validate_suspend_mode( 180c8c2e606SJoseph Lo enum tegra_suspend_mode mode) 181c8c2e606SJoseph Lo { 182c8c2e606SJoseph Lo /* 18395872f42SJoseph Lo * The Tegra devices support suspending to LP1 or lower currently. 184c8c2e606SJoseph Lo */ 18595872f42SJoseph Lo if (mode > TEGRA_SUSPEND_LP1) 18695872f42SJoseph Lo return TEGRA_SUSPEND_LP1; 187c8c2e606SJoseph Lo 188c8c2e606SJoseph Lo return mode; 189c8c2e606SJoseph Lo } 190c8c2e606SJoseph Lo 19195872f42SJoseph Lo static int tegra_sleep_core(unsigned long v2p) 19295872f42SJoseph Lo { 19395872f42SJoseph Lo setup_mm_for_reboot(); 19495872f42SJoseph Lo tegra_sleep_core_finish(v2p); 19595872f42SJoseph Lo 19695872f42SJoseph Lo /* should never here */ 19795872f42SJoseph Lo BUG(); 19895872f42SJoseph Lo 19995872f42SJoseph Lo return 0; 20095872f42SJoseph Lo } 20195872f42SJoseph Lo 20295872f42SJoseph Lo /* 20395872f42SJoseph Lo * tegra_lp1_iram_hook 20495872f42SJoseph Lo * 20595872f42SJoseph Lo * Hooking the address of LP1 reset vector and SDRAM self-refresh code in 20695872f42SJoseph Lo * SDRAM. These codes not be copied to IRAM in this fuction. We need to 20795872f42SJoseph Lo * copy these code to IRAM before LP0/LP1 suspend and restore the content 20895872f42SJoseph Lo * of IRAM after resume. 20995872f42SJoseph Lo */ 21095872f42SJoseph Lo static bool tegra_lp1_iram_hook(void) 21195872f42SJoseph Lo { 212e7a932b1SJoseph Lo switch (tegra_chip_id) { 213731a9274SJoseph Lo case TEGRA20: 214731a9274SJoseph Lo if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) 215731a9274SJoseph Lo tegra20_lp1_iram_hook(); 216731a9274SJoseph Lo break; 217e7a932b1SJoseph Lo case TEGRA30: 218e9f62449SJoseph Lo case TEGRA114: 219e9f62449SJoseph Lo if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || 220e9f62449SJoseph Lo IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) 221e7a932b1SJoseph Lo tegra30_lp1_iram_hook(); 222e7a932b1SJoseph Lo break; 223e7a932b1SJoseph Lo default: 224e7a932b1SJoseph Lo break; 225e7a932b1SJoseph Lo } 226e7a932b1SJoseph Lo 22795872f42SJoseph Lo if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr) 22895872f42SJoseph Lo return false; 22995872f42SJoseph Lo 23095872f42SJoseph Lo iram_save_size = tegra_lp1_iram.end_addr - tegra_lp1_iram.start_addr; 23195872f42SJoseph Lo iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL); 23295872f42SJoseph Lo if (!iram_save_addr) 23395872f42SJoseph Lo return false; 23495872f42SJoseph Lo 23595872f42SJoseph Lo return true; 23695872f42SJoseph Lo } 23795872f42SJoseph Lo 23895872f42SJoseph Lo static bool tegra_sleep_core_init(void) 23995872f42SJoseph Lo { 240e7a932b1SJoseph Lo switch (tegra_chip_id) { 241731a9274SJoseph Lo case TEGRA20: 242731a9274SJoseph Lo if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) 243731a9274SJoseph Lo tegra20_sleep_core_init(); 244731a9274SJoseph Lo break; 245e7a932b1SJoseph Lo case TEGRA30: 246e9f62449SJoseph Lo case TEGRA114: 247e9f62449SJoseph Lo if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || 248e9f62449SJoseph Lo IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) 249e7a932b1SJoseph Lo tegra30_sleep_core_init(); 250e7a932b1SJoseph Lo break; 251e7a932b1SJoseph Lo default: 252e7a932b1SJoseph Lo break; 253e7a932b1SJoseph Lo } 254e7a932b1SJoseph Lo 25595872f42SJoseph Lo if (!tegra_sleep_core_finish) 25695872f42SJoseph Lo return false; 25795872f42SJoseph Lo 25895872f42SJoseph Lo return true; 25995872f42SJoseph Lo } 26095872f42SJoseph Lo 26195872f42SJoseph Lo static void tegra_suspend_enter_lp1(void) 26295872f42SJoseph Lo { 26395872f42SJoseph Lo tegra_pmc_suspend(); 26495872f42SJoseph Lo 26595872f42SJoseph Lo /* copy the reset vector & SDRAM shutdown code into IRAM */ 266*fddb770dSStephen Warren memcpy(iram_save_addr, IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), 26795872f42SJoseph Lo iram_save_size); 268*fddb770dSStephen Warren memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), 269*fddb770dSStephen Warren tegra_lp1_iram.start_addr, iram_save_size); 27095872f42SJoseph Lo 27195872f42SJoseph Lo *((u32 *)tegra_cpu_lp1_mask) = 1; 27295872f42SJoseph Lo } 27395872f42SJoseph Lo 27495872f42SJoseph Lo static void tegra_suspend_exit_lp1(void) 27595872f42SJoseph Lo { 27695872f42SJoseph Lo tegra_pmc_resume(); 27795872f42SJoseph Lo 27895872f42SJoseph Lo /* restore IRAM */ 279*fddb770dSStephen Warren memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), iram_save_addr, 28095872f42SJoseph Lo iram_save_size); 28195872f42SJoseph Lo 28295872f42SJoseph Lo *(u32 *)tegra_cpu_lp1_mask = 0; 28395872f42SJoseph Lo } 28495872f42SJoseph Lo 285c8c2e606SJoseph Lo static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { 286c8c2e606SJoseph Lo [TEGRA_SUSPEND_NONE] = "none", 287c8c2e606SJoseph Lo [TEGRA_SUSPEND_LP2] = "LP2", 288c8c2e606SJoseph Lo [TEGRA_SUSPEND_LP1] = "LP1", 289c8c2e606SJoseph Lo [TEGRA_SUSPEND_LP0] = "LP0", 290c8c2e606SJoseph Lo }; 291c8c2e606SJoseph Lo 2928bd26e3aSPaul Gortmaker static int tegra_suspend_enter(suspend_state_t state) 293c8c2e606SJoseph Lo { 294c8c2e606SJoseph Lo enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); 295c8c2e606SJoseph Lo 296c8c2e606SJoseph Lo if (WARN_ON(mode < TEGRA_SUSPEND_NONE || 297c8c2e606SJoseph Lo mode >= TEGRA_MAX_SUSPEND_MODE)) 298c8c2e606SJoseph Lo return -EINVAL; 299c8c2e606SJoseph Lo 300c8c2e606SJoseph Lo pr_info("Entering suspend state %s\n", lp_state[mode]); 301c8c2e606SJoseph Lo 302c8c2e606SJoseph Lo tegra_pmc_pm_set(mode); 303c8c2e606SJoseph Lo 304c8c2e606SJoseph Lo local_fiq_disable(); 305c8c2e606SJoseph Lo 306c8c2e606SJoseph Lo suspend_cpu_complex(); 307c8c2e606SJoseph Lo switch (mode) { 30895872f42SJoseph Lo case TEGRA_SUSPEND_LP1: 30995872f42SJoseph Lo tegra_suspend_enter_lp1(); 31095872f42SJoseph Lo break; 311c8c2e606SJoseph Lo case TEGRA_SUSPEND_LP2: 3128f6a0b65SJoseph Lo tegra_set_cpu_in_lp2(); 313c8c2e606SJoseph Lo break; 314c8c2e606SJoseph Lo default: 315c8c2e606SJoseph Lo break; 316c8c2e606SJoseph Lo } 317c8c2e606SJoseph Lo 31895872f42SJoseph Lo cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, tegra_sleep_func); 319c8c2e606SJoseph Lo 320c8c2e606SJoseph Lo switch (mode) { 32195872f42SJoseph Lo case TEGRA_SUSPEND_LP1: 32295872f42SJoseph Lo tegra_suspend_exit_lp1(); 32395872f42SJoseph Lo break; 324c8c2e606SJoseph Lo case TEGRA_SUSPEND_LP2: 3258f6a0b65SJoseph Lo tegra_clear_cpu_in_lp2(); 326c8c2e606SJoseph Lo break; 327c8c2e606SJoseph Lo default: 328c8c2e606SJoseph Lo break; 329c8c2e606SJoseph Lo } 330c8c2e606SJoseph Lo restore_cpu_complex(); 331c8c2e606SJoseph Lo 332c8c2e606SJoseph Lo local_fiq_enable(); 333c8c2e606SJoseph Lo 334c8c2e606SJoseph Lo return 0; 335c8c2e606SJoseph Lo } 336c8c2e606SJoseph Lo 337c8c2e606SJoseph Lo static const struct platform_suspend_ops tegra_suspend_ops = { 338c8c2e606SJoseph Lo .valid = suspend_valid_only_mem, 339c8c2e606SJoseph Lo .enter = tegra_suspend_enter, 340c8c2e606SJoseph Lo }; 341c8c2e606SJoseph Lo 342c8c2e606SJoseph Lo void __init tegra_init_suspend(void) 343c8c2e606SJoseph Lo { 34495872f42SJoseph Lo enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); 34595872f42SJoseph Lo 34695872f42SJoseph Lo if (mode == TEGRA_SUSPEND_NONE) 347c8c2e606SJoseph Lo return; 348c8c2e606SJoseph Lo 349bf91add4SJoseph Lo tegra_tear_down_cpu_init(); 350c8c2e606SJoseph Lo tegra_pmc_suspend_init(); 351c8c2e606SJoseph Lo 35295872f42SJoseph Lo if (mode >= TEGRA_SUSPEND_LP1) { 35395872f42SJoseph Lo if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) { 35495872f42SJoseph Lo pr_err("%s: unable to allocate memory for SDRAM" 35595872f42SJoseph Lo "self-refresh -- LP0/LP1 unavailable\n", 35695872f42SJoseph Lo __func__); 35795872f42SJoseph Lo tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2); 35895872f42SJoseph Lo mode = TEGRA_SUSPEND_LP2; 35995872f42SJoseph Lo } 36095872f42SJoseph Lo } 36195872f42SJoseph Lo 36295872f42SJoseph Lo /* set up sleep function for cpu_suspend */ 36395872f42SJoseph Lo switch (mode) { 36495872f42SJoseph Lo case TEGRA_SUSPEND_LP1: 36595872f42SJoseph Lo tegra_sleep_func = tegra_sleep_core; 36695872f42SJoseph Lo break; 36795872f42SJoseph Lo case TEGRA_SUSPEND_LP2: 36895872f42SJoseph Lo tegra_sleep_func = tegra_sleep_cpu; 36995872f42SJoseph Lo break; 37095872f42SJoseph Lo default: 37195872f42SJoseph Lo break; 37295872f42SJoseph Lo } 37395872f42SJoseph Lo 374c8c2e606SJoseph Lo suspend_set_ops(&tegra_suspend_ops); 375c8c2e606SJoseph Lo } 376d457ef35SJoseph Lo #endif 377