tegra124: Add PSCI support for Tegra124

This is based on Thierry Reding's work and uses Ian Campell's
preparatory patches. It comes with full support for CPU_ON/OFF PSCI
services. The algorithm used in this version for turning CPUs on and
off was proposed by Peter De Schrijver and Thierry Reding in
http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/210881. It
consists of first enabling CPU1..3 via the PMC, just to powergate them
again with the help of the Flow Controller. Once the Flow Controller is
in place, we can leave the PMC alone while processing CPU_ON and CPU_OFF
PSCI requests.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Tom Warren <twarren@nvidia.com>
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 */;
+}