msm: mpm: Prevent XO/Vdd min on pending non-monitored interrupts

At certain low power modes, when GIC and GPIO interrupt controllers are
not functional, the MPM driver programs the MPM HW to monitor the
interrupts. If any of the enabled interrupts cannot be monitored by MPM,
then disable low power modes in which the non-monitored interrupt events
could be lost.

Change-Id: I818e7f2c8db9b8fea60ffecdd4b003db020cabca
Signed-off-by: Mahesh Sivasubramanian <msivasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/lpm_levels.c b/arch/arm/mach-msm/lpm_levels.c
index fcb4299..b0b17ea 100644
--- a/arch/arm/mach-msm/lpm_levels.c
+++ b/arch/arm/mach-msm/lpm_levels.c
@@ -176,6 +176,18 @@
 	}
 	return best->latency_us - 1;
 }
+static bool msm_lpm_irqs_detectable(struct msm_rpmrs_limits *limits,
+		bool irqs_detectable, bool gpio_detectable)
+{
+	if (!limits->irqs_detectable)
+		return irqs_detectable;
+
+	if (!limits->gpio_detectable)
+		return gpio_detectable;
+
+	return true;
+
+}
 
 static void *msm_lpm_lowest_limits(bool from_idle,
 		enum msm_pm_sleep_mode sleep_mode,
@@ -186,11 +198,19 @@
 	uint32_t pwr;
 	int i;
 	int best_level_iter = msm_lpm_level_count + 1;
+	bool irqs_detect = false;
+	bool gpio_detect = false;
+
 	if (!msm_lpm_levels)
 		return NULL;
 
 	msm_lpm_level_update();
 
+	if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
+		irqs_detect = msm_mpm_irqs_detectable(from_idle);
+		gpio_detect = msm_mpm_gpio_irqs_detectable(from_idle);
+	}
+
 	for (i = 0; i < msm_lpm_level_count; i++) {
 		struct msm_rpmrs_level *level = &msm_lpm_levels[i];
 
@@ -203,6 +223,11 @@
 		if (time_param->latency_us < level->latency_us)
 			continue;
 
+		if ((sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) &&
+			!msm_lpm_irqs_detectable(&level->rs_limits,
+				irqs_detect, gpio_detect))
+				continue;
+
 		if ((MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE == sleep_mode)
 			|| (MSM_PM_SLEEP_MODE_POWER_COLLAPSE == sleep_mode))
 			if (!cpu && msm_rpm_waiting_for_ack())
diff --git a/arch/arm/mach-msm/mpm-of.c b/arch/arm/mach-msm/mpm-of.c
index 8f78735..09f784d 100644
--- a/arch/arm/mach-msm/mpm-of.c
+++ b/arch/arm/mach-msm/mpm-of.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -51,11 +51,14 @@
 	unsigned long pin;
 	struct hlist_node node;
 };
+#define MAX_DOMAIN_NAME 5
 
 struct mpm_irqs {
 	struct irq_domain *domain;
 	unsigned long *enabled_irqs;
 	unsigned long *wakeup_irqs;
+	unsigned long size;
+	char domain_name[MAX_DOMAIN_NAME];
 };
 
 static struct mpm_irqs unlisted_irqs[MSM_MPM_NR_IRQ_DOMAINS];
@@ -437,28 +440,53 @@
 	return 0;
 }
 
-bool msm_mpm_irqs_detectable(bool from_idle)
+static bool msm_mpm_interrupts_detectable(int d, bool from_idle)
 {
-	/* TODO:
-	 * Return true if unlisted irqs is empty
-	 */
+	unsigned long *irq_bitmap;
+	bool debug_mask, ret = false;
+	struct mpm_irqs *unlisted = &unlisted_irqs[d];
 
 	if (!msm_mpm_is_initialized())
 		return false;
 
-	return true;
+	if (from_idle) {
+		irq_bitmap = unlisted->enabled_irqs;
+		debug_mask = msm_mpm_debug_mask &
+				MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE;
+	} else {
+		irq_bitmap = unlisted->wakeup_irqs;
+		debug_mask = msm_mpm_debug_mask &
+				MSM_MPM_DEBUG_NON_DETECTABLE_IRQ;
+	}
+
+	ret = (bool) __bitmap_empty(irq_bitmap, unlisted->size);
+
+	if (debug_mask && !ret) {
+		int i = 0;
+		i = find_first_bit(irq_bitmap, unlisted->size);
+		pr_info("%s(): %s preventing system sleep modes during %s\n",
+				__func__, unlisted->domain_name,
+				from_idle ? "idle" : "suspend");
+
+		while (i < unlisted->size) {
+			pr_info("\thwirq: %d\n", i);
+			i = find_next_bit(irq_bitmap, unlisted->size, i + 1);
+		}
+	}
+
+	return ret;
 }
 
 bool msm_mpm_gpio_irqs_detectable(bool from_idle)
 {
-	/* TODO:
-	 * Return true if unlisted irqs is empty
-	 */
-	if (!msm_mpm_is_initialized())
-		return false;
-	return true;
+	return msm_mpm_interrupts_detectable(MSM_MPM_GPIO_IRQ_DOMAIN,
+			from_idle);
 }
-
+bool msm_mpm_irqs_detectable(bool from_idle)
+{
+	return msm_mpm_interrupts_detectable(MSM_MPM_GIC_IRQ_DOMAIN,
+			from_idle);
+}
 void msm_mpm_enter_sleep(uint32_t sclk_count, bool from_idle)
 {
 	cycle_t wakeup = (u64)sclk_count * ARCH_TIMER_HZ;
@@ -614,6 +642,7 @@
 	struct mpm_of {
 		char *pkey;
 		char *map;
+		char name[MAX_DOMAIN_NAME];
 		struct irq_chip *chip;
 		int (*get_max_irqs)(struct irq_domain *d);
 	};
@@ -623,12 +652,14 @@
 		{
 			"qcom,gic-parent",
 			"qcom,gic-map",
+			"gic",
 			&gic_arch_extn,
 			mpm_irq_domain_linear_size,
 		},
 		{
 			"qcom,gpio-parent",
 			"qcom,gpio-map",
+			"gpio",
 			&msm_gpio_irq_extn,
 			mpm_irq_domain_legacy_size,
 		},
@@ -665,6 +696,9 @@
 		}
 
 		size = mpm_of_map[i].get_max_irqs(domain);
+		unlisted_irqs[i].size = size;
+		memcpy(unlisted_irqs[i].domain_name, mpm_of_map[i].name,
+				MAX_DOMAIN_NAME);
 
 		unlisted_irqs[i].enabled_irqs =
 			kzalloc(BITS_TO_LONGS(size) * sizeof(unsigned long),