aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/gee_defconfig1
-rw-r--r--drivers/cpufreq/Kconfig30
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/cpufreq_ondemandplus.c984
-rw-r--r--include/linux/cpufreq.h3
-rw-r--r--include/trace/events/cpufreq_ondemandplus.h76
6 files changed, 1095 insertions, 0 deletions
diff --git a/arch/arm/configs/gee_defconfig b/arch/arm/configs/gee_defconfig
index dc0a2749ef7..97de8d6efee 100644
--- a/arch/arm/configs/gee_defconfig
+++ b/arch/arm/configs/gee_defconfig
@@ -561,6 +561,7 @@ CONFIG_CPU_FREQ_GOV_INTELLIACTIVE=y
CONFIG_CPU_FREQ_GOV_INTELLIMM=y
CONFIG_CPU_FREQ_GOV_SMARTMAX=y
CONFIG_CPU_FREQ_GOV_SMARTASS2=y
+CONFIG_CPU_FREQ_GOV_ONDEMANDPLUS=y
CONFIG_MSM_HOTPLUG=y
CONFIG_PARTIALRESUME=y
CONFIG_DEDUCE_WAKEUP_REASONS=y
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 729ba336e42..93174d2d0c0 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -107,6 +107,18 @@ config CPU_FREQ_DEFAULT_GOV_ELEMENTALX
help
Use the CPUFreq governor 'elementalx' as default.
+config CPU_FREQ_DEFAULT_GOV_ONDEMANDPLUS
+ bool "ondemandplus"
+ select CPU_FREQ_GOV_ONDEMANDPLUS
+ select CPU_FREQ_GOV_PERFORMANCE
+ help
+ Use the CPUFreq governor 'ondemandplus' as default. This allows
+ you to get a full dynamic frequency capable system by simply
+ loading your cpufreq low-level hardware driver.
+ Be aware that not all cpufreq drivers support the ondemandplus
+ governor. If unsure have a look at the help section of the
+ driver. Fallback governor will be the performance governor.
+
config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
bool "conservative"
select CPU_FREQ_GOV_CONSERVATIVE
@@ -214,6 +226,24 @@ config CPU_FREQ_GOV_ELEMENTALX
If in doubt, say N.
+config CPU_FREQ_GOV_ONDEMANDPLUS
+ tristate "'ondemandplus' cpufreq policy governor"
+ select CPU_FREQ_TABLE
+ help
+ 'ondemandplus' - This driver adds a dynamic cpufreq policy
+ governor. The governor does a periodic polling and
+ changes frequency based on the CPU utilization.
+ The support for this governor depends on CPU capability to
+ do fast frequency switching (i.e, very low latency frequency
+ transitions).
+
+ To compile this driver as a module, choose M here: the
+ module will be called cpufreq_ondemandplus.
+
+ For details, take a look at linux/Documentation/cpu-freq.
+
+ If in doubt, say N.
+
config CPU_FREQ_GOV_INTERACTIVE
tristate "'interactive' cpufreq policy governor"
help
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index e7c5cd54926..4b839573f62 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o
obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o
obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o
obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o
+obj-$(CONFIG_CPU_FREQ_GOV_ONDEMANDPLUS) += cpufreq_ondemandplus.o
obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o
obj-$(CONFIG_CPU_FREQ_GOV_INTELLIACTIVE)+= cpufreq_intelliactive.o
diff --git a/drivers/cpufreq/cpufreq_ondemandplus.c b/drivers/cpufreq/cpufreq_ondemandplus.c
new file mode 100644
index 00000000000..e41077e13a7
--- /dev/null
+++ b/drivers/cpufreq/cpufreq_ondemandplus.c
@@ -0,0 +1,984 @@
+/*
+ * drivers/cpufreq/cpufreq_ondemandplus.c
+ * Copyright (C) 2013 Boy Petersen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * based upon:
+ *
+ *
+ * drivers/cpufreq/cpufreq_interactive.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Author: Mike Chan (mike@android.com)
+ *
+ *
+ * and:
+ *
+ * drivers/cpufreq/cpufreq_ondemand.c
+ *
+ * Copyright (C) 2001 Russell King
+ * (C) 2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
+ * Jun Nakajima <jun.nakajima@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpufreq.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/kernel_stat.h>
+#include <asm/cputime.h>
+#include <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/cpufreq_ondemandplus.h>
+
+static atomic_t active_count = ATOMIC_INIT(0);
+
+struct cpufreq_ondemandplus_cpuinfo {
+ struct timer_list cpu_timer;
+ int timer_idlecancel;
+ u64 time_in_idle;
+ u64 idle_exit_time;
+ u64 timer_run_time;
+ int idling;
+ u64 target_set_time;
+ u64 target_set_time_in_idle;
+ struct cpufreq_policy *policy;
+ struct cpufreq_frequency_table *freq_table;
+ unsigned int target_freq;
+ int governor_enabled;
+};
+
+static DEFINE_PER_CPU(struct cpufreq_ondemandplus_cpuinfo, cpuinfo);
+
+/* realtime thread handles frequency scaling */
+static struct task_struct *speedchange_task;
+static cpumask_t speedchange_cpumask;
+static spinlock_t speedchange_cpumask_lock;
+
+/*
+ * Tunables start
+ */
+
+#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC)
+static unsigned long timer_rate;
+
+#define DEFAULT_UP_THRESHOLD 95
+static unsigned long up_threshold;
+
+#define DEFAULT_DOWN_DIFFERENTIAL 62
+static unsigned long down_differential;
+
+#define DEFAULT_MIN_FREQ 162000
+static u64 allowed_min;
+
+#define DEFAULT_MAX_FREQ 1512000
+static u64 allowed_max;
+
+#define DEFAULT_INTER_HIFREQ 1350000
+static u64 inter_hifreq;
+
+#define DEFAULT_INTER_LOFREQ 1026000
+static u64 inter_lofreq;
+
+#define SUSPEND_FREQ 702000
+static u64 suspend_frequency;
+
+#define DEFAULT_INTER_STAYCYCLES 2
+static unsigned long inter_staycycles;
+
+#define DEFAULT_STAYCYCLES_RESETFREQ 486000
+static u64 staycycles_resetfreq;
+
+#define DEFAULT_IO_IS_BUSY 2
+static unsigned int io_is_busy;
+
+/*
+ * Tunables end
+ */
+
+static int cpufreq_governor_ondemandplus(struct cpufreq_policy *policy,
+ unsigned int event);
+
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDPLUS
+static
+#endif
+struct cpufreq_governor cpufreq_gov_ondemandplus = {
+ .name = "ondemandplus",
+ .governor = cpufreq_governor_ondemandplus,
+ .max_transition_latency = 10000000,
+ .owner = THIS_MODULE,
+};
+
+static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu,
+ cputime64_t *wall)
+{
+ u64 idle_time;
+ u64 cur_wall_time;
+ u64 busy_time;
+
+ cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
+
+ busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE];
+
+ idle_time = cur_wall_time - busy_time;
+ if (wall)
+ *wall = jiffies_to_usecs(cur_wall_time);
+
+ return jiffies_to_usecs(idle_time);
+}
+
+static inline cputime64_t get_cpu_idle_time(unsigned int cpu,
+ cputime64_t *wall)
+{
+ u64 idle_time = get_cpu_idle_time_us(cpu, wall);
+
+ if (idle_time == -1ULL)
+ idle_time = get_cpu_idle_time_jiffy(cpu, wall);
+ else if (io_is_busy == 2)
+ idle_time += (get_cpu_iowait_time_us(cpu, wall) / 2);
+ else if (!io_is_busy)
+ idle_time += get_cpu_iowait_time_us(cpu, wall);
+
+ return idle_time;
+}
+
+static void cpufreq_ondemandplus_timer(unsigned long data)
+{
+ unsigned int delta_idle;
+ unsigned int delta_time;
+ int cpu_load;
+ unsigned int load_freq;
+ int load_since_change;
+ u64 time_in_idle;
+ u64 idle_exit_time;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, data);
+ u64 now_idle;
+ unsigned int new_freq;
+ unsigned int index;
+ static unsigned int stay_counter;
+ unsigned long flags;
+
+ smp_rmb();
+
+ if (!pcpu->governor_enabled)
+ goto exit;
+
+ /*
+ * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time,
+ * this lets idle exit know the current idle time sample has
+ * been processed, and idle exit can generate a new sample and
+ * re-arm the timer. This prevents a concurrent idle
+ * exit on that CPU from writing a new set of info at the same time
+ * the timer function runs (the timer function can't use that info
+ * until more time passes).
+ */
+
+ time_in_idle = pcpu->time_in_idle;
+ idle_exit_time = pcpu->idle_exit_time;
+ now_idle = get_cpu_idle_time(data, &pcpu->timer_run_time);
+ smp_wmb();
+
+ /* If we raced with cancelling a timer, skip. */
+ if (!idle_exit_time)
+ goto exit;
+
+ delta_idle = (unsigned int) (now_idle - time_in_idle);
+ delta_time = (unsigned int) (pcpu->timer_run_time - idle_exit_time);
+
+ /*
+ * If timer ran less than 1ms after short-term sample started, retry.
+ */
+ if (delta_time < 1000)
+ goto rearm;
+
+ if (delta_idle > delta_time)
+ cpu_load = 0;
+ else
+ cpu_load = 100 * (delta_time - delta_idle) / delta_time;
+
+ delta_idle = (unsigned int) (now_idle - pcpu->target_set_time_in_idle);
+ delta_time = (unsigned int) (pcpu->timer_run_time - pcpu->target_set_time);
+
+ if ((delta_time == 0) || (delta_idle > delta_time))
+ load_since_change = 0;
+ else
+ load_since_change =
+ 100 * (delta_time - delta_idle) / delta_time;
+
+ /*
+ * If short-term load (since last idle timer started or
+ * timer function re-armed itself) is higher than long-term
+ * load (since last frequency change), use short-term load
+ * to be able to scale up quickly.
+ * When long-term load is higher than short-term load,
+ * use the average of short-term load and long-term load
+ * (instead of just long-term load) to be able to scale
+ * down faster, with the long-term load being able to delay
+ * down scaling a little to maintain responsiveness.
+ */
+ if (load_since_change > cpu_load) {
+ cpu_load = (cpu_load + load_since_change) / 2;
+ }
+
+ load_freq = cpu_load * pcpu->target_freq;
+
+ new_freq = pcpu->target_freq;
+
+ /* suspended scaling behavior */
+ if (allowed_max == suspend_frequency) {
+ if (stay_counter) {
+ stay_counter = 0;
+ }
+
+ /* Check for frequency increase */
+ if (load_freq > up_threshold * pcpu->target_freq) {
+ /* if we are already at full speed then break out early */
+ if (pcpu->target_freq < suspend_frequency) {
+
+ new_freq = pcpu->target_freq + pcpu->policy->max / 10;
+
+ if (new_freq > suspend_frequency) {
+ new_freq = suspend_frequency;
+ }
+
+ cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq,
+ CPUFREQ_RELATION_L, &index);
+
+ new_freq = pcpu->freq_table[index].frequency;
+ }
+
+ /* Check for frequency decrease */
+
+ /*
+ * The optimal frequency is the frequency that is the lowest that
+ * can support the current CPU usage without triggering the up
+ * policy. To be safe, we focus 10 points under the threshold.
+ */
+ } else if (load_freq < (up_threshold - down_differential) *
+ pcpu->target_freq) {
+ /* if we are already at full speed then break out early */
+ if (pcpu->target_freq != pcpu->policy->min) {
+
+ new_freq = pcpu->target_freq - pcpu->policy->max / 10;
+
+ if (new_freq < pcpu->policy->min) {
+ new_freq = pcpu->policy->min;
+ }
+
+ cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq,
+ CPUFREQ_RELATION_H, &index);
+
+ new_freq = pcpu->freq_table[index].frequency;
+ }
+ }
+ /* screen-on scaling behavior */
+ } else {
+ /* Check for frequency increase */
+ if (load_freq > up_threshold * pcpu->target_freq) {
+ /* if we are already at full speed then break out early */
+ if (pcpu->target_freq < pcpu->policy->max) {
+
+ if (stay_counter == 0 && inter_staycycles != 0) {
+ new_freq = inter_lofreq;
+ stay_counter++;
+ } else if (stay_counter == 1 && inter_staycycles != 1) {
+ new_freq = inter_hifreq;
+ stay_counter++;
+ } else if (stay_counter < inter_staycycles) {
+ stay_counter++;
+ goto rearm;
+ } else {
+ new_freq = pcpu->policy->max;
+ }
+ }
+ }
+
+ /* Check for frequency decrease */
+
+ /*
+ * The optimal frequency is the frequency that is the lowest that
+ * can support the current CPU usage without triggering the up
+ * policy. To be safe, we focus 10 points under the threshold.
+ */
+ if (load_freq < (up_threshold - down_differential) *
+ pcpu->target_freq) {
+
+ if (pcpu->target_freq != allowed_min) {
+ new_freq = load_freq /
+ (up_threshold - down_differential);
+
+ if (new_freq <= staycycles_resetfreq) {
+ stay_counter = 0;
+ }
+
+ if (new_freq < allowed_min) {
+ new_freq = allowed_min;
+ }
+ }
+ } else if (pcpu->target_freq == pcpu->policy->max &&
+ load_freq < (up_threshold - down_differential / 2) *
+ pcpu->target_freq) {
+ new_freq = load_freq / (up_threshold - down_differential * 2 / 3);
+ }
+
+ }
+
+ if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
+ new_freq, CPUFREQ_RELATION_H,
+ &index)) {
+ pr_warn_once("timer %d: cpufreq_frequency_table_target error\n",
+ (int) data);
+ goto rearm;
+ }
+
+ new_freq = pcpu->freq_table[index].frequency;
+
+ if (pcpu->target_freq == new_freq) {
+ trace_cpufreq_ondemandplus_already(data, cpu_load,
+ pcpu->target_freq, new_freq);
+ goto rearm_if_notmax;
+ }
+
+ trace_cpufreq_ondemandplus_target(data, cpu_load, pcpu->target_freq,
+ new_freq);
+ pcpu->target_set_time_in_idle = now_idle;
+ pcpu->target_set_time = pcpu->timer_run_time;
+
+ pcpu->target_freq = new_freq;
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+ cpumask_set_cpu(data, &speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+ wake_up_process(speedchange_task);
+
+rearm_if_notmax:
+ /*
+ * Already set max speed and don't see a need to change that,
+ * wait until next idle to re-evaluate, don't need timer.
+ */
+ if (pcpu->target_freq == pcpu->policy->max)
+ goto exit;
+
+rearm:
+ if (!timer_pending(&pcpu->cpu_timer)) {
+ /*
+ * If already at min: if that CPU is idle, don't set timer.
+ * Else cancel the timer if that CPU goes idle. We don't
+ * need to re-evaluate speed until the next idle exit.
+ */
+
+ unsigned int cur_min_policy;
+ if (allowed_max == suspend_frequency) {
+ cur_min_policy = pcpu->policy->min;
+ } else {
+ cur_min_policy = allowed_min;
+ }
+
+ if (pcpu->target_freq == cur_min_policy) {
+ smp_rmb();
+
+ if (pcpu->idling)
+ goto exit;
+
+ pcpu->timer_idlecancel = 1;
+ }
+
+ pcpu->time_in_idle = get_cpu_idle_time(
+ data, &pcpu->idle_exit_time);
+ mod_timer(&pcpu->cpu_timer,
+ jiffies + usecs_to_jiffies(timer_rate));
+ }
+
+exit:
+ return;
+}
+
+static void cpufreq_ondemandplus_idle_start(void)
+{
+ struct cpufreq_ondemandplus_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+ int pending;
+
+ if (!pcpu->governor_enabled)
+ return;
+
+ pcpu->idling = 1;
+ smp_wmb();
+ pending = timer_pending(&pcpu->cpu_timer);
+
+ if (pcpu->target_freq != pcpu->policy->min) {
+#ifdef CONFIG_SMP
+ /*
+ * Entering idle while not at lowest speed. On some
+ * platforms this can hold the other CPU(s) at that speed
+ * even though the CPU is idle. Set a timer to re-evaluate
+ * speed so this idle CPU doesn't hold the other CPUs above
+ * min indefinitely. This should probably be a quirk of
+ * the CPUFreq driver.
+ */
+ if (!pending) {
+ pcpu->time_in_idle = get_cpu_idle_time(
+ smp_processor_id(), &pcpu->idle_exit_time);
+ pcpu->timer_idlecancel = 0;
+ mod_timer(&pcpu->cpu_timer,
+ jiffies + usecs_to_jiffies(timer_rate));
+ }
+#endif
+ } else {
+ /*
+ * If at min speed and entering idle after load has
+ * already been evaluated, and a timer has been set just in
+ * case the CPU suddenly goes busy, cancel that timer. The
+ * CPU didn't go busy; we'll recheck things upon idle exit.
+ */
+ if (pending && pcpu->timer_idlecancel) {
+ del_timer(&pcpu->cpu_timer);
+ /*
+ * Ensure last timer run time is after current idle
+ * sample start time, so next idle exit will always
+ * start a new idle sampling period.
+ */
+ pcpu->idle_exit_time = 0;
+ pcpu->timer_idlecancel = 0;
+ }
+ }
+
+}
+
+static void cpufreq_ondemandplus_idle_end(void)
+{
+ struct cpufreq_ondemandplus_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+
+ pcpu->idling = 0;
+ smp_wmb();
+
+ /*
+ * Arm the timer for 1-2 ticks later if not already, and if the timer
+ * function has already processed the previous load sampling
+ * interval. (If the timer is not pending but has not processed
+ * the previous interval, it is probably racing with us on another
+ * CPU. Let it compute load based on the previous sample and then
+ * re-arm the timer for another interval when it's done, rather
+ * than updating the interval start time to be "now", which doesn't
+ * give the timer function enough time to make a decision on this
+ * run.)
+ */
+ if (timer_pending(&pcpu->cpu_timer) == 0 &&
+ pcpu->timer_run_time >= pcpu->idle_exit_time &&
+ pcpu->governor_enabled) {
+ pcpu->time_in_idle =
+ get_cpu_idle_time(smp_processor_id(),
+ &pcpu->idle_exit_time);
+ pcpu->timer_idlecancel = 0;
+ mod_timer(&pcpu->cpu_timer,
+ jiffies + usecs_to_jiffies(timer_rate));
+ }
+
+}
+
+static int cpufreq_ondemandplus_speedchange_task(void *data)
+{
+ unsigned int cpu;
+ cpumask_t tmp_mask;
+ unsigned long flags;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu;
+
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+
+ if (cpumask_empty(&speedchange_cpumask)) {
+ spin_unlock_irqrestore(&speedchange_cpumask_lock,
+ flags);
+ schedule();
+
+ if (kthread_should_stop())
+ break;
+
+ spin_lock_irqsave(&speedchange_cpumask_lock, flags);
+ }
+
+ set_current_state(TASK_RUNNING);
+ tmp_mask = speedchange_cpumask;
+ cpumask_clear(&speedchange_cpumask);
+ spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
+
+ for_each_cpu(cpu, &tmp_mask) {
+ unsigned int j;
+ unsigned int max_freq = 0;
+
+ pcpu = &per_cpu(cpuinfo, cpu);
+ smp_rmb();
+
+ if (!pcpu->governor_enabled)
+ continue;
+
+ for_each_cpu(j, pcpu->policy->cpus) {
+ struct cpufreq_ondemandplus_cpuinfo *pjcpu =
+ &per_cpu(cpuinfo, j);
+
+ if (pjcpu->target_freq > max_freq)
+ max_freq = pjcpu->target_freq;
+ }
+
+ if (max_freq != pcpu->policy->cur)
+ __cpufreq_driver_target(pcpu->policy,
+ max_freq,
+ CPUFREQ_RELATION_H);
+ trace_cpufreq_ondemandplus_setspeed(cpu,
+ pcpu->target_freq,
+ pcpu->policy->cur);
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t show_timer_rate(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", timer_rate);
+}
+
+static ssize_t store_timer_rate(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ timer_rate = val;
+ return count;
+}
+
+static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644,
+ show_timer_rate, store_timer_rate);
+
+static ssize_t show_up_threshold(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", up_threshold);
+}
+
+static ssize_t store_up_threshold(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val > 100)
+ val = 100;
+
+ if (val < 1)
+ val = 1;
+
+ up_threshold = val;
+ return count;
+}
+
+static struct global_attr up_threshold_attr = __ATTR(up_threshold, 0644,
+ show_up_threshold, store_up_threshold);
+
+static ssize_t show_down_differential(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", down_differential);
+}
+
+static ssize_t store_down_differential(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val > 100)
+ val = 100;
+
+ down_differential = val;
+ return count;
+}
+
+static struct global_attr down_differential_attr = __ATTR(down_differential, 0644,
+ show_down_differential, store_down_differential);
+
+static ssize_t show_inter_hifreq(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%llu\n", inter_hifreq);
+}
+
+static ssize_t store_inter_hifreq(struct kobject *kobj,
+ struct attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ u64 val;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+ unsigned int index;
+
+ ret = strict_strtoull(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ index = 0;
+ cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, val,
+ CPUFREQ_RELATION_L, &index);
+ val = pcpu->freq_table[index].frequency;
+
+ if (val > pcpu->policy->max)
+ val = pcpu->policy->max;
+
+ if (val < allowed_min)
+ val = allowed_min;
+
+ inter_hifreq = val;
+ return count;
+}
+
+static struct global_attr inter_hifreq_attr = __ATTR(inter_hifreq, 0644,
+ show_inter_hifreq, store_inter_hifreq);
+
+static ssize_t show_inter_lofreq(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%llu\n", inter_lofreq);
+}
+
+static ssize_t store_inter_lofreq(struct kobject *kobj,
+ struct attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ u64 val;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+ unsigned int index;
+
+ ret = strict_strtoull(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ index = 0;
+ cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, val,
+ CPUFREQ_RELATION_H, &index);
+ val = pcpu->freq_table[index].frequency;
+
+ if (val > pcpu->policy->max)
+ val = pcpu->policy->max;
+
+ if (val < allowed_min)
+ val = allowed_min;
+
+ inter_lofreq = val;
+ return count;
+}
+
+static struct global_attr inter_lofreq_attr = __ATTR(inter_lofreq, 0644,
+ show_inter_lofreq, store_inter_lofreq);
+
+static ssize_t show_inter_staycycles(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", inter_staycycles);
+}
+
+static ssize_t store_inter_staycycles(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val > 10)
+ val = 10;
+
+ inter_staycycles = val;
+ return count;
+}
+
+static struct global_attr inter_staycycles_attr = __ATTR(inter_staycycles, 0644,
+ show_inter_staycycles, store_inter_staycycles);
+
+static ssize_t show_staycycles_resetfreq(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%llu\n", staycycles_resetfreq);
+}
+
+static ssize_t store_staycycles_resetfreq(struct kobject *kobj,
+ struct attribute *attr, const char *buf,
+ size_t count)
+{
+ int ret;
+ u64 val;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu =
+ &per_cpu(cpuinfo, smp_processor_id());
+
+ ret = strict_strtoull(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val > pcpu->policy->max)
+ val = pcpu->policy->max;
+
+ if (val < allowed_min)
+ val = allowed_min;
+
+ staycycles_resetfreq = val;
+ return count;
+}
+
+static struct global_attr staycycles_resetfreq_attr = __ATTR(staycycles_resetfreq, 0644,
+ show_staycycles_resetfreq, store_staycycles_resetfreq);
+
+static ssize_t show_io_is_busy(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", io_is_busy);
+}
+
+static ssize_t store_io_is_busy(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ io_is_busy = val;
+ return count;
+}
+
+static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644,
+ show_io_is_busy, store_io_is_busy);
+
+static struct attribute *ondemandplus_attributes[] = {
+ &timer_rate_attr.attr,
+ &up_threshold_attr.attr,
+ &down_differential_attr.attr,
+ &inter_hifreq_attr.attr,
+ &inter_lofreq_attr.attr,
+ &inter_staycycles_attr.attr,
+ &staycycles_resetfreq_attr.attr,
+ &io_is_busy_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ondemandplus_attr_group = {
+ .attrs = ondemandplus_attributes,
+ .name = "ondemandplus",
+};
+
+static int cpufreq_ondemandplus_idle_notifier(struct notifier_block *nb,
+ unsigned long val,
+ void *data)
+{
+ switch (val) {
+ case IDLE_START:
+ cpufreq_ondemandplus_idle_start();
+ break;
+ case IDLE_END:
+ cpufreq_ondemandplus_idle_end();
+ break;
+ }
+
+ return 0;
+}
+
+static struct notifier_block cpufreq_ondemandplus_idle_nb = {
+ .notifier_call = cpufreq_ondemandplus_idle_notifier,
+};
+
+static int cpufreq_governor_ondemandplus(struct cpufreq_policy *policy,
+ unsigned int event)
+{
+ int rc;
+ unsigned int j;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu;
+ struct cpufreq_frequency_table *freq_table;
+
+ switch (event) {
+ case CPUFREQ_GOV_START:
+ if (!cpu_online(policy->cpu))
+ return -EINVAL;
+
+ freq_table =
+ cpufreq_frequency_get_table(policy->cpu);
+
+ for_each_cpu(j, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, j);
+ pcpu->policy = policy;
+ pcpu->target_freq = policy->cur;
+ pcpu->freq_table = freq_table;
+ pcpu->target_set_time_in_idle =
+ get_cpu_idle_time(j,
+ &pcpu->target_set_time);
+ pcpu->governor_enabled = 1;
+ smp_wmb();
+ }
+
+ /*
+ * Do not register the idle hook and create sysfs
+ * entries if we have already done so.
+ */
+ if (atomic_inc_return(&active_count) > 1)
+ return 0;
+
+ rc = sysfs_create_group(cpufreq_global_kobject,
+ &ondemandplus_attr_group);
+ if (rc)
+ return rc;
+
+ idle_notifier_register(&cpufreq_ondemandplus_idle_nb);
+ break;
+
+ case CPUFREQ_GOV_STOP:
+ for_each_cpu(j, policy->cpus) {
+ pcpu = &per_cpu(cpuinfo, j);
+ pcpu->governor_enabled = 0;
+ smp_wmb();
+ del_timer_sync(&pcpu->cpu_timer);
+
+ /*
+ * Reset idle exit time since we may cancel the timer
+ * before it can run after the last idle exit time,
+ * to avoid tripping the check in idle exit for a timer
+ * that is trying to run.
+ */
+ pcpu->idle_exit_time = 0;
+ }
+
+ if (atomic_dec_return(&active_count) > 0)
+ return 0;
+
+ idle_notifier_unregister(&cpufreq_ondemandplus_idle_nb);
+ sysfs_remove_group(cpufreq_global_kobject,
+ &ondemandplus_attr_group);
+
+ break;
+
+ case CPUFREQ_GOV_LIMITS:
+ if (policy->max < policy->cur)
+ __cpufreq_driver_target(policy,
+ policy->max, CPUFREQ_RELATION_H);
+ else if (policy->min > policy->cur)
+ __cpufreq_driver_target(policy,
+ policy->min, CPUFREQ_RELATION_L);
+ break;
+ }
+ return 0;
+}
+
+static int __init cpufreq_ondemandplus_init(void)
+{
+ unsigned int i;
+ struct cpufreq_ondemandplus_cpuinfo *pcpu;
+ struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
+
+ timer_rate = DEFAULT_TIMER_RATE;
+ up_threshold = DEFAULT_UP_THRESHOLD;
+ down_differential = DEFAULT_DOWN_DIFFERENTIAL;
+ inter_hifreq = DEFAULT_INTER_HIFREQ;
+ allowed_min = DEFAULT_MIN_FREQ;
+ allowed_max = DEFAULT_MAX_FREQ;
+ suspend_frequency = SUSPEND_FREQ;
+ inter_lofreq = DEFAULT_INTER_LOFREQ;
+ inter_staycycles = DEFAULT_INTER_STAYCYCLES;
+ staycycles_resetfreq = DEFAULT_STAYCYCLES_RESETFREQ;
+ io_is_busy = DEFAULT_IO_IS_BUSY;
+
+ /* Initalize per-cpu timers */
+ for_each_possible_cpu(i) {
+ pcpu = &per_cpu(cpuinfo, i);
+ init_timer(&pcpu->cpu_timer);
+ pcpu->cpu_timer.function = cpufreq_ondemandplus_timer;
+ pcpu->cpu_timer.data = i;
+ }
+
+ spin_lock_init(&speedchange_cpumask_lock);
+ speedchange_task =
+ kthread_create(cpufreq_ondemandplus_speedchange_task, NULL,
+ "cfondemandplus");
+ if (IS_ERR(speedchange_task))
+ return PTR_ERR(speedchange_task);
+
+ sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, &param);
+ get_task_struct(speedchange_task);
+
+ /* NB: wake up so the thread does not look hung to the freezer */
+ wake_up_process(speedchange_task);
+
+ return cpufreq_register_governor(&cpufreq_gov_ondemandplus);
+
+ put_task_struct(speedchange_task);
+ return -ENOMEM;
+}
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDPLUS
+fs_initcall(cpufreq_ondemandplus_init);
+#else
+module_init(cpufreq_ondemandplus_init);
+#endif
+
+static void __exit cpufreq_ondemandplus_exit(void)
+{
+ cpufreq_unregister_governor(&cpufreq_gov_ondemandplus);
+ kthread_stop(speedchange_task);
+ put_task_struct(speedchange_task);
+}
+
+module_exit(cpufreq_ondemandplus_exit);
+
+MODULE_AUTHOR("Mike Chan <mike@android.com>");
+MODULE_DESCRIPTION("'cpufreq_ondemandplus' - A cpufreq governor for "
+ "semi-aggressive scaling");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index d772d00bb4f..f7550e692e0 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -371,6 +371,9 @@ extern struct cpufreq_governor cpufreq_gov_userspace;
#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND)
extern struct cpufreq_governor cpufreq_gov_ondemand;
#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemand)
+#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMANDPLUS)
+extern struct cpufreq_governor cpufreq_gov_ondemandplus;
+#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_ondemandplus)
#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ELEMENTALX)
extern struct cpufreq_governor cpufreq_gov_elementalx;
#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_elementalx)
diff --git a/include/trace/events/cpufreq_ondemandplus.h b/include/trace/events/cpufreq_ondemandplus.h
new file mode 100644
index 00000000000..cf84638f4df
--- /dev/null
+++ b/include/trace/events/cpufreq_ondemandplus.h
@@ -0,0 +1,76 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cpufreq_ondemandplus
+
+#if !defined(_TRACE_CPUFREQ_ONDEMANDPLUS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CPUFREQ_ONDEMANDPLUS_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(set,
+ TP_PROTO(u32 cpu_id, unsigned long targfreq,
+ unsigned long actualfreq),
+ TP_ARGS(cpu_id, targfreq, actualfreq),
+
+ TP_STRUCT__entry(
+ __field( u32, cpu_id )
+ __field(unsigned long, targfreq )
+ __field(unsigned long, actualfreq )
+ ),
+
+ TP_fast_assign(
+ __entry->cpu_id = (u32) cpu_id;
+ __entry->targfreq = targfreq;
+ __entry->actualfreq = actualfreq;
+ ),
+
+ TP_printk("cpu=%u targ=%lu actual=%lu",
+ __entry->cpu_id, __entry->targfreq,
+ __entry->actualfreq)
+);
+
+DEFINE_EVENT(set, cpufreq_ondemandplus_setspeed,
+ TP_PROTO(u32 cpu_id, unsigned long targfreq,
+ unsigned long actualfreq),
+ TP_ARGS(cpu_id, targfreq, actualfreq)
+);
+
+DECLARE_EVENT_CLASS(loadeval,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curfreq, unsigned long targfreq),
+ TP_ARGS(cpu_id, load, curfreq, targfreq),
+
+ TP_STRUCT__entry(
+ __field(unsigned long, cpu_id )
+ __field(unsigned long, load )
+ __field(unsigned long, curfreq )
+ __field(unsigned long, targfreq )
+ ),
+
+ TP_fast_assign(
+ __entry->cpu_id = cpu_id;
+ __entry->load = load;
+ __entry->curfreq = curfreq;
+ __entry->targfreq = targfreq;
+ ),
+
+ TP_printk("cpu=%lu load=%lu cur=%lu targ=%lu",
+ __entry->cpu_id, __entry->load, __entry->curfreq,
+ __entry->targfreq)
+);
+
+DEFINE_EVENT(loadeval, cpufreq_ondemandplus_target,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curfreq, unsigned long targfreq),
+ TP_ARGS(cpu_id, load, curfreq, targfreq)
+);
+
+DEFINE_EVENT(loadeval, cpufreq_ondemandplus_already,
+ TP_PROTO(unsigned long cpu_id, unsigned long load,
+ unsigned long curfreq, unsigned long targfreq),
+ TP_ARGS(cpu_id, load, curfreq, targfreq)
+);
+
+#endif /* _TRACE_CPUFREQ_ONDEMANDPLUS_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>