.. | .. |
---|
| 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 |
---|