cpufreq: Add Input Boost feature to the cpu-boost driver
On incoming input events boost the frequency of all online cpus for at least input_boost_ms ms. This is accomplished by changing the policy->min of all the online cpus to input_boost_freq. Change-Id: Idb0ab75d68ae4ceff259cbbaaec1a9bb3bc871d3 Signed-off-by: Rohit Gupta <rohgup@codeaurora.org>
This commit is contained in:
parent
43cc939d93
commit
e80ff9a87c
1 changed files with 157 additions and 7 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue