diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra124/psci.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra124/psci.c | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra124/psci.c b/arch/arm/mach-tegra/tegra124/psci.c new file mode 100644 index 0000000..16d1965 --- /dev/null +++ b/arch/arm/mach-tegra/tegra124/psci.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2015, Siemens AG + * Author: Jan Kiszka <jan.kiszka@siemens.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/psci.h> +#include <asm/arch/flow.h> +#include <asm/arch/powergate.h> +#include <asm/arch-tegra/ap.h> +#include <asm/arch-tegra/pmc.h> + +static void park_cpu(void) +{ + while (1) + asm volatile("wfi"); +} + +/** + * Initialize power management for application processors + */ +void psci_board_init(void) +{ + struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; + + writel((u32)park_cpu, EXCEP_VECTOR_CPU_RESET_VECTOR); + + /* + * The naturally expected order of putting these CPUs under Flow + * Controller regime would be + * - configure the Flow Controller + * - power up the CPUs + * - wait for the CPUs to hit wfi and be powered down again + * + * However, this doesn't work in practice. We rather need to power them + * up first and park them in wfi. While they are waiting there, we can + * indeed program the Flow Controller to powergate them on wfi, which + * will then happen immediately as they are already in that state. + */ + tegra_powergate_power_on(TEGRA_POWERGATE_CPU1); + tegra_powergate_power_on(TEGRA_POWERGATE_CPU2); + tegra_powergate_power_on(TEGRA_POWERGATE_CPU3); + + writel((2 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu1_csr); + writel((4 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu2_csr); + writel((8 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu3_csr); + + writel(EVENT_MODE_STOP, &flow->halt_cpu1_events); + writel(EVENT_MODE_STOP, &flow->halt_cpu2_events); + writel(EVENT_MODE_STOP, &flow->halt_cpu3_events); + + while (!(readl(&flow->cpu1_csr) & CSR_PWR_OFF_STS) || + !(readl(&flow->cpu2_csr) & CSR_PWR_OFF_STS) || + !(readl(&flow->cpu3_csr) & CSR_PWR_OFF_STS)) + /* wait */; +} |