msm: Ensure droop detectors do not affect krait PLLs

The Droop detector mechanism present in Glitch-Free (GFMUX)
goes to nondeterministic state during frequent
standalone power collapse operation. It affects
Krait PLL output so that the PLL won't lock to
required frequency.

Since Droop detector isn't used, set sc_pdn (PLL_CLAMP)
permanently high to ensure droop detector is off and it
does not affect Krait PLL.
CRs-Fixed: 381144

Conflicts:

	arch/arm/mach-msm/Kconfig
	arch/arm/mach-msm/Makefile

Change-Id: I69a0dffeccaa3837302d6e3dd53f6b62fa683368
Signed-off-by: Jay Chokshi <jchokshi@codeaurora.org>
Signed-off-by: Neha Pandey <nehap@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index a161180..82468c8 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -177,6 +177,7 @@
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
 	select MSM_IOMMU_GPU_SYNC
+	select MSM_CPU_PWRCTL
 
 config ARCH_MSM8930
 	bool "MSM8930"
@@ -210,6 +211,7 @@
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
 	select MSM_IOMMU_GPU_SYNC
+	select MSM_CPU_PWRCTL
 
 config ARCH_APQ8064
 	bool "APQ8064"
@@ -238,6 +240,7 @@
 	select ARM_HAS_SG_CHAIN
 	select MSM_KRAIT_WFE_FIXUP
 	select MSM_IOMMU_GPU_SYNC
+	select MSM_CPU_PWRCTL
 
 config ARCH_MSM8974
 	bool "MSM8974"
@@ -2593,4 +2596,11 @@
 	  can be read and written to send character data to the sysmon port of
 	  the modem over USB.
 
+config MSM_CPU_PWRCTL
+	bool "Ensures that krait droop detectors are always off"
+	help
+	  Droop detector mechanism can adversely affect krait plls during
+	  stand alone power collapse operation. Selecting this option
+	  ensures that they are always off.
+
 endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 492e305..e496a9e 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -389,3 +389,4 @@
 ifdef CONFIG_MSM_CPR
 obj-$(CONFIG_DEBUG_FS) += msm_cpr-debug.o
 endif
+obj-$(CONFIG_MSM_CPU_PWRCTL) +=  msm_cpu_pwrctl.o
diff --git a/arch/arm/mach-msm/msm_cpu_pwrctl.c b/arch/arm/mach-msm/msm_cpu_pwrctl.c
new file mode 100644
index 0000000..6e339dd
--- /dev/null
+++ b/arch/arm/mach-msm/msm_cpu_pwrctl.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 2012, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/compiler.h>
+#include <linux/notifier.h>
+#include <linux/percpu.h>
+#include <linux/workqueue.h>
+
+#define MSM_CPU_SECONDARY_CORE_OFFSET	0x10000
+
+static const phys_addr_t primary_cpu_pwrctl_phys = 0x2088004;
+static DEFINE_PER_CPU(int, pll_clamp_set);
+static void msm_cpu_pwrctl_work_cb(struct work_struct *work);
+static __cpuinitdata DECLARE_WORK(msm_cpu_pwrctl_work, msm_cpu_pwrctl_work_cb);
+static int nr_cpus_done;
+static int __cpuinit msm_cpu_pwrctl_cpu_callback(struct notifier_block *nfb,
+				    unsigned long action, void *hcpu);
+static struct notifier_block __cpuinitdata msm_cpu_pwrctl_cpu_notifier = {
+	.notifier_call = msm_cpu_pwrctl_cpu_callback,
+};
+
+static void __cpuinit msm_cpu_pwrctl_work_cb(struct work_struct *work)
+{
+	unregister_hotcpu_notifier(&msm_cpu_pwrctl_cpu_notifier);
+}
+
+static int __cpuinit msm_cpu_pwrctl_cpu_callback(struct notifier_block *nfb,
+				    unsigned long action, void *hcpu)
+{
+	int cpu = (int) hcpu;
+	int *pll_clamp;
+	void *pwrctl_ptr;
+	unsigned int value;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_ONLINE:
+		pll_clamp = &per_cpu(pll_clamp_set, cpu);
+		if (likely(*pll_clamp))
+			goto done;
+
+		pwrctl_ptr = ioremap_nocache(primary_cpu_pwrctl_phys +
+			(cpu * MSM_CPU_SECONDARY_CORE_OFFSET), SZ_4K);
+		if (unlikely(!pwrctl_ptr))
+			goto done;
+
+		value = readl_relaxed(pwrctl_ptr);
+		value |= 0x100;
+		writel_relaxed(value, pwrctl_ptr);
+		*pll_clamp = 1;
+		iounmap(pwrctl_ptr);
+
+		if (++nr_cpus_done == cpumask_weight(cpu_possible_mask))
+			schedule_work(&msm_cpu_pwrctl_work);
+done:
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+
+}
+
+static int __init msm_cpu_pwrctl_init(void)
+{
+	int cpu = smp_processor_id();
+
+	/* We won't get cpu online notification for this CPU,
+	 * so take this opportunity to process this CPU.
+	 */
+	msm_cpu_pwrctl_cpu_callback(&msm_cpu_pwrctl_cpu_notifier,
+					CPU_ONLINE, (void *) cpu);
+
+	register_hotcpu_notifier(&msm_cpu_pwrctl_cpu_notifier);
+	return 0;
+}
+
+early_initcall(msm_cpu_pwrctl_init);