ti: k3: common: Enable ARM cluster power down

When all cores in a cluster are powered down the parent cluster can
be also powered down. When the last core has requested powering down
follow by sending the cluster power down sequence to the system
power controller firmware.

Signed-off-by: Andrew F. Davis <afd@ti.com>
Change-Id: I0ffeb339852c66ef62743aecd3e17ca20bad6216
diff --git a/plat/ti/k3/common/k3_psci.c b/plat/ti/k3/common/k3_psci.c
index 15bdc3e..cf0a21d 100644
--- a/plat/ti/k3/common/k3_psci.c
+++ b/plat/ti/k3/common/k3_psci.c
@@ -17,6 +17,10 @@
 #include <k3_gicv3.h>
 #include <ti_sci.h>
 
+#define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0])
+#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1])
+#define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
+
 uintptr_t k3_sec_entrypoint;
 
 static void k3_cpu_standby(plat_local_state_t cpu_state)
@@ -60,6 +64,16 @@
 		return PSCI_E_INTERN_FAIL;
 	}
 
+	/* sanity check these are off before starting a core */
+	ret = ti_sci_proc_set_boot_ctrl(proc_id,
+			0, PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ |
+			   PROC_BOOT_CTRL_FLAG_ARMV8_AINACTS |
+			   PROC_BOOT_CTRL_FLAG_ARMV8_ACINACTM);
+	if (ret) {
+		ERROR("Request to clear boot configuration failed: %d\n", ret);
+		return PSCI_E_INTERN_FAIL;
+	}
+
 	ret = ti_sci_device_get(device_id);
 	if (ret) {
 		ERROR("Request to start core failed: %d\n", ret);
@@ -71,14 +85,32 @@
 
 void k3_pwr_domain_off(const psci_power_state_t *target_state)
 {
-	int core, proc_id, device_id, ret;
+	int core, cluster, proc_id, device_id, cluster_id, ret;
+
+	/* At very least the local core should be powering down */
+	assert(CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
 
 	/* Prevent interrupts from spuriously waking up this cpu */
 	k3_gic_cpuif_disable();
 
 	core = plat_my_core_pos();
+	cluster = MPIDR_AFFLVL1_VAL(read_mpidr_el1());
 	proc_id = PLAT_PROC_START_ID + core;
 	device_id = PLAT_PROC_DEVICE_START_ID + core;
+	cluster_id = PLAT_CLUSTER_DEVICE_START_ID + (cluster * 2);
+
+	/*
+	 * If we are the last core in the cluster then we take a reference to
+	 * the cluster device so that it does not get shutdown before we
+	 * execute the entire cluster L2 cleaning sequence below.
+	 */
+	if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+		ret = ti_sci_device_get(cluster_id);
+		if (ret) {
+			ERROR("Request to get cluster failed: %d\n", ret);
+			return;
+		}
+	}
 
 	/* Start by sending wait for WFI command */
 	ret = ti_sci_proc_wait_boot_status_no_wait(proc_id,
@@ -100,6 +132,67 @@
 		ERROR("Sending core shutdown message failed (%d)\n", ret);
 		return;
 	}
+
+	/* If our cluster is not going down we stop here */
+	if (CLUSTER_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
+		return;
+
+	/* set AINACTS */
+	ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id,
+			PROC_BOOT_CTRL_FLAG_ARMV8_AINACTS, 0);
+	if (ret) {
+		ERROR("Sending set control message failed (%d)\n", ret);
+		return;
+	}
+
+	/* set L2FLUSHREQ */
+	ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id,
+			PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ, 0);
+	if (ret) {
+		ERROR("Sending set control message failed (%d)\n", ret);
+		return;
+	}
+
+	/* wait for L2FLUSHDONE*/
+	ret = ti_sci_proc_wait_boot_status_no_wait(proc_id,
+			UINT8_MAX, 2, UINT8_MAX, UINT8_MAX,
+			PROC_BOOT_STATUS_FLAG_ARMV8_L2F_DONE, 0, 0, 0);
+	if (ret) {
+		ERROR("Sending wait message failed (%d)\n", ret);
+		return;
+	}
+
+	/* clear L2FLUSHREQ */
+	ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id,
+			0, PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ);
+	if (ret) {
+		ERROR("Sending set control message failed (%d)\n", ret);
+		return;
+	}
+
+	/* set ACINACTM */
+	ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id,
+			PROC_BOOT_CTRL_FLAG_ARMV8_ACINACTM, 0);
+	if (ret) {
+		ERROR("Sending set control message failed (%d)\n", ret);
+		return;
+	}
+
+	/* wait for STANDBYWFIL2 */
+	ret = ti_sci_proc_wait_boot_status_no_wait(proc_id,
+			UINT8_MAX, 2, UINT8_MAX, UINT8_MAX,
+			PROC_BOOT_STATUS_FLAG_ARMV8_STANDBYWFIL2, 0, 0, 0);
+	if (ret) {
+		ERROR("Sending wait message failed (%d)\n", ret);
+		return;
+	}
+
+	/* Now queue up the cluster shutdown request */
+	ret = ti_sci_device_put_no_wait(cluster_id);
+	if (ret) {
+		ERROR("Sending cluster shutdown message failed (%d)\n", ret);
+		return;
+	}
 }
 
 void k3_pwr_domain_on_finish(const psci_power_state_t *target_state)