unplugged-kernel/drivers/misc/mediatek/performance/tchbst/kernel/ktch.c

393 lines
8.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#define pr_fmt(fmt) "[ktch]"fmt
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/input.h>
#include "tchbst.h"
#include "boost_ctrl.h"
#include "mtk_perfmgr_internal.h"
#define MAX_CORE (8)
#define MAX_FREQ (20000000)
#define TARGET_CORE (-1)
#define TARGET_FREQ (1183000)
struct boost {
spinlock_t touch_lock;
wait_queue_head_t wq;
struct task_struct *thread;
int touch_event;
atomic_t event;
};
/*--------------------------------------------*/
static struct boost ktchboost;
static int ktch_mgr_enable = 1;
static int ktch_mgr_core = 1;
static int ktch_mgr_freq = 1;
static int ktch_mgr_clstr = 1;
/*--------------------FUNCTION----------------*/
int ktch_get_target_core(void)
{
return TARGET_CORE;
}
int ktch_get_target_freq(void)
{
return TARGET_FREQ;
}
void set_freq(int enable, int core, int freq)
{
struct cpu_ctrl_data freq_to_set[perfmgr_clusters];
int i, targetclu;
targetclu = get_min_clstr_cap();
for (i = 0 ; i < perfmgr_clusters ; i++) {
freq_to_set[i].min = -1;
freq_to_set[i].max = -1;
}
if (enable)
freq_to_set[targetclu].min = freq;
update_userlimit_cpu_freq(CPU_KIR_PERFTOUCH,
perfmgr_clusters, freq_to_set);
}
static int ktchboost_thread(void *ptr)
{
int event, core, freq;
unsigned long flags;
set_user_nice(current, -10);
while (!kthread_should_stop()) {
while (!atomic_read(&ktchboost.event))
wait_event(ktchboost.wq, atomic_read(&ktchboost.event));
atomic_dec(&ktchboost.event);
spin_lock_irqsave(&ktchboost.touch_lock, flags);
event = ktchboost.touch_event;
core = ktch_mgr_core;
freq = ktch_mgr_freq;
spin_unlock_irqrestore(&ktchboost.touch_lock, flags);
pr_debug("%s\n", __func__);
set_freq(event, core, freq);
}
return 0;
}
static ssize_t perfmgr_tb_enable_write(struct file *filp, const char *ubuf,
size_t cnt, loff_t *data)
{
char buf[64];
unsigned long val;
int ret;
unsigned long flags;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
ret = kstrtoul(buf, 10, &val);
if (ret < 0)
return ret;
if (val > 1)
return -1;
spin_lock_irqsave(&ktchboost.touch_lock, flags);
ktch_mgr_enable = val;
spin_unlock_irqrestore(&ktchboost.touch_lock, flags);
return cnt;
}
static int perfmgr_tb_enable_show(struct seq_file *m, void *v)
{
if (m)
seq_printf(m, "%d\n", ktch_mgr_enable);
return 0;
}
static int perfmgr_tb_enable_open(struct inode *inode, struct file *file)
{
return single_open(file, perfmgr_tb_enable_show, inode->i_private);
}
static const struct file_operations perfmgr_tb_enable_fops = {
.open = perfmgr_tb_enable_open,
.write = perfmgr_tb_enable_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t perfmgr_tb_core_write(struct file *filp, const char *ubuf,
size_t cnt, loff_t *data)
{
char buf[64];
unsigned long val;
int ret;
unsigned long flags;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
ret = kstrtoul(buf, 10, &val);
if (ret < 0)
return ret;
if (val > MAX_CORE)
return -1;
spin_lock_irqsave(&ktchboost.touch_lock, flags);
ktch_mgr_core = val;
spin_unlock_irqrestore(&ktchboost.touch_lock, flags);
return cnt;
}
static int perfmgr_tb_core_show(struct seq_file *m, void *v)
{
if (m)
seq_printf(m, "%d\n", ktch_mgr_core);
return 0;
}
static int perfmgr_tb_core_open(struct inode *inode, struct file *file)
{
return single_open(file, perfmgr_tb_core_show, inode->i_private);
}
static const struct file_operations perfmgr_tb_core_fops = {
.open = perfmgr_tb_core_open,
.write = perfmgr_tb_core_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t perfmgr_tb_freq_write(struct file *filp, const char *ubuf,
size_t cnt, loff_t *data)
{
char buf[64];
unsigned long val;
int ret;
unsigned long flags;
if (cnt >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, ubuf, cnt))
return -EFAULT;
buf[cnt] = 0;
ret = kstrtoul(buf, 10, &val);
if (ret < 0)
return ret;
if (val > MAX_FREQ)
return -1;
spin_lock_irqsave(&ktchboost.touch_lock, flags);
ktch_mgr_freq = val;
spin_unlock_irqrestore(&ktchboost.touch_lock, flags);
return cnt;
}
static int perfmgr_tb_freq_show(struct seq_file *m, void *v)
{
if (m)
seq_printf(m, "%d\n", ktch_mgr_freq);
return 0;
}
static int perfmgr_tb_freq_open(struct inode *inode, struct file *file)
{
return single_open(file, perfmgr_tb_freq_show, inode->i_private);
}
static const struct file_operations perfmgr_tb_freq_fops = {
.open = perfmgr_tb_freq_open,
.write = perfmgr_tb_freq_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int perfmgr_tb_clstr_show(struct seq_file *m, void *v)
{
if (m)
seq_printf(m, "%d\n", ktch_mgr_clstr);
return 0;
}
static int perfmgr_tb_clstr_open(struct inode *inode, struct file *file)
{
return single_open(file, perfmgr_tb_clstr_show, inode->i_private);
}
static const struct file_operations perfmgr_tb_clstr_fops = {
.open = perfmgr_tb_clstr_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void dbs_input_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
unsigned long flags;
if (!ktch_mgr_enable)
return;
if ((type == EV_KEY) && (code == BTN_TOUCH)) {
pr_debug("input cb, type:%d, code:%d, value:%d\n",
type, code, value);
spin_lock_irqsave(&ktchboost.touch_lock, flags);
ktchboost.touch_event = value;
spin_unlock_irqrestore(&ktchboost.touch_lock, flags);
atomic_inc(&ktchboost.event);
wake_up(&ktchboost.wq);
}
}
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 = "perfmgr";
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,
};
/*--------------------INIT------------------------*/
int init_ktch(struct proc_dir_entry *parent)
{
struct proc_dir_entry *ktch_root = NULL;
struct proc_dir_entry *tbe_dir, *tbc_dir, *tbf_dir, *tbclstr_dir;
int handle;
pr_debug("init_ktch_touch\n");
ktch_mgr_core = ktch_get_target_core();
ktch_mgr_freq = ktch_get_target_freq();
ktch_mgr_clstr = perfmgr_clusters;
/*create kernel touch root file*/
ktch_root = proc_mkdir("kernel", parent);
if (!ktch_root)
pr_debug("ktch_root not create\n");
/* touch */
tbe_dir = proc_create("tb_enable", 0644, ktch_root,
&perfmgr_tb_enable_fops);
if (!tbe_dir)
pr_debug("tbe_dir not create\n");
tbc_dir = proc_create("tb_core", 0644, ktch_root,
&perfmgr_tb_core_fops);
if (!tbc_dir)
pr_debug("tbc_dir not create\n");
tbf_dir = proc_create("tb_freq", 0644, ktch_root,
&perfmgr_tb_freq_fops);
if (!tbf_dir)
pr_debug("tbf_dir not create\n");
tbclstr_dir = proc_create("tb_clstr", 0644, ktch_root,
&perfmgr_tb_clstr_fops);
if (!tbclstr_dir)
pr_debug("tbclstr_dir not create\n");
spin_lock_init(&ktchboost.touch_lock);
init_waitqueue_head(&ktchboost.wq);
atomic_set(&ktchboost.event, 0);
ktchboost.thread = (struct task_struct *)kthread_run(ktchboost_thread,
&ktchboost, "touch_boost");
if (IS_ERR(ktchboost.thread))
return -EINVAL;
handle = input_register_handler(&dbs_input_handler);
return 0;
}
int ktch_suspend(void)
{
/*pr_debug(TAG"perfmgr_touch_suspend\n");*/
set_freq(0, 0, 0);
return 0;
}