1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/cpufreq/cpufreq_conservative.c |
4 | * |
5 | * Copyright (C) 2001 Russell King |
6 | * (C) 2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>. |
7 | * Jun Nakajima <jun.nakajima@intel.com> |
8 | * (C) 2009 Alexander Clouter <alex@digriz.org.uk> |
9 | */ |
10 | |
11 | #include <linux/slab.h> |
12 | #include "cpufreq_governor.h" |
13 | |
14 | struct cs_policy_dbs_info { |
15 | struct policy_dbs_info policy_dbs; |
16 | unsigned int down_skip; |
17 | unsigned int requested_freq; |
18 | }; |
19 | |
20 | static inline struct cs_policy_dbs_info *to_dbs_info(struct policy_dbs_info *policy_dbs) |
21 | { |
22 | return container_of(policy_dbs, struct cs_policy_dbs_info, policy_dbs); |
23 | } |
24 | |
25 | struct cs_dbs_tuners { |
26 | unsigned int down_threshold; |
27 | unsigned int freq_step; |
28 | }; |
29 | |
30 | /* Conservative governor macros */ |
31 | #define DEF_FREQUENCY_UP_THRESHOLD (80) |
32 | #define DEF_FREQUENCY_DOWN_THRESHOLD (20) |
33 | #define DEF_FREQUENCY_STEP (5) |
34 | #define DEF_SAMPLING_DOWN_FACTOR (1) |
35 | #define MAX_SAMPLING_DOWN_FACTOR (10) |
36 | |
37 | static inline unsigned int get_freq_step(struct cs_dbs_tuners *cs_tuners, |
38 | struct cpufreq_policy *policy) |
39 | { |
40 | unsigned int freq_step = (cs_tuners->freq_step * policy->max) / 100; |
41 | |
42 | /* max freq cannot be less than 100. But who knows... */ |
43 | if (unlikely(freq_step == 0)) |
44 | freq_step = DEF_FREQUENCY_STEP; |
45 | |
46 | return freq_step; |
47 | } |
48 | |
49 | /* |
50 | * Every sampling_rate, we check, if current idle time is less than 20% |
51 | * (default), then we try to increase frequency. Every sampling_rate * |
52 | * sampling_down_factor, we check, if current idle time is more than 80% |
53 | * (default), then we try to decrease frequency |
54 | * |
55 | * Frequency updates happen at minimum steps of 5% (default) of maximum |
56 | * frequency |
57 | */ |
58 | static unsigned int cs_dbs_update(struct cpufreq_policy *policy) |
59 | { |
60 | struct policy_dbs_info *policy_dbs = policy->governor_data; |
61 | struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy_dbs); |
62 | unsigned int requested_freq = dbs_info->requested_freq; |
63 | struct dbs_data *dbs_data = policy_dbs->dbs_data; |
64 | struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; |
65 | unsigned int load = dbs_update(policy); |
66 | unsigned int freq_step; |
67 | |
68 | /* |
69 | * break out if we 'cannot' reduce the speed as the user might |
70 | * want freq_step to be zero |
71 | */ |
72 | if (cs_tuners->freq_step == 0) |
73 | goto out; |
74 | |
75 | /* |
76 | * If requested_freq is out of range, it is likely that the limits |
77 | * changed in the meantime, so fall back to current frequency in that |
78 | * case. |
79 | */ |
80 | if (requested_freq > policy->max || requested_freq < policy->min) { |
81 | requested_freq = policy->cur; |
82 | dbs_info->requested_freq = requested_freq; |
83 | } |
84 | |
85 | freq_step = get_freq_step(cs_tuners, policy); |
86 | |
87 | /* |
88 | * Decrease requested_freq one freq_step for each idle period that |
89 | * we didn't update the frequency. |
90 | */ |
91 | if (policy_dbs->idle_periods < UINT_MAX) { |
92 | unsigned int freq_steps = policy_dbs->idle_periods * freq_step; |
93 | |
94 | if (requested_freq > policy->min + freq_steps) |
95 | requested_freq -= freq_steps; |
96 | else |
97 | requested_freq = policy->min; |
98 | |
99 | policy_dbs->idle_periods = UINT_MAX; |
100 | } |
101 | |
102 | /* Check for frequency increase */ |
103 | if (load > dbs_data->up_threshold) { |
104 | dbs_info->down_skip = 0; |
105 | |
106 | /* if we are already at full speed then break out early */ |
107 | if (requested_freq == policy->max) |
108 | goto out; |
109 | |
110 | requested_freq += freq_step; |
111 | if (requested_freq > policy->max) |
112 | requested_freq = policy->max; |
113 | |
114 | __cpufreq_driver_target(policy, target_freq: requested_freq, |
115 | CPUFREQ_RELATION_HE); |
116 | dbs_info->requested_freq = requested_freq; |
117 | goto out; |
118 | } |
119 | |
120 | /* if sampling_down_factor is active break out early */ |
121 | if (++dbs_info->down_skip < dbs_data->sampling_down_factor) |
122 | goto out; |
123 | dbs_info->down_skip = 0; |
124 | |
125 | /* Check for frequency decrease */ |
126 | if (load < cs_tuners->down_threshold) { |
127 | /* |
128 | * if we cannot reduce the frequency anymore, break out early |
129 | */ |
130 | if (requested_freq == policy->min) |
131 | goto out; |
132 | |
133 | if (requested_freq > freq_step) |
134 | requested_freq -= freq_step; |
135 | else |
136 | requested_freq = policy->min; |
137 | |
138 | __cpufreq_driver_target(policy, target_freq: requested_freq, |
139 | CPUFREQ_RELATION_LE); |
140 | dbs_info->requested_freq = requested_freq; |
141 | } |
142 | |
143 | out: |
144 | return dbs_data->sampling_rate; |
145 | } |
146 | |
147 | /************************** sysfs interface ************************/ |
148 | |
149 | static ssize_t sampling_down_factor_store(struct gov_attr_set *attr_set, |
150 | const char *buf, size_t count) |
151 | { |
152 | struct dbs_data *dbs_data = to_dbs_data(attr_set); |
153 | unsigned int input; |
154 | int ret; |
155 | ret = sscanf(buf, "%u" , &input); |
156 | |
157 | if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) |
158 | return -EINVAL; |
159 | |
160 | dbs_data->sampling_down_factor = input; |
161 | return count; |
162 | } |
163 | |
164 | static ssize_t up_threshold_store(struct gov_attr_set *attr_set, |
165 | const char *buf, size_t count) |
166 | { |
167 | struct dbs_data *dbs_data = to_dbs_data(attr_set); |
168 | struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; |
169 | unsigned int input; |
170 | int ret; |
171 | ret = sscanf(buf, "%u" , &input); |
172 | |
173 | if (ret != 1 || input > 100 || input <= cs_tuners->down_threshold) |
174 | return -EINVAL; |
175 | |
176 | dbs_data->up_threshold = input; |
177 | return count; |
178 | } |
179 | |
180 | static ssize_t down_threshold_store(struct gov_attr_set *attr_set, |
181 | const char *buf, size_t count) |
182 | { |
183 | struct dbs_data *dbs_data = to_dbs_data(attr_set); |
184 | struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; |
185 | unsigned int input; |
186 | int ret; |
187 | ret = sscanf(buf, "%u" , &input); |
188 | |
189 | /* cannot be lower than 1 otherwise freq will not fall */ |
190 | if (ret != 1 || input < 1 || input >= dbs_data->up_threshold) |
191 | return -EINVAL; |
192 | |
193 | cs_tuners->down_threshold = input; |
194 | return count; |
195 | } |
196 | |
197 | static ssize_t ignore_nice_load_store(struct gov_attr_set *attr_set, |
198 | const char *buf, size_t count) |
199 | { |
200 | struct dbs_data *dbs_data = to_dbs_data(attr_set); |
201 | unsigned int input; |
202 | int ret; |
203 | |
204 | ret = sscanf(buf, "%u" , &input); |
205 | if (ret != 1) |
206 | return -EINVAL; |
207 | |
208 | if (input > 1) |
209 | input = 1; |
210 | |
211 | if (input == dbs_data->ignore_nice_load) /* nothing to do */ |
212 | return count; |
213 | |
214 | dbs_data->ignore_nice_load = input; |
215 | |
216 | /* we need to re-evaluate prev_cpu_idle */ |
217 | gov_update_cpu_data(dbs_data); |
218 | |
219 | return count; |
220 | } |
221 | |
222 | static ssize_t freq_step_store(struct gov_attr_set *attr_set, const char *buf, |
223 | size_t count) |
224 | { |
225 | struct dbs_data *dbs_data = to_dbs_data(attr_set); |
226 | struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; |
227 | unsigned int input; |
228 | int ret; |
229 | ret = sscanf(buf, "%u" , &input); |
230 | |
231 | if (ret != 1) |
232 | return -EINVAL; |
233 | |
234 | if (input > 100) |
235 | input = 100; |
236 | |
237 | /* |
238 | * no need to test here if freq_step is zero as the user might actually |
239 | * want this, they would be crazy though :) |
240 | */ |
241 | cs_tuners->freq_step = input; |
242 | return count; |
243 | } |
244 | |
245 | gov_show_one_common(sampling_rate); |
246 | gov_show_one_common(sampling_down_factor); |
247 | gov_show_one_common(up_threshold); |
248 | gov_show_one_common(ignore_nice_load); |
249 | gov_show_one(cs, down_threshold); |
250 | gov_show_one(cs, freq_step); |
251 | |
252 | gov_attr_rw(sampling_rate); |
253 | gov_attr_rw(sampling_down_factor); |
254 | gov_attr_rw(up_threshold); |
255 | gov_attr_rw(ignore_nice_load); |
256 | gov_attr_rw(down_threshold); |
257 | gov_attr_rw(freq_step); |
258 | |
259 | static struct attribute *cs_attrs[] = { |
260 | &sampling_rate.attr, |
261 | &sampling_down_factor.attr, |
262 | &up_threshold.attr, |
263 | &down_threshold.attr, |
264 | &ignore_nice_load.attr, |
265 | &freq_step.attr, |
266 | NULL |
267 | }; |
268 | ATTRIBUTE_GROUPS(cs); |
269 | |
270 | /************************** sysfs end ************************/ |
271 | |
272 | static struct policy_dbs_info *cs_alloc(void) |
273 | { |
274 | struct cs_policy_dbs_info *dbs_info; |
275 | |
276 | dbs_info = kzalloc(size: sizeof(*dbs_info), GFP_KERNEL); |
277 | return dbs_info ? &dbs_info->policy_dbs : NULL; |
278 | } |
279 | |
280 | static void cs_free(struct policy_dbs_info *policy_dbs) |
281 | { |
282 | kfree(objp: to_dbs_info(policy_dbs)); |
283 | } |
284 | |
285 | static int cs_init(struct dbs_data *dbs_data) |
286 | { |
287 | struct cs_dbs_tuners *tuners; |
288 | |
289 | tuners = kzalloc(size: sizeof(*tuners), GFP_KERNEL); |
290 | if (!tuners) |
291 | return -ENOMEM; |
292 | |
293 | tuners->down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD; |
294 | tuners->freq_step = DEF_FREQUENCY_STEP; |
295 | dbs_data->up_threshold = DEF_FREQUENCY_UP_THRESHOLD; |
296 | dbs_data->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR; |
297 | dbs_data->ignore_nice_load = 0; |
298 | dbs_data->tuners = tuners; |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | static void cs_exit(struct dbs_data *dbs_data) |
304 | { |
305 | kfree(objp: dbs_data->tuners); |
306 | } |
307 | |
308 | static void cs_start(struct cpufreq_policy *policy) |
309 | { |
310 | struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy_dbs: policy->governor_data); |
311 | |
312 | dbs_info->down_skip = 0; |
313 | dbs_info->requested_freq = policy->cur; |
314 | } |
315 | |
316 | static struct dbs_governor cs_governor = { |
317 | .gov = CPUFREQ_DBS_GOVERNOR_INITIALIZER("conservative" ), |
318 | .kobj_type = { .default_groups = cs_groups }, |
319 | .gov_dbs_update = cs_dbs_update, |
320 | .alloc = cs_alloc, |
321 | .free = cs_free, |
322 | .init = cs_init, |
323 | .exit = cs_exit, |
324 | .start = cs_start, |
325 | }; |
326 | |
327 | #define CPU_FREQ_GOV_CONSERVATIVE (cs_governor.gov) |
328 | |
329 | MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>" ); |
330 | MODULE_DESCRIPTION("'cpufreq_conservative' - A dynamic cpufreq governor for " |
331 | "Low Latency Frequency Transition capable processors " |
332 | "optimised for use in a battery environment" ); |
333 | MODULE_LICENSE("GPL" ); |
334 | |
335 | #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE |
336 | struct cpufreq_governor *cpufreq_default_governor(void) |
337 | { |
338 | return &CPU_FREQ_GOV_CONSERVATIVE; |
339 | } |
340 | #endif |
341 | |
342 | cpufreq_governor_init(CPU_FREQ_GOV_CONSERVATIVE); |
343 | cpufreq_governor_exit(CPU_FREQ_GOV_CONSERVATIVE); |
344 | |