summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/cpufreq/cpu-boost.c164
1 files changed, 157 insertions, 7 deletions
diff --git a/drivers/cpufreq/cpu-boost.c b/drivers/cpufreq/cpu-boost.c
index d1fa3d0caff2..9846eb77d2ad 100644
--- a/drivers/cpufreq/cpu-boost.c
+++ b/drivers/cpufreq/cpu-boost.c
@@ -21,26 +21,43 @@
#include <linux/jiffies.h>
#include <linux/kthread.h>
#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/time.h>
struct cpu_sync {
struct task_struct *thread;
wait_queue_head_t sync_wq;
struct delayed_work boost_rem;
+ struct delayed_work input_boost_rem;
int cpu;
spinlock_t lock;
bool pending;
int src_cpu;
unsigned int boost_min;
+ unsigned int input_boost_min;
};
static DEFINE_PER_CPU(struct cpu_sync, sync_info);
-static struct workqueue_struct *boost_rem_wq;
+static struct workqueue_struct *cpu_boost_wq;
+
+static struct work_struct input_boost_work;
static unsigned int boost_ms;
module_param(boost_ms, uint, 0644);
static unsigned int sync_threshold;
module_param(sync_threshold, uint, 0644);
+
+static unsigned int input_boost_freq;
+module_param(input_boost_freq, uint, 0644);
+
+static unsigned int input_boost_ms = 40;
+module_param(input_boost_ms, uint, 0644);
+
+static u64 last_input_time;
+#define MIN_INPUT_INTERVAL (150 * USEC_PER_MSEC)
+
/*
* The CPUFREQ_ADJUST notifier is used to override the current policy min to
* make sure policy min >= boost_min. The cpufreq framework then does the job
@@ -52,14 +69,18 @@ static int boost_adjust_notify(struct notifier_block *nb, unsigned long val,
struct cpufreq_policy *policy = data;
unsigned int cpu = policy->cpu;
struct cpu_sync *s = &per_cpu(sync_info, cpu);
- unsigned int min = s->boost_min;
+ unsigned int b_min = s->boost_min;
+ unsigned int ib_min = s->input_boost_min;
+ unsigned int min;
if (val != CPUFREQ_ADJUST)
return NOTIFY_OK;
- if (min == 0)
+ if (!b_min && !ib_min)
return NOTIFY_OK;
+ min = max(b_min, ib_min);
+
pr_debug("CPU%u policy min before boost: %u kHz\n",
cpu, policy->min);
pr_debug("CPU%u boost min: %u kHz\n", cpu, min);
@@ -87,6 +108,17 @@ static void do_boost_rem(struct work_struct *work)
cpufreq_update_policy(s->cpu);
}
+static void do_input_boost_rem(struct work_struct *work)
+{
+ struct cpu_sync *s = container_of(work, struct cpu_sync,
+ input_boost_rem.work);
+
+ pr_debug("Removing input boost for CPU%d\n", s->cpu);
+ s->input_boost_min = 0;
+ /* Force policy re-evaluation to trigger adjust notifier. */
+ cpufreq_update_policy(s->cpu);
+}
+
static int boost_mig_sync_thread(void *data)
{
int dest_cpu = (int) data;
@@ -133,7 +165,7 @@ static int boost_mig_sync_thread(void *data)
/* Force policy re-evaluation to trigger adjust notifier. */
cpufreq_update_policy(dest_cpu);
- queue_delayed_work_on(s->cpu, boost_rem_wq,
+ queue_delayed_work_on(s->cpu, cpu_boost_wq,
&s->boost_rem, msecs_to_jiffies(boost_ms));
}
@@ -163,29 +195,147 @@ static struct notifier_block boost_migration_nb = {
.notifier_call = boost_migration_notify,
};
+static void do_input_boost(struct work_struct *work)
+{
+ unsigned int i, ret;
+ struct cpu_sync *i_sync_info;
+ struct cpufreq_policy policy;
+
+ for_each_online_cpu(i) {
+
+ i_sync_info = &per_cpu(sync_info, i);
+ ret = cpufreq_get_policy(&policy, i);
+ if (ret)
+ continue;
+ if (policy.cur >= input_boost_freq)
+ continue;
+
+ cancel_delayed_work_sync(&i_sync_info->input_boost_rem);
+ i_sync_info->input_boost_min = input_boost_freq;
+ cpufreq_update_policy(i);
+ queue_delayed_work_on(i_sync_info->cpu, cpu_boost_wq,
+ &i_sync_info->input_boost_rem,
+ msecs_to_jiffies(input_boost_ms));
+ }
+}
+
+static void cpuboost_input_event(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value)
+{
+ u64 now;
+
+ if (!input_boost_freq)
+ return;
+
+ now = ktime_to_us(ktime_get());
+ if (now - last_input_time < MIN_INPUT_INTERVAL)
+ return;
+
+ if (work_pending(&input_boost_work))
+ return;
+
+ queue_work(cpu_boost_wq, &input_boost_work);
+ last_input_time = ktime_to_us(ktime_get());
+}
+
+static int cpuboost_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 cpuboost_input_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id cpuboost_ids[] = {
+ /* multi-touch touchscreen */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT_MASK(EV_ABS) },
+ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+ BIT_MASK(ABS_MT_POSITION_X) |
+ BIT_MASK(ABS_MT_POSITION_Y) },
+ },
+ /* touchpad */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_KEYBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+ .absbit = { [BIT_WORD(ABS_X)] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
+ },
+ /* Keypad */
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+ .evbit = { BIT_MASK(EV_KEY) },
+ },
+ { },
+};
+
+static struct input_handler cpuboost_input_handler = {
+ .event = cpuboost_input_event,
+ .connect = cpuboost_input_connect,
+ .disconnect = cpuboost_input_disconnect,
+ .name = "cpu-boost",
+ .id_table = cpuboost_ids,
+};
+
static int cpu_boost_init(void)
{
- int cpu;
+ int cpu, ret;
struct cpu_sync *s;
cpufreq_register_notifier(&boost_adjust_nb, CPUFREQ_POLICY_NOTIFIER);
- boost_rem_wq = alloc_workqueue("cpuboost_rem_wq", WQ_HIGHPRI, 0);
- if (!boost_rem_wq)
+ cpu_boost_wq = alloc_workqueue("cpuboost_wq", WQ_HIGHPRI, 0);
+ if (!cpu_boost_wq)
return -EFAULT;
+ INIT_WORK(&input_boost_work, do_input_boost);
+
for_each_possible_cpu(cpu) {
s = &per_cpu(sync_info, cpu);
s->cpu = cpu;
init_waitqueue_head(&s->sync_wq);
spin_lock_init(&s->lock);
INIT_DELAYED_WORK(&s->boost_rem, do_boost_rem);
+ INIT_DELAYED_WORK(&s->input_boost_rem, do_input_boost_rem);
s->thread = kthread_run(boost_mig_sync_thread, (void *)cpu,
"boost_sync/%d", cpu);
}
atomic_notifier_chain_register(&migration_notifier_head,
&boost_migration_nb);
+ ret = input_register_handler(&cpuboost_input_handler);
return 0;
}
late_initcall(cpu_boost_init);