soc: rockchip: add different performance level support

CONFIG_ROCKCHIP_PERFORMANCE_LEVEL=0 for low performance, low power consumption
CONFIG_ROCKCHIP_PERFORMANCE_LEVEL=1 for normal performance
CONFIG_ROCKCHIP_PERFORMANCE_LEVEL=2 for high performance, high power consumption

Change-Id: I7a88f1a2e43513f647a860b427d8344e34165fa6
Signed-off-by: Liang Chen <cl@rock-chips.com>
This commit is contained in:
Liang Chen
2022-03-04 09:13:06 +08:00
committed by Tao Huang
parent 157bfadfc2
commit e8a022189c
8 changed files with 229 additions and 0 deletions

View File

@@ -68,6 +68,27 @@ config ROCKCHIP_OPP
help
Say y here to enable rockchip OPP support.
config ROCKCHIP_PERFORMANCE
bool "Rockchip performance configuration support"
depends on NO_GKI
help
This config aims to support different requests between power consumption
and performance.
config ROCKCHIP_PERFORMANCE_LEVEL
int "Rockchip performance default level"
depends on ROCKCHIP_PERFORMANCE
range 0 2
default 1
help
Select default performance level:
0 for low-performance (powersave),
1 for normal performance,
2 for high-performance.
This can also be changed at runtime (via the level module parameter).
config ROCKCHIP_PM_DOMAINS
tristate "Rockchip generic power domain"
depends on PM

View File

@@ -14,6 +14,7 @@ obj-$(CONFIG_ROCKCHIP_FLASH_VENDOR_STORAGE) += flash_vendor_storage.o
obj-$(CONFIG_ROCKCHIP_MTD_VENDOR_STORAGE) += mtd_vendor_storage.o
obj-$(CONFIG_ROCKCHIP_IPA) += rockchip_ipa.o
obj-$(CONFIG_ROCKCHIP_OPP) += rockchip_opp_select.o
obj-$(CONFIG_ROCKCHIP_PERFORMANCE) += rockchip_performance.o
obj-$(CONFIG_ROCKCHIP_PVTM) += rockchip_pvtm.o
obj-$(CONFIG_ROCKCHIP_RAMDISK) += rockchip_ramdisk.o
obj-$(CONFIG_ROCKCHIP_SUSPEND_MODE) += rockchip_pm_config.o

View File

@@ -0,0 +1,162 @@
// 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 <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)
{
sysctl_sched_uclamp_util_min_rt_default = 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;
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) == SCHED_CAPACITY_SCALE)
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;
}
#ifdef CONFIG_SMP
int rockchip_perf_select_rt_cpu(int prev_cpu, struct cpumask *lowest_mask)
{
int cpu = nr_cpu_ids;
if (!perf_init_done)
return prev_cpu;
if (static_branch_unlikely(&sched_asym_cpucapacity)) {
if (perf_level == 0)
cpu = cpumask_first_and(lowest_mask, cpul_mask);
if (perf_level == 2)
cpu = cpumask_first_and(lowest_mask, cpub_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 */

View File

@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2022 Rockchip Electronics Co., Ltd
*/
#ifndef __SOC_ROCKCHIP_PERFORMANCE_H
#define __SOC_ROCKCHIP_PERFORMANCE_H
#ifdef CONFIG_ROCKCHIP_PERFORMANCE
extern int rockchip_perf_get_level(void);
extern int rockchip_perf_select_rt_cpu(int prev_cpu, struct cpumask *lowest_mask);
extern bool rockchip_perf_misfit_rt(int cpu);
extern void rockchip_perf_uclamp_sync_util_min_rt_default(void);
#else
static inline int rockchip_perf_get_level(void) { return 1; }
static inline int rockchip_perf_select_rt_cpu(int prev_cpu, struct cpumask *lowest_mask)
{
return prev_cpu;
}
static inline bool rockchip_perf_misfit_rt(int cpu) { return false; }
static inline void rockchip_perf_uclamp_sync_util_min_rt_default(void) {}
#endif
#endif

View File

@@ -1098,6 +1098,14 @@ static void uclamp_sync_util_min_rt_default(void)
rcu_read_unlock();
}
#if IS_ENABLED(CONFIG_ROCKCHIP_PERFORMANCE)
void rockchip_perf_uclamp_sync_util_min_rt_default(void)
{
uclamp_sync_util_min_rt_default();
}
EXPORT_SYMBOL(rockchip_perf_uclamp_sync_util_min_rt_default);
#endif
static inline struct uclamp_se
uclamp_tg_restrict(struct task_struct *p, enum uclamp_id clamp_id)
{

View File

@@ -6745,6 +6745,10 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, int sy
prev_delta = compute_energy(p, prev_cpu, pd);
prev_delta -= base_energy_pd;
best_delta = min(best_delta, prev_delta);
if (IS_ENABLED(CONFIG_ROCKCHIP_PERFORMANCE)) {
if (prev_delta == best_delta)
best_energy_cpu = prev_cpu;
}
}
/*
@@ -6804,6 +6808,11 @@ unlock:
if (prev_delta == ULONG_MAX)
return best_energy_cpu;
if (IS_ENABLED(CONFIG_ROCKCHIP_PERFORMANCE)) {
if (rockchip_perf_get_level() == 0)
return best_energy_cpu;
}
if ((prev_delta - best_delta) > ((prev_delta + base_energy) >> 4))
return best_energy_cpu;

View File

@@ -1546,6 +1546,8 @@ select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags)
(unlikely(rt_task(curr)) &&
(curr->nr_cpus_allowed < 2 || curr->prio <= p->prio))));
if (IS_ENABLED(CONFIG_ROCKCHIP_PERFORMANCE))
test |= rockchip_perf_misfit_rt(cpu);
/*
* Respect the sync flag as long as the task can run on this CPU.
*/
@@ -1815,6 +1817,8 @@ static int find_lowest_rq(struct task_struct *task)
cpu = task_cpu(task);
if (IS_ENABLED(CONFIG_ROCKCHIP_PERFORMANCE))
cpu = rockchip_perf_select_rt_cpu(cpu, lowest_mask);
/*
* At this point we have built a mask of CPUs representing the
* lowest priority tasks in the system. Now we want to elect

View File

@@ -70,6 +70,7 @@
#include <asm/tlb.h>
#include <asm-generic/vmlinux.lds.h>
#include <soc/rockchip/rockchip_performance.h>
#ifdef CONFIG_PARAVIRT
# include <asm/paravirt.h>