1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun #include <linux/kernel.h>
6*4882a593Smuzhiyun #include <linux/proc_fs.h>
7*4882a593Smuzhiyun #include <linux/seq_file.h>
8*4882a593Smuzhiyun #include <soc/rockchip/rockchip_performance.h>
9*4882a593Smuzhiyun #include <../../kernel/sched/sched.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun static int perf_level = CONFIG_ROCKCHIP_PERFORMANCE_LEVEL;
12*4882a593Smuzhiyun static cpumask_var_t cpul_mask, cpub_mask;
13*4882a593Smuzhiyun static bool perf_init_done;
14*4882a593Smuzhiyun static DEFINE_MUTEX(update_mutex);
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #ifdef CONFIG_UCLAMP_TASK
set_uclamp_util_min_rt(unsigned int util)17*4882a593Smuzhiyun static inline void set_uclamp_util_min_rt(unsigned int util)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun sysctl_sched_uclamp_util_min_rt_default = util;
20*4882a593Smuzhiyun static_branch_enable(&sched_uclamp_used);
21*4882a593Smuzhiyun rockchip_perf_uclamp_sync_util_min_rt_default();
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun #else
set_uclamp_util_min_rt(unsigned int util)24*4882a593Smuzhiyun static inline void set_uclamp_util_min_rt(unsigned int util) { };
25*4882a593Smuzhiyun #endif
26*4882a593Smuzhiyun
update_perf_level_locked(int level)27*4882a593Smuzhiyun static void update_perf_level_locked(int level)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun struct em_perf_domain *em;
30*4882a593Smuzhiyun unsigned long target_cost, target_freq, max_freq;
31*4882a593Smuzhiyun unsigned long scale_cpu0 = arch_scale_cpu_capacity(0);
32*4882a593Smuzhiyun unsigned int uclamp_util_min_rt = scale_cpu0 * 2 / 3;
33*4882a593Smuzhiyun int i;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun if (perf_init_done && perf_level == level)
36*4882a593Smuzhiyun return;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun perf_level = level;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun if (level == 0) {
41*4882a593Smuzhiyun set_uclamp_util_min_rt(0);
42*4882a593Smuzhiyun return;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun if ((level == 1) || (level == 2)) {
46*4882a593Smuzhiyun set_uclamp_util_min_rt(SCHED_CAPACITY_SCALE);
47*4882a593Smuzhiyun return;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun /* find a better efficient frequency and consider performance */
51*4882a593Smuzhiyun em = em_cpu_get(0);
52*4882a593Smuzhiyun if (em) {
53*4882a593Smuzhiyun target_cost = em->table[0].cost + (em->table[0].cost >> 2);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun for (i = 1; i < em->nr_perf_states; i++) {
56*4882a593Smuzhiyun if (em->table[i].cost >= target_cost)
57*4882a593Smuzhiyun break;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun target_freq = em->table[i-1].frequency;
60*4882a593Smuzhiyun max_freq = em->table[em->nr_perf_states-1].frequency;
61*4882a593Smuzhiyun uclamp_util_min_rt = scale_cpu0 * target_freq / max_freq;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /* schedutil will reserve 20% util, and we need more 5% for debounce */
65*4882a593Smuzhiyun uclamp_util_min_rt = uclamp_util_min_rt * 3 / 4;
66*4882a593Smuzhiyun set_uclamp_util_min_rt(uclamp_util_min_rt);
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
update_perf_level(int level)69*4882a593Smuzhiyun static void update_perf_level(int level)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun mutex_lock(&update_mutex);
72*4882a593Smuzhiyun update_perf_level_locked(level);
73*4882a593Smuzhiyun mutex_unlock(&update_mutex);
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
param_set_level(const char * buf,const struct kernel_param * kp)76*4882a593Smuzhiyun static int param_set_level(const char *buf, const struct kernel_param *kp)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun int ret, level;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun ret = kstrtoint(buf, 10, &level);
81*4882a593Smuzhiyun if (ret || (level < 0) || (level > 2))
82*4882a593Smuzhiyun return -EINVAL;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (!perf_init_done)
85*4882a593Smuzhiyun return 0;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun update_perf_level(level);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun return 0;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun static const struct kernel_param_ops level_param_ops = {
93*4882a593Smuzhiyun .set = param_set_level,
94*4882a593Smuzhiyun .get = param_get_int,
95*4882a593Smuzhiyun };
96*4882a593Smuzhiyun module_param_cb(level, &level_param_ops, &perf_level, 0644);
97*4882a593Smuzhiyun
rockchip_perf_init(void)98*4882a593Smuzhiyun static __init int rockchip_perf_init(void)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun int cpu;
101*4882a593Smuzhiyun int cpub_min_cap = SCHED_CAPACITY_SCALE - (SCHED_CAPACITY_SCALE >> 3);
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun if (!zalloc_cpumask_var(&cpul_mask, GFP_KERNEL))
104*4882a593Smuzhiyun return -ENOMEM;
105*4882a593Smuzhiyun if (!zalloc_cpumask_var(&cpub_mask, GFP_KERNEL))
106*4882a593Smuzhiyun return -ENOMEM;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun for_each_possible_cpu(cpu) {
109*4882a593Smuzhiyun if (arch_scale_cpu_capacity(cpu) > cpub_min_cap)
110*4882a593Smuzhiyun cpumask_set_cpu(cpu, cpub_mask);
111*4882a593Smuzhiyun else
112*4882a593Smuzhiyun cpumask_set_cpu(cpu, cpul_mask);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun update_perf_level(perf_level);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun perf_init_done = true;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun return 0;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun late_initcall_sync(rockchip_perf_init);
122*4882a593Smuzhiyun
rockchip_perf_get_level(void)123*4882a593Smuzhiyun int rockchip_perf_get_level(void)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun return perf_level;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
rockchip_perf_get_cpul_mask(void)128*4882a593Smuzhiyun struct cpumask *rockchip_perf_get_cpul_mask(void)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun if (static_branch_unlikely(&sched_asym_cpucapacity))
131*4882a593Smuzhiyun return cpul_mask;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun return NULL;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
rockchip_perf_get_cpub_mask(void)136*4882a593Smuzhiyun struct cpumask *rockchip_perf_get_cpub_mask(void)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun if (static_branch_unlikely(&sched_asym_cpucapacity))
139*4882a593Smuzhiyun return cpub_mask;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun return NULL;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun #ifdef CONFIG_SMP
rockchip_perf_select_rt_cpu(int prev_cpu,struct cpumask * lowest_mask)145*4882a593Smuzhiyun int rockchip_perf_select_rt_cpu(int prev_cpu, struct cpumask *lowest_mask)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct cpumask target_mask;
148*4882a593Smuzhiyun int cpu = nr_cpu_ids;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun if (!perf_init_done)
151*4882a593Smuzhiyun return prev_cpu;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (static_branch_unlikely(&sched_asym_cpucapacity)) {
154*4882a593Smuzhiyun if (perf_level == 0)
155*4882a593Smuzhiyun cpumask_and(&target_mask, lowest_mask, cpul_mask);
156*4882a593Smuzhiyun if (perf_level == 2)
157*4882a593Smuzhiyun cpumask_and(&target_mask, lowest_mask, cpub_mask);
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (cpumask_test_cpu(prev_cpu, &target_mask))
160*4882a593Smuzhiyun return prev_cpu;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun cpu = cpumask_first(&target_mask);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun if (cpu < nr_cpu_ids)
165*4882a593Smuzhiyun return cpu;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun return prev_cpu;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
rockchip_perf_misfit_rt(int cpu)171*4882a593Smuzhiyun bool rockchip_perf_misfit_rt(int cpu)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun if (!perf_init_done)
174*4882a593Smuzhiyun return false;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (static_branch_unlikely(&sched_asym_cpucapacity)) {
177*4882a593Smuzhiyun if ((perf_level == 0) && cpumask_test_cpu(cpu, cpub_mask))
178*4882a593Smuzhiyun return true;
179*4882a593Smuzhiyun if ((perf_level == 2) && cpumask_test_cpu(cpu, cpul_mask))
180*4882a593Smuzhiyun return true;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun return false;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun #endif /* CONFIG_SMP */
186