Merge pull request #793 from jeenu-arm/gic-changes

GIC driver changes for extended power management
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index ac43372..5abaa1c 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.c
@@ -40,6 +40,13 @@
 static const gicv3_driver_data_t *driver_data;
 static unsigned int gicv2_compat;
 
+/*
+ * Redistributor power operations are weakly bound so that they can be
+ * overridden
+ */
+#pragma weak gicv3_rdistif_off
+#pragma weak gicv3_rdistif_on
+
 /*******************************************************************************
  * This function initialises the ARM GICv3 driver in EL3 with provided platform
  * inputs.
@@ -188,6 +195,9 @@
 
 	assert(IS_IN_EL3());
 
+	/* Power on redistributor */
+	gicv3_rdistif_on(proc_num);
+
 	gicr_base = driver_data->rdistif_base_addrs[proc_num];
 
 	/* Set the default attribute of all SGIs and PPIs */
@@ -211,6 +221,19 @@
 }
 
 /*******************************************************************************
+ * Functions to perform power operations on GIC Redistributor
+ ******************************************************************************/
+void gicv3_rdistif_off(unsigned int proc_num)
+{
+	return;
+}
+
+void gicv3_rdistif_on(unsigned int proc_num)
+{
+	return;
+}
+
+/*******************************************************************************
  * This function enables the GIC CPU interface of the calling CPU using only
  * system register accesses.
  ******************************************************************************/
diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h
index b7ad778..0f6034c 100644
--- a/include/drivers/arm/gicv3.h
+++ b/include/drivers/arm/gicv3.h
@@ -259,6 +259,8 @@
 void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data);
 void gicv3_distif_init(void);
 void gicv3_rdistif_init(unsigned int proc_num);
+void gicv3_rdistif_on(unsigned int proc_num);
+void gicv3_rdistif_off(unsigned int proc_num);
 void gicv3_cpuif_enable(unsigned int proc_num);
 void gicv3_cpuif_disable(unsigned int proc_num);
 unsigned int gicv3_get_pending_interrupt_type(void);
diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h
index bc32e40..c167aa2 100644
--- a/include/plat/arm/common/plat_arm.h
+++ b/include/plat/arm/common/plat_arm.h
@@ -194,6 +194,8 @@
 void plat_arm_gic_init(void);
 void plat_arm_gic_cpuif_enable(void);
 void plat_arm_gic_cpuif_disable(void);
+void plat_arm_gic_redistif_on(void);
+void plat_arm_gic_redistif_off(void);
 void plat_arm_gic_pcpu_init(void);
 void plat_arm_security_setup(void);
 void plat_arm_pwrc_setup(void);
diff --git a/plat/arm/board/fvp/fvp_pm.c b/plat/arm/board/fvp/fvp_pm.c
index 139f713..fde476a 100644
--- a/plat/arm/board/fvp/fvp_pm.c
+++ b/plat/arm/board/fvp/fvp_pm.c
@@ -66,19 +66,6 @@
 
 /*******************************************************************************
  * Function which implements the common FVP specific operations to power down a
- * cpu in response to a CPU_OFF or CPU_SUSPEND request.
- ******************************************************************************/
-static void fvp_cpu_pwrdwn_common(void)
-{
-	/* Prevent interrupts from spuriously waking up this cpu */
-	plat_arm_gic_cpuif_disable();
-
-	/* Program the power controller to power off this cpu. */
-	fvp_pwrc_write_ppoffr(read_mpidr_el1());
-}
-
-/*******************************************************************************
- * Function which implements the common FVP specific operations to power down a
  * cluster in response to a CPU_OFF or CPU_SUSPEND request.
  ******************************************************************************/
 static void fvp_cluster_pwrdwn_common(void)
@@ -180,7 +167,15 @@
 	 * suspended. Perform at least the cpu specific actions followed
 	 * by the cluster specific operations if applicable.
 	 */
-	fvp_cpu_pwrdwn_common();
+
+	/* Prevent interrupts from spuriously waking up this cpu */
+	plat_arm_gic_cpuif_disable();
+
+	/* Turn redistributor off */
+	plat_arm_gic_redistif_off();
+
+	/* Program the power controller to power off this cpu. */
+	fvp_pwrc_write_ppoffr(read_mpidr_el1());
 
 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
 					ARM_LOCAL_STATE_OFF)
@@ -213,8 +208,17 @@
 	/* Program the power controller to enable wakeup interrupts. */
 	fvp_pwrc_set_wen(mpidr);
 
+	/* Prevent interrupts from spuriously waking up this cpu */
+	plat_arm_gic_cpuif_disable();
+
+	/*
+	 * The Redistributor is not powered off as it can potentially prevent
+	 * wake up events reaching the CPUIF and/or might lead to losing
+	 * register context.
+	 */
+
-	/* Perform the common cpu specific operations */
-	fvp_cpu_pwrdwn_common();
+	/* Program the power controller to power off this cpu. */
+	fvp_pwrc_write_ppoffr(read_mpidr_el1());
 
 	/* Perform the common cluster specific operations */
 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
diff --git a/plat/arm/common/arm_gicv2.c b/plat/arm/common/arm_gicv2.c
index 2636d1c..1017c9e 100644
--- a/plat/arm/common/arm_gicv2.c
+++ b/plat/arm/common/arm_gicv2.c
@@ -97,3 +97,17 @@
 {
 	gicv2_pcpu_distif_init();
 }
+
+/******************************************************************************
+ * Stubs for Redistributor power management. Although GICv2 doesn't have
+ * Redistributor interface, these are provided for the sake of uniform GIC API
+ *****************************************************************************/
+void plat_arm_gic_redistif_on(void)
+{
+	return;
+}
+
+void plat_arm_gic_redistif_off(void)
+{
+	return;
+}
diff --git a/plat/arm/common/arm_gicv3.c b/plat/arm/common/arm_gicv3.c
index ac309f2..6d68bfb 100644
--- a/plat/arm/common/arm_gicv3.c
+++ b/plat/arm/common/arm_gicv3.c
@@ -43,6 +43,8 @@
 #pragma weak plat_arm_gic_cpuif_enable
 #pragma weak plat_arm_gic_cpuif_disable
 #pragma weak plat_arm_gic_pcpu_init
+#pragma weak plat_arm_gic_redistif_on
+#pragma weak plat_arm_gic_redistif_off
 
 /* The GICv3 driver only needs to be initialized in EL3 */
 static uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT];
@@ -115,3 +117,16 @@
 {
 	gicv3_rdistif_init(plat_my_core_pos());
 }
+
+/******************************************************************************
+ * ARM common helpers to power GIC redistributor interface
+ *****************************************************************************/
+void plat_arm_gic_redistif_on(void)
+{
+	gicv3_rdistif_on(plat_my_core_pos());
+}
+
+void plat_arm_gic_redistif_off(void)
+{
+	gicv3_rdistif_off(plat_my_core_pos());
+}
diff --git a/plat/arm/common/arm_gicv3_legacy.c b/plat/arm/common/arm_gicv3_legacy.c
index 8396b60..cbf11fb 100644
--- a/plat/arm/common/arm_gicv3_legacy.c
+++ b/plat/arm/common/arm_gicv3_legacy.c
@@ -94,3 +94,17 @@
 {
 	arm_gic_pcpu_distif_setup();
 }
+
+/******************************************************************************
+ * Stubs for Redistributor power management. Although legacy configuration isn't
+ * supported, these are provided for the sake of uniform GIC API
+ *****************************************************************************/
+void plat_arm_gic_redistif_on(void)
+{
+	return;
+}
+
+void plat_arm_gic_redistif_off(void)
+{
+	return;
+}