mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-09-22 19:44:24 +08:00
[dm][dvfs] support Dynamic Voltage and Frequency Scaling (DVFS)
1. Support DVFS and finsh cmd, there are 6 governors: - conservative - freedom - performance - powersave - schedutil 2. Support DVFS for SCMI. 3. Port the Cooling device for DVFS. 4. Port the PM with DVFS. Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
@@ -21,6 +21,12 @@ if RT_USING_THERMAL
|
||||
comment "Thermal Cool Drivers"
|
||||
endif
|
||||
|
||||
config RT_THERMAL_COOL_DVFS
|
||||
bool "DVFS"
|
||||
depends on RT_USING_THERMAL
|
||||
depends on RT_USING_DVFS
|
||||
default n
|
||||
|
||||
config RT_THERMAL_COOL_PWM_FAN
|
||||
bool "PWM Fan"
|
||||
depends on RT_USING_THERMAL
|
||||
|
||||
@@ -13,6 +13,9 @@ src = ['thermal.c', 'thermal_dm.c']
|
||||
if GetDepend(['RT_THERMAL_SCMI']):
|
||||
src += ['thermal-scmi.c']
|
||||
|
||||
if GetDepend(['RT_THERMAL_COOL_DVFS']):
|
||||
src += ['thermal-cool-dvfs.c']
|
||||
|
||||
if GetDepend(['RT_THERMAL_COOL_PWM_FAN']):
|
||||
src += ['thermal-cool-pwm-fan.c']
|
||||
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2023, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Change Logs:
|
||||
* Date Author Notes
|
||||
* 2023-02-25 GuEe-GUI the first version
|
||||
*/
|
||||
|
||||
#include <rthw.h>
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
|
||||
#define DBG_TAG "thermal.cool.dvfs"
|
||||
#define DBG_LVL DBG_INFO
|
||||
#include <rtdbg.h>
|
||||
|
||||
struct dvfs_cool
|
||||
{
|
||||
struct rt_thermal_cooling_device parent;
|
||||
|
||||
struct rt_dvfs_scaling *dvfs;
|
||||
};
|
||||
|
||||
#define raw_to_dvfs_cool(raw) rt_container_of(raw, struct dvfs_cool, parent)
|
||||
|
||||
static rt_err_t dvfs_cool_get_max_level(struct rt_thermal_cooling_device *cdev,
|
||||
rt_ubase_t *out_level)
|
||||
{
|
||||
struct dvfs_cool *dvfs_cool = raw_to_dvfs_cool(cdev);
|
||||
struct rt_dvfs_scaling *dvfs = dvfs_cool->dvfs;
|
||||
|
||||
rt_dvfs_scaling_enter(dvfs);
|
||||
|
||||
if (dvfs->opp_table->opp_nodes.next)
|
||||
{
|
||||
*out_level = rt_list_len(&dvfs->opp_table->opp_nodes);
|
||||
|
||||
if (*out_level > 0)
|
||||
{
|
||||
(*out_level)--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*out_level = 0;
|
||||
}
|
||||
|
||||
rt_dvfs_scaling_leave(dvfs);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t dvfs_cool_get_cur_level(struct rt_thermal_cooling_device *cdev,
|
||||
rt_ubase_t *out_level)
|
||||
{
|
||||
struct rt_dvfs_opp *opp;
|
||||
struct dvfs_cool *dvfs_cool = raw_to_dvfs_cool(cdev);
|
||||
struct rt_dvfs_scaling *dvfs = dvfs_cool->dvfs;
|
||||
|
||||
rt_dvfs_scaling_enter(dvfs);
|
||||
|
||||
*out_level = 0;
|
||||
rt_list_for_each_entry(opp, &dvfs->opp_table->opp_nodes, list)
|
||||
{
|
||||
if (opp == dvfs->opp_table->current_opp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
(*out_level)++;
|
||||
}
|
||||
|
||||
rt_dvfs_scaling_leave(dvfs);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static rt_err_t dvfs_cool_set_cur_level(struct rt_thermal_cooling_device *cdev,
|
||||
rt_ubase_t level)
|
||||
{
|
||||
struct rt_dvfs_opp *opp, *target_opp = RT_NULL;
|
||||
struct dvfs_cool *dvfs_cool = raw_to_dvfs_cool(cdev);
|
||||
struct rt_dvfs_scaling *dvfs = dvfs_cool->dvfs;
|
||||
|
||||
rt_dvfs_scaling_enter(dvfs);
|
||||
|
||||
rt_list_for_each_entry(opp, &dvfs->opp_table->opp_nodes, list)
|
||||
{
|
||||
if (!level)
|
||||
{
|
||||
target_opp = opp;
|
||||
break;
|
||||
}
|
||||
|
||||
--level;
|
||||
}
|
||||
|
||||
rt_dvfs_scaling_leave(dvfs);
|
||||
|
||||
if (target_opp)
|
||||
{
|
||||
return rt_dvfs_scaling_apply_opp(dvfs, target_opp);
|
||||
}
|
||||
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
const static struct rt_thermal_cooling_device_ops dvfs_cool_ops =
|
||||
{
|
||||
.get_max_level = dvfs_cool_get_max_level,
|
||||
.get_cur_level = dvfs_cool_get_cur_level,
|
||||
.set_cur_level = dvfs_cool_set_cur_level,
|
||||
};
|
||||
|
||||
static rt_err_t dvfs_cool_probe(struct rt_platform_device *pdev)
|
||||
{
|
||||
rt_err_t err;
|
||||
struct dvfs_cool *dvfs_cool;
|
||||
struct rt_device *dev = &pdev->parent;
|
||||
|
||||
if (!pdev->priv)
|
||||
{
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
if (!(dvfs_cool = rt_calloc(1, sizeof(*dvfs_cool))))
|
||||
{
|
||||
return -RT_ENOMEM;
|
||||
}
|
||||
dvfs_cool->dvfs = pdev->priv;
|
||||
|
||||
rt_dm_dev_set_name(&dvfs_cool->parent.parent, "%s-cool",
|
||||
rt_dm_dev_get_name(dvfs_cool->dvfs->dev));
|
||||
dvfs_cool->parent.parent.ofw_node = dev->ofw_node;
|
||||
dvfs_cool->parent.ops = &dvfs_cool_ops;
|
||||
|
||||
if ((err = rt_thermal_cooling_device_register(&dvfs_cool->parent)))
|
||||
{
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
dev->user_data = dvfs_cool;
|
||||
|
||||
return RT_EOK;
|
||||
|
||||
_fail:
|
||||
rt_free(dvfs_cool);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static rt_err_t dvfs_cool_remove(struct rt_platform_device *pdev)
|
||||
{
|
||||
struct dvfs_cool *dvfs_cool = pdev->parent.user_data;
|
||||
|
||||
rt_thermal_cooling_device_unregister(&dvfs_cool->parent);
|
||||
rt_free(dvfs_cool);
|
||||
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static struct rt_platform_driver dvfs_cool_driver =
|
||||
{
|
||||
.name = "dvfs-cool",
|
||||
|
||||
.probe = dvfs_cool_probe,
|
||||
.remove = dvfs_cool_remove,
|
||||
};
|
||||
RT_PLATFORM_DRIVER_EXPORT(dvfs_cool_driver);
|
||||
@@ -199,6 +199,8 @@ _scan_cooling:
|
||||
}
|
||||
|
||||
cdev_np = args.data;
|
||||
cell->cooling_np = cdev_np;
|
||||
rt_ofw_node_get(cdev_np);
|
||||
|
||||
rt_spin_lock(&nodes_lock);
|
||||
device_foreach(cdev, &thermal_cooling_device_nodes)
|
||||
@@ -218,17 +220,54 @@ _scan_cooling:
|
||||
{
|
||||
thermal_bind(cell->cooling_devices, zdev);
|
||||
}
|
||||
|
||||
rt_ofw_node_put(cdev_np);
|
||||
}
|
||||
}
|
||||
_end:
|
||||
;
|
||||
}
|
||||
void thermal_cooling_device_bind_zones(struct rt_thermal_cooling_device *cdev)
|
||||
{
|
||||
struct rt_thermal_zone_device *zdev;
|
||||
|
||||
if (!cdev || !cdev->parent.ofw_node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rt_spin_lock(&nodes_lock);
|
||||
|
||||
device_foreach(zdev, &thermal_zone_device_nodes)
|
||||
{
|
||||
for (int i = 0; i < zdev->cooling_maps_nr; ++i)
|
||||
{
|
||||
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
|
||||
|
||||
for (int c = 0; c < map->cells_nr; ++c)
|
||||
{
|
||||
struct rt_thermal_cooling_cell *cell = &map->cells[c];
|
||||
|
||||
if (cell->cooling_devices || cell->cooling_np != cdev->parent.ofw_node)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cell->cooling_devices = cdev;
|
||||
thermal_bind(cdev, zdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rt_spin_unlock(&nodes_lock);
|
||||
}
|
||||
#else
|
||||
rt_inline void thermal_ofw_setup(struct rt_ofw_node *np, struct rt_thermal_zone_device *zdev)
|
||||
{
|
||||
}
|
||||
|
||||
rt_inline void thermal_cooling_device_bind_zones(struct rt_thermal_cooling_device *cdev)
|
||||
{
|
||||
RT_UNUSED(cdev);
|
||||
}
|
||||
#endif /* RT_USING_OFW */
|
||||
|
||||
static void thermal_zone_poll(struct rt_work *work, void *work_data)
|
||||
@@ -311,17 +350,24 @@ rt_err_t rt_thermal_zone_device_unregister(struct rt_thermal_zone_device *zdev)
|
||||
{
|
||||
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;
|
||||
struct rt_thermal_cooling_cell *cell = &map->cells[c];
|
||||
|
||||
if (cdev)
|
||||
if (cell->cooling_devices)
|
||||
{
|
||||
thermal_unbind(cdev, zdev);
|
||||
thermal_unbind(cell->cooling_devices, zdev);
|
||||
}
|
||||
|
||||
#ifdef RT_USING_OFW
|
||||
if (cell->cooling_np)
|
||||
{
|
||||
rt_ofw_node_put(cell->cooling_np);
|
||||
cell->cooling_np = RT_NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
rt_free(map->cells);
|
||||
@@ -358,6 +404,10 @@ rt_err_t rt_thermal_cooling_device_register(struct rt_thermal_cooling_device *cd
|
||||
rt_spin_unlock(&nodes_lock);
|
||||
|
||||
err = rt_thermal_cooling_device_change_governor(cdev, RT_NULL);
|
||||
if (!err)
|
||||
{
|
||||
thermal_cooling_device_bind_zones(cdev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -624,6 +674,7 @@ void rt_thermal_zone_device_update(struct rt_thermal_zone_device *zdev, rt_ubase
|
||||
|
||||
if (!need_cool && zdev->cooling)
|
||||
{
|
||||
zdev->cooling = RT_FALSE;
|
||||
rt_thermal_cooling_device_kick(zdev);
|
||||
}
|
||||
|
||||
@@ -721,6 +772,7 @@ void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev)
|
||||
for (int i = 0; i < zdev->cooling_maps_nr; ++i)
|
||||
{
|
||||
rt_ubase_t level;
|
||||
rt_ubase_t max_level;
|
||||
struct rt_thermal_cooling_device *cdev;
|
||||
struct rt_thermal_cooling_cell *cell;
|
||||
struct rt_thermal_cooling_map *map = &zdev->cooling_maps[i];
|
||||
@@ -736,12 +788,24 @@ void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev)
|
||||
}
|
||||
|
||||
/* Update status */
|
||||
if (cdev->ops->get_max_level(cdev, &cdev->max_level))
|
||||
if (cdev->ops->get_max_level(cdev, &max_level))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cdev->ops->get_cur_level(cdev, &level) || level > cdev->max_level)
|
||||
cdev->max_level = max_level;
|
||||
|
||||
if (!zdev->cooling)
|
||||
{
|
||||
/* Release cooling: restore full performance (highest OPP). */
|
||||
if (!cdev->ops->get_cur_level(cdev, &level) && level != max_level)
|
||||
{
|
||||
cdev->ops->set_cur_level(cdev, max_level);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cdev->ops->get_cur_level(cdev, &level) || level > max_level)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -754,7 +818,7 @@ void rt_thermal_cooling_device_kick(struct rt_thermal_zone_device *zdev)
|
||||
}
|
||||
|
||||
cdev->gov->tuning(zdev, i, c, &level);
|
||||
level = rt_min_t(rt_ubase_t, level, cdev->max_level);
|
||||
level = rt_min_t(rt_ubase_t, level, max_level);
|
||||
|
||||
cdev->ops->set_cur_level(cdev, level);
|
||||
}
|
||||
|
||||
@@ -24,4 +24,6 @@ rt_err_t thermal_bind(struct rt_thermal_cooling_device *cdev,
|
||||
rt_err_t thermal_unbind(struct rt_thermal_cooling_device *cdev,
|
||||
struct rt_thermal_zone_device *zdev);
|
||||
|
||||
void thermal_cooling_device_bind_zones(struct rt_thermal_cooling_device *cdev);
|
||||
|
||||
#endif /* __THERMAL_DM_H__ */
|
||||
|
||||
Reference in New Issue
Block a user