cpu_pm: Add level to the cluster pm notification

Cluster pm notifications without level information increases difficulty
and complexity for the registered drivers to figure out when the last
coherency level is going into power collapse.

Send notifications with level information that allows the registered
drivers to easily determine the cluster level that is going in/out of
power collapse.

There is an issue with this implementation. GIC driver saves and
restores the distributed registers as part of cluster notifications. On
newer platforms there are multiple cluster levels are defined (e.g l2,
cci etc). These cluster level notofications can happen independently.
On MSM platforms GIC is still active while the cluster sleeps in idle,
causing the GIC state to be overwritten with an incorrect
previous state
of the interrupts. This leads to a system hang. Do not save and restore
on any L2 and higher cache coherency level sleep entry and exit.

Change-Id: I31918d6383f19e80fe3b064cfaf0b55e16b97eb6
Signed-off-by: Archana Sathyakumar <asathyak@codeaurora.org>
Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org>
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
Signed-off-by: Stepan Moskovchenko <stepanm@codeaurora.org>
diff --git a/arch/arm/mach-highbank/pm.c b/arch/arm/mach-highbank/pm.c
index 4003116..165e44e 100644
--- a/arch/arm/mach-highbank/pm.c
+++ b/arch/arm/mach-highbank/pm.c
@@ -36,11 +36,11 @@
 static int highbank_pm_enter(suspend_state_t state)
 {
 	cpu_pm_enter();
-	cpu_cluster_pm_enter();
+	cpu_cluster_pm_enter(0);
 
 	cpu_suspend(0, highbank_suspend_finish);
 
-	cpu_cluster_pm_exit();
+	cpu_cluster_pm_exit(0);
 	cpu_pm_exit();
 
 	return 0;
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
index fa138d4..c11ecd9 100644
--- a/arch/arm/mach-omap2/cpuidle44xx.c
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -127,7 +127,7 @@
 		 * to save GIC and wakeupgen context.
 		 */
 		if (mpuss_can_lose_context)
-			cpu_cluster_pm_enter();
+			cpu_cluster_pm_enter(0);
 	}
 
 	omap4_enter_lowpower(dev->cpu, cx->cpu_state);
@@ -165,7 +165,7 @@
 	 * to restore GIC and wakeupgen context.
 	 */
 	if (dev->cpu == 0 && mpuss_can_lose_context)
-		cpu_cluster_pm_exit();
+		cpu_cluster_pm_exit(0);
 
 	tick_broadcast_exit();
 
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index b0f48a3..9eabddc 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -192,13 +192,13 @@
 {
 	tegra_pm_set(TEGRA_SUSPEND_LP2);
 
-	cpu_cluster_pm_enter();
+	cpu_cluster_pm_enter(0);
 	suspend_cpu_complex();
 
 	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
 
 	restore_cpu_complex();
-	cpu_cluster_pm_exit();
+	cpu_cluster_pm_exit(0);
 }
 
 enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index d6c404b..bc6d121 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -710,7 +710,8 @@
 	gic_cpu_if_up(gic);
 }
 
-static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,
+			void *aff_level)
 {
 	int i;
 
@@ -729,11 +730,20 @@
 			gic_cpu_restore(&gic_data[i]);
 			break;
 		case CPU_CLUSTER_PM_ENTER:
-			gic_dist_save(&gic_data[i]);
+			/*
+			 * Affinity level of the node
+			 * eg:
+			 *    cpu level = 0
+			 *    l2 level  = 1
+			 *    cci level = 2
+			 */
+			if (!(unsigned long)aff_level)
+				gic_dist_save(&gic_data[i]);
 			break;
 		case CPU_CLUSTER_PM_ENTER_FAILED:
 		case CPU_CLUSTER_PM_EXIT:
-			gic_dist_restore(&gic_data[i]);
+			if (!(unsigned long)aff_level)
+				gic_dist_restore(&gic_data[i]);
 			break;
 		}
 	}
diff --git a/include/linux/cpu_pm.h b/include/linux/cpu_pm.h
index 455b233..91117bc 100644
--- a/include/linux/cpu_pm.h
+++ b/include/linux/cpu_pm.h
@@ -71,8 +71,8 @@
 int cpu_pm_unregister_notifier(struct notifier_block *nb);
 int cpu_pm_enter(void);
 int cpu_pm_exit(void);
-int cpu_cluster_pm_enter(void);
-int cpu_cluster_pm_exit(void);
+int cpu_cluster_pm_enter(unsigned long aff_level);
+int cpu_cluster_pm_exit(unsigned long aff_level);
 
 #else
 
@@ -96,12 +96,12 @@
 	return 0;
 }
 
-static inline int cpu_cluster_pm_enter(void)
+static inline int cpu_cluster_pm_enter(unsigned long aff_level)
 {
 	return 0;
 }
 
-static inline int cpu_cluster_pm_exit(void)
+static inline int cpu_cluster_pm_exit(unsigned long aff_level)
 {
 	return 0;
 }
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
index 009cc9a..6c3523f 100644
--- a/kernel/cpu_pm.c
+++ b/kernel/cpu_pm.c
@@ -25,11 +25,12 @@
 static DEFINE_RWLOCK(cpu_pm_notifier_lock);
 static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
 
-static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
+static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls,
+		void *data)
 {
 	int ret;
 
-	ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
+	ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, data,
 		nr_to_call, nr_calls);
 
 	return notifier_to_errno(ret);
@@ -101,13 +102,13 @@
 	int ret = 0;
 
 	read_lock(&cpu_pm_notifier_lock);
-	ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
+	ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls, NULL);
 	if (ret)
 		/*
 		 * Inform listeners (nr_calls - 1) about failure of CPU PM
 		 * PM entry who are notified earlier to prepare for it.
 		 */
-		cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
+		cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL, NULL);
 	read_unlock(&cpu_pm_notifier_lock);
 
 	return ret;
@@ -131,7 +132,7 @@
 	int ret;
 
 	read_lock(&cpu_pm_notifier_lock);
-	ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
+	ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL, NULL);
 	read_unlock(&cpu_pm_notifier_lock);
 
 	return ret;
@@ -154,19 +155,21 @@
  *
  * Return conditions are same as __raw_notifier_call_chain.
  */
-int cpu_cluster_pm_enter(void)
+int cpu_cluster_pm_enter(unsigned long aff_level)
 {
 	int nr_calls;
 	int ret = 0;
 
 	read_lock(&cpu_pm_notifier_lock);
-	ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
+	ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls,
+			(void *) aff_level);
 	if (ret)
 		/*
 		 * Inform listeners (nr_calls - 1) about failure of CPU cluster
 		 * PM entry who are notified earlier to prepare for it.
 		 */
-		cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
+		cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL,
+				(void *) aff_level);
 	read_unlock(&cpu_pm_notifier_lock);
 
 	return ret;
@@ -188,12 +191,12 @@
  *
  * Return conditions are same as __raw_notifier_call_chain.
  */
-int cpu_cluster_pm_exit(void)
+int cpu_cluster_pm_exit(unsigned long aff_level)
 {
 	int ret;
 
 	read_lock(&cpu_pm_notifier_lock);
-	ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
+	ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL, (void *) aff_level);
 	read_unlock(&cpu_pm_notifier_lock);
 
 	return ret;
@@ -209,13 +212,13 @@
 	if (ret)
 		return ret;
 
-	ret = cpu_cluster_pm_enter();
+	ret = cpu_cluster_pm_enter(0);
 	return ret;
 }
 
 static void cpu_pm_resume(void)
 {
-	cpu_cluster_pm_exit();
+	cpu_cluster_pm_exit(0);
 	cpu_pm_exit();
 }