2、sensor注册函数thermal_zone_of_sensor_register
struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
const struct thermal_zone_of_device_ops *ops)
{
struct device_node *np, *child, *sensor_np;
struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
struct thermal_zone_device *first_tzd = NULL;
struct __sensor_param *sens_param = NULL;
np = of_find_node_by_name(NULL, "thermal-zones");//获取thermal-zones设备节点
if (!np)
return ERR_PTR(-ENODEV);
if (!dev || !dev->of_node) {
of_node_put(np);
return ERR_PTR(-EINVAL);
}
sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);//分配sensors param 空间
if (!sens_param) {
of_node_put(np);
return ERR_PTR(-ENOMEM);
}
sens_param->sensor_data = data;//用于回调函数传递的参数
sens_param->ops = ops;//sensor回调
INIT_LIST_HEAD(&sens_param->first_tz);
sens_param->trip_high = INT_MAX;
sens_param->trip_low = INT_MIN;
mutex_init(&sens_param->lock);
sensor_np = of_node_get(dev->of_node);//从设备中获取设备节点
for_each_available_child_of_node(np, child) {//遍历thermal-zones设备节点中的每一个孩子节点
struct of_phandle_args sensor_specs;
int ret, id;
struct __thermal_zone *tz;
/* For now, thermal framework supports only 1 sensor per zone */
ret = of_parse_phandle_with_args(child, "thermal-sensors",
"#thermal-sensor-cells",
0, &sensor_specs);//从每一个孩子节点中获取thermal-sensors参数
if (ret)
continue;
if (sensor_specs.args_count >= 1) {
id = sensor_specs.args[0];
WARN(sensor_specs.args_count > 1,
"%s: too many cells in sensor specifier %d\n",
sensor_specs.np->name, sensor_specs.args_count);
} else {
id = 0;
}
if (sensor_specs.np == sensor_np && id == sensor_id) {//设备树中参数和函数传进来的参数相同时添加sensor
tzd = thermal_zone_of_add_sensor(child, sensor_np,
sens_param);//添加sensor
if (!IS_ERR(tzd)) {
if (!first_tzd)
first_tzd = tzd;
tz = tzd->devdata;
if (!tz->default_disable)
tzd->ops->set_mode(tzd,
THERMAL_DEVICE_ENABLED);//使能
}
}
of_node_put(sensor_specs.np);
}
of_node_put(sensor_np);
of_node_put(np);
if (!first_tzd) {
first_tzd = ERR_PTR(-ENODEV);
kfree(sens_param);
}
return first_tzd;
}
2.1 thermal_zone_of_add_sensor函数
static struct thermal_zone_device *
thermal_zone_of_add_sensor(struct device_node *zone,
struct device_node *sensor,
struct __sensor_param *sens_param)
{
struct thermal_zone_device *tzd;
struct __thermal_zone *tz;
tzd = thermal_zone_get_zone_by_name(zone->name);//通过设备节点的名字获取thermal zone设备
if (IS_ERR(tzd))
return ERR_PTR(-EPROBE_DEFER);
tz = tzd->devdata;
if (!sens_param->ops)
return ERR_PTR(-EINVAL);
mutex_lock(&tzd->lock);
tz->senps = sens_param;
tzd->ops->get_temp = of_thermal_get_temp;//获取温度回调,将通过此回调调用sensor回调
tzd->ops->get_trend = of_thermal_get_trend;//获取trend回调,将通过此回调调用sensor回调
/*
* The thermal zone core will calculate the window if they have set the
* optional set_trips pointer.
*/
if (sens_param->ops->set_trips)//如果sensor回调存在
tzd->ops->set_trips = of_thermal_set_trips;//设置触发点回调,将通过此回调调用sensor回调
if (sens_param->ops->set_emul_temp)
tzd->ops->set_emul_temp = of_thermal_set_emul_temp;//将通过此回调调用sensor回调
list_add_tail(&tz->list, &sens_param->first_tz);//添加到链表中
mutex_unlock(&tzd->lock);
return tzd;
}
2.1.1 of_thermal_get_temp函数
static int of_thermal_get_temp(struct thermal_zone_device *tz,
int *temp)
{
struct __thermal_zone *data = tz->devdata;
if (!data->senps || !data->senps->ops->get_temp)
return -EINVAL;
if (data->mode == THERMAL_DEVICE_DISABLED) {//如果设备关闭,返回
*temp = tz->tzp->tracks_low ?
THERMAL_TEMP_INVALID_LOW :
THERMAL_TEMP_INVALID;
return 0;
}
return data->senps->ops->get_temp(data->senps->sensor_data, temp);//调用sensor回调
}
3、cooling 设备注册函数thermal_of_cooling_device_register
struct thermal_cooling_device *
thermal_of_cooling_device_register(struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
{
return __thermal_cooling_device_register(np, type, devdata, ops);
}
static struct thermal_cooling_device *
__thermal_cooling_device_register(struct device_node *np,
char *type, void *devdata,
const struct thermal_cooling_device_ops *ops)
{
struct thermal_cooling_device *cdev;
struct thermal_zone_device *pos = NULL;
int result;
if (type && strlen(type) >= THERMAL_NAME_LENGTH)
return ERR_PTR(-EINVAL);
if (!ops || !ops->get_max_state || !ops->get_cur_state ||
!ops->set_cur_state)
return ERR_PTR(-EINVAL);
cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL);//分配空间
if (!cdev)
return ERR_PTR(-ENOMEM);
result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id);//分配一个idr
if (result) {
kfree(cdev);
return ERR_PTR(result);
}
strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
mutex_init(&cdev->lock);
INIT_LIST_HEAD(&cdev->thermal_instances);//初始化cooling device 链表
cdev->np = np;
cdev->ops = ops;
cdev->updated = false;
cdev->device.class = &thermal_class;
cdev->device.groups = cooling_device_attr_groups;//用于创建相关的cool节点
cdev->devdata = devdata;
cdev->sysfs_cur_state_req = 0;
cdev->sysfs_min_state_req = ULONG_MAX;
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);//设置cooling device name
result = device_register(&cdev->device);//注册设备
if (result) {
release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
kfree(cdev);
return ERR_PTR(result);
}
/* Add 'this' new cdev to the global cdev list */
mutex_lock(&thermal_list_lock);
list_add(&cdev->node, &thermal_cdev_list);//添加到链表中
mutex_unlock(&thermal_list_lock);
/* Update binding information for 'this' new cdev */
bind_cdev(cdev);//绑定设备
mutex_lock(&thermal_list_lock);
list_for_each_entry(pos, &thermal_tz_list, node)
if (atomic_cmpxchg(&pos->need_update, 1, 0))//此函数返回&tz->need_update的counter值来判断是否需要更新,如果&tz->need_update的counter值等于1,&tz->need_update的counter值将更新为0
thermal_zone_device_update(pos,
THERMAL_EVENT_UNSPECIFIED);//thermal zone 设备更新,上一篇1.2.3.1有介绍,这里不多介绍了
mutex_unlock(&thermal_list_lock);//释放锁
return cdev;
}
3.1 bind_cdev函数
static void bind_cdev(struct thermal_cooling_device *cdev)
{
int i, ret;
const struct thermal_zone_params *tzp;
struct thermal_zone_device *pos = NULL;
mutex_lock(&thermal_list_lock);
list_for_each_entry(pos, &thermal_tz_list, node) {//遍历每一个thermal_zone_device和此cooling device绑定
if (!pos->tzp && !pos->ops->bind)
continue;
if (pos->ops->bind) {//来自结构of_thermal_ops
ret = pos->ops->bind(pos, cdev);
if (ret)
print_bind_err_msg(pos, cdev, ret);
continue;
}
tzp = pos->tzp;
if (!tzp || !tzp->tbp)
continue;
for (i = 0; i < tzp->num_tbps; i++) {
if (tzp->tbp[i].cdev || !tzp->tbp[i].match)//tzp->tbp[i].match
continue;
if (tzp->tbp[i].match(pos, cdev))
continue;
tzp->tbp[i].cdev = cdev;
__bind(pos, tzp->tbp[i].trip_mask, cdev,
tzp->tbp[i].binding_limits,
tzp->tbp[i].weight);
}
}
mutex_unlock(&thermal_list_lock);
}
4. 例程:
4.1设备树
定义一个test_sensor的设备
test_sensor: test_sensor@0 {
compatible = "test,test_sensor";
#thermal-sensor-cells = <0>;//代表是thermal sensor,0代表的是test_sensor带的参数个数
};
定义一个test_cool的设备
test_cool: test_cool@0 {
compatible = "test,test_cool";
#cooling-cells = <2>;
};
&thermal_zones {
test_step {
polling-delay-passive = <1000>;
polling-delay = <10000>;
thermal-sensors = <&test_sensor>;
wake-capable-sensor;
thermal-governor = "step_wise";
trips {
test_config: test-config {
temperature = <65000>;
hysteresis = <10000>;
type = "passive";
};
test_config1: test-config1 {
temperature = <60000>;
hysteresis = <1000>;
type = "passive";
};
};
cooling-maps {
test_cdev {
trip = <&test_config>;
cooling-device =
<&test_cool 0
THERMAL_MAX_LIMIT>;
};
test_cdev1 {
trip = <&test_config1>;
cooling-device =
<&test_cool 0
THERMAL_MAX_LIMIT>;
};
};
};
};
4.2 例程代码部分:
#include <linux/thermal.h>
struct test_tz_priv {
int val;
struct thermal_zone_device *tz_dev;
char name[32];
};
struct test_tz_priv tz_data0;
static ssize_t test_sensor_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int count;
count = sprintf(buf, "%d", tz_data0.val);
return count;
}
static ssize_t test_sensor_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = -EINVAL;
int val;
ret = sscanf(buf, "%d", &val);
if (ret <= 0) {
return ret;
}
tz_data0.val = val;
return count;
}
static DEVICE_ATTR_RW(test_sensor);
/*
* test_get_temp - get wsa temperature
* @thermal: thermal zone device
* @temp: temperature value
*
* Get the temperature of test.
*
* Return: 0 on success or negative error code on failure.
*/
static int test_get_temp(void *data, int *val)
{
struct test_tz_priv *tz_pdata = data;
*val = tz_pdata->val;
pr_info("%s %d\n", __func__, __LINE__);
return 0;
}
EXPORT_SYMBOL(test_get_temp);
static struct thermal_zone_of_device_ops test_thermal_ops = {
.get_temp = test_get_temp,
};
static int test_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int ret = 0;
struct test_tz_priv *tz_pdata;
tz_pdata = &tz_data0;
tz_pdata->tz_dev = thermal_zone_of_sensor_register(dev, 0, tz_pdata,
&test_thermal_ops);
if (IS_ERR(tz_pdata->tz_dev)) {
pr_err("%s: thermal device register failed.\n", __func__);
return -EINVAL;
}
ret = device_create_file(dev, &dev_attr_test_sensor);
return ret;
}
static int test_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
return 0;
}
static const struct of_device_id test_of_match[] = {
{.compatible = "test,test_sensor", },
{ }
};
static struct platform_driver test_sensor_driver = {
.driver = {
.name = "test_sensor_driver",
.owner = THIS_MODULE,
.of_match_table = test_of_match,
},
.probe = test_probe,
.remove = test_remove,
};
/**************************************************************/
/*********************test cooling*****************************/
struct test_cooling_priv {
struct thermal_cooling_device *cdev;
unsigned long max_data;
unsigned long data;
};
struct test_cooling_priv cooling_data0;
static int test_cooling_get_max_xxx(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct test_cooling_priv *pdata = (struct test_cooling_priv *)cdev->devdata;
*state = pdata->max_data;
pr_info("%s %d\n", __func__, __LINE__);
return 0;
}
static int test_cooling_get_cur_xxx(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct test_cooling_priv *pdata = (struct test_cooling_priv *)cdev->devdata;
*state = pdata->data;
pr_info("%s %d\n", __func__, __LINE__);
return 0;
}
static int test_cooling_set_cur_xxx(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct test_cooling_priv *pdata = (struct test_cooling_priv *)cdev->devdata;
pdata->data = state;
pr_info("%s %d %d\n", __func__, __LINE__, state);
return 0;
}
static struct thermal_cooling_device_ops test_cooling_ops = {
.get_max_state = test_cooling_get_max_xxx,
.get_cur_state = test_cooling_get_cur_xxx,
.set_cur_state = test_cooling_set_cur_xxx,
};
static int test_cooling_probe(struct platform_device *pdev)
{
int ret = 0;
cooling_data0.max_data = 8;
cooling_data0.cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
(char *)dev_name(&pdev->dev), &cooling_data0, &test_cooling_ops);
if (unlikely(!cooling_data0.cdev))
{
pr_err("Cooling device register failed\n");
return -EINVAL;
}
return ret;
}
static int test_cooling_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id test_cooling_of_match[] = {
{.compatible = "test,test_cool", },
{ }
};
static struct platform_driver test_cooling_driver = {
.driver = {
.name = "test_cooling_driver",
.owner = THIS_MODULE,
.of_match_table = test_cooling_of_match,
},
.probe = test_cooling_probe,
.remove = test_cooling_remove,
};
/*******************************************************/
static int __init test_init(void)
{
int ret;
ret = platform_driver_register(&test_sensor_driver);
ret = platform_driver_register(&test_cooling_driver);
return ret;
}
static void __exit test_exit(void)
{
platform_driver_unregister(&test_sensor_driver);
platform_driver_unregister(&test_cooling_driver);
}
module_init(test_init);
module_exit(test_exit);
MODULE_DESCRIPTION("test driver");
MODULE_AUTHOR("xxxxxxxxxx");
MODULE_LICENSE("GPL");