Files
GuEe-GUI e280f213bf [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>
2026-06-23 08:02:09 +08:00

247 lines
4.0 KiB
C

/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-21 GuEe-GUI first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "dvfs.event"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
rt_err_t rt_dvfs_event_register(struct rt_dvfs_event *ev)
{
if (!ev || !ev->dev || !ev->ops)
{
return -RT_EINVAL;
}
RT_ASSERT(ev->ops->ready != RT_NULL);
RT_ASSERT(ev->ops->read != RT_NULL);
ev->enable_count = 0;
rt_spin_lock_init(&ev->lock);
rt_dm_dev_bind_fwdata(ev->dev, RT_NULL, ev);
return RT_EOK;
}
rt_err_t rt_dvfs_event_unregister(struct rt_dvfs_event *ev)
{
if (!ev || !ev->dev)
{
return -RT_EINVAL;
}
#ifdef RT_USING_OFW
if (ev->dev->ofw_node)
{
struct rt_ofw_node *np = ev->dev->ofw_node;
if (rt_ref_read(&np->ref) > 1)
{
return -RT_EBUSY;
}
}
#endif /* RT_USING_OFW */
rt_dvfs_event_disable(ev);
rt_dm_dev_unbind_fwdata(ev->dev, RT_NULL);
return RT_EOK;
}
rt_err_t rt_dvfs_event_ready(struct rt_dvfs_event *ev)
{
rt_err_t err;
if (!ev)
{
return -RT_EINVAL;
}
if (!rt_dvfs_event_is_enabled(ev))
{
return -RT_EIO;
}
rt_spin_lock(&ev->lock);
err = ev->ops->ready(ev);
rt_spin_unlock(&ev->lock);
return err;
}
rt_err_t rt_dvfs_event_read(struct rt_dvfs_event *ev, struct rt_dvfs_event_data *evd)
{
rt_err_t err;
if (!ev || !evd)
{
return -RT_EINVAL;
}
if (!rt_dvfs_event_is_enabled(ev))
{
return -RT_EIO;
}
evd->total_count = 0;
evd->load_count = 0;
rt_spin_lock(&ev->lock);
err = ev->ops->read(ev, evd);
rt_spin_unlock(&ev->lock);
return err;
}
rt_err_t rt_dvfs_event_enable(struct rt_dvfs_event *ev)
{
rt_err_t err = RT_EOK;
if (!ev)
{
return -RT_EINVAL;
}
rt_spin_lock(&ev->lock);
if (ev->ops->enable && ev->enable_count == 0)
{
if ((err = ev->ops->enable(ev)))
{
goto _out_lock;
}
}
++ev->enable_count;
_out_lock:
rt_spin_unlock(&ev->lock);
return err;
}
rt_err_t rt_dvfs_event_disable(struct rt_dvfs_event *ev)
{
rt_err_t err = RT_EOK;
if (!ev)
{
return -RT_EINVAL;
}
rt_spin_lock(&ev->lock);
if (ev->enable_count <= 0)
{
LOG_W("%s: No enabled before", rt_dm_dev_get_name(ev->dev));
err = -RT_EIO;
goto _out_lock;
}
if (ev->ops->disable && ev->enable_count == 1)
{
if ((err = ev->ops->disable(ev)))
{
goto _out_lock;
}
}
--ev->enable_count;
_out_lock:
rt_spin_unlock(&ev->lock);
return err;
}
rt_bool_t rt_dvfs_event_is_enabled(struct rt_dvfs_event *ev)
{
rt_bool_t res;
if (!ev)
{
return RT_FALSE;
}
rt_spin_lock(&ev->lock);
res = ev->enable_count > 0;
rt_spin_unlock(&ev->lock);
return res;
}
rt_err_t rt_dvfs_event_reset(struct rt_dvfs_event *ev)
{
rt_err_t err;
if (!ev)
{
return -RT_EINVAL;
}
if (!rt_dvfs_event_is_enabled(ev))
{
return -RT_EIO;
}
rt_spin_lock(&ev->lock);
if (ev->ops->reset)
{
err = ev->ops->reset(ev);
}
else
{
err = RT_EOK;
}
rt_spin_unlock(&ev->lock);
return err;
}
struct rt_dvfs_event *rt_dvfs_event_get(struct rt_device *dev, const char *name, int index)
{
struct rt_dvfs_event *ev = rt_err_ptr(-RT_ENOSYS);
#ifdef RT_USING_OFW
if (dev->ofw_node)
{
struct rt_ofw_node *np = rt_ofw_parse_phandle(dev->ofw_node, name, index);
if (!np)
{
return rt_err_ptr(-RT_EEMPTY);
}
ev = rt_ofw_data(np);
}
#endif /* RT_USING_OFW */
return ev;
}
void rt_dvfs_event_put(struct rt_dvfs_event *ev)
{
if (!ev)
{
return;
}
#ifdef RT_USING_OFW
rt_ofw_node_put(ev->dev->ofw_node);
#endif
}