123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2013-2015,2017,2019-2021, The Linux Foundation. All rights reserved.
- */
- #define pr_fmt(fmt) "input-boost: " fmt
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/cpufreq.h>
- #include <linux/cpu.h>
- #include <linux/sched.h>
- #include <linux/slab.h>
- #include <linux/input.h>
- #include <linux/time.h>
- #include <linux/sysfs.h>
- #include <linux/pm_qos.h>
- #include "walt.h"
- #define input_boost_attr_rw(_name) \
- static struct kobj_attribute _name##_attr = \
- __ATTR(_name, 0644, show_##_name, store_##_name)
- #define show_one(file_name) \
- static ssize_t show_##file_name \
- (struct kobject *kobj, struct kobj_attribute *attr, char *buf) \
- { \
- return scnprintf(buf, PAGE_SIZE, "%u\n", file_name); \
- }
- #define store_one(file_name) \
- static ssize_t store_##file_name \
- (struct kobject *kobj, struct kobj_attribute *attr, \
- const char *buf, size_t count) \
- { \
- \
- sscanf(buf, "%u", &file_name); \
- return count; \
- }
- struct cpu_sync {
- int cpu;
- unsigned int input_boost_min;
- unsigned int input_boost_freq;
- };
- static DEFINE_PER_CPU(struct cpu_sync, sync_info);
- static struct workqueue_struct *input_boost_wq;
- static struct work_struct input_boost_work;
- static bool sched_boost_active;
- static struct delayed_work input_boost_rem;
- static u64 last_input_time;
- #define MIN_INPUT_INTERVAL (150 * USEC_PER_MSEC)
- static DEFINE_PER_CPU(struct freq_qos_request, qos_req);
- static void boost_adjust_notify(struct cpufreq_policy *policy)
- {
- unsigned int cpu = policy->cpu;
- struct cpu_sync *s = &per_cpu(sync_info, cpu);
- unsigned int ib_min = s->input_boost_min;
- struct freq_qos_request *req = &per_cpu(qos_req, cpu);
- int ret;
- pr_debug("CPU%u policy min before boost: %u kHz\n",
- cpu, policy->min);
- pr_debug("CPU%u boost min: %u kHz\n", cpu, ib_min);
- ret = freq_qos_update_request(req, ib_min);
- if (ret < 0)
- pr_err("Failed to update freq constraint in boost_adjust: %d\n",
- ib_min);
- pr_debug("CPU%u policy min after boost: %u kHz\n", cpu, policy->min);
- }
- static void update_policy_online(void)
- {
- unsigned int i;
- struct cpufreq_policy *policy;
- struct cpumask online_cpus;
- /* Re-evaluate policy to trigger adjust notifier for online CPUs */
- cpus_read_lock();
- online_cpus = *cpu_online_mask;
- for_each_cpu(i, &online_cpus) {
- policy = cpufreq_cpu_get(i);
- if (!policy) {
- pr_err("%s: cpufreq policy not found for cpu%d\n",
- __func__, i);
- return;
- }
- cpumask_andnot(&online_cpus, &online_cpus,
- policy->related_cpus);
- boost_adjust_notify(policy);
- }
- cpus_read_unlock();
- }
- static void do_input_boost_rem(struct work_struct *work)
- {
- unsigned int i, ret;
- struct cpu_sync *i_sync_info;
- /* Reset the input_boost_min for all CPUs in the system */
- pr_debug("Resetting input boost min for all CPUs\n");
- for_each_possible_cpu(i) {
- i_sync_info = &per_cpu(sync_info, i);
- i_sync_info->input_boost_min = 0;
- }
- /* Update policies for all online CPUs */
- update_policy_online();
- if (sched_boost_active) {
- ret = sched_set_boost(0);
- if (!ret)
- pr_err("input-boost: sched boost disable failed\n");
- sched_boost_active = false;
- }
- }
- static void do_input_boost(struct work_struct *work)
- {
- unsigned int cpu, ret;
- struct cpu_sync *i_sync_info;
- cancel_delayed_work_sync(&input_boost_rem);
- if (sched_boost_active) {
- sched_set_boost(0);
- sched_boost_active = false;
- }
- /* Set the input_boost_min for all CPUs in the system */
- pr_debug("Setting input boost min for all CPUs\n");
- for_each_possible_cpu(cpu) {
- i_sync_info = &per_cpu(sync_info, cpu);
- i_sync_info->input_boost_min = sysctl_input_boost_freq[cpu];
- }
- /* Update policies for all online CPUs */
- update_policy_online();
- /* Enable scheduler boost to migrate tasks to big cluster */
- if (sysctl_sched_boost_on_input > 0) {
- ret = sched_set_boost(sysctl_sched_boost_on_input);
- if (ret)
- pr_err("input-boost: sched boost enable failed\n");
- else
- sched_boost_active = true;
- }
- queue_delayed_work(input_boost_wq, &input_boost_rem,
- msecs_to_jiffies(sysctl_input_boost_ms));
- }
- static void inputboost_input_event(struct input_handle *handle,
- unsigned int type, unsigned int code, int value)
- {
- u64 now;
- int cpu;
- int enabled = 0;
- for_each_possible_cpu(cpu) {
- if (sysctl_input_boost_freq[cpu] > 0) {
- enabled = 1;
- break;
- }
- }
- if (!enabled)
- 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(input_boost_wq, &input_boost_work);
- last_input_time = ktime_to_us(ktime_get());
- }
- static int inputboost_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 inputboost_input_disconnect(struct input_handle *handle)
- {
- input_close_device(handle);
- input_unregister_handle(handle);
- kfree(handle);
- }
- static const struct input_device_id inputboost_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 inputboost_input_handler = {
- .event = inputboost_input_event,
- .connect = inputboost_input_connect,
- .disconnect = inputboost_input_disconnect,
- .name = "input-boost",
- .id_table = inputboost_ids,
- };
- struct kobject *input_boost_kobj;
- int input_boost_init(void)
- {
- int cpu, ret;
- struct cpu_sync *s;
- struct cpufreq_policy *policy;
- struct freq_qos_request *req;
- input_boost_wq = alloc_workqueue("inputboost_wq", WQ_HIGHPRI, 0);
- if (!input_boost_wq)
- return -EFAULT;
- INIT_WORK(&input_boost_work, do_input_boost);
- INIT_DELAYED_WORK(&input_boost_rem, do_input_boost_rem);
- for_each_possible_cpu(cpu) {
- s = &per_cpu(sync_info, cpu);
- s->cpu = cpu;
- req = &per_cpu(qos_req, cpu);
- policy = cpufreq_cpu_get(cpu);
- if (!policy) {
- pr_err("%s: cpufreq policy not found for cpu%d\n",
- __func__, cpu);
- return -ESRCH;
- }
- ret = freq_qos_add_request(&policy->constraints, req,
- FREQ_QOS_MIN, policy->min);
- if (ret < 0) {
- pr_err("%s: Failed to add freq constraint (%d)\n",
- __func__, ret);
- return ret;
- }
- }
- ret = input_register_handler(&inputboost_input_handler);
- return 0;
- }
|