msm: dcvs_idle: Provide CPU core idle information to msm_dcvs
The msm_dcvs algorithm takes in core idle information and outputs the
frequency for the core to run at. This driver registers with the
CPU_PM interface to get the idle enter and idle exit notification and
passes this to the msm_dcvs driver for every CPU core.
Change-Id: I2b7ddbae2ac00f7c129d307022f8b1b928e2956e
Signed-off-by: Praveen Chidambaram <pchidamb@codeaurora.org>
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 6c51af4..f46b226 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -314,7 +314,7 @@
obj-$(CONFIG_MSM_SLEEP_STATS) += msm_rq_stats.o idle_stats.o
obj-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += idle_stats_device.o
-obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o
+obj-$(CONFIG_MSM_DCVS) += msm_dcvs_scm.o msm_dcvs.o msm_dcvs_idle.o
obj-$(CONFIG_MSM_SHOW_RESUME_IRQ) += msm_show_resume_irq.o
obj-$(CONFIG_BT_MSM_PINTEST) += btpintest.o
obj-$(CONFIG_MSM_FAKE_BATTERY) += fish_battery.o
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index d798da6..f7677f4 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -1776,6 +1776,7 @@
#ifdef CONFIG_MSM_RTB
&msm_rtb_device,
#endif
+ &apq8064_cpu_idle_device,
};
static struct platform_device *sim_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 9dcbb67..f42f839 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1812,6 +1812,7 @@
#ifdef CONFIG_MSM_RTB
&msm_rtb_device,
#endif
+ &msm8930_cpu_idle_device,
};
static struct platform_device *cdp_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index faa36c1..4da1c7e 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -2190,6 +2190,7 @@
#ifdef CONFIG_MSM_RTB
&msm_rtb_device,
#endif
+ &msm8960_cpu_idle_device,
};
static struct platform_device *sim_devices[] __initdata = {
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 3632d80..f3c014c 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -2139,3 +2139,13 @@
.num_resources = ARRAY_SIZE(mdm_resources),
.resource = mdm_resources,
};
+
+static int apq8064_LPM_latency = 1000; /* >100 usec for WFI */
+
+struct platform_device apq8064_cpu_idle_device = {
+ .name = "msm_cpu_idle",
+ .id = -1,
+ .dev = {
+ .platform_data = &apq8064_LPM_latency,
+ },
+};
diff --git a/arch/arm/mach-msm/devices-8930.c b/arch/arm/mach-msm/devices-8930.c
index 81c52d1..16f4db7 100644
--- a/arch/arm/mach-msm/devices-8930.c
+++ b/arch/arm/mach-msm/devices-8930.c
@@ -275,3 +275,12 @@
},
};
+static int msm8930_LPM_latency = 1000; /* >100 usec for WFI */
+
+struct platform_device msm8930_cpu_idle_device = {
+ .name = "msm_cpu_idle",
+ .id = -1,
+ .dev = {
+ .platform_data = &msm8930_LPM_latency,
+ },
+};
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index f4af677..18e4f5e 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -3219,3 +3219,13 @@
};
#endif
+
+static int msm8960_LPM_latency = 1000; /* >100 usec for WFI */
+
+struct platform_device msm8960_cpu_idle_device = {
+ .name = "msm_cpu_idle",
+ .id = -1,
+ .dev = {
+ .platform_data = &msm8960_LPM_latency,
+ },
+};
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index aa09056..baa2a73 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -330,3 +330,7 @@
extern struct platform_device msm_dsps_device_8064;
extern struct platform_device *msm_copper_stub_regulator_devices[];
extern int msm_copper_stub_regulator_devices_len;
+
+extern struct platform_device msm8960_cpu_idle_device;
+extern struct platform_device msm8930_cpu_idle_device;
+extern struct platform_device apq8064_cpu_idle_device;
diff --git a/arch/arm/mach-msm/msm_dcvs_idle.c b/arch/arm/mach-msm/msm_dcvs_idle.c
new file mode 100644
index 0000000..59f2742
--- /dev/null
+++ b/arch/arm/mach-msm/msm_dcvs_idle.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/init.h>
+#include <linux/module.h>
+#include <linux/cpu_pm.h>
+#include <linux/platform_device.h>
+#include <linux/pm_qos_params.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;
+};
+
+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 struct pm_qos_request_list qos_req;
+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(&qos_req, PM_QOS_DEFAULT_VALUE);
+ break;
+
+ case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
+ pm_qos_update_request(&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_ENTER_FAILED:
+ 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 int msm_dcvs_idle_probe(struct platform_device *pdev)
+{
+ int cpu;
+ struct cpu_idle_info *info = NULL;
+ struct msm_dcvs_idle *inotify = NULL;
+
+ for_each_possible_cpu(cpu) {
+ 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];
+ inotify->enable = msm_dcvs_idle_notifier;
+ info->handle = msm_dcvs_idle_source_register(inotify);
+ BUG_ON(info->handle < 0);
+ }
+
+ latency = *((uint32_t *)pdev->dev.platform_data);
+ pm_qos_add_request(&qos_req, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ return cpu_pm_register_notifier(&idle_nb);
+}
+
+static int msm_dcvs_idle_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ int rc = 0;
+ int cpu = 0;
+ struct msm_dcvs_idle *inotify = NULL;
+ struct cpu_idle_info *info = NULL;
+
+ rc = cpu_pm_unregister_notifier(&idle_nb);
+
+ for_each_possible_cpu(cpu) {
+ info = &per_cpu(cpu_idle_info, cpu);
+ inotify = &info->dcvs_notifier;
+ ret = msm_dcvs_idle_source_unregister(inotify);
+ if (ret) {
+ rc = -EFAULT;
+ pr_err("Error de-registering core %d idle notifier.\n",
+ cpu);
+ }
+ }
+
+ return rc;
+}
+
+static struct platform_driver idle_pdrv = {
+ .probe = msm_dcvs_idle_probe,
+ .remove = __devexit_p(msm_dcvs_idle_remove),
+ .driver = {
+ .name = "msm_cpu_idle",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int msm_dcvs_idle_init(void)
+{
+ return platform_driver_register(&idle_pdrv);
+}
+late_initcall(msm_dcvs_idle_init);