| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * CPU complex suspend & resume functions for Tegra SoCs |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 7 | | - * under the terms and conditions of the GNU General Public License, |
|---|
| 8 | | - * version 2, as published by the Free Software Foundation. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope it will be useful, but WITHOUT |
|---|
| 11 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 12 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 13 | | - * more details. |
|---|
| 14 | | - * |
|---|
| 15 | | - * You should have received a copy of the GNU General Public License |
|---|
| 16 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | 6 | */ |
|---|
| 18 | 7 | |
|---|
| 19 | 8 | #include <linux/clk/tegra.h> |
|---|
| .. | .. |
|---|
| 27 | 16 | #include <linux/spinlock.h> |
|---|
| 28 | 17 | #include <linux/suspend.h> |
|---|
| 29 | 18 | |
|---|
| 19 | +#include <linux/firmware/trusted_foundations.h> |
|---|
| 20 | + |
|---|
| 30 | 21 | #include <soc/tegra/flowctrl.h> |
|---|
| 31 | 22 | #include <soc/tegra/fuse.h> |
|---|
| 32 | 23 | #include <soc/tegra/pm.h> |
|---|
| 33 | 24 | #include <soc/tegra/pmc.h> |
|---|
| 34 | 25 | |
|---|
| 35 | 26 | #include <asm/cacheflush.h> |
|---|
| 27 | +#include <asm/firmware.h> |
|---|
| 36 | 28 | #include <asm/idmap.h> |
|---|
| 37 | 29 | #include <asm/proc-fns.h> |
|---|
| 38 | 30 | #include <asm/smp_plat.h> |
|---|
| .. | .. |
|---|
| 118 | 110 | flowctrl_cpu_suspend_enter(cpu); |
|---|
| 119 | 111 | } |
|---|
| 120 | 112 | |
|---|
| 121 | | -void tegra_clear_cpu_in_lp2(void) |
|---|
| 113 | +void tegra_pm_clear_cpu_in_lp2(void) |
|---|
| 122 | 114 | { |
|---|
| 123 | 115 | int phy_cpu_id = cpu_logical_map(smp_processor_id()); |
|---|
| 124 | 116 | u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; |
|---|
| .. | .. |
|---|
| 131 | 123 | spin_unlock(&tegra_lp2_lock); |
|---|
| 132 | 124 | } |
|---|
| 133 | 125 | |
|---|
| 134 | | -bool tegra_set_cpu_in_lp2(void) |
|---|
| 126 | +void tegra_pm_set_cpu_in_lp2(void) |
|---|
| 135 | 127 | { |
|---|
| 136 | 128 | int phy_cpu_id = cpu_logical_map(smp_processor_id()); |
|---|
| 137 | | - bool last_cpu = false; |
|---|
| 138 | | - cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask; |
|---|
| 139 | 129 | u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; |
|---|
| 140 | 130 | |
|---|
| 141 | 131 | spin_lock(&tegra_lp2_lock); |
|---|
| .. | .. |
|---|
| 143 | 133 | BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id))); |
|---|
| 144 | 134 | *cpu_in_lp2 |= BIT(phy_cpu_id); |
|---|
| 145 | 135 | |
|---|
| 146 | | - if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) |
|---|
| 147 | | - last_cpu = true; |
|---|
| 148 | | - else if (tegra_get_chip_id() == TEGRA20 && phy_cpu_id == 1) |
|---|
| 149 | | - tegra20_cpu_set_resettable_soon(); |
|---|
| 150 | | - |
|---|
| 151 | 136 | spin_unlock(&tegra_lp2_lock); |
|---|
| 152 | | - return last_cpu; |
|---|
| 153 | | -} |
|---|
| 154 | | - |
|---|
| 155 | | -int tegra_cpu_do_idle(void) |
|---|
| 156 | | -{ |
|---|
| 157 | | - return cpu_do_idle(); |
|---|
| 158 | 137 | } |
|---|
| 159 | 138 | |
|---|
| 160 | 139 | static int tegra_sleep_cpu(unsigned long v2p) |
|---|
| 161 | 140 | { |
|---|
| 141 | + if (tegra_cpu_car_ops->rail_off_ready && |
|---|
| 142 | + WARN_ON(!tegra_cpu_rail_off_ready())) |
|---|
| 143 | + return -EBUSY; |
|---|
| 144 | + |
|---|
| 145 | + /* |
|---|
| 146 | + * L2 cache disabling using kernel API only allowed when all |
|---|
| 147 | + * secondary CPU's are offline. Cache have to be disabled with |
|---|
| 148 | + * MMU-on if cache maintenance is done via Trusted Foundations |
|---|
| 149 | + * firmware. Note that CPUIDLE won't ever enter powergate on Tegra30 |
|---|
| 150 | + * if any of secondary CPU's is online and this is the LP2-idle |
|---|
| 151 | + * code-path only for Tegra20/30. |
|---|
| 152 | + */ |
|---|
| 153 | +#ifdef CONFIG_OUTER_CACHE |
|---|
| 154 | + if (trusted_foundations_registered() && outer_cache.disable) |
|---|
| 155 | + outer_cache.disable(); |
|---|
| 156 | +#endif |
|---|
| 157 | + /* |
|---|
| 158 | + * Note that besides of setting up CPU reset vector this firmware |
|---|
| 159 | + * call may also do the following, depending on the FW version: |
|---|
| 160 | + * 1) Disable L2. But this doesn't matter since we already |
|---|
| 161 | + * disabled the L2. |
|---|
| 162 | + * 2) Disable D-cache. This need to be taken into account in |
|---|
| 163 | + * particular by the tegra_disable_clean_inv_dcache() which |
|---|
| 164 | + * shall avoid the re-disable. |
|---|
| 165 | + */ |
|---|
| 166 | + call_firmware_op(prepare_idle, TF_PM_MODE_LP2); |
|---|
| 167 | + |
|---|
| 162 | 168 | setup_mm_for_reboot(); |
|---|
| 163 | 169 | tegra_sleep_cpu_finish(v2p); |
|---|
| 164 | 170 | |
|---|
| .. | .. |
|---|
| 188 | 194 | tegra_pmc_enter_suspend_mode(mode); |
|---|
| 189 | 195 | } |
|---|
| 190 | 196 | |
|---|
| 191 | | -void tegra_idle_lp2_last(void) |
|---|
| 197 | +int tegra_pm_enter_lp2(void) |
|---|
| 192 | 198 | { |
|---|
| 199 | + int err; |
|---|
| 200 | + |
|---|
| 193 | 201 | tegra_pm_set(TEGRA_SUSPEND_LP2); |
|---|
| 194 | 202 | |
|---|
| 195 | 203 | cpu_cluster_pm_enter(); |
|---|
| 196 | 204 | suspend_cpu_complex(); |
|---|
| 197 | 205 | |
|---|
| 198 | | - cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); |
|---|
| 206 | + err = cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); |
|---|
| 207 | + |
|---|
| 208 | + /* |
|---|
| 209 | + * Resume L2 cache if it wasn't re-enabled early during resume, |
|---|
| 210 | + * which is the case for Tegra30 that has to re-enable the cache |
|---|
| 211 | + * via firmware call. In other cases cache is already enabled and |
|---|
| 212 | + * hence re-enabling is a no-op. This is always a no-op on Tegra114+. |
|---|
| 213 | + */ |
|---|
| 214 | + outer_resume(); |
|---|
| 199 | 215 | |
|---|
| 200 | 216 | restore_cpu_complex(); |
|---|
| 201 | 217 | cpu_cluster_pm_exit(); |
|---|
| 218 | + |
|---|
| 219 | + call_firmware_op(prepare_idle, TF_PM_MODE_NONE); |
|---|
| 220 | + |
|---|
| 221 | + return err; |
|---|
| 202 | 222 | } |
|---|
| 203 | 223 | |
|---|
| 204 | 224 | enum tegra_suspend_mode tegra_pm_validate_suspend_mode( |
|---|
| .. | .. |
|---|
| 215 | 235 | |
|---|
| 216 | 236 | static int tegra_sleep_core(unsigned long v2p) |
|---|
| 217 | 237 | { |
|---|
| 238 | + /* |
|---|
| 239 | + * Cache have to be disabled with MMU-on if cache maintenance is done |
|---|
| 240 | + * via Trusted Foundations firmware. This is a no-op on Tegra114+. |
|---|
| 241 | + */ |
|---|
| 242 | + if (trusted_foundations_registered()) |
|---|
| 243 | + outer_disable(); |
|---|
| 244 | + |
|---|
| 245 | + call_firmware_op(prepare_idle, TF_PM_MODE_LP1); |
|---|
| 246 | + |
|---|
| 218 | 247 | setup_mm_for_reboot(); |
|---|
| 219 | 248 | tegra_sleep_core_finish(v2p); |
|---|
| 220 | 249 | |
|---|
| .. | .. |
|---|
| 334 | 363 | tegra_suspend_enter_lp1(); |
|---|
| 335 | 364 | break; |
|---|
| 336 | 365 | case TEGRA_SUSPEND_LP2: |
|---|
| 337 | | - tegra_set_cpu_in_lp2(); |
|---|
| 366 | + tegra_pm_set_cpu_in_lp2(); |
|---|
| 338 | 367 | break; |
|---|
| 339 | 368 | default: |
|---|
| 340 | 369 | break; |
|---|
| .. | .. |
|---|
| 342 | 371 | |
|---|
| 343 | 372 | cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, tegra_sleep_func); |
|---|
| 344 | 373 | |
|---|
| 374 | + /* |
|---|
| 375 | + * Resume L2 cache if it wasn't re-enabled early during resume, |
|---|
| 376 | + * which is the case for Tegra30 that has to re-enable the cache |
|---|
| 377 | + * via firmware call. In other cases cache is already enabled and |
|---|
| 378 | + * hence re-enabling is a no-op. |
|---|
| 379 | + */ |
|---|
| 380 | + outer_resume(); |
|---|
| 381 | + |
|---|
| 345 | 382 | switch (mode) { |
|---|
| 346 | 383 | case TEGRA_SUSPEND_LP1: |
|---|
| 347 | 384 | tegra_suspend_exit_lp1(); |
|---|
| 348 | 385 | break; |
|---|
| 349 | 386 | case TEGRA_SUSPEND_LP2: |
|---|
| 350 | | - tegra_clear_cpu_in_lp2(); |
|---|
| 387 | + tegra_pm_clear_cpu_in_lp2(); |
|---|
| 351 | 388 | break; |
|---|
| 352 | 389 | default: |
|---|
| 353 | 390 | break; |
|---|
| .. | .. |
|---|
| 355 | 392 | restore_cpu_complex(); |
|---|
| 356 | 393 | |
|---|
| 357 | 394 | local_fiq_enable(); |
|---|
| 395 | + |
|---|
| 396 | + call_firmware_op(prepare_idle, TF_PM_MODE_NONE); |
|---|
| 358 | 397 | |
|---|
| 359 | 398 | return 0; |
|---|
| 360 | 399 | } |
|---|
| .. | .. |
|---|
| 397 | 436 | |
|---|
| 398 | 437 | suspend_set_ops(&tegra_suspend_ops); |
|---|
| 399 | 438 | } |
|---|
| 439 | + |
|---|
| 440 | +int tegra_pm_park_secondary_cpu(unsigned long cpu) |
|---|
| 441 | +{ |
|---|
| 442 | + if (cpu > 0) { |
|---|
| 443 | + tegra_disable_clean_inv_dcache(TEGRA_FLUSH_CACHE_LOUIS); |
|---|
| 444 | + |
|---|
| 445 | + if (tegra_get_chip_id() == TEGRA20) |
|---|
| 446 | + tegra20_hotplug_shutdown(); |
|---|
| 447 | + else |
|---|
| 448 | + tegra30_hotplug_shutdown(); |
|---|
| 449 | + } |
|---|
| 450 | + |
|---|
| 451 | + return -EINVAL; |
|---|
| 452 | +} |
|---|
| 400 | 453 | #endif |
|---|