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