1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Thermal Management.
9 * The power allocation is in a separate unit. This module
10 * only probe the temperature and calculate the power budget. The allocation
11 * is done in `power_allocation`.
12 * It is meant to work as a performance plugin.
13 */
14
15 #include "thermal_mgmt.h"
16
17 #if THERMAL_HAS_ASYNC_SENSORS
18 static const fwk_id_t mod_thermal_event_id_read_temp = FWK_ID_EVENT_INIT(
19 FWK_MODULE_IDX_THERMAL_MGMT,
20 MOD_THERMAL_EVENT_IDX_READ_TEMP);
21 #endif
22
23 static struct mod_thermal_mgmt_ctx mod_ctx;
24
get_dev_ctx(fwk_id_t id)25 struct mod_thermal_mgmt_dev_ctx *get_dev_ctx(fwk_id_t id)
26 {
27 return &mod_ctx.dev_ctx_table[fwk_id_get_element_idx(id)];
28 }
29
get_actor_ctx(struct mod_thermal_mgmt_dev_ctx * dev_ctx,unsigned int actor)30 struct mod_thermal_mgmt_actor_ctx *get_actor_ctx(
31 struct mod_thermal_mgmt_dev_ctx *dev_ctx,
32 unsigned int actor)
33 {
34 return &dev_ctx->actor_ctx_table[actor];
35 }
36
pi_control(fwk_id_t id)37 static void pi_control(fwk_id_t id)
38 {
39 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
40 int32_t err, terr, pi_power;
41 int32_t k_p, k_i;
42
43 dev_ctx = get_dev_ctx(id);
44
45 if (dev_ctx->cur_temp <
46 dev_ctx->config->pi_controller.switch_on_temperature) {
47 /* The PI loop is not activated */
48 dev_ctx->integral_error = 0;
49 dev_ctx->thermal_allocatable_power = (uint32_t)dev_ctx->config->tdp;
50
51 return;
52 }
53
54 k_i = dev_ctx->config->pi_controller.k_integral;
55
56 err = (int32_t)dev_ctx->config->pi_controller.control_temperature -
57 (int32_t)dev_ctx->cur_temp;
58
59 k_p = (err < 0) ? dev_ctx->config->pi_controller.k_p_overshoot :
60 dev_ctx->config->pi_controller.k_p_undershoot;
61
62 /* Evaluate the integral term */
63 if (((err > 0) && (dev_ctx->integral_error < (INT32_MAX - err))) ||
64 ((err < 0) && (dev_ctx->integral_error > (INT32_MIN - err)))) {
65 terr = dev_ctx->integral_error + err;
66
67 if ((err < dev_ctx->config->pi_controller.integral_cutoff) &&
68 (terr < dev_ctx->config->pi_controller.integral_max)) {
69 /*
70 * The error is below the cutoff value and,
71 * the accumulated error is still within the maximum permissible
72 * value, thus continue integration.
73 */
74 dev_ctx->integral_error = terr;
75 }
76 }
77
78 pi_power = (k_p * err) + (k_i * dev_ctx->integral_error);
79
80 if (pi_power + (int32_t)dev_ctx->config->tdp > 0) {
81 dev_ctx->thermal_allocatable_power =
82 pi_power + (uint32_t)dev_ctx->config->tdp;
83 } else {
84 dev_ctx->thermal_allocatable_power = 0;
85 }
86 }
87
thermal_protection(struct mod_thermal_mgmt_dev_ctx * dev_ctx)88 static void thermal_protection(struct mod_thermal_mgmt_dev_ctx *dev_ctx)
89 {
90 if (dev_ctx->cur_temp >=
91 dev_ctx->config->temp_protection->crit_temp_threshold) {
92 FWK_LOG_CRIT(
93 "[THERMAL][%s] temp (%u) reached critical threshold!\n",
94 fwk_module_get_element_name(dev_ctx->id),
95 (unsigned int)dev_ctx->cur_temp);
96
97 if (dev_ctx->thermal_protection_api->critical != NULL) {
98 dev_ctx->thermal_protection_api->critical(
99 dev_ctx->config->temp_protection->driver_id, dev_ctx->id);
100 }
101 } else if (
102 dev_ctx->cur_temp >=
103 dev_ctx->config->temp_protection->warn_temp_threshold) {
104 FWK_LOG_WARN(
105 "[THERMAL][%s] temp (%u) reached warning threshold!\n",
106 fwk_module_get_element_name(dev_ctx->id),
107 (unsigned int)dev_ctx->cur_temp);
108
109 if (dev_ctx->thermal_protection_api->warning != NULL) {
110 dev_ctx->thermal_protection_api->warning(
111 dev_ctx->config->temp_protection->driver_id, dev_ctx->id);
112 }
113 }
114 }
115
read_temperature(fwk_id_t id)116 static int read_temperature(fwk_id_t id)
117 {
118 #if THERMAL_HAS_ASYNC_SENSORS
119 /* Initiate the temperature reading sequence */
120 struct fwk_event_light event = {
121 .source_id = id,
122 .target_id = id,
123 .id = mod_thermal_event_id_read_temp,
124 };
125
126 return fwk_put_event(&event);
127 #else
128 int status;
129 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
130
131 dev_ctx = get_dev_ctx(id);
132
133 status = dev_ctx->sensor_api->get_data(
134 dev_ctx->config->sensor_id, &dev_ctx->sensor_data);
135 if (status == FWK_SUCCESS) {
136 dev_ctx->cur_temp = (uint32_t)dev_ctx->sensor_data.value;
137
138 dev_ctx->control_needs_update = true;
139 }
140
141 return status;
142 #endif
143 }
144
control_update(fwk_id_t id)145 static int control_update(fwk_id_t id)
146 {
147 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
148 int status;
149
150 dev_ctx = get_dev_ctx(id);
151
152 dev_ctx->tick_counter++;
153 if (dev_ctx->tick_counter > dev_ctx->config->slow_loop_mult) {
154 dev_ctx->tick_counter = 0;
155
156 if (dev_ctx->control_needs_update) {
157 /* The last reading was not processed */
158 FWK_LOG_WARN("[TPM] Failed to process last reading\n");
159
160 return FWK_E_PANIC;
161 }
162
163 /*
164 * ASYNC: The new temperature is not yet available but should be ready
165 * by next fast-loop tick.
166 * SYNC: The new temperature is ready.
167 *
168 * Either way we need to attempt to continue to process the loop.
169 */
170 status = read_temperature(id);
171 if (status != FWK_SUCCESS) {
172 return FWK_E_DEVICE;
173 }
174 }
175
176 if (dev_ctx->control_needs_update) {
177 dev_ctx->control_needs_update = false;
178 if (dev_ctx->config->thermal_actors_count > 0) {
179 pi_control(id);
180 }
181
182 dev_ctx->tot_spare_power = 0;
183
184 if (dev_ctx->config->temp_protection != NULL) {
185 thermal_protection(dev_ctx);
186 }
187 }
188
189 return FWK_SUCCESS;
190 }
191
192 /*
193 * Thermal Management should be configured in such a way that this callback
194 * is called once every performance update via the plugins handler and the data
195 * contains all the performance domains info (see _TYPE_FULL view).
196 */
thermal_update(struct perf_plugins_perf_update * data)197 static int thermal_update(struct perf_plugins_perf_update *data)
198 {
199 unsigned int dev_idx;
200 fwk_id_t dev_id;
201 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
202 int status;
203
204 for (dev_idx = 0; dev_idx < mod_ctx.dev_ctx_count; dev_idx++) {
205 dev_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_THERMAL_MGMT, dev_idx);
206 dev_ctx = get_dev_ctx(dev_id);
207
208 status = control_update(dev_ctx->id);
209 if (status != FWK_SUCCESS) {
210 return status;
211 }
212 if (dev_ctx->config->thermal_actors_count > 0) {
213 distribute_power(dev_ctx->id, data->level, data->adj_max_limit);
214 }
215 }
216
217 return FWK_SUCCESS;
218 }
219
220 struct perf_plugins_api mod_thermal_perf_plugins_api = {
221 .update = thermal_update,
222 };
223
224 /*
225 * Framework handler functions.
226 */
227
thermal_mgmt_init(fwk_id_t module_id,unsigned int element_count,const void * data)228 static int thermal_mgmt_init(
229 fwk_id_t module_id,
230 unsigned int element_count,
231 const void *data)
232 {
233 mod_ctx.dev_ctx_table =
234 fwk_mm_calloc(element_count, sizeof(struct mod_thermal_mgmt_dev_ctx));
235 mod_ctx.dev_ctx_count = element_count;
236
237 return FWK_SUCCESS;
238 }
239
thermal_mgmt_dev_init(fwk_id_t element_id,unsigned int sub_element_count,const void * data)240 static int thermal_mgmt_dev_init(
241 fwk_id_t element_id,
242 unsigned int sub_element_count,
243 const void *data)
244 {
245 struct mod_thermal_mgmt_dev_config *config;
246 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
247 struct mod_thermal_mgmt_actor_ctx *actor_ctx;
248
249 uint64_t sum_weights;
250 uint32_t actor;
251
252 if (data == NULL) {
253 return FWK_E_PARAM;
254 }
255
256 config = (struct mod_thermal_mgmt_dev_config *)data;
257 dev_ctx = get_dev_ctx(element_id);
258 dev_ctx->config = config;
259 dev_ctx->id = element_id;
260
261 if (dev_ctx->config->thermal_actors_count > 0) {
262 /* Assume TDP until PI loop is updated */
263 dev_ctx->thermal_allocatable_power = (uint32_t)dev_ctx->config->tdp;
264
265 dev_ctx->actor_ctx_table = fwk_mm_calloc(
266 dev_ctx->config->thermal_actors_count,
267 sizeof(struct mod_thermal_mgmt_actor_ctx));
268 } else if (dev_ctx->config->temp_protection == NULL) {
269 /* There is neither temperature protection enabled nor actors defined */
270 return FWK_E_PARAM;
271 }
272
273 for (actor = 0; actor < dev_ctx->config->thermal_actors_count; actor++) {
274 /* Get actor context and set configuration */
275 actor_ctx = get_actor_ctx(dev_ctx, actor);
276 actor_ctx->config = &config->thermal_actors_table[actor];
277
278 if (!fwk_module_is_valid_element_id(
279 actor_ctx->config->dvfs_domain_id)) {
280 return FWK_E_PARAM;
281 }
282
283 if (!fwk_id_type_is_valid(actor_ctx->config->driver_id) ||
284 fwk_id_is_equal(actor_ctx->config->driver_id, FWK_ID_NONE)) {
285 return FWK_E_PARAM;
286 }
287
288 sum_weights = actor_ctx->config->weight * config->tdp * config->tdp;
289
290 if (sum_weights > UINT32_MAX) {
291 FWK_LOG_WARN(
292 "[THERMAL] WARN: Possible overflow for device %u",
293 fwk_id_get_element_idx(element_id));
294 }
295 }
296
297 return FWK_SUCCESS;
298 }
299
thermal_mgmt_bind(fwk_id_t id,unsigned int round)300 static int thermal_mgmt_bind(fwk_id_t id, unsigned int round)
301 {
302 int status;
303 unsigned int actor;
304 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
305 struct mod_thermal_mgmt_actor_ctx *actor_ctx;
306
307 if (round > 0) {
308 return FWK_SUCCESS;
309 }
310
311 if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
312 return FWK_SUCCESS;
313 }
314
315 dev_ctx = get_dev_ctx(id);
316
317 /* Bind to sensor */
318 status = fwk_module_bind(
319 dev_ctx->config->sensor_id,
320 mod_sensor_api_id_sensor,
321 &dev_ctx->sensor_api);
322 if (status != FWK_SUCCESS) {
323 return FWK_E_PANIC;
324 }
325
326 if (dev_ctx->config->temp_protection != NULL) {
327 /* Bind to thermal protection driver */
328 status = fwk_module_bind(
329 dev_ctx->config->temp_protection->driver_id,
330 dev_ctx->config->temp_protection->driver_api_id,
331 &dev_ctx->thermal_protection_api);
332 if (status != FWK_SUCCESS) {
333 return FWK_E_PANIC;
334 }
335 }
336
337 /* Bind to a respective thermal driver */
338 status = fwk_module_bind(
339 FWK_ID_MODULE(fwk_id_get_module_idx(dev_ctx->config->driver_api_id)),
340 dev_ctx->config->driver_api_id,
341 &dev_ctx->driver_api);
342 if (status != FWK_SUCCESS) {
343 return FWK_E_PANIC;
344 }
345
346 for (actor = 0; actor < dev_ctx->config->thermal_actors_count; actor++) {
347 /* Get actor context and bind */
348 actor_ctx = get_actor_ctx(dev_ctx, actor);
349
350 if (actor_ctx->config->activity_factor != NULL) {
351 /* Bind to thermal protection driver */
352 status = fwk_module_bind(
353 actor_ctx->config->activity_factor->driver_id,
354 actor_ctx->config->activity_factor->driver_api_id,
355 &actor_ctx->activity_api);
356 if (status != FWK_SUCCESS) {
357 return FWK_E_PANIC;
358 }
359 }
360 }
361
362 return FWK_SUCCESS;
363 }
364
thermal_mgmt_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** thermal_bind_request_api)365 static int thermal_mgmt_process_bind_request(
366 fwk_id_t source_id,
367 fwk_id_t target_id,
368 fwk_id_t api_id,
369 const void **thermal_bind_request_api)
370 {
371 *thermal_bind_request_api = &mod_thermal_perf_plugins_api;
372
373 return FWK_SUCCESS;
374 }
375
376 #if THERMAL_HAS_ASYNC_SENSORS
thermal_mgmt_process_event(const struct fwk_event * event,struct fwk_event * resp_event)377 static int thermal_mgmt_process_event(
378 const struct fwk_event *event,
379 struct fwk_event *resp_event)
380 {
381 struct mod_thermal_mgmt_dev_ctx *dev_ctx;
382 int status;
383
384 dev_ctx = get_dev_ctx(event->target_id);
385
386 if (fwk_id_is_equal(event->id, mod_thermal_event_id_read_temp)) {
387 /* Temperature-reading event */
388 status = dev_ctx->sensor_api->get_data(
389 dev_ctx->config->sensor_id, &dev_ctx->sensor_data);
390 if (status == FWK_SUCCESS) {
391 dev_ctx->cur_temp = (uint32_t)dev_ctx->sensor_data.value;
392 }
393 } else if (fwk_id_is_equal(event->id, mod_sensor_event_id_read_request)) {
394 /* Response event from Sensor HAL */
395 if (dev_ctx->sensor_data.status == FWK_SUCCESS) {
396 dev_ctx->cur_temp = (uint32_t)dev_ctx->sensor_data.value;
397 status = FWK_SUCCESS;
398 } else {
399 status = FWK_E_DEVICE;
400 }
401 } else {
402 status = FWK_E_PARAM;
403 }
404
405 if (status == FWK_SUCCESS) {
406 dev_ctx->control_needs_update = true;
407 } else if (status == FWK_PENDING) {
408 status = FWK_SUCCESS;
409 }
410
411 return status;
412 }
413 #endif
414
415 const struct fwk_module module_thermal_mgmt = {
416 .type = FWK_MODULE_TYPE_SERVICE,
417 .api_count = (unsigned int)MOD_THERMAL_API_COUNT,
418 .event_count = (unsigned int)MOD_THERMAL_EVENT_IDX_COUNT,
419 .init = thermal_mgmt_init,
420 .element_init = thermal_mgmt_dev_init,
421 .bind = thermal_mgmt_bind,
422 .process_bind_request = thermal_mgmt_process_bind_request,
423 #if THERMAL_HAS_ASYNC_SENSORS
424 .process_event = thermal_mgmt_process_event,
425 #endif
426 };
427