918 lines
22 KiB
C
918 lines
22 KiB
C
|
/*
|
||
|
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*
|
||
|
* Change Logs:
|
||
|
* Date Author Notes
|
||
|
* 2022-3-08 GuEe-GUI the first version
|
||
|
*/
|
||
|
|
||
|
#include <drivers/platform.h>
|
||
|
|
||
|
#define DBG_TAG "rtdm.thermal"
|
||
|
#define DBG_LVL DBG_INFO
|
||
|
#include <rtdbg.h>
|
||
|
|
||
|
#include "thermal_dm.h"
|
||
|
|
||
|
#ifndef INT_MAX
|
||
|
#define INT_MAX (RT_UINT32_MAX >> 1)
|
||
|
#endif
|
||
|
|
||
|
#define device_list(dev) (dev)->parent.parent.list
|
||
|
#define device_foreach(dev, nodes) rt_list_for_each_entry(dev, nodes, parent.parent.list)
|
||
|
|
||
|
static struct rt_spinlock nodes_lock = {};
|
||
|
static rt_list_t thermal_zone_device_nodes = RT_LIST_OBJECT_INIT(thermal_zone_device_nodes);
|
||
|
static rt_list_t thermal_cooling_device_nodes = RT_LIST_OBJECT_INIT(thermal_cooling_device_nodes);
|
||
|
static rt_list_t thermal_cooling_governor_nodes = RT_LIST_OBJECT_INIT(thermal_cooling_governor_nodes);
|
||
|
|
||
|
#ifdef RT_USING_OFW
|
||
|
static void thermal_ofw_params_parse(struct rt_ofw_node *np,
|
||
|
struct rt_thermal_zone_params *tz_params)
|
||
|
{
|
||
|
rt_uint32_t coef[2], prop;
|
||
|
|
||
|
if (!np)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!rt_ofw_prop_read_u32(np, "sustainable-power", &prop))
|
||
|
{
|
||
|
tz_params->sustainable_power = prop;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* For now, the thermal framework supports only one sensor per thermal zone.
|
||
|
* Thus, we are considering only the first two values as slope and offset.
|
||
|
*/
|
||
|
if (rt_ofw_prop_read_u32_array_index(np, "coefficients", 0, 1, coef) < 0)
|
||
|
{
|
||
|
coef[0] = 1;
|
||
|
coef[1] = 0;
|
||
|
}
|
||
|
|
||
|
tz_params->slope = coef[0];
|
||
|
tz_params->offset = coef[1];
|
||
|
}
|
||
|
|
||
|
static void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev)
|
||
|
{
|
||
|
int i = 0;
|
||
|
rt_uint32_t delay, pdelay;
|
||
|
struct rt_ofw_cell_args args;
|
||
|
struct rt_ofw_node *tmp_np, *tz_np, *trip_np, *cm_np, *cdev_np;
|
||
|
|
||
|
if (!np || !zdev)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tmp_np = rt_ofw_find_node_by_path("/thermal-zones");
|
||
|
|
||
|
if (!tmp_np)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
rt_ofw_foreach_child_node(tmp_np, tz_np)
|
||
|
{
|
||
|
if (!rt_ofw_parse_phandle_cells(tz_np, "thermal-sensors", "#thermal-sensor-cells", 0, &args))
|
||
|
{
|
||
|
if (args.data == np && (!args.args_count || args.args[0] == zdev->zone_id))
|
||
|
{
|
||
|
rt_ofw_node_put(args.data);
|
||
|
|
||
|
goto _found;
|
||
|
}
|
||
|
rt_ofw_node_put(args.data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
_found:
|
||
|
rt_ofw_prop_read_u32(tz_np, "polling-delay-passive", &pdelay);
|
||
|
rt_ofw_prop_read_u32(tz_np, "polling-delay", &delay);
|
||
|
|
||
|
zdev->passive_delay = rt_tick_from_millisecond(pdelay);
|
||
|
zdev->polling_delay = rt_tick_from_millisecond(delay);
|
||
|
|
||
|
thermal_ofw_params_parse(tz_np, &zdev->params);
|
||
|
|
||
|
if (zdev->trips_nr)
|
||
|
{
|
||
|
goto _scan_cooling;
|
||
|
}
|
||
|
|
||
|
tmp_np = rt_ofw_get_child_by_tag(tz_np, "trips");
|
||
|
if (!tmp_np)
|
||
|
{
|
||
|
goto _scan_cooling;
|
||
|
}
|
||
|
|
||
|
zdev->trips_nr = rt_ofw_get_child_count(tmp_np);
|
||
|
if (!zdev->trips_nr)
|
||
|
{
|
||
|
goto _scan_cooling;
|
||
|
}
|
||
|
zdev->trips = rt_calloc(zdev->trips_nr, sizeof(*zdev->trips));
|
||
|
zdev->trips_free = RT_TRUE;
|
||
|
|
||
|
if (!zdev->trips)
|
||
|
{
|
||
|
LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "trips");
|
||
|
RT_ASSERT(0);
|
||
|
}
|
||
|
|
||
|
rt_ofw_foreach_child_node(tmp_np, trip_np)
|
||
|
{
|
||
|
const char *type;
|
||
|
|
||
|
rt_ofw_prop_read_u32(trip_np, "temperature", (rt_uint32_t *)&zdev->trips[i].temperature);
|
||
|
rt_ofw_prop_read_u32(trip_np, "hysteresis", (rt_uint32_t *)&zdev->trips[i].hysteresis);
|
||
|
rt_ofw_prop_read_string(trip_np, "type", &type);
|
||
|
zdev->trips[i].type = thermal_type(type);
|
||
|
|
||
|
rt_ofw_data(trip_np) = &zdev->trips[i];
|
||
|
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
_scan_cooling:
|
||
|
i = 0;
|
||
|
tmp_np = rt_ofw_get_child_by_tag(tz_np, "cooling-maps");
|
||
|
if (!tmp_np)
|
||
|
{
|
||
|
goto _end;
|
||
|
}
|
||
|
|
||
|
zdev->cooling_maps_nr = rt_ofw_get_child_count(tmp_np);
|
||
|
if (!zdev->cooling_maps_nr)
|
||
|
{
|
||
|
goto _end;
|
||
|
}
|
||
|
zdev->cooling_maps = rt_calloc(zdev->cooling_maps_nr, sizeof(*zdev->cooling_maps));
|
||
|
|
||
|
if (!zdev->cooling_maps)
|
||
|
{
|
||
|
LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "cooling_maps");
|
||
|
RT_ASSERT(0);
|
||
|
}
|
||
|
|
||
|
rt_ofw_foreach_child_node(tmp_np, cm_np)
|
||
|
{
|
||
|
struct rt_thermal_cooling_device *cdev;
|
||
|
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i++];
|
||
|
|
||
|
map->cells_nr = rt_ofw_count_phandle_cells(cm_np, "cooling-device", "#cooling-cells");
|
||
|
map->cells = rt_calloc(sizeof(*map->cells), map->cells_nr);
|
||
|
|
||
|
if (!map->cells)
|
||
|
{
|
||
|
LOG_E("%s: No memory to create %s", rt_ofw_node_full_name(np), "cells");
|
||
|
RT_ASSERT(0);
|
||
|
}
|
||
|
|
||
|
trip_np = rt_ofw_parse_phandle(cm_np, "trip", 0);
|
||
|
map->trips = rt_ofw_data(trip_np);
|
||
|
rt_ofw_node_put(trip_np);
|
||
|
|
||
|
if (!map->trips)
|
||
|
{
|
||
|
LOG_E("%s: trips(%s) not found", rt_ofw_node_full_name(np),
|
||
|
rt_ofw_node_full_name(trip_np));
|
||
|
RT_ASSERT(0);
|
||
|
}
|
||
|
|
||
|
rt_ofw_prop_read_u32(cm_np, "contribution", &map->contribution);
|
||
|
|
||
|
for (int c = 0; c < map->cells_nr; ++c)
|
||
|
{
|
||
|
struct rt_thermal_cooling_cell *cell = &map->cells[c];
|
||
|
|
||
|
if (rt_ofw_parse_phandle_cells(cm_np, "cooling-device", "#cooling-cells", c, &args))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
cdev_np = args.data;
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
device_foreach(cdev, &thermal_cooling_device_nodes)
|
||
|
{
|
||
|
if (cdev->parent.ofw_node == cdev_np)
|
||
|
{
|
||
|
cell->cooling_devices = cdev;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
cell->level_range[0] = args.args[0];
|
||
|
cell->level_range[1] = args.args[1];
|
||
|
|
||
|
if (cell->cooling_devices)
|
||
|
{
|
||
|
thermal_bind(cell->cooling_devices, zdev);
|
||
|
}
|
||
|
|
||
|
rt_ofw_node_put(cdev_np);
|
||
|
}
|
||
|
}
|
||
|
_end:
|
||
|
}
|
||
|
#else
|
||
|
rt_inline void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev)
|
||
|
{
|
||
|
}
|
||
|
#endif /* RT_USING_OFW */
|
||
|
|
||
|
static void thermal_zone_poll(struct rt_work *work, void *work_data)
|
||
|
{
|
||
|
struct rt_thermal_zone_device *zdev = work_data;
|
||
|
|
||
|
rt_thermal_zone_device_update(zdev, RT_THERMAL_MSG_EVENT_UNSPECIFIED);
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_zone_device_register(struct rt_thermal_zone_device *zdev)
|
||
|
{
|
||
|
if (!zdev || !zdev->ops || !zdev->ops->get_temp)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
zdev->ops->get_temp(zdev, &zdev->temperature);
|
||
|
zdev->last_temperature = zdev->temperature;
|
||
|
|
||
|
if (!zdev->trips)
|
||
|
{
|
||
|
zdev->trips_nr = 0;
|
||
|
}
|
||
|
|
||
|
rt_spin_lock_init(&zdev->nodes_lock);
|
||
|
rt_list_init(&zdev->notifier_nodes);
|
||
|
rt_list_init(&device_list(zdev));
|
||
|
rt_mutex_init(&zdev->mutex, rt_dm_dev_get_name(&zdev->parent), RT_IPC_FLAG_PRIO);
|
||
|
|
||
|
zdev->temperature = RT_THERMAL_TEMP_INVALID;
|
||
|
zdev->prev_low_trip = -INT_MAX;
|
||
|
zdev->prev_high_trip = INT_MAX;
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
rt_list_insert_before(&thermal_zone_device_nodes, &device_list(zdev));
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
thermal_ofw_setup(zdev->parent.ofw_node, zdev);
|
||
|
|
||
|
rt_work_init(&zdev->poller, thermal_zone_poll, zdev);
|
||
|
zdev->enabled = RT_TRUE;
|
||
|
|
||
|
/* Start to poll */
|
||
|
rt_work_submit(&zdev->poller, zdev->polling_delay);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_zone_device_unregister(struct rt_thermal_zone_device *zdev)
|
||
|
{
|
||
|
if (!zdev)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_spin_lock(&zdev->nodes_lock);
|
||
|
if (rt_list_isempty(&zdev->notifier_nodes))
|
||
|
{
|
||
|
LOG_E("%s: there is %u user", rt_dm_dev_get_name(&zdev->parent),
|
||
|
rt_list_len(&zdev->notifier_nodes));
|
||
|
|
||
|
rt_spin_unlock(&zdev->nodes_lock);
|
||
|
|
||
|
return -RT_EBUSY;
|
||
|
}
|
||
|
rt_spin_unlock(&zdev->nodes_lock);
|
||
|
|
||
|
rt_work_cancel(&zdev->poller);
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
rt_list_remove(&device_list(zdev));
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
if (zdev->trips_free && zdev->trips)
|
||
|
{
|
||
|
rt_free(zdev->trips);
|
||
|
}
|
||
|
|
||
|
if (zdev->cooling_maps_nr && zdev->cooling_maps_nr)
|
||
|
{
|
||
|
for (int i = 0; i < zdev->cooling_maps_nr; ++i)
|
||
|
{
|
||
|
struct rt_thermal_cooling_device *cdev;
|
||
|
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
|
||
|
|
||
|
for (int c = 0; c < map->cells_nr; ++c)
|
||
|
{
|
||
|
cdev = map->cells[i].cooling_devices;
|
||
|
|
||
|
if (cdev)
|
||
|
{
|
||
|
thermal_unbind(cdev, zdev);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rt_free(map->cells);
|
||
|
}
|
||
|
|
||
|
rt_free(zdev->cooling_maps);
|
||
|
}
|
||
|
|
||
|
rt_mutex_detach(&zdev->mutex);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_cooling_device_register(struct rt_thermal_cooling_device *cdev)
|
||
|
{
|
||
|
rt_err_t err;
|
||
|
|
||
|
if (!cdev || !cdev->ops ||
|
||
|
!cdev->ops->get_max_level || !cdev->ops->get_cur_level || !cdev->ops->set_cur_level)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
if ((err = cdev->ops->get_max_level(cdev, &cdev->max_level)))
|
||
|
{
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rt_list_init(&device_list(cdev));
|
||
|
rt_list_init(&cdev->governor_node);
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
rt_list_insert_before(&thermal_cooling_device_nodes, &device_list(cdev));
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
err = rt_thermal_cooling_device_change_governor(cdev, RT_NULL);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_cooling_device_unregister(struct rt_thermal_cooling_device *cdev)
|
||
|
{
|
||
|
if (!cdev)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
if (cdev->parent.ref_count)
|
||
|
{
|
||
|
LOG_E("%s: there is %u user",
|
||
|
rt_dm_dev_get_name(&cdev->parent), cdev->parent.ref_count);
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
rt_list_remove(&device_list(cdev));
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
static void dumb_governor_tuning(struct rt_thermal_zone_device *zdev,
|
||
|
int map_idx, int cell_idx, rt_ubase_t *level)
|
||
|
{
|
||
|
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[map_idx];
|
||
|
|
||
|
if (zdev->cooling && zdev->temperature > map->trips->temperature)
|
||
|
{
|
||
|
if (zdev->temperature - zdev->last_temperature > map->trips->hysteresis)
|
||
|
{
|
||
|
++*level;
|
||
|
}
|
||
|
else if (zdev->last_temperature - zdev->temperature > map->trips->hysteresis)
|
||
|
{
|
||
|
--*level;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*level = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static struct rt_thermal_cooling_governor dumb_governor =
|
||
|
{
|
||
|
.name = "dumb",
|
||
|
.tuning = dumb_governor_tuning,
|
||
|
};
|
||
|
|
||
|
static int system_thermal_cooling_governor_init(void)
|
||
|
{
|
||
|
rt_thermal_cooling_governor_register(&dumb_governor);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
INIT_CORE_EXPORT(system_thermal_cooling_governor_init);
|
||
|
|
||
|
rt_err_t rt_thermal_cooling_governor_register(struct rt_thermal_cooling_governor *gov)
|
||
|
{
|
||
|
rt_err_t err = RT_EOK;
|
||
|
struct rt_thermal_cooling_governor *gov_tmp;
|
||
|
|
||
|
if (!gov || !gov->name || !gov->tuning)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_list_init(&gov->list);
|
||
|
rt_list_init(&gov->cdev_nodes);
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
|
||
|
rt_list_for_each_entry(gov_tmp, &thermal_cooling_governor_nodes, list)
|
||
|
{
|
||
|
if (!rt_strcmp(gov_tmp->name, gov->name))
|
||
|
{
|
||
|
err = -RT_ERROR;
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rt_list_insert_before(&thermal_cooling_governor_nodes, &gov->list);
|
||
|
|
||
|
_out_unlock:
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_cooling_governor_unregister(struct rt_thermal_cooling_governor *gov)
|
||
|
{
|
||
|
if (!gov)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
if (gov == &dumb_governor)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
|
||
|
if (!rt_list_isempty(&gov->cdev_nodes))
|
||
|
{
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
|
||
|
rt_list_remove(&gov->list);
|
||
|
|
||
|
_out_unlock:
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_cooling_device_change_governor(struct rt_thermal_cooling_device *cdev,
|
||
|
const char *name)
|
||
|
{
|
||
|
rt_err_t err;
|
||
|
struct rt_thermal_cooling_governor *gov;
|
||
|
|
||
|
if (!cdev)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
name = name ? : dumb_governor.name;
|
||
|
err = -RT_ENOSYS;
|
||
|
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
|
||
|
rt_list_for_each_entry(gov, &thermal_cooling_governor_nodes, list)
|
||
|
{
|
||
|
if (!rt_strcmp(gov->name, name))
|
||
|
{
|
||
|
if (cdev->gov)
|
||
|
{
|
||
|
rt_list_remove(&cdev->governor_node);
|
||
|
}
|
||
|
|
||
|
cdev->gov = gov;
|
||
|
rt_list_insert_before(&cdev->governor_node, &gov->cdev_nodes);
|
||
|
|
||
|
err = RT_EOK;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_zone_notifier_register(struct rt_thermal_zone_device *zdev,
|
||
|
struct rt_thermal_notifier *notifier)
|
||
|
{
|
||
|
if (!zdev || !notifier)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
notifier->zdev = zdev;
|
||
|
rt_list_init(¬ifier->list);
|
||
|
|
||
|
rt_spin_lock(&zdev->nodes_lock);
|
||
|
rt_list_insert_after(&zdev->notifier_nodes, ¬ifier->list);
|
||
|
rt_spin_unlock(&zdev->nodes_lock);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_zone_notifier_unregister(struct rt_thermal_zone_device *zdev,
|
||
|
struct rt_thermal_notifier *notifier)
|
||
|
{
|
||
|
if (!zdev || !notifier)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_spin_lock(&zdev->nodes_lock);
|
||
|
rt_list_remove(¬ifier->list);
|
||
|
rt_spin_unlock(&zdev->nodes_lock);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
void rt_thermal_zone_device_update(struct rt_thermal_zone_device *zdev, rt_ubase_t msg)
|
||
|
{
|
||
|
rt_err_t err;
|
||
|
rt_bool_t passive = RT_FALSE, need_cool = RT_FALSE;
|
||
|
struct rt_thermal_notifier *notifier, *next_notifier;
|
||
|
|
||
|
RT_ASSERT(zdev != RT_NULL);
|
||
|
|
||
|
if (!rt_interrupt_get_nest())
|
||
|
{
|
||
|
rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER);
|
||
|
}
|
||
|
|
||
|
/* Check thermal zone status */
|
||
|
if (msg == RT_THERMAL_MSG_DEVICE_DOWN)
|
||
|
{
|
||
|
zdev->enabled = RT_FALSE;
|
||
|
}
|
||
|
else if (msg == RT_THERMAL_MSG_DEVICE_UP)
|
||
|
{
|
||
|
zdev->enabled = RT_TRUE;
|
||
|
}
|
||
|
|
||
|
/* Read temperature */
|
||
|
zdev->last_temperature = zdev->temperature;
|
||
|
zdev->ops->get_temp(zdev, &zdev->temperature);
|
||
|
|
||
|
for (int i = 0; i < zdev->trips_nr; ++i)
|
||
|
{
|
||
|
struct rt_thermal_trip *tmp_trip = &zdev->trips[i];
|
||
|
|
||
|
if (zdev->temperature <= tmp_trip->temperature)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
switch (tmp_trip->type)
|
||
|
{
|
||
|
case RT_THERMAL_TRIP_PASSIVE:
|
||
|
passive = RT_TRUE;
|
||
|
goto cooling;
|
||
|
|
||
|
case RT_THERMAL_TRIP_CRITICAL:
|
||
|
if (zdev->ops->critical)
|
||
|
{
|
||
|
zdev->ops->critical(zdev);
|
||
|
}
|
||
|
else if (zdev->last_temperature > tmp_trip->temperature)
|
||
|
{
|
||
|
/* Tried to cool already, but failed */
|
||
|
rt_hw_cpu_reset();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
goto cooling;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case RT_THERMAL_TRIP_HOT:
|
||
|
if (zdev->ops->hot)
|
||
|
{
|
||
|
zdev->ops->hot(zdev);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
cooling:
|
||
|
zdev->cooling = need_cool = RT_TRUE;
|
||
|
rt_thermal_cooling_device_kick(zdev);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!need_cool && zdev->cooling)
|
||
|
{
|
||
|
rt_thermal_cooling_device_kick(zdev);
|
||
|
}
|
||
|
|
||
|
/* Set the new trips */
|
||
|
if (zdev->ops->set_trips)
|
||
|
{
|
||
|
rt_bool_t same_trip = RT_FALSE;
|
||
|
int low = -INT_MAX, high = INT_MAX;
|
||
|
struct rt_thermal_trip trip;
|
||
|
|
||
|
for (int i = 0; i < zdev->trips_nr; ++i)
|
||
|
{
|
||
|
int trip_low;
|
||
|
rt_bool_t low_set = RT_FALSE;
|
||
|
|
||
|
if (i >= zdev->trips_nr)
|
||
|
{
|
||
|
goto _call_notifier;
|
||
|
}
|
||
|
rt_memcpy(&trip, &zdev->trips[i], sizeof(trip));
|
||
|
|
||
|
trip_low = trip.temperature - trip.hysteresis;
|
||
|
|
||
|
if (trip_low < zdev->temperature && trip_low > low)
|
||
|
{
|
||
|
low = trip_low;
|
||
|
low_set = RT_TRUE;
|
||
|
same_trip = RT_FALSE;
|
||
|
}
|
||
|
|
||
|
if (trip.temperature > zdev->temperature && trip.temperature < high)
|
||
|
{
|
||
|
high = trip.temperature;
|
||
|
same_trip = low_set;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* No need to change trip points */
|
||
|
if (zdev->prev_low_trip == low && zdev->prev_high_trip == high)
|
||
|
{
|
||
|
goto _call_notifier;
|
||
|
}
|
||
|
|
||
|
if (same_trip &&
|
||
|
(zdev->prev_low_trip != -INT_MAX || zdev->prev_high_trip != INT_MAX))
|
||
|
{
|
||
|
goto _call_notifier;
|
||
|
}
|
||
|
|
||
|
zdev->prev_low_trip = low;
|
||
|
zdev->prev_high_trip = high;
|
||
|
|
||
|
if ((err = zdev->ops->set_trips(zdev, low, high)))
|
||
|
{
|
||
|
LOG_E("%s: Set trips error = %s", rt_dm_dev_get_name(&zdev->parent),
|
||
|
rt_strerror(err));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Call all notifier, maybe have governor */
|
||
|
_call_notifier:
|
||
|
rt_spin_lock(&zdev->nodes_lock);
|
||
|
|
||
|
rt_list_for_each_entry_safe(notifier, next_notifier, &zdev->notifier_nodes, list)
|
||
|
{
|
||
|
rt_spin_unlock(&zdev->nodes_lock);
|
||
|
|
||
|
notifier->callback(notifier, msg);
|
||
|
|
||
|
rt_spin_lock(&zdev->nodes_lock);
|
||
|
}
|
||
|
|
||
|
rt_spin_unlock(&zdev->nodes_lock);
|
||
|
|
||
|
/* Prepare for the next report */
|
||
|
if (!zdev->enabled)
|
||
|
{
|
||
|
rt_work_cancel(&zdev->poller);
|
||
|
}
|
||
|
else if (passive && zdev->passive_delay)
|
||
|
{
|
||
|
rt_work_submit(&zdev->poller, zdev->passive_delay);
|
||
|
}
|
||
|
else if (zdev->polling_delay)
|
||
|
{
|
||
|
rt_work_submit(&zdev->poller, zdev->polling_delay);
|
||
|
}
|
||
|
|
||
|
if (!rt_interrupt_get_nest())
|
||
|
{
|
||
|
rt_mutex_release(&zdev->mutex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev)
|
||
|
{
|
||
|
RT_ASSERT(zdev != RT_NULL);
|
||
|
|
||
|
for (int i = 0; i < zdev->cooling_maps_nr; ++i)
|
||
|
{
|
||
|
rt_ubase_t level;
|
||
|
struct rt_thermal_cooling_device *cdev;
|
||
|
struct rt_thermal_cooling_cell *cell;
|
||
|
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
|
||
|
|
||
|
for (int c = 0; c < map->cells_nr; ++c)
|
||
|
{
|
||
|
cell = &map->cells[c];
|
||
|
cdev = cell->cooling_devices;
|
||
|
|
||
|
if (!cdev)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Update status */
|
||
|
if (cdev->ops->get_max_level(cdev, &cdev->max_level))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (cdev->ops->get_cur_level(cdev, &level) || level > cdev->max_level)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Check if cooling is required */
|
||
|
if (level >= cell->level_range[0] && level <= cell->level_range[1])
|
||
|
{
|
||
|
/* Is cooling, not call */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
cdev->gov->tuning(zdev, i, c, &level);
|
||
|
level = rt_min_t(rt_ubase_t, level, cdev->max_level);
|
||
|
|
||
|
cdev->ops->set_cur_level(cdev, level);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_zone_set_trip(struct rt_thermal_zone_device *zdev, int trip_id,
|
||
|
const struct rt_thermal_trip *trip)
|
||
|
{
|
||
|
rt_err_t err;
|
||
|
struct rt_thermal_trip tmp_trip;
|
||
|
|
||
|
if (!zdev || !trip)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER);
|
||
|
|
||
|
if (!zdev->ops->set_trip_temp && !zdev->ops->set_trip_hyst && !zdev->trips)
|
||
|
{
|
||
|
err = -RT_EINVAL;
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
|
||
|
if (trip_id >= zdev->trips_nr)
|
||
|
{
|
||
|
err = -RT_EINVAL;
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
|
||
|
rt_memcpy(&tmp_trip, &zdev->trips[trip_id], sizeof(tmp_trip));
|
||
|
|
||
|
if (tmp_trip.type != trip->type)
|
||
|
{
|
||
|
err = -RT_EINVAL;
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
|
||
|
if (tmp_trip.temperature != trip->temperature && zdev->ops->set_trip_temp)
|
||
|
{
|
||
|
if ((err = zdev->ops->set_trip_temp(zdev, trip_id, trip->temperature)))
|
||
|
{
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tmp_trip.hysteresis != trip->hysteresis && zdev->ops->set_trip_hyst)
|
||
|
{
|
||
|
if ((err = zdev->ops->set_trip_hyst(zdev, trip_id, trip->hysteresis)))
|
||
|
{
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (zdev->trips &&
|
||
|
(tmp_trip.temperature != trip->temperature || tmp_trip.hysteresis != trip->hysteresis))
|
||
|
{
|
||
|
zdev->trips[trip_id] = *trip;
|
||
|
}
|
||
|
|
||
|
_out_unlock:
|
||
|
rt_mutex_release(&zdev->mutex);
|
||
|
|
||
|
if (!err)
|
||
|
{
|
||
|
rt_thermal_zone_device_update(zdev, RT_THERMAL_MSG_TRIP_CHANGED);
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
rt_err_t rt_thermal_zone_get_trip(struct rt_thermal_zone_device *zdev, int trip_id,
|
||
|
struct rt_thermal_trip *out_trip)
|
||
|
{
|
||
|
rt_err_t err = RT_EOK;
|
||
|
|
||
|
if (!zdev || !out_trip)
|
||
|
{
|
||
|
return -RT_EINVAL;
|
||
|
}
|
||
|
|
||
|
rt_mutex_take(&zdev->mutex, RT_WAITING_FOREVER);
|
||
|
|
||
|
if (!zdev->trips_nr)
|
||
|
{
|
||
|
err = -RT_ENOSYS;
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
|
||
|
if (trip_id >= zdev->trips_nr)
|
||
|
{
|
||
|
err = -RT_EINVAL;
|
||
|
goto _out_unlock;
|
||
|
}
|
||
|
|
||
|
*out_trip = zdev->trips[trip_id];
|
||
|
|
||
|
_out_unlock:
|
||
|
rt_mutex_release(&zdev->mutex);
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
|
||
|
static int list_thermal(int argc, char**argv)
|
||
|
{
|
||
|
struct rt_thermal_zone_device *zdev;
|
||
|
|
||
|
/* Thermal is an important subsystem, please do not output too much. */
|
||
|
rt_spin_lock(&nodes_lock);
|
||
|
device_foreach(zdev, &thermal_zone_device_nodes)
|
||
|
{
|
||
|
int temperature = zdev->temperature;
|
||
|
|
||
|
rt_kprintf("%s-%d\n", rt_dm_dev_get_name(&zdev->parent), zdev->zone_id);
|
||
|
rt_kprintf("temperature:\t%+d.%u C\n", temperature / 1000, rt_abs(temperature) % 1000);
|
||
|
|
||
|
for (int i = 0, id = 0; i < zdev->cooling_maps_nr; ++i)
|
||
|
{
|
||
|
rt_ubase_t level;
|
||
|
struct rt_thermal_trip *trips;
|
||
|
struct rt_thermal_cooling_device *cdev;
|
||
|
struct rt_thermal_cooling_cell *cell;
|
||
|
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
|
||
|
|
||
|
for (int c = 0; c < map->cells_nr; ++c, ++id)
|
||
|
{
|
||
|
trips = map->trips;
|
||
|
cell = &map->cells[c];
|
||
|
cdev = cell->cooling_devices;
|
||
|
|
||
|
if (cdev)
|
||
|
{
|
||
|
cdev->ops->get_cur_level(cdev, &level);
|
||
|
|
||
|
rt_kprintf("cooling%u:\t%s[%+d.%u C] %d\n", id,
|
||
|
rt_dm_dev_get_name(&cdev->parent),
|
||
|
trips->temperature / 1000, rt_abs(trips->temperature) % 1000,
|
||
|
level);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rt_kprintf("cooling%u:\t%s[%+d.%u C] %d\n", id,
|
||
|
"(not supported)",
|
||
|
trips->temperature / 1000, rt_abs(trips->temperature) % 1000,
|
||
|
0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
rt_spin_unlock(&nodes_lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
MSH_CMD_EXPORT(list_thermal, dump all of thermal information);
|
||
|
#endif /* RT_USING_CONSOLE && RT_USING_MSH */
|