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/board/generic/include/board_def.h b/plat/ti/k3/board/generic/include/board_def.h
index c1a5966..0d45116 100644
--- a/plat/ti/k3/board/generic/include/board_def.h
+++ b/plat/ti/k3/board/generic/include/board_def.h
@@ -27,5 +27,6 @@
 
 #define PLAT_PROC_START_ID		32
 #define PLAT_PROC_DEVICE_START_ID	202
+#define PLAT_CLUSTER_DEVICE_START_ID	198
 
 #endif /* BOARD_DEF_H */
diff --git a/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h b/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h
index a921e51..2d23f9a 100644
--- a/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h
+++ b/plat/ti/k3/common/drivers/ti_sci/ti_sci_protocol.h
@@ -563,8 +563,13 @@
 	uint32_t config_flags_clear;
 } __packed;
 
+/* ARMV8 Control Flags */
+#define PROC_BOOT_CTRL_FLAG_ARMV8_ACINACTM      0x00000001
+#define PROC_BOOT_CTRL_FLAG_ARMV8_AINACTS       0x00000002
+#define PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ    0x00000100
+
 /* R5 Control Flags */
-#define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT                0x00000001
+#define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT        0x00000001
 
 /**
  * struct ti_sci_msg_req_set_proc_boot_ctrl - Set Processor boot control flags
@@ -618,6 +623,8 @@
 /* ARMv8 Status Flags */
 #define PROC_BOOT_STATUS_FLAG_ARMV8_WFE			0x00000001
 #define PROC_BOOT_STATUS_FLAG_ARMV8_WFI			0x00000002
+#define PROC_BOOT_STATUS_FLAG_ARMV8_L2F_DONE		0x00000010
+#define PROC_BOOT_STATUS_FLAG_ARMV8_STANDBYWFIL2	0x00000020
 
 /* R5 Status Flags */
 #define PROC_BOOT_STATUS_FLAG_R5_WFE			0x00000001
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)