msm: dcvs: remove idle notification registration.

Register the idle enable callback along with the core. The code
becomes cleaner and easy to update.

Importantly, the msm_dcvs_idle driver becomes useless. Remove it
and instead let the msm governor handle idle enabling and disabling.

Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
(cherry picked from commit c1ed66c9035b4fbf240e46837d86a9a6442531f1)

Signed-off-by: Ram Kumar Chakravarthy Chebathini <rcheba@codeaurora.org>
(cherry picked from commit b4f5c2274fa2180b53563f2db0922eef212c0fcd)

Change-Id: Ice039e608d45bdeb9b8b718e5fbbf82a698d584d
Signed-off-by: Sudhir Sharma <sudsha@codeaurora.org>
diff --git a/drivers/cpufreq/cpufreq_gov_msm.c b/drivers/cpufreq/cpufreq_gov_msm.c
index 9ad1811..2b68d2a 100644
--- a/drivers/cpufreq/cpufreq_gov_msm.c
+++ b/drivers/cpufreq/cpufreq_gov_msm.c
@@ -18,8 +18,124 @@
 #include <linux/kobject.h>
 #include <linux/cpufreq.h>
 #include <linux/platform_device.h>
+#include <linux/cpu_pm.h>
+#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
 #include <mach/msm_dcvs.h>
 
+struct cpu_idle_info {
+	int cpu;
+	int enabled;
+	int handle;
+	struct msm_dcvs_idle dcvs_notifier;
+	struct pm_qos_request pm_qos_req;
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
+static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
+static char core_name[NR_CPUS][10];
+static uint32_t latency;
+
+static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self,
+		enum msm_core_control_event event)
+{
+	struct cpu_idle_info *info = container_of(self,
+				struct cpu_idle_info, dcvs_notifier);
+
+	switch (event) {
+	case MSM_DCVS_ENABLE_IDLE_PULSE:
+		info->enabled = true;
+		break;
+
+	case MSM_DCVS_DISABLE_IDLE_PULSE:
+		info->enabled = false;
+		break;
+
+	case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
+		pm_qos_update_request(&info->pm_qos_req, PM_QOS_DEFAULT_VALUE);
+		break;
+
+	case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
+		pm_qos_update_request(&info->pm_qos_req, latency);
+		break;
+	}
+
+	return 0;
+}
+
+static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
+		void *v)
+{
+	struct cpu_idle_info *info =
+		&per_cpu(cpu_idle_info, smp_processor_id());
+	u64 io_wait_us = 0;
+	u64 prev_io_wait_us = 0;
+	u64 last_update_time = 0;
+	u64 val = 0;
+	uint32_t iowaited = 0;
+
+	if (!info->enabled)
+		return NOTIFY_OK;
+
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		val = get_cpu_iowait_time_us(smp_processor_id(),
+					&last_update_time);
+		/* val could be -1 when NOHZ is not enabled */
+		if (val == (u64)-1)
+			val = 0;
+		per_cpu(iowait_on_cpu, smp_processor_id()) = val;
+		msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0);
+		break;
+
+	case CPU_PM_EXIT:
+		prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
+		val = get_cpu_iowait_time_us(smp_processor_id(),
+				&last_update_time);
+		if (val == (u64)-1)
+			val = 0;
+		io_wait_us = val;
+		iowaited = (io_wait_us - prev_io_wait_us);
+		msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block idle_nb = {
+	.notifier_call = msm_cpuidle_notifier,
+};
+
+static void msm_gov_idle_source_init(int cpu)
+{
+	struct cpu_idle_info *info = NULL;
+	struct msm_dcvs_idle *inotify = NULL;
+
+	info = &per_cpu(cpu_idle_info, cpu);
+	info->cpu = cpu;
+	inotify = &info->dcvs_notifier;
+	snprintf(core_name[cpu], 10, "cpu%d", cpu);
+	inotify->core_name = core_name[cpu];
+	info->handle = msm_dcvs_idle_source_register(inotify);
+	BUG_ON(info->handle < 0);
+
+	pm_qos_add_request(&info->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+}
+
+static int msm_gov_idle_source_uninit(int cpu)
+{
+	struct cpu_idle_info *info = NULL;
+	struct msm_dcvs_idle *inotify = NULL;
+
+	info = &per_cpu(cpu_idle_info, cpu);
+	info->cpu = cpu;
+	inotify = &info->dcvs_notifier;
+	return msm_dcvs_idle_source_unregister(inotify);
+}
+
 struct msm_gov {
 	int cpu;
 	unsigned int cur_freq;
@@ -147,28 +263,42 @@
 	int ret = 0;
 	int cpu;
 	struct msm_dcvs_core_info *core = NULL;
+	struct msm_dcvs_core_info *core_info = NULL;
+	struct msm_gov_platform_data *pdata = pdev->dev.platform_data;
 	int sensor = 0;
 
 	core = pdev->dev.platform_data;
+	core_info = pdata->info;
+	latency = pdata->latency;
 
 	for_each_possible_cpu(cpu) {
 		mutex_init(&per_cpu(gov_mutex, cpu));
 		snprintf(core_name[cpu], 10, "cpu%d", cpu);
 		if (cpu < core->num_cores)
-			sensor = core->sensors[cpu];
-		ret = msm_dcvs_register_core(core_name[cpu], core,
+			sensor = core_info->sensors[cpu];
+		ret = msm_dcvs_register_core(core_name[cpu], core_info,
 						msm_dcvs_freq_set,
 						msm_dcvs_freq_get,
+						msm_dcvs_idle_notifier,
 						sensor);
 		if (ret)
 			pr_err("Unable to register core for %d\n", cpu);
+
+		msm_gov_idle_source_init(cpu);
 	}
 
+	cpu_pm_register_notifier(&idle_nb);
+
 	return cpufreq_register_governor(&cpufreq_gov_msm);
 }
 
 static int __devexit msm_gov_remove(struct platform_device *pdev)
 {
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		msm_gov_idle_source_uninit(cpu);
+	}
 	platform_set_drvdata(pdev, NULL);
 	return 0;
 }