]>
Commit | Line | Data |
---|---|---|
6b775e87 JM |
1 | Power allocator governor tunables |
2 | ================================= | |
3 | ||
4 | Trip points | |
5 | ----------- | |
6 | ||
8b7b390f | 7 | The governor works optimally with the following two passive trip points: |
6b775e87 JM |
8 | |
9 | 1. "switch on" trip point: temperature above which the governor | |
10 | control loop starts operating. This is the first passive trip | |
11 | point of the thermal zone. | |
12 | ||
13 | 2. "desired temperature" trip point: it should be higher than the | |
14 | "switch on" trip point. This the target temperature the governor | |
15 | is controlling for. This is the last passive trip point of the | |
16 | thermal zone. | |
17 | ||
18 | PID Controller | |
19 | -------------- | |
20 | ||
21 | The power allocator governor implements a | |
22 | Proportional-Integral-Derivative controller (PID controller) with | |
23 | temperature as the control input and power as the controlled output: | |
24 | ||
25 | P_max = k_p * e + k_i * err_integral + k_d * diff_err + sustainable_power | |
26 | ||
27 | where | |
28 | e = desired_temperature - current_temperature | |
29 | err_integral is the sum of previous errors | |
30 | diff_err = e - previous_error | |
31 | ||
32 | It is similar to the one depicted below: | |
33 | ||
34 | k_d | |
35 | | | |
36 | current_temp | | |
37 | | v | |
38 | | +----------+ +---+ | |
39 | | +----->| diff_err |-->| X |------+ | |
40 | | | +----------+ +---+ | | |
41 | | | | tdp actor | |
42 | | | k_i | | get_requested_power() | |
43 | | | | | | | | | |
44 | | | | | | | | ... | |
45 | v | v v v v v | |
46 | +---+ | +-------+ +---+ +---+ +---+ +----------+ | |
47 | | S |-------+----->| sum e |----->| X |--->| S |-->| S |-->|power | | |
48 | +---+ | +-------+ +---+ +---+ +---+ |allocation| | |
49 | ^ | ^ +----------+ | |
50 | | | | | | | |
51 | | | +---+ | | | | |
52 | | +------->| X |-------------------+ v v | |
53 | | +---+ granted performance | |
54 | desired_temperature ^ | |
55 | | | |
56 | | | |
57 | k_po/k_pu | |
58 | ||
59 | Sustainable power | |
60 | ----------------- | |
61 | ||
62 | An estimate of the sustainable dissipatable power (in mW) should be | |
63 | provided while registering the thermal zone. This estimates the | |
64 | sustained power that can be dissipated at the desired control | |
65 | temperature. This is the maximum sustained power for allocation at | |
66 | the desired maximum temperature. The actual sustained power can vary | |
67 | for a number of reasons. The closed loop controller will take care of | |
68 | variations such as environmental conditions, and some factors related | |
69 | to the speed-grade of the silicon. `sustainable_power` is therefore | |
70 | simply an estimate, and may be tuned to affect the aggressiveness of | |
71 | the thermal ramp. For reference, the sustainable power of a 4" phone | |
72 | is typically 2000mW, while on a 10" tablet is around 4500mW (may vary | |
73 | depending on screen size). | |
74 | ||
75 | If you are using device tree, do add it as a property of the | |
76 | thermal-zone. For example: | |
77 | ||
78 | thermal-zones { | |
79 | soc_thermal { | |
80 | polling-delay = <1000>; | |
81 | polling-delay-passive = <100>; | |
82 | sustainable-power = <2500>; | |
83 | ... | |
84 | ||
85 | Instead, if the thermal zone is registered from the platform code, pass a | |
86 | `thermal_zone_params` that has a `sustainable_power`. If no | |
87 | `thermal_zone_params` were being passed, then something like below | |
88 | will suffice: | |
89 | ||
90 | static const struct thermal_zone_params tz_params = { | |
91 | .sustainable_power = 3500, | |
92 | }; | |
93 | ||
94 | and then pass `tz_params` as the 5th parameter to | |
95 | `thermal_zone_device_register()` | |
96 | ||
97 | k_po and k_pu | |
98 | ------------- | |
99 | ||
100 | The implementation of the PID controller in the power allocator | |
101 | thermal governor allows the configuration of two proportional term | |
102 | constants: `k_po` and `k_pu`. `k_po` is the proportional term | |
103 | constant during temperature overshoot periods (current temperature is | |
104 | above "desired temperature" trip point). Conversely, `k_pu` is the | |
105 | proportional term constant during temperature undershoot periods | |
106 | (current temperature below "desired temperature" trip point). | |
107 | ||
108 | These controls are intended as the primary mechanism for configuring | |
109 | the permitted thermal "ramp" of the system. For instance, a lower | |
110 | `k_pu` value will provide a slower ramp, at the cost of capping | |
111 | available capacity at a low temperature. On the other hand, a high | |
112 | value of `k_pu` will result in the governor granting very high power | |
113 | whilst temperature is low, and may lead to temperature overshooting. | |
114 | ||
115 | The default value for `k_pu` is: | |
116 | ||
117 | 2 * sustainable_power / (desired_temperature - switch_on_temp) | |
118 | ||
119 | This means that at `switch_on_temp` the output of the controller's | |
120 | proportional term will be 2 * `sustainable_power`. The default value | |
121 | for `k_po` is: | |
122 | ||
123 | sustainable_power / (desired_temperature - switch_on_temp) | |
124 | ||
125 | Focusing on the proportional and feed forward values of the PID | |
126 | controller equation we have: | |
127 | ||
128 | P_max = k_p * e + sustainable_power | |
129 | ||
130 | The proportional term is proportional to the difference between the | |
131 | desired temperature and the current one. When the current temperature | |
132 | is the desired one, then the proportional component is zero and | |
133 | `P_max` = `sustainable_power`. That is, the system should operate in | |
134 | thermal equilibrium under constant load. `sustainable_power` is only | |
135 | an estimate, which is the reason for closed-loop control such as this. | |
136 | ||
137 | Expanding `k_pu` we get: | |
138 | P_max = 2 * sustainable_power * (T_set - T) / (T_set - T_on) + | |
139 | sustainable_power | |
140 | ||
141 | where | |
142 | T_set is the desired temperature | |
143 | T is the current temperature | |
144 | T_on is the switch on temperature | |
145 | ||
146 | When the current temperature is the switch_on temperature, the above | |
147 | formula becomes: | |
148 | ||
149 | P_max = 2 * sustainable_power * (T_set - T_on) / (T_set - T_on) + | |
150 | sustainable_power = 2 * sustainable_power + sustainable_power = | |
151 | 3 * sustainable_power | |
152 | ||
153 | Therefore, the proportional term alone linearly decreases power from | |
154 | 3 * `sustainable_power` to `sustainable_power` as the temperature | |
155 | rises from the switch on temperature to the desired temperature. | |
156 | ||
157 | k_i and integral_cutoff | |
158 | ----------------------- | |
159 | ||
160 | `k_i` configures the PID loop's integral term constant. This term | |
161 | allows the PID controller to compensate for long term drift and for | |
162 | the quantized nature of the output control: cooling devices can't set | |
163 | the exact power that the governor requests. When the temperature | |
164 | error is below `integral_cutoff`, errors are accumulated in the | |
165 | integral term. This term is then multiplied by `k_i` and the result | |
166 | added to the output of the controller. Typically `k_i` is set low (1 | |
167 | or 2) and `integral_cutoff` is 0. | |
168 | ||
169 | k_d | |
170 | --- | |
171 | ||
172 | `k_d` configures the PID loop's derivative term constant. It's | |
173 | recommended to leave it as the default: 0. | |
174 | ||
175 | Cooling device power API | |
176 | ======================== | |
177 | ||
178 | Cooling devices controlled by this governor must supply the additional | |
179 | "power" API in their `cooling_device_ops`. It consists on three ops: | |
180 | ||
181 | 1. int get_requested_power(struct thermal_cooling_device *cdev, | |
182 | struct thermal_zone_device *tz, u32 *power); | |
183 | @cdev: The `struct thermal_cooling_device` pointer | |
184 | @tz: thermal zone in which we are currently operating | |
185 | @power: pointer in which to store the calculated power | |
186 | ||
187 | `get_requested_power()` calculates the power requested by the device | |
188 | in milliwatts and stores it in @power . It should return 0 on | |
189 | success, -E* on failure. This is currently used by the power | |
190 | allocator governor to calculate how much power to give to each cooling | |
191 | device. | |
192 | ||
193 | 2. int state2power(struct thermal_cooling_device *cdev, struct | |
194 | thermal_zone_device *tz, unsigned long state, u32 *power); | |
195 | @cdev: The `struct thermal_cooling_device` pointer | |
196 | @tz: thermal zone in which we are currently operating | |
197 | @state: A cooling device state | |
198 | @power: pointer in which to store the equivalent power | |
199 | ||
200 | Convert cooling device state @state into power consumption in | |
201 | milliwatts and store it in @power. It should return 0 on success, -E* | |
202 | on failure. This is currently used by thermal core to calculate the | |
203 | maximum power that an actor can consume. | |
204 | ||
205 | 3. int power2state(struct thermal_cooling_device *cdev, u32 power, | |
206 | unsigned long *state); | |
207 | @cdev: The `struct thermal_cooling_device` pointer | |
208 | @power: power in milliwatts | |
209 | @state: pointer in which to store the resulting state | |
210 | ||
211 | Calculate a cooling device state that would make the device consume at | |
212 | most @power mW and store it in @state. It should return 0 on success, | |
213 | -E* on failure. This is currently used by the thermal core to convert | |
214 | a given power set by the power allocator governor to a state that the | |
215 | cooling device can set. It is a function because this conversion may | |
216 | depend on external factors that may change so this function should the | |
217 | best conversion given "current circumstances". | |
218 | ||
219 | Cooling device weights | |
220 | ---------------------- | |
221 | ||
222 | Weights are a mechanism to bias the allocation among cooling | |
223 | devices. They express the relative power efficiency of different | |
224 | cooling devices. Higher weight can be used to express higher power | |
225 | efficiency. Weighting is relative such that if each cooling device | |
226 | has a weight of one they are considered equal. This is particularly | |
227 | useful in heterogeneous systems where two cooling devices may perform | |
228 | the same kind of compute, but with different efficiency. For example, | |
229 | a system with two different types of processors. | |
230 | ||
231 | If the thermal zone is registered using | |
232 | `thermal_zone_device_register()` (i.e., platform code), then weights | |
233 | are passed as part of the thermal zone's `thermal_bind_parameters`. | |
234 | If the platform is registered using device tree, then they are passed | |
235 | as the `contribution` property of each map in the `cooling-maps` node. | |
236 | ||
237 | Limitations of the power allocator governor | |
238 | =========================================== | |
239 | ||
240 | The power allocator governor's PID controller works best if there is a | |
241 | periodic tick. If you have a driver that calls | |
242 | `thermal_zone_device_update()` (or anything that ends up calling the | |
243 | governor's `throttle()` function) repetitively, the governor response | |
244 | won't be very good. Note that this is not particular to this | |
245 | governor, step-wise will also misbehave if you call its throttle() | |
246 | faster than the normal thermal framework tick (due to interrupts for | |
247 | example) as it will overreact. |