Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 891360e..a175ae7 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -22,6 +22,9 @@
#include <linux/tick.h>
#include <linux/ktime.h>
#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
/*
* dbs is used in this file as a shortform for demandbased switching
@@ -37,6 +40,7 @@
#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000)
#define MIN_FREQUENCY_UP_THRESHOLD (11)
#define MAX_FREQUENCY_UP_THRESHOLD (100)
+#define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1)
/*
* The polling frequency of this governor depends on the capability of
@@ -103,6 +107,10 @@
*/
static DEFINE_MUTEX(dbs_mutex);
+static struct workqueue_struct *input_wq;
+
+static DEFINE_PER_CPU(struct work_struct, dbs_refresh_work);
+
static struct dbs_tuners {
unsigned int sampling_rate;
unsigned int up_threshold;
@@ -252,6 +260,7 @@
show_one(sampling_rate, sampling_rate);
show_one(io_is_busy, io_is_busy);
show_one(up_threshold, up_threshold);
+show_one(down_differential, down_differential);
show_one(sampling_down_factor, sampling_down_factor);
show_one(ignore_nice_load, ignore_nice);
show_one(powersave_bias, powersave_bias);
@@ -296,6 +305,23 @@
return count;
}
+static ssize_t store_down_differential(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+ ret = sscanf(buf, "%u", &input);
+
+ if (ret != 1 || input >= dbs_tuners_ins.up_threshold ||
+ input < MIN_FREQUENCY_DOWN_DIFFERENTIAL) {
+ return -EINVAL;
+ }
+
+ dbs_tuners_ins.down_differential = input;
+
+ return count;
+}
+
static ssize_t store_sampling_down_factor(struct kobject *a,
struct attribute *b, const char *buf, size_t count)
{
@@ -370,6 +396,7 @@
define_one_global_rw(sampling_rate);
define_one_global_rw(io_is_busy);
define_one_global_rw(up_threshold);
+define_one_global_rw(down_differential);
define_one_global_rw(sampling_down_factor);
define_one_global_rw(ignore_nice_load);
define_one_global_rw(powersave_bias);
@@ -378,6 +405,7 @@
&sampling_rate_min.attr,
&sampling_rate.attr,
&up_threshold.attr,
+ &down_differential.attr,
&sampling_down_factor.attr,
&ignore_nice_load.attr,
&powersave_bias.attr,
@@ -619,6 +647,89 @@
return 0;
}
+static void dbs_refresh_callback(struct work_struct *unused)
+{
+ struct cpufreq_policy *policy;
+ struct cpu_dbs_info_s *this_dbs_info;
+ unsigned int cpu = smp_processor_id();
+
+ if (lock_policy_rwsem_write(cpu) < 0)
+ return;
+
+ this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+ policy = this_dbs_info->cur_policy;
+
+ if (policy->cur < policy->max) {
+ policy->cur = policy->max;
+
+ __cpufreq_driver_target(policy, policy->max,
+ CPUFREQ_RELATION_L);
+ this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu,
+ &this_dbs_info->prev_cpu_wall);
+ }
+ unlock_policy_rwsem_write(cpu);
+}
+
+static void dbs_input_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ int i;
+
+ for_each_online_cpu(i) {
+ queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i));
+ }
+}
+
+static int dbs_input_connect(struct input_handler *handler,
+ struct input_dev *dev, const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int error;
+
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = "cpufreq";
+
+ error = input_register_handle(handle);
+ if (error)
+ goto err2;
+
+ error = input_open_device(handle);
+ if (error)
+ goto err1;
+
+ return 0;
+err1:
+ input_unregister_handle(handle);
+err2:
+ kfree(handle);
+ return error;
+}
+
+static void dbs_input_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id dbs_ids[] = {
+ { .driver_info = 1 },
+ { },
+};
+
+static struct input_handler dbs_input_handler = {
+ .event = dbs_input_event,
+ .connect = dbs_input_connect,
+ .disconnect = dbs_input_disconnect,
+ .name = "cpufreq_ond",
+ .id_table = dbs_ids,
+};
+
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int event)
{
@@ -678,6 +789,8 @@
latency * LATENCY_MULTIPLIER);
dbs_tuners_ins.io_is_busy = should_io_be_busy();
}
+ if (!cpu)
+ rc = input_register_handler(&dbs_input_handler);
mutex_unlock(&dbs_mutex);
mutex_init(&this_dbs_info->timer_mutex);
@@ -690,6 +803,8 @@
mutex_lock(&dbs_mutex);
mutex_destroy(&this_dbs_info->timer_mutex);
dbs_enable--;
+ if (!cpu)
+ input_unregister_handler(&dbs_input_handler);
mutex_unlock(&dbs_mutex);
if (!dbs_enable)
sysfs_remove_group(cpufreq_global_kobject,
@@ -715,6 +830,7 @@
{
cputime64_t wall;
u64 idle_time;
+ unsigned int i;
int cpu = get_cpu();
idle_time = get_cpu_idle_time_us(cpu, &wall);
@@ -736,12 +852,22 @@
MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10);
}
+ input_wq = create_workqueue("iewq");
+ if (!input_wq) {
+ printk(KERN_ERR "Failed to create iewq workqueue\n");
+ return -EFAULT;
+ }
+ for_each_possible_cpu(i) {
+ INIT_WORK(&per_cpu(dbs_refresh_work, i), dbs_refresh_callback);
+ }
+
return cpufreq_register_governor(&cpufreq_gov_ondemand);
}
static void __exit cpufreq_gov_dbs_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_ondemand);
+ destroy_workqueue(input_wq);
}