aboutsummaryrefslogtreecommitdiff
path: root/drivers/cpuidle/governors/scheduled.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpuidle/governors/scheduled.c')
-rw-r--r--drivers/cpuidle/governors/scheduled.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/drivers/cpuidle/governors/scheduled.c b/drivers/cpuidle/governors/scheduled.c
new file mode 100644
index 000000000000..b597bd267932
--- /dev/null
+++ b/drivers/cpuidle/governors/scheduled.c
@@ -0,0 +1,113 @@
+/*
+ * scheduled.c - A governor that selects idle state based on external input
+ *
+ * Copyright 2013 Linaro Limited
+ * Author:
+ * Tuukka Tikkanen <tuukka.tikkanen@linaro.org>
+ *
+ * This code is licenced under the GPL version 2 as described
+ * in the COPYING file that acompanies the Linux Kernel.
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpuidle_scheduled.h>
+#include <linux/cpuidle.h>
+#include <linux/pm_qos.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+
+/**
+ * scheduled_select - selects the next idle state to enter
+ * @drv: cpuidle driver containing state data
+ * @dev: the CPU
+ */
+static int scheduled_select(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ int state;
+ struct sched_pm *pmdata = this_cpu_ptr(&sched_stat);
+
+ state = cpuidle_cstate_lookup(drv, dev,
+ pmdata->idle_time_until_timer,
+ pmdata->idle_length_estimate,
+ pmdata->idle_max_latency,
+ NULL);
+ pmdata->idle_current_state = &drv->states[state];
+
+ return state;
+}
+
+/**
+ * scheduled_reflect - records the actual idle period length
+ * @dev: the CPU
+ * @index: the index of actual entered state
+ */
+static void scheduled_reflect(struct cpuidle_device *dev, int index)
+{
+ unsigned int last_idle_us;
+ struct sched_pm *pmdata = this_cpu_ptr(&sched_stat);
+ unsigned int timer_limit = pmdata->idle_time_until_timer;
+ struct cpuidle_state *state = pmdata->idle_current_state;
+
+ if (unlikely(!(state->flags & CPUIDLE_FLAG_TIME_VALID))) {
+ last_idle_us = timer_limit;
+ } else {
+ last_idle_us = cpuidle_get_last_residency(dev);
+ if (last_idle_us > state->exit_latency)
+ last_idle_us -= state->exit_latency;
+ if (last_idle_us > timer_limit)
+ last_idle_us = timer_limit;
+ }
+
+ cpuidle_scheduled_result(state, last_idle_us);
+
+ pmdata->idle_current_state = NULL;
+}
+
+
+/**
+ * scheduled_enable_device - reset per cpu variables after hotplug
+ * @drv: cpuidle driver
+ * @dev: the CPU
+ */
+static int scheduled_enable_device(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ struct sched_pm *pmdata = &per_cpu(sched_stat, dev->cpu);
+
+ pmdata->idle_time_until_timer = UINT_MAX;
+ pmdata->idle_length_estimate = UINT_MAX;
+ pmdata->idle_max_latency = INT_MAX;
+ pmdata->idle_current_state = NULL;
+
+ return 0;
+}
+
+static struct cpuidle_governor scheduled_governor = {
+ .name = "scheduled",
+ .rating = 100,
+ .enable = scheduled_enable_device,
+ .select = scheduled_select,
+ .reflect = scheduled_reflect,
+ .owner = THIS_MODULE,
+};
+
+/**
+ * init_scheduled_idle_gov - initializes the governor
+ */
+static int __init init_scheduled_idle_gov(void)
+{
+ return cpuidle_register_governor(&scheduled_governor);
+}
+
+/**
+ * exit_scheduled_idle_gov - exits the governor
+ */
+static void __exit exit_scheduled_idle_gov(void)
+{
+ cpuidle_unregister_governor(&scheduled_governor);
+}
+
+MODULE_LICENSE("GPL");
+module_init(init_scheduled_idle_gov);
+module_exit(exit_scheduled_idle_gov);