diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 84435eadaf52..766f36c56e47 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -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 diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index 29a21b7f6b94..5371974689dd 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -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 diff --git a/drivers/soc/rockchip/rockchip_performance.c b/drivers/soc/rockchip/rockchip_performance.c new file mode 100644 index 000000000000..005bf7b1b578 --- /dev/null +++ b/drivers/soc/rockchip/rockchip_performance.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + */ +#include +#include +#include +#include +#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 */ diff --git a/include/soc/rockchip/rockchip_performance.h b/include/soc/rockchip/rockchip_performance.h new file mode 100644 index 000000000000..629a4c01a82c --- /dev/null +++ b/include/soc/rockchip/rockchip_performance.h @@ -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 diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6d7e7881813c..31dc0f5d428c 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -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) { diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7d009aaccb72..acdad507b044 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -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; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 1c4b0b14777f..e067dd59417b 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -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 diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 518da844fe2a..af46fcbae927 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -70,6 +70,7 @@ #include #include +#include #ifdef CONFIG_PARAVIRT # include