Commit | Line | Data |
---|---|---|
0de967f2 | 1 | // SPDX-License-Identifier: GPL-2.0 |
6b775e87 JM |
2 | /* |
3 | * A power allocator to manage temperature | |
4 | * | |
5 | * Copyright (C) 2014 ARM Ltd. | |
6 | * | |
6b775e87 JM |
7 | */ |
8 | ||
9 | #define pr_fmt(fmt) "Power allocator: " fmt | |
10 | ||
11 | #include <linux/rculist.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/thermal.h> | |
14 | ||
6828a471 JM |
15 | #define CREATE_TRACE_POINTS |
16 | #include <trace/events/thermal_power_allocator.h> | |
17 | ||
6b775e87 JM |
18 | #include "thermal_core.h" |
19 | ||
8b7b390f JM |
20 | #define INVALID_TRIP -1 |
21 | ||
6b775e87 JM |
22 | #define FRAC_BITS 10 |
23 | #define int_to_frac(x) ((x) << FRAC_BITS) | |
24 | #define frac_to_int(x) ((x) >> FRAC_BITS) | |
25 | ||
26 | /** | |
27 | * mul_frac() - multiply two fixed-point numbers | |
28 | * @x: first multiplicand | |
29 | * @y: second multiplicand | |
30 | * | |
31 | * Return: the result of multiplying two fixed-point numbers. The | |
32 | * result is also a fixed-point number. | |
33 | */ | |
34 | static inline s64 mul_frac(s64 x, s64 y) | |
35 | { | |
36 | return (x * y) >> FRAC_BITS; | |
37 | } | |
38 | ||
39 | /** | |
40 | * div_frac() - divide two fixed-point numbers | |
41 | * @x: the dividend | |
42 | * @y: the divisor | |
43 | * | |
44 | * Return: the result of dividing two fixed-point numbers. The | |
45 | * result is also a fixed-point number. | |
46 | */ | |
47 | static inline s64 div_frac(s64 x, s64 y) | |
48 | { | |
49 | return div_s64(x << FRAC_BITS, y); | |
50 | } | |
51 | ||
52 | /** | |
53 | * struct power_allocator_params - parameters for the power allocator governor | |
f5cbb182 JM |
54 | * @allocated_tzp: whether we have allocated tzp for this thermal zone and |
55 | * it needs to be freed on unbind | |
6b775e87 JM |
56 | * @err_integral: accumulated error in the PID controller. |
57 | * @prev_err: error in the previous iteration of the PID controller. | |
58 | * Used to calculate the derivative term. | |
59 | * @trip_switch_on: first passive trip point of the thermal zone. The | |
60 | * governor switches on when this trip point is crossed. | |
8b7b390f JM |
61 | * If the thermal zone only has one passive trip point, |
62 | * @trip_switch_on should be INVALID_TRIP. | |
6b775e87 JM |
63 | * @trip_max_desired_temperature: last passive trip point of the thermal |
64 | * zone. The temperature we are | |
65 | * controlling for. | |
66 | */ | |
67 | struct power_allocator_params { | |
f5cbb182 | 68 | bool allocated_tzp; |
6b775e87 JM |
69 | s64 err_integral; |
70 | s32 prev_err; | |
71 | int trip_switch_on; | |
72 | int trip_max_desired_temperature; | |
73 | }; | |
74 | ||
e055bb0f JM |
75 | /** |
76 | * estimate_sustainable_power() - Estimate the sustainable power of a thermal zone | |
77 | * @tz: thermal zone we are operating in | |
78 | * | |
79 | * For thermal zones that don't provide a sustainable_power in their | |
80 | * thermal_zone_params, estimate one. Calculate it using the minimum | |
81 | * power of all the cooling devices as that gives a valid value that | |
82 | * can give some degree of functionality. For optimal performance of | |
83 | * this governor, provide a sustainable_power in the thermal zone's | |
84 | * thermal_zone_params. | |
85 | */ | |
86 | static u32 estimate_sustainable_power(struct thermal_zone_device *tz) | |
87 | { | |
88 | u32 sustainable_power = 0; | |
89 | struct thermal_instance *instance; | |
90 | struct power_allocator_params *params = tz->governor_data; | |
91 | ||
92 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
93 | struct thermal_cooling_device *cdev = instance->cdev; | |
94 | u32 min_power; | |
95 | ||
96 | if (instance->trip != params->trip_max_desired_temperature) | |
97 | continue; | |
98 | ||
8132df3a LL |
99 | if (!cdev_is_power_actor(cdev)) |
100 | continue; | |
101 | ||
102 | if (cdev->ops->state2power(cdev, instance->upper, &min_power)) | |
e055bb0f JM |
103 | continue; |
104 | ||
105 | sustainable_power += min_power; | |
106 | } | |
107 | ||
108 | return sustainable_power; | |
109 | } | |
110 | ||
111 | /** | |
112 | * estimate_pid_constants() - Estimate the constants for the PID controller | |
113 | * @tz: thermal zone for which to estimate the constants | |
114 | * @sustainable_power: sustainable power for the thermal zone | |
115 | * @trip_switch_on: trip point number for the switch on temperature | |
116 | * @control_temp: target temperature for the power allocator governor | |
117 | * @force: whether to force the update of the constants | |
118 | * | |
119 | * This function is used to update the estimation of the PID | |
120 | * controller constants in struct thermal_zone_parameters. | |
121 | * Sustainable power is provided in case it was estimated. The | |
122 | * estimated sustainable_power should not be stored in the | |
123 | * thermal_zone_parameters so it has to be passed explicitly to this | |
124 | * function. | |
125 | * | |
126 | * If @force is not set, the values in the thermal zone's parameters | |
127 | * are preserved if they are not zero. If @force is set, the values | |
128 | * in thermal zone's parameters are overwritten. | |
129 | */ | |
130 | static void estimate_pid_constants(struct thermal_zone_device *tz, | |
131 | u32 sustainable_power, int trip_switch_on, | |
132 | int control_temp, bool force) | |
133 | { | |
134 | int ret; | |
135 | int switch_on_temp; | |
136 | u32 temperature_threshold; | |
e34a7233 | 137 | s32 k_i; |
e055bb0f JM |
138 | |
139 | ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp); | |
140 | if (ret) | |
141 | switch_on_temp = 0; | |
142 | ||
143 | temperature_threshold = control_temp - switch_on_temp; | |
44241628 AA |
144 | /* |
145 | * estimate_pid_constants() tries to find appropriate default | |
146 | * values for thermal zones that don't provide them. If a | |
147 | * system integrator has configured a thermal zone with two | |
148 | * passive trip points at the same temperature, that person | |
149 | * hasn't put any effort to set up the thermal zone properly | |
150 | * so just give up. | |
151 | */ | |
152 | if (!temperature_threshold) | |
153 | return; | |
e055bb0f JM |
154 | |
155 | if (!tz->tzp->k_po || force) | |
156 | tz->tzp->k_po = int_to_frac(sustainable_power) / | |
157 | temperature_threshold; | |
158 | ||
159 | if (!tz->tzp->k_pu || force) | |
160 | tz->tzp->k_pu = int_to_frac(2 * sustainable_power) / | |
161 | temperature_threshold; | |
162 | ||
e34a7233 LL |
163 | if (!tz->tzp->k_i || force) { |
164 | k_i = tz->tzp->k_pu / 10; | |
165 | tz->tzp->k_i = k_i > 0 ? k_i : 1; | |
166 | } | |
167 | ||
e055bb0f JM |
168 | /* |
169 | * The default for k_d and integral_cutoff is 0, so we can | |
170 | * leave them as they are. | |
171 | */ | |
172 | } | |
173 | ||
6b775e87 JM |
174 | /** |
175 | * pid_controller() - PID controller | |
176 | * @tz: thermal zone we are operating in | |
6b775e87 JM |
177 | * @control_temp: the target temperature in millicelsius |
178 | * @max_allocatable_power: maximum allocatable power for this thermal zone | |
179 | * | |
180 | * This PID controller increases the available power budget so that the | |
181 | * temperature of the thermal zone gets as close as possible to | |
182 | * @control_temp and limits the power if it exceeds it. k_po is the | |
183 | * proportional term when we are overshooting, k_pu is the | |
184 | * proportional term when we are undershooting. integral_cutoff is a | |
185 | * threshold below which we stop accumulating the error. The | |
186 | * accumulated error is only valid if the requested power will make | |
187 | * the system warmer. If the system is mostly idle, there's no point | |
188 | * in accumulating positive error. | |
189 | * | |
190 | * Return: The power budget for the next period. | |
191 | */ | |
192 | static u32 pid_controller(struct thermal_zone_device *tz, | |
17e8351a | 193 | int control_temp, |
6b775e87 JM |
194 | u32 max_allocatable_power) |
195 | { | |
196 | s64 p, i, d, power_range; | |
197 | s32 err, max_power_frac; | |
e055bb0f | 198 | u32 sustainable_power; |
6b775e87 JM |
199 | struct power_allocator_params *params = tz->governor_data; |
200 | ||
201 | max_power_frac = int_to_frac(max_allocatable_power); | |
202 | ||
e055bb0f JM |
203 | if (tz->tzp->sustainable_power) { |
204 | sustainable_power = tz->tzp->sustainable_power; | |
205 | } else { | |
206 | sustainable_power = estimate_sustainable_power(tz); | |
207 | estimate_pid_constants(tz, sustainable_power, | |
208 | params->trip_switch_on, control_temp, | |
209 | true); | |
210 | } | |
211 | ||
bb404db4 | 212 | err = control_temp - tz->temperature; |
6b775e87 JM |
213 | err = int_to_frac(err); |
214 | ||
215 | /* Calculate the proportional term */ | |
216 | p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err); | |
217 | ||
218 | /* | |
219 | * Calculate the integral term | |
220 | * | |
221 | * if the error is less than cut off allow integration (but | |
222 | * the integral is limited to max power) | |
223 | */ | |
224 | i = mul_frac(tz->tzp->k_i, params->err_integral); | |
225 | ||
226 | if (err < int_to_frac(tz->tzp->integral_cutoff)) { | |
227 | s64 i_next = i + mul_frac(tz->tzp->k_i, err); | |
228 | ||
79211c8e | 229 | if (abs(i_next) < max_power_frac) { |
6b775e87 JM |
230 | i = i_next; |
231 | params->err_integral += err; | |
232 | } | |
233 | } | |
234 | ||
235 | /* | |
236 | * Calculate the derivative term | |
237 | * | |
238 | * We do err - prev_err, so with a positive k_d, a decreasing | |
239 | * error (i.e. driving closer to the line) results in less | |
240 | * power being applied, slowing down the controller) | |
241 | */ | |
242 | d = mul_frac(tz->tzp->k_d, err - params->prev_err); | |
243 | d = div_frac(d, tz->passive_delay); | |
244 | params->prev_err = err; | |
245 | ||
246 | power_range = p + i + d; | |
247 | ||
248 | /* feed-forward the known sustainable dissipatable power */ | |
e055bb0f | 249 | power_range = sustainable_power + frac_to_int(power_range); |
6b775e87 | 250 | |
6828a471 JM |
251 | power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power); |
252 | ||
253 | trace_thermal_power_allocator_pid(tz, frac_to_int(err), | |
254 | frac_to_int(params->err_integral), | |
255 | frac_to_int(p), frac_to_int(i), | |
256 | frac_to_int(d), power_range); | |
257 | ||
258 | return power_range; | |
6b775e87 JM |
259 | } |
260 | ||
345a8af7 LL |
261 | /** |
262 | * power_actor_set_power() - limit the maximum power a cooling device consumes | |
263 | * @cdev: pointer to &thermal_cooling_device | |
264 | * @instance: thermal instance to update | |
265 | * @power: the power in milliwatts | |
266 | * | |
267 | * Set the cooling device to consume at most @power milliwatts. The limit is | |
268 | * expected to be a cap at the maximum power consumption. | |
269 | * | |
270 | * Return: 0 on success, -EINVAL if the cooling device does not | |
271 | * implement the power actor API or -E* for other failures. | |
272 | */ | |
273 | static int | |
274 | power_actor_set_power(struct thermal_cooling_device *cdev, | |
275 | struct thermal_instance *instance, u32 power) | |
276 | { | |
277 | unsigned long state; | |
278 | int ret; | |
279 | ||
280 | ret = cdev->ops->power2state(cdev, power, &state); | |
281 | if (ret) | |
282 | return ret; | |
283 | ||
284 | instance->target = clamp_val(state, instance->lower, instance->upper); | |
285 | mutex_lock(&cdev->lock); | |
286 | cdev->updated = false; | |
287 | mutex_unlock(&cdev->lock); | |
288 | thermal_cdev_update(cdev); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
6b775e87 JM |
293 | /** |
294 | * divvy_up_power() - divvy the allocated power between the actors | |
295 | * @req_power: each actor's requested power | |
296 | * @max_power: each actor's maximum available power | |
297 | * @num_actors: size of the @req_power, @max_power and @granted_power's array | |
298 | * @total_req_power: sum of @req_power | |
299 | * @power_range: total allocated power | |
300 | * @granted_power: output array: each actor's granted power | |
301 | * @extra_actor_power: an appropriately sized array to be used in the | |
302 | * function as temporary storage of the extra power given | |
303 | * to the actors | |
304 | * | |
305 | * This function divides the total allocated power (@power_range) | |
306 | * fairly between the actors. It first tries to give each actor a | |
307 | * share of the @power_range according to how much power it requested | |
308 | * compared to the rest of the actors. For example, if only one actor | |
309 | * requests power, then it receives all the @power_range. If | |
310 | * three actors each requests 1mW, each receives a third of the | |
311 | * @power_range. | |
312 | * | |
313 | * If any actor received more than their maximum power, then that | |
314 | * surplus is re-divvied among the actors based on how far they are | |
315 | * from their respective maximums. | |
316 | * | |
317 | * Granted power for each actor is written to @granted_power, which | |
318 | * should've been allocated by the calling function. | |
319 | */ | |
320 | static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors, | |
321 | u32 total_req_power, u32 power_range, | |
322 | u32 *granted_power, u32 *extra_actor_power) | |
323 | { | |
324 | u32 extra_power, capped_extra_power; | |
325 | int i; | |
326 | ||
327 | /* | |
328 | * Prevent division by 0 if none of the actors request power. | |
329 | */ | |
330 | if (!total_req_power) | |
331 | total_req_power = 1; | |
332 | ||
333 | capped_extra_power = 0; | |
334 | extra_power = 0; | |
335 | for (i = 0; i < num_actors; i++) { | |
f9d03814 | 336 | u64 req_range = (u64)req_power[i] * power_range; |
6b775e87 | 337 | |
ea54cac9 JM |
338 | granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range, |
339 | total_req_power); | |
6b775e87 JM |
340 | |
341 | if (granted_power[i] > max_power[i]) { | |
342 | extra_power += granted_power[i] - max_power[i]; | |
343 | granted_power[i] = max_power[i]; | |
344 | } | |
345 | ||
346 | extra_actor_power[i] = max_power[i] - granted_power[i]; | |
347 | capped_extra_power += extra_actor_power[i]; | |
348 | } | |
349 | ||
350 | if (!extra_power) | |
351 | return; | |
352 | ||
353 | /* | |
354 | * Re-divvy the reclaimed extra among actors based on | |
355 | * how far they are from the max | |
356 | */ | |
357 | extra_power = min(extra_power, capped_extra_power); | |
358 | if (capped_extra_power > 0) | |
359 | for (i = 0; i < num_actors; i++) | |
360 | granted_power[i] += (extra_actor_power[i] * | |
361 | extra_power) / capped_extra_power; | |
362 | } | |
363 | ||
364 | static int allocate_power(struct thermal_zone_device *tz, | |
17e8351a | 365 | int control_temp) |
6b775e87 JM |
366 | { |
367 | struct thermal_instance *instance; | |
368 | struct power_allocator_params *params = tz->governor_data; | |
369 | u32 *req_power, *max_power, *granted_power, *extra_actor_power; | |
d5f83109 JM |
370 | u32 *weighted_req_power; |
371 | u32 total_req_power, max_allocatable_power, total_weighted_req_power; | |
6828a471 | 372 | u32 total_granted_power, power_range; |
6b775e87 JM |
373 | int i, num_actors, total_weight, ret = 0; |
374 | int trip_max_desired_temperature = params->trip_max_desired_temperature; | |
375 | ||
376 | mutex_lock(&tz->lock); | |
377 | ||
378 | num_actors = 0; | |
379 | total_weight = 0; | |
380 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
381 | if ((instance->trip == trip_max_desired_temperature) && | |
382 | cdev_is_power_actor(instance->cdev)) { | |
383 | num_actors++; | |
384 | total_weight += instance->weight; | |
385 | } | |
386 | } | |
387 | ||
97584d18 JM |
388 | if (!num_actors) { |
389 | ret = -ENODEV; | |
390 | goto unlock; | |
391 | } | |
392 | ||
6b775e87 | 393 | /* |
d5f83109 JM |
394 | * We need to allocate five arrays of the same size: |
395 | * req_power, max_power, granted_power, extra_actor_power and | |
396 | * weighted_req_power. They are going to be needed until this | |
397 | * function returns. Allocate them all in one go to simplify | |
398 | * the allocation and deallocation logic. | |
6b775e87 JM |
399 | */ |
400 | BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power)); | |
401 | BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power)); | |
402 | BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power)); | |
d5f83109 | 403 | BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power)); |
9751a9e4 | 404 | req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL); |
6b775e87 JM |
405 | if (!req_power) { |
406 | ret = -ENOMEM; | |
407 | goto unlock; | |
408 | } | |
409 | ||
410 | max_power = &req_power[num_actors]; | |
411 | granted_power = &req_power[2 * num_actors]; | |
412 | extra_actor_power = &req_power[3 * num_actors]; | |
d5f83109 | 413 | weighted_req_power = &req_power[4 * num_actors]; |
6b775e87 JM |
414 | |
415 | i = 0; | |
d5f83109 | 416 | total_weighted_req_power = 0; |
6b775e87 JM |
417 | total_req_power = 0; |
418 | max_allocatable_power = 0; | |
419 | ||
420 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
421 | int weight; | |
422 | struct thermal_cooling_device *cdev = instance->cdev; | |
423 | ||
424 | if (instance->trip != trip_max_desired_temperature) | |
425 | continue; | |
426 | ||
427 | if (!cdev_is_power_actor(cdev)) | |
428 | continue; | |
429 | ||
ecd1d2a3 | 430 | if (cdev->ops->get_requested_power(cdev, &req_power[i])) |
6b775e87 JM |
431 | continue; |
432 | ||
433 | if (!total_weight) | |
434 | weight = 1 << FRAC_BITS; | |
435 | else | |
436 | weight = instance->weight; | |
437 | ||
d5f83109 | 438 | weighted_req_power[i] = frac_to_int(weight * req_power[i]); |
6b775e87 | 439 | |
8132df3a LL |
440 | if (cdev->ops->state2power(cdev, instance->lower, |
441 | &max_power[i])) | |
6b775e87 JM |
442 | continue; |
443 | ||
444 | total_req_power += req_power[i]; | |
445 | max_allocatable_power += max_power[i]; | |
d5f83109 | 446 | total_weighted_req_power += weighted_req_power[i]; |
6b775e87 JM |
447 | |
448 | i++; | |
449 | } | |
450 | ||
bb404db4 | 451 | power_range = pid_controller(tz, control_temp, max_allocatable_power); |
6b775e87 | 452 | |
d5f83109 JM |
453 | divvy_up_power(weighted_req_power, max_power, num_actors, |
454 | total_weighted_req_power, power_range, granted_power, | |
455 | extra_actor_power); | |
6b775e87 | 456 | |
6828a471 | 457 | total_granted_power = 0; |
6b775e87 JM |
458 | i = 0; |
459 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | |
460 | if (instance->trip != trip_max_desired_temperature) | |
461 | continue; | |
462 | ||
463 | if (!cdev_is_power_actor(instance->cdev)) | |
464 | continue; | |
465 | ||
466 | power_actor_set_power(instance->cdev, instance, | |
467 | granted_power[i]); | |
6828a471 | 468 | total_granted_power += granted_power[i]; |
6b775e87 JM |
469 | |
470 | i++; | |
471 | } | |
472 | ||
6828a471 JM |
473 | trace_thermal_power_allocator(tz, req_power, total_req_power, |
474 | granted_power, total_granted_power, | |
475 | num_actors, power_range, | |
bb404db4 KS |
476 | max_allocatable_power, tz->temperature, |
477 | control_temp - tz->temperature); | |
6828a471 | 478 | |
cf736ea6 | 479 | kfree(req_power); |
6b775e87 JM |
480 | unlock: |
481 | mutex_unlock(&tz->lock); | |
482 | ||
483 | return ret; | |
484 | } | |
485 | ||
8b7b390f JM |
486 | /** |
487 | * get_governor_trips() - get the number of the two trip points that are key for this governor | |
488 | * @tz: thermal zone to operate on | |
489 | * @params: pointer to private data for this governor | |
490 | * | |
491 | * The power allocator governor works optimally with two trips points: | |
492 | * a "switch on" trip point and a "maximum desired temperature". These | |
493 | * are defined as the first and last passive trip points. | |
494 | * | |
495 | * If there is only one trip point, then that's considered to be the | |
496 | * "maximum desired temperature" trip point and the governor is always | |
497 | * on. If there are no passive or active trip points, then the | |
498 | * governor won't do anything. In fact, its throttle function | |
499 | * won't be called at all. | |
500 | */ | |
501 | static void get_governor_trips(struct thermal_zone_device *tz, | |
502 | struct power_allocator_params *params) | |
6b775e87 | 503 | { |
8b7b390f | 504 | int i, last_active, last_passive; |
6b775e87 JM |
505 | bool found_first_passive; |
506 | ||
507 | found_first_passive = false; | |
8b7b390f JM |
508 | last_active = INVALID_TRIP; |
509 | last_passive = INVALID_TRIP; | |
6b775e87 JM |
510 | |
511 | for (i = 0; i < tz->trips; i++) { | |
512 | enum thermal_trip_type type; | |
8b7b390f | 513 | int ret; |
6b775e87 JM |
514 | |
515 | ret = tz->ops->get_trip_type(tz, i, &type); | |
8b7b390f JM |
516 | if (ret) { |
517 | dev_warn(&tz->device, | |
518 | "Failed to get trip point %d type: %d\n", i, | |
519 | ret); | |
520 | continue; | |
521 | } | |
6b775e87 | 522 | |
8b7b390f JM |
523 | if (type == THERMAL_TRIP_PASSIVE) { |
524 | if (!found_first_passive) { | |
6b775e87 JM |
525 | params->trip_switch_on = i; |
526 | found_first_passive = true; | |
8b7b390f JM |
527 | } else { |
528 | last_passive = i; | |
6b775e87 | 529 | } |
8b7b390f JM |
530 | } else if (type == THERMAL_TRIP_ACTIVE) { |
531 | last_active = i; | |
6b775e87 JM |
532 | } else { |
533 | break; | |
534 | } | |
535 | } | |
536 | ||
8b7b390f | 537 | if (last_passive != INVALID_TRIP) { |
6b775e87 | 538 | params->trip_max_desired_temperature = last_passive; |
8b7b390f JM |
539 | } else if (found_first_passive) { |
540 | params->trip_max_desired_temperature = params->trip_switch_on; | |
541 | params->trip_switch_on = INVALID_TRIP; | |
6b775e87 | 542 | } else { |
8b7b390f JM |
543 | params->trip_switch_on = INVALID_TRIP; |
544 | params->trip_max_desired_temperature = last_active; | |
6b775e87 | 545 | } |
6b775e87 JM |
546 | } |
547 | ||
548 | static void reset_pid_controller(struct power_allocator_params *params) | |
549 | { | |
550 | params->err_integral = 0; | |
551 | params->prev_err = 0; | |
552 | } | |
553 | ||
554 | static void allow_maximum_power(struct thermal_zone_device *tz) | |
555 | { | |
556 | struct thermal_instance *instance; | |
557 | struct power_allocator_params *params = tz->governor_data; | |
558 | ||
a5de11d6 | 559 | mutex_lock(&tz->lock); |
6b775e87 JM |
560 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { |
561 | if ((instance->trip != params->trip_max_desired_temperature) || | |
562 | (!cdev_is_power_actor(instance->cdev))) | |
563 | continue; | |
564 | ||
565 | instance->target = 0; | |
d0b7306d | 566 | mutex_lock(&instance->cdev->lock); |
6b775e87 | 567 | instance->cdev->updated = false; |
d0b7306d | 568 | mutex_unlock(&instance->cdev->lock); |
6b775e87 JM |
569 | thermal_cdev_update(instance->cdev); |
570 | } | |
a5de11d6 | 571 | mutex_unlock(&tz->lock); |
6b775e87 JM |
572 | } |
573 | ||
574 | /** | |
575 | * power_allocator_bind() - bind the power_allocator governor to a thermal zone | |
576 | * @tz: thermal zone to bind it to | |
577 | * | |
8b7b390f JM |
578 | * Initialize the PID controller parameters and bind it to the thermal |
579 | * zone. | |
6b775e87 | 580 | * |
f5cbb182 | 581 | * Return: 0 on success, or -ENOMEM if we ran out of memory. |
6b775e87 JM |
582 | */ |
583 | static int power_allocator_bind(struct thermal_zone_device *tz) | |
584 | { | |
585 | int ret; | |
586 | struct power_allocator_params *params; | |
e055bb0f | 587 | int control_temp; |
6b775e87 | 588 | |
cf736ea6 | 589 | params = kzalloc(sizeof(*params), GFP_KERNEL); |
6b775e87 JM |
590 | if (!params) |
591 | return -ENOMEM; | |
592 | ||
f5cbb182 JM |
593 | if (!tz->tzp) { |
594 | tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL); | |
595 | if (!tz->tzp) { | |
596 | ret = -ENOMEM; | |
597 | goto free_params; | |
598 | } | |
599 | ||
600 | params->allocated_tzp = true; | |
601 | } | |
602 | ||
e055bb0f JM |
603 | if (!tz->tzp->sustainable_power) |
604 | dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n"); | |
605 | ||
8b7b390f | 606 | get_governor_trips(tz, params); |
6b775e87 | 607 | |
8b7b390f JM |
608 | if (tz->trips > 0) { |
609 | ret = tz->ops->get_trip_temp(tz, | |
610 | params->trip_max_desired_temperature, | |
611 | &control_temp); | |
612 | if (!ret) | |
613 | estimate_pid_constants(tz, tz->tzp->sustainable_power, | |
614 | params->trip_switch_on, | |
615 | control_temp, false); | |
616 | } | |
6b775e87 | 617 | |
6b775e87 JM |
618 | reset_pid_controller(params); |
619 | ||
620 | tz->governor_data = params; | |
621 | ||
622 | return 0; | |
f5cbb182 JM |
623 | |
624 | free_params: | |
625 | kfree(params); | |
626 | ||
627 | return ret; | |
6b775e87 JM |
628 | } |
629 | ||
630 | static void power_allocator_unbind(struct thermal_zone_device *tz) | |
631 | { | |
f5cbb182 JM |
632 | struct power_allocator_params *params = tz->governor_data; |
633 | ||
6b775e87 | 634 | dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); |
f5cbb182 JM |
635 | |
636 | if (params->allocated_tzp) { | |
637 | kfree(tz->tzp); | |
638 | tz->tzp = NULL; | |
639 | } | |
640 | ||
cf736ea6 | 641 | kfree(tz->governor_data); |
6b775e87 JM |
642 | tz->governor_data = NULL; |
643 | } | |
644 | ||
645 | static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) | |
646 | { | |
647 | int ret; | |
bb404db4 | 648 | int switch_on_temp, control_temp; |
6b775e87 JM |
649 | struct power_allocator_params *params = tz->governor_data; |
650 | ||
651 | /* | |
652 | * We get called for every trip point but we only need to do | |
653 | * our calculations once | |
654 | */ | |
655 | if (trip != params->trip_max_desired_temperature) | |
656 | return 0; | |
657 | ||
6b775e87 JM |
658 | ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, |
659 | &switch_on_temp); | |
bb404db4 | 660 | if (!ret && (tz->temperature < switch_on_temp)) { |
6b775e87 JM |
661 | tz->passive = 0; |
662 | reset_pid_controller(params); | |
663 | allow_maximum_power(tz); | |
664 | return 0; | |
665 | } | |
666 | ||
667 | tz->passive = 1; | |
668 | ||
669 | ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, | |
670 | &control_temp); | |
671 | if (ret) { | |
672 | dev_warn(&tz->device, | |
673 | "Failed to get the maximum desired temperature: %d\n", | |
674 | ret); | |
675 | return ret; | |
676 | } | |
677 | ||
bb404db4 | 678 | return allocate_power(tz, control_temp); |
6b775e87 JM |
679 | } |
680 | ||
681 | static struct thermal_governor thermal_gov_power_allocator = { | |
682 | .name = "power_allocator", | |
683 | .bind_to_tz = power_allocator_bind, | |
684 | .unbind_from_tz = power_allocator_unbind, | |
685 | .throttle = power_allocator_throttle, | |
686 | }; | |
57c5b2ec | 687 | THERMAL_GOVERNOR_DECLARE(thermal_gov_power_allocator); |