diff options
Diffstat (limited to 'drivers/thermal/power_allocator.c')
-rw-r--r-- | drivers/thermal/power_allocator.c | 253 |
1 files changed, 189 insertions, 64 deletions
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c index 9c8a7aad0252..e570ff084add 100644 --- a/drivers/thermal/power_allocator.c +++ b/drivers/thermal/power_allocator.c | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | #include "thermal_core.h" | 25 | #include "thermal_core.h" |
26 | 26 | ||
27 | #define INVALID_TRIP -1 | ||
28 | |||
27 | #define FRAC_BITS 10 | 29 | #define FRAC_BITS 10 |
28 | #define int_to_frac(x) ((x) << FRAC_BITS) | 30 | #define int_to_frac(x) ((x) << FRAC_BITS) |
29 | #define frac_to_int(x) ((x) >> FRAC_BITS) | 31 | #define frac_to_int(x) ((x) >> FRAC_BITS) |
@@ -56,16 +58,21 @@ static inline s64 div_frac(s64 x, s64 y) | |||
56 | 58 | ||
57 | /** | 59 | /** |
58 | * struct power_allocator_params - parameters for the power allocator governor | 60 | * struct power_allocator_params - parameters for the power allocator governor |
61 | * @allocated_tzp: whether we have allocated tzp for this thermal zone and | ||
62 | * it needs to be freed on unbind | ||
59 | * @err_integral: accumulated error in the PID controller. | 63 | * @err_integral: accumulated error in the PID controller. |
60 | * @prev_err: error in the previous iteration of the PID controller. | 64 | * @prev_err: error in the previous iteration of the PID controller. |
61 | * Used to calculate the derivative term. | 65 | * Used to calculate the derivative term. |
62 | * @trip_switch_on: first passive trip point of the thermal zone. The | 66 | * @trip_switch_on: first passive trip point of the thermal zone. The |
63 | * governor switches on when this trip point is crossed. | 67 | * governor switches on when this trip point is crossed. |
68 | * If the thermal zone only has one passive trip point, | ||
69 | * @trip_switch_on should be INVALID_TRIP. | ||
64 | * @trip_max_desired_temperature: last passive trip point of the thermal | 70 | * @trip_max_desired_temperature: last passive trip point of the thermal |
65 | * zone. The temperature we are | 71 | * zone. The temperature we are |
66 | * controlling for. | 72 | * controlling for. |
67 | */ | 73 | */ |
68 | struct power_allocator_params { | 74 | struct power_allocator_params { |
75 | bool allocated_tzp; | ||
69 | s64 err_integral; | 76 | s64 err_integral; |
70 | s32 prev_err; | 77 | s32 prev_err; |
71 | int trip_switch_on; | 78 | int trip_switch_on; |
@@ -73,6 +80,98 @@ struct power_allocator_params { | |||
73 | }; | 80 | }; |
74 | 81 | ||
75 | /** | 82 | /** |
83 | * estimate_sustainable_power() - Estimate the sustainable power of a thermal zone | ||
84 | * @tz: thermal zone we are operating in | ||
85 | * | ||
86 | * For thermal zones that don't provide a sustainable_power in their | ||
87 | * thermal_zone_params, estimate one. Calculate it using the minimum | ||
88 | * power of all the cooling devices as that gives a valid value that | ||
89 | * can give some degree of functionality. For optimal performance of | ||
90 | * this governor, provide a sustainable_power in the thermal zone's | ||
91 | * thermal_zone_params. | ||
92 | */ | ||
93 | static u32 estimate_sustainable_power(struct thermal_zone_device *tz) | ||
94 | { | ||
95 | u32 sustainable_power = 0; | ||
96 | struct thermal_instance *instance; | ||
97 | struct power_allocator_params *params = tz->governor_data; | ||
98 | |||
99 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | ||
100 | struct thermal_cooling_device *cdev = instance->cdev; | ||
101 | u32 min_power; | ||
102 | |||
103 | if (instance->trip != params->trip_max_desired_temperature) | ||
104 | continue; | ||
105 | |||
106 | if (power_actor_get_min_power(cdev, tz, &min_power)) | ||
107 | continue; | ||
108 | |||
109 | sustainable_power += min_power; | ||
110 | } | ||
111 | |||
112 | return sustainable_power; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * estimate_pid_constants() - Estimate the constants for the PID controller | ||
117 | * @tz: thermal zone for which to estimate the constants | ||
118 | * @sustainable_power: sustainable power for the thermal zone | ||
119 | * @trip_switch_on: trip point number for the switch on temperature | ||
120 | * @control_temp: target temperature for the power allocator governor | ||
121 | * @force: whether to force the update of the constants | ||
122 | * | ||
123 | * This function is used to update the estimation of the PID | ||
124 | * controller constants in struct thermal_zone_parameters. | ||
125 | * Sustainable power is provided in case it was estimated. The | ||
126 | * estimated sustainable_power should not be stored in the | ||
127 | * thermal_zone_parameters so it has to be passed explicitly to this | ||
128 | * function. | ||
129 | * | ||
130 | * If @force is not set, the values in the thermal zone's parameters | ||
131 | * are preserved if they are not zero. If @force is set, the values | ||
132 | * in thermal zone's parameters are overwritten. | ||
133 | */ | ||
134 | static void estimate_pid_constants(struct thermal_zone_device *tz, | ||
135 | u32 sustainable_power, int trip_switch_on, | ||
136 | int control_temp, bool force) | ||
137 | { | ||
138 | int ret; | ||
139 | int switch_on_temp; | ||
140 | u32 temperature_threshold; | ||
141 | |||
142 | ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp); | ||
143 | if (ret) | ||
144 | switch_on_temp = 0; | ||
145 | |||
146 | temperature_threshold = control_temp - switch_on_temp; | ||
147 | /* | ||
148 | * estimate_pid_constants() tries to find appropriate default | ||
149 | * values for thermal zones that don't provide them. If a | ||
150 | * system integrator has configured a thermal zone with two | ||
151 | * passive trip points at the same temperature, that person | ||
152 | * hasn't put any effort to set up the thermal zone properly | ||
153 | * so just give up. | ||
154 | */ | ||
155 | if (!temperature_threshold) | ||
156 | return; | ||
157 | |||
158 | if (!tz->tzp->k_po || force) | ||
159 | tz->tzp->k_po = int_to_frac(sustainable_power) / | ||
160 | temperature_threshold; | ||
161 | |||
162 | if (!tz->tzp->k_pu || force) | ||
163 | tz->tzp->k_pu = int_to_frac(2 * sustainable_power) / | ||
164 | temperature_threshold; | ||
165 | |||
166 | if (!tz->tzp->k_i || force) | ||
167 | tz->tzp->k_i = int_to_frac(10) / 1000; | ||
168 | /* | ||
169 | * The default for k_d and integral_cutoff is 0, so we can | ||
170 | * leave them as they are. | ||
171 | */ | ||
172 | } | ||
173 | |||
174 | /** | ||
76 | * pid_controller() - PID controller | 175 | * pid_controller() - PID controller |
77 | * @tz: thermal zone we are operating in | 176 | * @tz: thermal zone we are operating in |
78 | * @current_temp: the current temperature in millicelsius | 177 | * @current_temp: the current temperature in millicelsius |
@@ -98,10 +197,20 @@ static u32 pid_controller(struct thermal_zone_device *tz, | |||
98 | { | 197 | { |
99 | s64 p, i, d, power_range; | 198 | s64 p, i, d, power_range; |
100 | s32 err, max_power_frac; | 199 | s32 err, max_power_frac; |
200 | u32 sustainable_power; | ||
101 | struct power_allocator_params *params = tz->governor_data; | 201 | struct power_allocator_params *params = tz->governor_data; |
102 | 202 | ||
103 | max_power_frac = int_to_frac(max_allocatable_power); | 203 | max_power_frac = int_to_frac(max_allocatable_power); |
104 | 204 | ||
205 | if (tz->tzp->sustainable_power) { | ||
206 | sustainable_power = tz->tzp->sustainable_power; | ||
207 | } else { | ||
208 | sustainable_power = estimate_sustainable_power(tz); | ||
209 | estimate_pid_constants(tz, sustainable_power, | ||
210 | params->trip_switch_on, control_temp, | ||
211 | true); | ||
212 | } | ||
213 | |||
105 | err = control_temp - current_temp; | 214 | err = control_temp - current_temp; |
106 | err = int_to_frac(err); | 215 | err = int_to_frac(err); |
107 | 216 | ||
@@ -139,7 +248,7 @@ static u32 pid_controller(struct thermal_zone_device *tz, | |||
139 | power_range = p + i + d; | 248 | power_range = p + i + d; |
140 | 249 | ||
141 | /* feed-forward the known sustainable dissipatable power */ | 250 | /* feed-forward the known sustainable dissipatable power */ |
142 | power_range = tz->tzp->sustainable_power + frac_to_int(power_range); | 251 | power_range = sustainable_power + frac_to_int(power_range); |
143 | 252 | ||
144 | power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power); | 253 | power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power); |
145 | 254 | ||
@@ -247,6 +356,11 @@ static int allocate_power(struct thermal_zone_device *tz, | |||
247 | } | 356 | } |
248 | } | 357 | } |
249 | 358 | ||
359 | if (!num_actors) { | ||
360 | ret = -ENODEV; | ||
361 | goto unlock; | ||
362 | } | ||
363 | |||
250 | /* | 364 | /* |
251 | * We need to allocate five arrays of the same size: | 365 | * We need to allocate five arrays of the same size: |
252 | * req_power, max_power, granted_power, extra_actor_power and | 366 | * req_power, max_power, granted_power, extra_actor_power and |
@@ -340,43 +454,66 @@ unlock: | |||
340 | return ret; | 454 | return ret; |
341 | } | 455 | } |
342 | 456 | ||
343 | static int get_governor_trips(struct thermal_zone_device *tz, | 457 | /** |
344 | struct power_allocator_params *params) | 458 | * get_governor_trips() - get the number of the two trip points that are key for this governor |
459 | * @tz: thermal zone to operate on | ||
460 | * @params: pointer to private data for this governor | ||
461 | * | ||
462 | * The power allocator governor works optimally with two trips points: | ||
463 | * a "switch on" trip point and a "maximum desired temperature". These | ||
464 | * are defined as the first and last passive trip points. | ||
465 | * | ||
466 | * If there is only one trip point, then that's considered to be the | ||
467 | * "maximum desired temperature" trip point and the governor is always | ||
468 | * on. If there are no passive or active trip points, then the | ||
469 | * governor won't do anything. In fact, its throttle function | ||
470 | * won't be called at all. | ||
471 | */ | ||
472 | static void get_governor_trips(struct thermal_zone_device *tz, | ||
473 | struct power_allocator_params *params) | ||
345 | { | 474 | { |
346 | int i, ret, last_passive; | 475 | int i, last_active, last_passive; |
347 | bool found_first_passive; | 476 | bool found_first_passive; |
348 | 477 | ||
349 | found_first_passive = false; | 478 | found_first_passive = false; |
350 | last_passive = -1; | 479 | last_active = INVALID_TRIP; |
351 | ret = -EINVAL; | 480 | last_passive = INVALID_TRIP; |
352 | 481 | ||
353 | for (i = 0; i < tz->trips; i++) { | 482 | for (i = 0; i < tz->trips; i++) { |
354 | enum thermal_trip_type type; | 483 | enum thermal_trip_type type; |
484 | int ret; | ||
355 | 485 | ||
356 | ret = tz->ops->get_trip_type(tz, i, &type); | 486 | ret = tz->ops->get_trip_type(tz, i, &type); |
357 | if (ret) | 487 | if (ret) { |
358 | return ret; | 488 | dev_warn(&tz->device, |
489 | "Failed to get trip point %d type: %d\n", i, | ||
490 | ret); | ||
491 | continue; | ||
492 | } | ||
359 | 493 | ||
360 | if (!found_first_passive) { | 494 | if (type == THERMAL_TRIP_PASSIVE) { |
361 | if (type == THERMAL_TRIP_PASSIVE) { | 495 | if (!found_first_passive) { |
362 | params->trip_switch_on = i; | 496 | params->trip_switch_on = i; |
363 | found_first_passive = true; | 497 | found_first_passive = true; |
498 | } else { | ||
499 | last_passive = i; | ||
364 | } | 500 | } |
365 | } else if (type == THERMAL_TRIP_PASSIVE) { | 501 | } else if (type == THERMAL_TRIP_ACTIVE) { |
366 | last_passive = i; | 502 | last_active = i; |
367 | } else { | 503 | } else { |
368 | break; | 504 | break; |
369 | } | 505 | } |
370 | } | 506 | } |
371 | 507 | ||
372 | if (last_passive != -1) { | 508 | if (last_passive != INVALID_TRIP) { |
373 | params->trip_max_desired_temperature = last_passive; | 509 | params->trip_max_desired_temperature = last_passive; |
374 | ret = 0; | 510 | } else if (found_first_passive) { |
511 | params->trip_max_desired_temperature = params->trip_switch_on; | ||
512 | params->trip_switch_on = INVALID_TRIP; | ||
375 | } else { | 513 | } else { |
376 | ret = -EINVAL; | 514 | params->trip_switch_on = INVALID_TRIP; |
515 | params->trip_max_desired_temperature = last_active; | ||
377 | } | 516 | } |
378 | |||
379 | return ret; | ||
380 | } | 517 | } |
381 | 518 | ||
382 | static void reset_pid_controller(struct power_allocator_params *params) | 519 | static void reset_pid_controller(struct power_allocator_params *params) |
@@ -405,60 +542,45 @@ static void allow_maximum_power(struct thermal_zone_device *tz) | |||
405 | * power_allocator_bind() - bind the power_allocator governor to a thermal zone | 542 | * power_allocator_bind() - bind the power_allocator governor to a thermal zone |
406 | * @tz: thermal zone to bind it to | 543 | * @tz: thermal zone to bind it to |
407 | * | 544 | * |
408 | * Check that the thermal zone is valid for this governor, that is, it | 545 | * Initialize the PID controller parameters and bind it to the thermal |
409 | * has two thermal trips. If so, initialize the PID controller | 546 | * zone. |
410 | * parameters and bind it to the thermal zone. | ||
411 | * | 547 | * |
412 | * Return: 0 on success, -EINVAL if the trips were invalid or -ENOMEM | 548 | * Return: 0 on success, or -ENOMEM if we ran out of memory. |
413 | * if we ran out of memory. | ||
414 | */ | 549 | */ |
415 | static int power_allocator_bind(struct thermal_zone_device *tz) | 550 | static int power_allocator_bind(struct thermal_zone_device *tz) |
416 | { | 551 | { |
417 | int ret; | 552 | int ret; |
418 | struct power_allocator_params *params; | 553 | struct power_allocator_params *params; |
419 | int switch_on_temp, control_temp; | 554 | int control_temp; |
420 | u32 temperature_threshold; | ||
421 | |||
422 | if (!tz->tzp || !tz->tzp->sustainable_power) { | ||
423 | dev_err(&tz->device, | ||
424 | "power_allocator: missing sustainable_power\n"); | ||
425 | return -EINVAL; | ||
426 | } | ||
427 | 555 | ||
428 | params = kzalloc(sizeof(*params), GFP_KERNEL); | 556 | params = kzalloc(sizeof(*params), GFP_KERNEL); |
429 | if (!params) | 557 | if (!params) |
430 | return -ENOMEM; | 558 | return -ENOMEM; |
431 | 559 | ||
432 | ret = get_governor_trips(tz, params); | 560 | if (!tz->tzp) { |
433 | if (ret) { | 561 | tz->tzp = kzalloc(sizeof(*tz->tzp), GFP_KERNEL); |
434 | dev_err(&tz->device, | 562 | if (!tz->tzp) { |
435 | "thermal zone %s has wrong trip setup for power allocator\n", | 563 | ret = -ENOMEM; |
436 | tz->type); | 564 | goto free_params; |
437 | goto free; | 565 | } |
438 | } | ||
439 | 566 | ||
440 | ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, | 567 | params->allocated_tzp = true; |
441 | &switch_on_temp); | 568 | } |
442 | if (ret) | ||
443 | goto free; | ||
444 | 569 | ||
445 | ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature, | 570 | if (!tz->tzp->sustainable_power) |
446 | &control_temp); | 571 | dev_warn(&tz->device, "power_allocator: sustainable_power will be estimated\n"); |
447 | if (ret) | ||
448 | goto free; | ||
449 | 572 | ||
450 | temperature_threshold = control_temp - switch_on_temp; | 573 | get_governor_trips(tz, params); |
451 | 574 | ||
452 | tz->tzp->k_po = tz->tzp->k_po ?: | 575 | if (tz->trips > 0) { |
453 | int_to_frac(tz->tzp->sustainable_power) / temperature_threshold; | 576 | ret = tz->ops->get_trip_temp(tz, |
454 | tz->tzp->k_pu = tz->tzp->k_pu ?: | 577 | params->trip_max_desired_temperature, |
455 | int_to_frac(2 * tz->tzp->sustainable_power) / | 578 | &control_temp); |
456 | temperature_threshold; | 579 | if (!ret) |
457 | tz->tzp->k_i = tz->tzp->k_i ?: int_to_frac(10) / 1000; | 580 | estimate_pid_constants(tz, tz->tzp->sustainable_power, |
458 | /* | 581 | params->trip_switch_on, |
459 | * The default for k_d and integral_cutoff is 0, so we can | 582 | control_temp, false); |
460 | * leave them as they are. | 583 | } |
461 | */ | ||
462 | 584 | ||
463 | reset_pid_controller(params); | 585 | reset_pid_controller(params); |
464 | 586 | ||
@@ -466,14 +588,23 @@ static int power_allocator_bind(struct thermal_zone_device *tz) | |||
466 | 588 | ||
467 | return 0; | 589 | return 0; |
468 | 590 | ||
469 | free: | 591 | free_params: |
470 | kfree(params); | 592 | kfree(params); |
593 | |||
471 | return ret; | 594 | return ret; |
472 | } | 595 | } |
473 | 596 | ||
474 | static void power_allocator_unbind(struct thermal_zone_device *tz) | 597 | static void power_allocator_unbind(struct thermal_zone_device *tz) |
475 | { | 598 | { |
599 | struct power_allocator_params *params = tz->governor_data; | ||
600 | |||
476 | dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); | 601 | dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); |
602 | |||
603 | if (params->allocated_tzp) { | ||
604 | kfree(tz->tzp); | ||
605 | tz->tzp = NULL; | ||
606 | } | ||
607 | |||
477 | kfree(tz->governor_data); | 608 | kfree(tz->governor_data); |
478 | tz->governor_data = NULL; | 609 | tz->governor_data = NULL; |
479 | } | 610 | } |
@@ -499,13 +630,7 @@ static int power_allocator_throttle(struct thermal_zone_device *tz, int trip) | |||
499 | 630 | ||
500 | ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, | 631 | ret = tz->ops->get_trip_temp(tz, params->trip_switch_on, |
501 | &switch_on_temp); | 632 | &switch_on_temp); |
502 | if (ret) { | 633 | if (!ret && (current_temp < switch_on_temp)) { |
503 | dev_warn(&tz->device, | ||
504 | "Failed to get switch on temperature: %d\n", ret); | ||
505 | return ret; | ||
506 | } | ||
507 | |||
508 | if (current_temp < switch_on_temp) { | ||
509 | tz->passive = 0; | 634 | tz->passive = 0; |
510 | reset_pid_controller(params); | 635 | reset_pid_controller(params); |
511 | allow_maximum_power(tz); | 636 | allow_maximum_power(tz); |