hikey960: Fix race condition between hotplug and idles

From the hotplug testing on Hikey960, in some case cores fail to become
online in the system. When some cores are hotplugged off, if other
cores in the same cluster enter into CPU idle states at the meantime,
the cluster will be powered off. This introduces the state machine
malfunction in the power controller, thus when hotplug on the core
afterwards, it fails to boot up the core because the power controller
thinks the cluster is powered on.

This patch is to avoid race condition between hotplug and idles by
preventing cluster power off when some of cores in the cluster are
hotplugged off, if all cores in the same cluster are hotplugged off,
the cluster can be powered off.

Change-Id: Ib2feeb093357c70409a3536cb4f9da9b504fdcbe
Signed-off-by: Wei Yu <yuwei3@hisilicon.com>
Signed-off-by: Leo Yan <leo.yan@linaro.org>
diff --git a/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c
index 659a1c4..bcf6865 100644
--- a/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c
+++ b/plat/hisilicon/hikey960/drivers/pwrc/hisi_pwrc.c
@@ -147,13 +147,19 @@
 
 }
 
-int hisi_test_ap_suspend_flag(unsigned int cluster)
+int hisi_test_ap_suspend_flag(void)
 {
-	unsigned int val;
+	unsigned int val1;
+	unsigned int val2;
+
+	val1 = mmio_read_32(CPUIDLE_FLAG_REG(0));
+	val1 &= AP_SUSPEND_FLAG;
 
-	val = mmio_read_32(CPUIDLE_FLAG_REG(cluster));
-	val &= AP_SUSPEND_FLAG;
-	return !!val;
+	val2 = mmio_read_32(CPUIDLE_FLAG_REG(1));
+	val2 &= AP_SUSPEND_FLAG;
+
+	val1 |= val2;
+	return (val1 != 0);
 }
 
 void hisi_set_cluster_pwdn_flag(unsigned int cluster,
@@ -164,7 +170,8 @@
 	hisi_cpuhotplug_lock(cluster, core);
 
 	val = mmio_read_32(REG_SCBAKDATA3_OFFSET);
-	val = (value << (cluster << 1)) | (val & 0xFFFFFFF);
+	val &= ~(0x3U << ((2 * cluster) + 28));
+	val |= (value << (2 * cluster));
 	mmio_write_32(REG_SCBAKDATA3_OFFSET, val);
 
 	hisi_cpuhotplug_unlock(cluster, core);
@@ -258,6 +265,17 @@
 	return val;
 }
 
+static int check_hotplug(unsigned int cluster, unsigned int boot_flag)
+{
+	unsigned int mask = 0xF;
+
+	if (hisi_test_ap_suspend_flag() ||
+	    ((boot_flag & mask) == mask))
+		return 0;
+
+	return 1;
+}
+
 int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core)
 {
 	unsigned int mask = 0xf << (core * 4);
@@ -268,7 +286,8 @@
 	mask = (PDC_COREPWRSTAT_MASK & (~mask));
 	pdc_stat &= mask;
 
-	if ((boot_flag ^ cpuidle_flag) || pdc_stat)
+	if ((boot_flag ^ cpuidle_flag) || pdc_stat ||
+	    check_hotplug(cluster, boot_flag))
 		return 0;
 	else
 		return 1;