| /* |
| * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| |
| #include <arch_helpers.h> |
| #include <common/debug.h> |
| #include <lib/mmio.h> |
| |
| #include <pmc.h> |
| #include <tegra_def.h> |
| |
| #define RESET_ENABLE 0x10U |
| |
| /* Module IDs used during power ungate procedure */ |
| static const uint32_t pmc_cpu_powergate_id[4] = { |
| 14, /* CPU 0 */ |
| 9, /* CPU 1 */ |
| 10, /* CPU 2 */ |
| 11 /* CPU 3 */ |
| }; |
| |
| /******************************************************************************* |
| * Power ungate CPU to start the boot process. CPU reset vectors must be |
| * populated before calling this function. |
| ******************************************************************************/ |
| void tegra_pmc_cpu_on(int32_t cpu) |
| { |
| uint32_t val; |
| |
| /* |
| * Check if CPU is already power ungated |
| */ |
| val = tegra_pmc_read_32(PMC_PWRGATE_STATUS); |
| if ((val & (1U << pmc_cpu_powergate_id[cpu])) == 0U) { |
| /* |
| * The PMC deasserts the START bit when it starts the power |
| * ungate process. Loop till no power toggle is in progress. |
| */ |
| do { |
| val = tegra_pmc_read_32(PMC_PWRGATE_TOGGLE); |
| } while ((val & PMC_TOGGLE_START) != 0U); |
| |
| /* |
| * Start the power ungate procedure |
| */ |
| val = pmc_cpu_powergate_id[cpu] | PMC_TOGGLE_START; |
| tegra_pmc_write_32(PMC_PWRGATE_TOGGLE, val); |
| |
| /* |
| * The PMC deasserts the START bit when it starts the power |
| * ungate process. Loop till powergate START bit is asserted. |
| */ |
| do { |
| val = tegra_pmc_read_32(PMC_PWRGATE_TOGGLE); |
| } while ((val & (1U << 8)) != 0U); |
| |
| /* loop till the CPU is power ungated */ |
| do { |
| val = tegra_pmc_read_32(PMC_PWRGATE_STATUS); |
| } while ((val & (1U << pmc_cpu_powergate_id[cpu])) == 0U); |
| } |
| } |
| |
| /******************************************************************************* |
| * Setup CPU vectors for resume from deep sleep |
| ******************************************************************************/ |
| void tegra_pmc_cpu_setup(uint64_t reset_addr) |
| { |
| uint32_t val; |
| |
| tegra_pmc_write_32(PMC_SECURE_SCRATCH34, |
| ((uint32_t)reset_addr & 0xFFFFFFFFU) | 1U); |
| val = (uint32_t)(reset_addr >> 32U); |
| tegra_pmc_write_32(PMC_SECURE_SCRATCH35, val & 0x7FFU); |
| } |
| |
| /******************************************************************************* |
| * Lock CPU vectors to restrict further writes |
| ******************************************************************************/ |
| void tegra_pmc_lock_cpu_vectors(void) |
| { |
| uint32_t val; |
| |
| /* lock PMC_SECURE_SCRATCH22 */ |
| val = tegra_pmc_read_32(PMC_SECURE_DISABLE2); |
| val |= PMC_SECURE_DISABLE2_WRITE22_ON; |
| tegra_pmc_write_32(PMC_SECURE_DISABLE2, val); |
| |
| /* lock PMC_SECURE_SCRATCH34/35 */ |
| val = tegra_pmc_read_32(PMC_SECURE_DISABLE3); |
| val |= (PMC_SECURE_DISABLE3_WRITE34_ON | |
| PMC_SECURE_DISABLE3_WRITE35_ON); |
| tegra_pmc_write_32(PMC_SECURE_DISABLE3, val); |
| } |
| |
| /******************************************************************************* |
| * Find out if this is the last standing CPU |
| ******************************************************************************/ |
| bool tegra_pmc_is_last_on_cpu(void) |
| { |
| int i, cpu = read_mpidr() & MPIDR_CPU_MASK; |
| uint32_t val = tegra_pmc_read_32(PMC_PWRGATE_STATUS);; |
| bool status = true; |
| |
| /* check if this is the last standing CPU */ |
| for (i = 0; i < PLATFORM_MAX_CPUS_PER_CLUSTER; i++) { |
| |
| /* skip the current CPU */ |
| if (i == cpu) |
| continue; |
| |
| /* are other CPUs already power gated? */ |
| if ((val & ((uint32_t)1 << pmc_cpu_powergate_id[i])) != 0U) { |
| status = false; |
| } |
| } |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| * Handler to be called on exiting System suspend. Right now only DPD registers |
| * are cleared. |
| ******************************************************************************/ |
| void tegra_pmc_resume(void) |
| { |
| |
| /* Clear DPD sample */ |
| mmio_write_32((TEGRA_PMC_BASE + PMC_IO_DPD_SAMPLE), 0x0); |
| |
| /* Clear DPD Enable */ |
| mmio_write_32((TEGRA_PMC_BASE + PMC_DPD_ENABLE_0), 0x0); |
| } |
| |
| /******************************************************************************* |
| * Restart the system |
| ******************************************************************************/ |
| __dead2 void tegra_pmc_system_reset(void) |
| { |
| uint32_t reg; |
| |
| reg = tegra_pmc_read_32(PMC_CONFIG); |
| reg |= RESET_ENABLE; /* restart */ |
| tegra_pmc_write_32(PMC_CONFIG, reg); |
| wfi(); |
| |
| ERROR("Tegra System Reset: operation not handled.\n"); |
| panic(); |
| } |