186 lines
4.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2022 Rockchip Electronics Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/sched/cputime.h>
#include <soc/rockchip/rockchip_performance.h>
#include <../../kernel/sched/sched.h>
static int perf_level = CONFIG_ROCKCHIP_PERFORMANCE_LEVEL;
static cpumask_var_t cpul_mask, cpub_mask;
static bool perf_init_done;
static DEFINE_MUTEX(update_mutex);
#ifdef CONFIG_UCLAMP_TASK
static inline void set_uclamp_util_min_rt(unsigned int util)
{
static_branch_enable(&sched_uclamp_used);
rockchip_perf_uclamp_sync_util_min_rt_default();
}
#else
static inline void set_uclamp_util_min_rt(unsigned int util) { };
#endif
static void update_perf_level_locked(int level)
{
struct em_perf_domain *em;
unsigned long target_cost, target_freq, max_freq;
unsigned long scale_cpu0 = arch_scale_cpu_capacity(0);
unsigned int uclamp_util_min_rt = scale_cpu0 * 2 / 3;
int i;
if (perf_init_done && perf_level == level)
return;
perf_level = level;
if (level == 0) {
set_uclamp_util_min_rt(0);
return;
}
if ((level == 1) || (level == 2)) {
set_uclamp_util_min_rt(SCHED_CAPACITY_SCALE);
return;
}
/* find a better efficient frequency and consider performance */
em = em_cpu_get(0);
if (em) {
target_cost = em->table[0].cost + (em->table[0].cost >> 2);
for (i = 1; i < em->nr_perf_states; i++) {
if (em->table[i].cost >= target_cost)
break;
}
target_freq = em->table[i-1].frequency;
max_freq = em->table[em->nr_perf_states-1].frequency;
uclamp_util_min_rt = scale_cpu0 * target_freq / max_freq;
}
/* schedutil will reserve 20% util, and we need more 5% for debounce */
uclamp_util_min_rt = uclamp_util_min_rt * 3 / 4;
set_uclamp_util_min_rt(uclamp_util_min_rt);
}
static void update_perf_level(int level)
{
mutex_lock(&update_mutex);
update_perf_level_locked(level);
mutex_unlock(&update_mutex);
}
static int param_set_level(const char *buf, const struct kernel_param *kp)
{
int ret, level;
ret = kstrtoint(buf, 10, &level);
if (ret || (level < 0) || (level > 2))
return -EINVAL;
if (!perf_init_done)
return 0;
update_perf_level(level);
return 0;
}
static const struct kernel_param_ops level_param_ops = {
.set = param_set_level,
.get = param_get_int,
};
module_param_cb(level, &level_param_ops, &perf_level, 0644);
static __init int rockchip_perf_init(void)
{
int cpu;
int cpub_min_cap = SCHED_CAPACITY_SCALE - (SCHED_CAPACITY_SCALE >> 3);
if (!zalloc_cpumask_var(&cpul_mask, GFP_KERNEL))
return -ENOMEM;
if (!zalloc_cpumask_var(&cpub_mask, GFP_KERNEL))
return -ENOMEM;
for_each_possible_cpu(cpu) {
if (arch_scale_cpu_capacity(cpu) > cpub_min_cap)
cpumask_set_cpu(cpu, cpub_mask);
else
cpumask_set_cpu(cpu, cpul_mask);
}
update_perf_level(perf_level);
perf_init_done = true;
return 0;
}
late_initcall_sync(rockchip_perf_init);
int rockchip_perf_get_level(void)
{
return perf_level;
}
struct cpumask *rockchip_perf_get_cpul_mask(void)
{
if (static_branch_unlikely(&sched_asym_cpucapacity))
return cpul_mask;
return NULL;
}
struct cpumask *rockchip_perf_get_cpub_mask(void)
{
if (static_branch_unlikely(&sched_asym_cpucapacity))
return cpub_mask;
return NULL;
}
#ifdef CONFIG_SMP
int rockchip_perf_select_rt_cpu(int prev_cpu, struct cpumask *lowest_mask)
{
struct cpumask target_mask;
int cpu;
if (!perf_init_done)
return prev_cpu;
if (static_branch_unlikely(&sched_asym_cpucapacity)) {
if (perf_level == 0)
cpumask_and(&target_mask, lowest_mask, cpul_mask);
if (perf_level == 2)
cpumask_and(&target_mask, lowest_mask, cpub_mask);
if (cpumask_test_cpu(prev_cpu, &target_mask))
return prev_cpu;
cpu = cpumask_first(&target_mask);
if (cpu < nr_cpu_ids)
return cpu;
}
return prev_cpu;
}
bool rockchip_perf_misfit_rt(int cpu)
{
if (!perf_init_done)
return false;
if (static_branch_unlikely(&sched_asym_cpucapacity)) {
if ((perf_level == 0) && cpumask_test_cpu(cpu, cpub_mask))
return true;
if ((perf_level == 2) && cpumask_test_cpu(cpu, cpul_mask))
return true;
}
return false;
}
#endif /* CONFIG_SMP */