1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2022-3-08      GuEe-GUI     the first version
9  */
10 
11 #include <drivers/platform.h>
12 
13 #define DBG_TAG "rtdm.thermal"
14 #define DBG_LVL DBG_INFO
15 #include <rtdbg.h>
16 
17 #include "thermal_dm.h"
18 
19 #ifndef INT_MAX
20 #define INT_MAX (RT_UINT32_MAX >> 1)
21 #endif
22 
23 #define device_list(dev)            (dev)->parent.parent.list
24 #define device_foreach(dev, nodes)  rt_list_for_each_entry(dev, nodes, parent.parent.list)
25 
26 static RT_DEFINE_SPINLOCK(nodes_lock);
27 static rt_list_t thermal_zone_device_nodes = RT_LIST_OBJECT_INIT(thermal_zone_device_nodes);
28 static rt_list_t thermal_cooling_device_nodes = RT_LIST_OBJECT_INIT(thermal_cooling_device_nodes);
29 static rt_list_t thermal_cooling_governor_nodes = RT_LIST_OBJECT_INIT(thermal_cooling_governor_nodes);
30 
31 #ifdef RT_USING_OFW
thermal_ofw_params_parse(struct rt_ofw_node * np,struct rt_thermal_zone_params * tz_params)32 static void thermal_ofw_params_parse(struct rt_ofw_node *np,
33         struct rt_thermal_zone_params *tz_params)
34 {
35     rt_uint32_t coef[2], prop;
36 
37     if (!np)
38     {
39         return;
40     }
41 
42     if (!rt_ofw_prop_read_u32(np, "sustainable-power", &prop))
43     {
44         tz_params->sustainable_power = prop;
45     }
46 
47     /*
48      * For now, the thermal framework supports only one sensor per thermal zone.
49      * Thus, we are considering only the first two values as slope and offset.
50      */
51     if (rt_ofw_prop_read_u32_array_index(np, "coefficients", 0, 2, coef) < 0)
52     {
53         coef[0] = 1;
54         coef[1] = 0;
55     }
56 
57     tz_params->slope = coef[0];
58     tz_params->offset = coef[1];
59 }
60 
thermal_ofw_setup(struct rt_ofw_node * np,struct rt_thermal_zone_device * zdev)61 static void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev)
62 {
63     int i = 0;
64     rt_uint32_t delay, pdelay;
65     struct rt_ofw_cell_args args;
66     struct rt_ofw_node *tmp_np, *tz_np, *trip_np, *cm_np, *cdev_np;
67 
68     if (!np || !zdev)
69     {
70         return;
71     }
72 
73     tmp_np = rt_ofw_find_node_by_path("/thermal-zones");
74 
75     if (!tmp_np)
76     {
77         return;
78     }
79 
80     rt_ofw_foreach_child_node(tmp_np, tz_np)
81     {
82         if (!rt_ofw_parse_phandle_cells(tz_np, "thermal-sensors", "#thermal-sensor-cells", 0, &args))
83         {
84             if (args.data == np && (!args.args_count || args.args[0] == zdev->zone_id))
85             {
86                 rt_ofw_node_put(args.data);
87 
88                 goto _found;
89             }
90             rt_ofw_node_put(args.data);
91         }
92     }
93 
94     return;
95 
96 _found:
97     rt_ofw_prop_read_u32(tz_np, "polling-delay-passive", &pdelay);
98     rt_ofw_prop_read_u32(tz_np, "polling-delay", &delay);
99 
100     zdev->passive_delay = rt_tick_from_millisecond(pdelay);
101     zdev->polling_delay = rt_tick_from_millisecond(delay);
102 
103     thermal_ofw_params_parse(tz_np, &zdev->params);
104 
105     if (zdev->trips_nr)
106     {
107         goto _scan_cooling;
108     }
109 
110     tmp_np = rt_ofw_get_child_by_tag(tz_np, "trips");
111     if (!tmp_np)
112     {
113         goto _scan_cooling;
114     }
115 
116     zdev->trips_nr = rt_ofw_get_child_count(tmp_np);
117     if (!zdev->trips_nr)
118     {
119         goto _scan_cooling;
120     }
121     zdev->trips = rt_calloc(zdev->trips_nr, sizeof(*zdev->trips));
122     zdev->trips_free = RT_TRUE;
123 
124     if (!zdev->trips)
125     {
126         LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "trips");
127         RT_ASSERT(0);
128     }
129 
130     rt_ofw_foreach_child_node(tmp_np, trip_np)
131     {
132         const char *type;
133 
134         rt_ofw_prop_read_u32(trip_np, "temperature", (rt_uint32_t *)&zdev->trips[i].temperature);
135         rt_ofw_prop_read_u32(trip_np, "hysteresis", (rt_uint32_t *)&zdev->trips[i].hysteresis);
136         rt_ofw_prop_read_string(trip_np, "type", &type);
137         zdev->trips[i].type = thermal_type(type);
138 
139         rt_ofw_data(trip_np) = &zdev->trips[i];
140 
141         ++i;
142     }
143 
144 _scan_cooling:
145     i = 0;
146     tmp_np = rt_ofw_get_child_by_tag(tz_np, "cooling-maps");
147     if (!tmp_np)
148     {
149         goto _end;
150     }
151 
152     zdev->cooling_maps_nr = rt_ofw_get_child_count(tmp_np);
153     if (!zdev->cooling_maps_nr)
154     {
155         goto _end;
156     }
157     zdev->cooling_maps = rt_calloc(zdev->cooling_maps_nr, sizeof(*zdev->cooling_maps));
158 
159     if (!zdev->cooling_maps)
160     {
161         LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "cooling_maps");
162         RT_ASSERT(0);
163     }
164 
165     rt_ofw_foreach_child_node(tmp_np, cm_np)
166     {
167         struct rt_thermal_cooling_device *cdev;
168         struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i++];
169 
170         map->cells_nr = rt_ofw_count_phandle_cells(cm_np, "cooling-device", "#cooling-cells");
171         map->cells = rt_calloc(sizeof(*map->cells), map->cells_nr);
172 
173         if (!map->cells)
174         {
175             LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "cells");
176             RT_ASSERT(0);
177         }
178 
179         trip_np = rt_ofw_parse_phandle(cm_np, "trip", 0);
180         map->trips = rt_ofw_data(trip_np);
181         rt_ofw_node_put(trip_np);
182 
183         if (!map->trips)
184         {
185             LOG_E("%s: trips(%s) not found", rt_ofw_node_full_name(np),
186                     rt_ofw_node_full_name(trip_np));
187             RT_ASSERT(0);
188         }
189 
190         rt_ofw_prop_read_u32(cm_np, "contribution", &map->contribution);
191 
192         for (int c = 0; c < map->cells_nr; ++c)
193         {
194             struct rt_thermal_cooling_cell *cell = &map->cells[c];
195 
196             if (rt_ofw_parse_phandle_cells(cm_np, "cooling-device", "#cooling-cells", c, &args))
197             {
198                 continue;
199             }
200 
201             cdev_np = args.data;
202 
203             rt_spin_lock(&nodes_lock);
204             device_foreach(cdev, &thermal_cooling_device_nodes)
205             {
206                 if (cdev->parent.ofw_node == cdev_np)
207                 {
208                     cell->cooling_devices = cdev;
209                     break;
210                 }
211             }
212             rt_spin_unlock(&nodes_lock);
213 
214             cell->level_range[0] = args.args[0];
215             cell->level_range[1] = args.args[1];
216 
217             if (cell->cooling_devices)
218             {
219                 thermal_bind(cell->cooling_devices, zdev);
220             }
221 
222             rt_ofw_node_put(cdev_np);
223         }
224     }
225 _end:
226 }
227 #else
thermal_ofw_setup(struct rt_ofw_node * np,struct rt_thermal_zone_device * zdev)228 rt_inline void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev)
229 {
230 }
231 #endif /* RT_USING_OFW */
232 
thermal_zone_poll(struct rt_work * work,void * work_data)233 static void thermal_zone_poll(struct rt_work *work, void *work_data)
234 {
235     struct rt_thermal_zone_device *zdev = work_data;
236 
237     rt_thermal_zone_device_update(zdev, RT_THERMAL_MSG_EVENT_UNSPECIFIED);
238 }
239 
rt_thermal_zone_device_register(struct rt_thermal_zone_device * zdev)240 rt_err_t rt_thermal_zone_device_register(struct rt_thermal_zone_device *zdev)
241 {
242     if (!zdev || !zdev->ops || !zdev->ops->get_temp)
243     {
244         return -RT_EINVAL;
245     }
246 
247     zdev->ops->get_temp(zdev, &zdev->temperature);
248     zdev->last_temperature = zdev->temperature;
249 
250     if (!zdev->trips)
251     {
252         zdev->trips_nr = 0;
253     }
254 
255     rt_spin_lock_init(&zdev->nodes_lock);
256     rt_list_init(&zdev->notifier_nodes);
257     rt_list_init(&device_list(zdev));
258     rt_mutex_init(&zdev->mutex, rt_dm_dev_get_name(&zdev->parent), RT_IPC_FLAG_PRIO);
259 
260     zdev->temperature = RT_THERMAL_TEMP_INVALID;
261     zdev->prev_low_trip = -INT_MAX;
262     zdev->prev_high_trip = INT_MAX;
263 
264     rt_spin_lock(&nodes_lock);
265     rt_list_insert_before(&thermal_zone_device_nodes, &device_list(zdev));
266     rt_spin_unlock(&nodes_lock);
267 
268     thermal_ofw_setup(zdev->parent.ofw_node, zdev);
269 
270     rt_work_init(&zdev->poller, thermal_zone_poll, zdev);
271     zdev->enabled = RT_TRUE;
272 
273     /* Start to poll */
274     rt_work_submit(&zdev->poller, zdev->polling_delay);
275 
276     return RT_EOK;
277 }
278 
rt_thermal_zone_device_unregister(struct rt_thermal_zone_device * zdev)279 rt_err_t rt_thermal_zone_device_unregister(struct rt_thermal_zone_device *zdev)
280 {
281     if (!zdev)
282     {
283         return -RT_EINVAL;
284     }
285 
286     rt_spin_lock(&zdev->nodes_lock);
287     if (rt_list_isempty(&zdev->notifier_nodes))
288     {
289         LOG_E("%s: there is %u user", rt_dm_dev_get_name(&zdev->parent),
290                 rt_list_len(&zdev->notifier_nodes));
291 
292         rt_spin_unlock(&zdev->nodes_lock);
293 
294         return -RT_EBUSY;
295     }
296     rt_spin_unlock(&zdev->nodes_lock);
297 
298     rt_work_cancel(&zdev->poller);
299 
300     rt_spin_lock(&nodes_lock);
301     rt_list_remove(&device_list(zdev));
302     rt_spin_unlock(&nodes_lock);
303 
304     if (zdev->trips_free && zdev->trips)
305     {
306         rt_free(zdev->trips);
307     }
308 
309     if (zdev->cooling_maps_nr && zdev->cooling_maps_nr)
310     {
311         for (int i = 0; i < zdev->cooling_maps_nr; ++i)
312         {
313             struct rt_thermal_cooling_device *cdev;
314             struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
315 
316             for (int c = 0; c < map->cells_nr; ++c)
317             {
318                 cdev = map->cells[i].cooling_devices;
319 
320                 if (cdev)
321                 {
322                     thermal_unbind(cdev, zdev);
323                 }
324             }
325 
326             rt_free(map->cells);
327         }
328 
329         rt_free(zdev->cooling_maps);
330     }
331 
332     rt_mutex_detach(&zdev->mutex);
333 
334     return RT_EOK;
335 }
336 
rt_thermal_cooling_device_register(struct rt_thermal_cooling_device * cdev)337 rt_err_t rt_thermal_cooling_device_register(struct rt_thermal_cooling_device *cdev)
338 {
339     rt_err_t err;
340 
341     if (!cdev || !cdev->ops ||
342         !cdev->ops->get_max_level || !cdev->ops->get_cur_level || !cdev->ops->set_cur_level)
343     {
344         return -RT_EINVAL;
345     }
346 
347     if ((err = cdev->ops->get_max_level(cdev, &cdev->max_level)))
348     {
349         return err;
350     }
351 
352     rt_list_init(&device_list(cdev));
353     rt_list_init(&cdev->governor_node);
354 
355     rt_spin_lock(&nodes_lock);
356     rt_list_insert_before(&thermal_cooling_device_nodes, &device_list(cdev));
357     rt_spin_unlock(&nodes_lock);
358 
359     err = rt_thermal_cooling_device_change_governor(cdev, RT_NULL);
360 
361     return err;
362 }
363 
rt_thermal_cooling_device_unregister(struct rt_thermal_cooling_device * cdev)364 rt_err_t rt_thermal_cooling_device_unregister(struct rt_thermal_cooling_device *cdev)
365 {
366     if (!cdev)
367     {
368         return -RT_EINVAL;
369     }
370 
371     if (cdev->parent.ref_count)
372     {
373         LOG_E("%s: there is %u user",
374                 rt_dm_dev_get_name(&cdev->parent), cdev->parent.ref_count);
375         return -RT_EINVAL;
376     }
377 
378     rt_spin_lock(&nodes_lock);
379     rt_list_remove(&device_list(cdev));
380     rt_spin_unlock(&nodes_lock);
381 
382     return RT_EOK;
383 }
384 
dumb_governor_tuning(struct rt_thermal_zone_device * zdev,int map_idx,int cell_idx,rt_ubase_t * level)385 static void dumb_governor_tuning(struct rt_thermal_zone_device *zdev,
386             int map_idx, int cell_idx, rt_ubase_t *level)
387 {
388     struct rt_thermal_cooling_map *map = &zdev->cooling_maps[map_idx];
389 
390     if (zdev->cooling && zdev->temperature > map->trips->temperature)
391     {
392         if (zdev->temperature - zdev->last_temperature > map->trips->hysteresis)
393         {
394             ++*level;
395         }
396         else if (zdev->last_temperature - zdev->temperature > map->trips->hysteresis)
397         {
398             --*level;
399         }
400     }
401     else
402     {
403         *level = 0;
404     }
405 }
406 
407 static struct rt_thermal_cooling_governor dumb_governor =
408 {
409     .name = "dumb",
410     .tuning = dumb_governor_tuning,
411 };
412 
system_thermal_cooling_governor_init(void)413 static int system_thermal_cooling_governor_init(void)
414 {
415     rt_thermal_cooling_governor_register(&dumb_governor);
416 
417     return 0;
418 }
419 INIT_CORE_EXPORT(system_thermal_cooling_governor_init);
420 
rt_thermal_cooling_governor_register(struct rt_thermal_cooling_governor * gov)421 rt_err_t rt_thermal_cooling_governor_register(struct rt_thermal_cooling_governor *gov)
422 {
423     rt_err_t err = RT_EOK;
424     struct rt_thermal_cooling_governor *gov_tmp;
425 
426     if (!gov || !gov->name || !gov->tuning)
427     {
428         return -RT_EINVAL;
429     }
430 
431     rt_list_init(&gov->list);
432     rt_list_init(&gov->cdev_nodes);
433 
434     rt_spin_lock(&nodes_lock);
435 
436     rt_list_for_each_entry(gov_tmp, &thermal_cooling_governor_nodes, list)
437     {
438         if (!rt_strcmp(gov_tmp->name, gov->name))
439         {
440             err = -RT_ERROR;
441             goto _out_unlock;
442         }
443     }
444 
445     rt_list_insert_before(&thermal_cooling_governor_nodes, &gov->list);
446 
447 _out_unlock:
448     rt_spin_unlock(&nodes_lock);
449 
450     return err;
451 }
452 
rt_thermal_cooling_governor_unregister(struct rt_thermal_cooling_governor * gov)453 rt_err_t rt_thermal_cooling_governor_unregister(struct rt_thermal_cooling_governor *gov)
454 {
455     if (!gov)
456     {
457         return -RT_EINVAL;
458     }
459 
460     if (gov == &dumb_governor)
461     {
462         return -RT_EINVAL;
463     }
464 
465     rt_spin_lock(&nodes_lock);
466 
467     if (!rt_list_isempty(&gov->cdev_nodes))
468     {
469         goto _out_unlock;
470     }
471 
472     rt_list_remove(&gov->list);
473 
474 _out_unlock:
475     rt_spin_unlock(&nodes_lock);
476 
477     return RT_EOK;
478 }
479 
rt_thermal_cooling_device_change_governor(struct rt_thermal_cooling_device * cdev,const char * name)480 rt_err_t rt_thermal_cooling_device_change_governor(struct rt_thermal_cooling_device *cdev,
481         const char *name)
482 {
483     rt_err_t err;
484     struct rt_thermal_cooling_governor *gov;
485 
486     if (!cdev)
487     {
488         return -RT_EINVAL;
489     }
490 
491     name = name ? : dumb_governor.name;
492     err = -RT_ENOSYS;
493 
494     rt_spin_lock(&nodes_lock);
495 
496     rt_list_for_each_entry(gov, &thermal_cooling_governor_nodes, list)
497     {
498         if (!rt_strcmp(gov->name, name))
499         {
500             if (cdev->gov)
501             {
502                 rt_list_remove(&cdev->governor_node);
503             }
504 
505             cdev->gov = gov;
506             rt_list_insert_before(&cdev->governor_node, &gov->cdev_nodes);
507 
508             err = RT_EOK;
509             break;
510         }
511     }
512 
513     rt_spin_unlock(&nodes_lock);
514 
515     return err;
516 }
517 
rt_thermal_zone_notifier_register(struct rt_thermal_zone_device * zdev,struct rt_thermal_notifier * notifier)518 rt_err_t rt_thermal_zone_notifier_register(struct rt_thermal_zone_device *zdev,
519         struct rt_thermal_notifier *notifier)
520 {
521     if (!zdev || !notifier)
522     {
523         return -RT_EINVAL;
524     }
525 
526     notifier->zdev = zdev;
527     rt_list_init(&notifier->list);
528 
529     rt_spin_lock(&zdev->nodes_lock);
530     rt_list_insert_after(&zdev->notifier_nodes, &notifier->list);
531     rt_spin_unlock(&zdev->nodes_lock);
532 
533     return RT_EOK;
534 }
535 
rt_thermal_zone_notifier_unregister(struct rt_thermal_zone_device * zdev,struct rt_thermal_notifier * notifier)536 rt_err_t rt_thermal_zone_notifier_unregister(struct rt_thermal_zone_device *zdev,
537         struct rt_thermal_notifier *notifier)
538 {
539     if (!zdev || !notifier)
540     {
541         return -RT_EINVAL;
542     }
543 
544     rt_spin_lock(&zdev->nodes_lock);
545     rt_list_remove(&notifier->list);
546     rt_spin_unlock(&zdev->nodes_lock);
547 
548     return RT_EOK;
549 }
550 
rt_thermal_zone_device_update(struct rt_thermal_zone_device * zdev,rt_ubase_t msg)551 void rt_thermal_zone_device_update(struct rt_thermal_zone_device *zdev, rt_ubase_t msg)
552 {
553     rt_err_t err;
554     rt_bool_t passive = RT_FALSE, need_cool = RT_FALSE;
555     struct rt_thermal_notifier *notifier, *next_notifier;
556 
557     RT_ASSERT(zdev != RT_NULL);
558 
559     if (!rt_interrupt_get_nest())
560     {
561         rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER);
562     }
563 
564     /* Check thermal zone status */
565     if (msg == RT_THERMAL_MSG_DEVICE_DOWN)
566     {
567         zdev->enabled = RT_FALSE;
568     }
569     else if (msg == RT_THERMAL_MSG_DEVICE_UP)
570     {
571         zdev->enabled = RT_TRUE;
572     }
573 
574     /* Read temperature */
575     zdev->last_temperature = zdev->temperature;
576     zdev->ops->get_temp(zdev, &zdev->temperature);
577 
578     for (int i = 0; i < zdev->trips_nr; ++i)
579     {
580         struct rt_thermal_trip *tmp_trip = &zdev->trips[i];
581 
582         if (zdev->temperature <= tmp_trip->temperature)
583         {
584             continue;
585         }
586 
587         switch (tmp_trip->type)
588         {
589         case RT_THERMAL_TRIP_PASSIVE:
590             passive = RT_TRUE;
591             goto cooling;
592 
593         case RT_THERMAL_TRIP_CRITICAL:
594             if (zdev->ops->critical)
595             {
596                 zdev->ops->critical(zdev);
597             }
598             else if (zdev->last_temperature > tmp_trip->temperature)
599             {
600                 /* Tried to cool already, but failed */
601                 rt_hw_cpu_reset();
602             }
603             else
604             {
605                 goto cooling;
606             }
607             break;
608 
609         case RT_THERMAL_TRIP_HOT:
610             if (zdev->ops->hot)
611             {
612                 zdev->ops->hot(zdev);
613                 break;
614             }
615 
616         default:
617         cooling:
618             zdev->cooling = need_cool = RT_TRUE;
619             rt_thermal_cooling_device_kick(zdev);
620             break;
621         }
622     }
623 
624     if (!need_cool && zdev->cooling)
625     {
626         rt_thermal_cooling_device_kick(zdev);
627     }
628 
629     /* Set the new trips */
630     if (zdev->ops->set_trips)
631     {
632         rt_bool_t same_trip = RT_FALSE;
633         int low = -INT_MAX, high = INT_MAX;
634         struct rt_thermal_trip trip;
635 
636         for (int i = 0; i < zdev->trips_nr; ++i)
637         {
638             int trip_low;
639             rt_bool_t low_set = RT_FALSE;
640 
641             rt_memcpy(&trip, &zdev->trips[i], sizeof(trip));
642 
643             trip_low = trip.temperature - trip.hysteresis;
644 
645             if (trip_low < zdev->temperature && trip_low > low)
646             {
647                 low = trip_low;
648                 low_set = RT_TRUE;
649                 same_trip = RT_FALSE;
650             }
651 
652             if (trip.temperature > zdev->temperature && trip.temperature < high)
653             {
654                 high = trip.temperature;
655                 same_trip = low_set;
656             }
657         }
658 
659         /* No need to change trip points */
660         if (zdev->prev_low_trip == low && zdev->prev_high_trip == high)
661         {
662             goto _call_notifier;
663         }
664 
665         if (same_trip &&
666             (zdev->prev_low_trip != -INT_MAX || zdev->prev_high_trip != INT_MAX))
667         {
668             goto _call_notifier;
669         }
670 
671         zdev->prev_low_trip = low;
672         zdev->prev_high_trip = high;
673 
674         if ((err = zdev->ops->set_trips(zdev, low, high)))
675         {
676             LOG_E("%s: Set trips error = %s", rt_dm_dev_get_name(&zdev->parent),
677                     rt_strerror(err));
678         }
679     }
680 
681     /* Call all notifier, maybe have governor */
682 _call_notifier:
683     rt_spin_lock(&zdev->nodes_lock);
684 
685     rt_list_for_each_entry_safe(notifier, next_notifier, &zdev->notifier_nodes, list)
686     {
687         rt_spin_unlock(&zdev->nodes_lock);
688 
689         notifier->callback(notifier, msg);
690 
691         rt_spin_lock(&zdev->nodes_lock);
692     }
693 
694     rt_spin_unlock(&zdev->nodes_lock);
695 
696     /* Prepare for the next report */
697     if (!zdev->enabled)
698     {
699         rt_work_cancel(&zdev->poller);
700     }
701     else if (passive && zdev->passive_delay)
702     {
703         rt_work_submit(&zdev->poller, zdev->passive_delay);
704     }
705     else if (zdev->polling_delay)
706     {
707         rt_work_submit(&zdev->poller, zdev->polling_delay);
708     }
709 
710     if (!rt_interrupt_get_nest())
711     {
712         rt_mutex_release(&zdev->mutex);
713     }
714 }
715 
rt_thermal_cooling_device_kick(struct rt_thermal_zone_device * zdev)716 void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev)
717 {
718     RT_ASSERT(zdev != RT_NULL);
719 
720     for (int i = 0; i < zdev->cooling_maps_nr; ++i)
721     {
722         rt_ubase_t level;
723         struct rt_thermal_cooling_device *cdev;
724         struct rt_thermal_cooling_cell *cell;
725         struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
726 
727         for (int c = 0; c < map->cells_nr; ++c)
728         {
729             cell = &map->cells[c];
730             cdev = cell->cooling_devices;
731 
732             if (!cdev)
733             {
734                 continue;
735             }
736 
737             /* Update status */
738             if (cdev->ops->get_max_level(cdev, &cdev->max_level))
739             {
740                 continue;
741             }
742 
743             if (cdev->ops->get_cur_level(cdev, &level) || level > cdev->max_level)
744             {
745                 continue;
746             }
747 
748             /* Check if cooling is required */
749             if (level >= cell->level_range[0] && level <= cell->level_range[1])
750             {
751                 /* Is cooling, not call */
752                 continue;
753             }
754 
755             cdev->gov->tuning(zdev, i, c, &level);
756             level = rt_min_t(rt_ubase_t, level, cdev->max_level);
757 
758             cdev->ops->set_cur_level(cdev, level);
759         }
760     }
761 }
762 
rt_thermal_zone_set_trip(struct rt_thermal_zone_device * zdev,int trip_id,const struct rt_thermal_trip * trip)763 rt_err_t rt_thermal_zone_set_trip(struct rt_thermal_zone_device *zdev, int trip_id,
764         const struct rt_thermal_trip *trip)
765 {
766     rt_err_t err;
767     struct rt_thermal_trip tmp_trip;
768 
769     if (!zdev || !trip)
770     {
771         return -RT_EINVAL;
772     }
773 
774     rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER);
775 
776     if (!zdev->ops->set_trip_temp && !zdev->ops->set_trip_hyst && !zdev->trips)
777     {
778         err = -RT_EINVAL;
779         goto _out_unlock;
780     }
781 
782     if (trip_id >= zdev->trips_nr)
783     {
784         err = -RT_EINVAL;
785         goto _out_unlock;
786     }
787 
788     rt_memcpy(&tmp_trip, &zdev->trips[trip_id], sizeof(tmp_trip));
789 
790     if (tmp_trip.type != trip->type)
791     {
792         err = -RT_EINVAL;
793         goto _out_unlock;
794     }
795 
796     if (tmp_trip.temperature != trip->temperature && zdev->ops->set_trip_temp)
797     {
798         if ((err = zdev->ops->set_trip_temp(zdev, trip_id, trip->temperature)))
799         {
800             goto _out_unlock;
801         }
802     }
803 
804     if (tmp_trip.hysteresis != trip->hysteresis && zdev->ops->set_trip_hyst)
805     {
806         if ((err = zdev->ops->set_trip_hyst(zdev, trip_id, trip->hysteresis)))
807         {
808             goto _out_unlock;
809         }
810     }
811 
812     if (zdev->trips &&
813         (tmp_trip.temperature != trip->temperature || tmp_trip.hysteresis != trip->hysteresis))
814     {
815         zdev->trips[trip_id] = *trip;
816     }
817 
818 _out_unlock:
819     rt_mutex_release(&zdev->mutex);
820 
821     if (!err)
822     {
823         rt_thermal_zone_device_update(zdev, RT_THERMAL_MSG_TRIP_CHANGED);
824     }
825 
826     return err;
827 }
828 
rt_thermal_zone_get_trip(struct rt_thermal_zone_device * zdev,int trip_id,struct rt_thermal_trip * out_trip)829 rt_err_t rt_thermal_zone_get_trip(struct rt_thermal_zone_device *zdev, int trip_id,
830         struct rt_thermal_trip *out_trip)
831 {
832     rt_err_t err = RT_EOK;
833 
834     if (!zdev || !out_trip)
835     {
836         return -RT_EINVAL;
837     }
838 
839     rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER);
840 
841     if (!zdev->trips_nr)
842     {
843         err = -RT_ENOSYS;
844         goto _out_unlock;
845     }
846 
847     if (trip_id >= zdev->trips_nr)
848     {
849         err = -RT_EINVAL;
850         goto _out_unlock;
851     }
852 
853     *out_trip = zdev->trips[trip_id];
854 
855 _out_unlock:
856     rt_mutex_release(&zdev->mutex);
857 
858     return err;
859 }
860 
861 #if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
list_thermal(int argc,char ** argv)862 static int list_thermal(int argc, char**argv)
863 {
864     struct rt_thermal_zone_device *zdev;
865 
866     /* Thermal is an important subsystem, please do not output too much. */
867     rt_spin_lock(&nodes_lock);
868     device_foreach(zdev, &thermal_zone_device_nodes)
869     {
870         int temperature = zdev->temperature;
871 
872         rt_kprintf("%s-%d\n", rt_dm_dev_get_name(&zdev->parent), zdev->zone_id);
873         rt_kprintf("temperature:\t%+d.%u C\n", temperature / 1000, rt_abs(temperature) % 1000);
874 
875         for (int i = 0, id = 0; i < zdev->cooling_maps_nr; ++i)
876         {
877             rt_ubase_t level;
878             struct rt_thermal_trip *trips;
879             struct rt_thermal_cooling_device *cdev;
880             struct rt_thermal_cooling_cell *cell;
881             struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
882 
883             for (int c = 0; c < map->cells_nr; ++c, ++id)
884             {
885                 trips = map->trips;
886                 cell = &map->cells[c];
887                 cdev = cell->cooling_devices;
888 
889                 if (cdev)
890                 {
891                     cdev->ops->get_cur_level(cdev, &level);
892 
893                     rt_kprintf("cooling%u:\t%s[%+d.%u C] %d\n", id,
894                             rt_dm_dev_get_name(&cdev->parent),
895                             trips->temperature / 1000, rt_abs(trips->temperature) % 1000,
896                             level);
897                 }
898                 else
899                 {
900                     rt_kprintf("cooling%u:\t%s[%+d.%u C] %d\n", id,
901                             "(not supported)",
902                             trips->temperature / 1000, rt_abs(trips->temperature) % 1000,
903                             0);
904                 }
905             }
906         }
907     }
908     rt_spin_unlock(&nodes_lock);
909 
910     return 0;
911 }
912 MSH_CMD_EXPORT(list_thermal, dump all of thermal information);
913 #endif /* RT_USING_CONSOLE && RT_USING_MSH */
914