Thermal子系统之thermal_init流程

Linux Thermal是Linux系统下的温度控制相关模块,主要用于解决设备因性能增强而引起的发热问题,确保设备温度维持在一个安全、舒适的范围内,防止硬件过热导致系统不稳定或缩减芯片寿命。Linux Thermal的主要框架包括获取温度的设备和控制温度的设备,以及一些使用温度控制设备的策略。在Linux Thermal框架中,获取温度的设备被抽象为Thermal Zone Device,控制温度的设备被抽象为Thermal Cooling Device。

内核中源码路径:

Thermal_core.c (drivers\thermal) 
 

1、thermal初始化:

static int __init thermal_init(void)
{
    int result;

    thermal_passive_wq = alloc_workqueue("thermal_passive_wq",
                        WQ_HIGHPRI | WQ_UNBOUND
                        | WQ_FREEZABLE,
                        THERMAL_MAX_ACTIVE);//为thermal_passive_wq分配工作队列空间
    if (!thermal_passive_wq) {
        result = -ENOMEM;
        goto init_exit;
    }

    result = thermal_register_governors();//注册热管理策略governors,
    if (result)
        goto destroy_wq;

    result = class_register(&thermal_class);//注册class节点
    if (result)
        goto unregister_governors;

    result = of_parse_thermal_zones();//解析thermal zone数据,并将硬件thermal zone信息填充到thermal框架中
    if (result)
        goto exit_zone_parse;

    result = register_pm_notifier(&thermal_pm_nb);//注册suspend notifier
    if (result)
        pr_warn("Thermal: Can not register suspend notifier, return %d\n",
            result);

    return 0;

exit_zone_parse:
    class_unregister(&thermal_class);
unregister_governors:
    thermal_unregister_governors();
destroy_wq:
    destroy_workqueue(thermal_passive_wq);
init_exit:
    idr_destroy(&thermal_tz_idr);
    idr_destroy(&thermal_cdev_idr);
    mutex_destroy(&thermal_idr_lock);
    mutex_destroy(&thermal_list_lock);
    mutex_destroy(&thermal_governor_lock);
    return result;
}


1.1注册热管理策略governors

static int __init thermal_register_governors(void)
{
    int result;

    result = thermal_gov_step_wise_register();//注册 Step Wise 热管理策略的函数
    if (result)
        return result;

    result = thermal_gov_fair_share_register();//注册公平分享(Fair Share)热管理策略的函数
    if (result)
        return result;

    result = thermal_gov_bang_bang_register();//注册 bang-bang 控制热管理策略的函数。Bang-bang 控制是一种控制理论中的控制策略,它通过简单地打开或关闭控制器来控制系统的输出。在热管理中,bang-bang 控制策略可以被用来快速响应温度变化,通过开启或关闭散热设备来控制温度。
    if (result)
        return result;

    result = thermal_gov_user_space_register();//注册用户空间热管理策略的函数。
    if (result)
        return result;

    result = thermal_gov_low_limits_register();//注册低限制热管理策略的函数。低限制策略是一种热管理策略,它通过设置较低的温度限制来控制设备的温度。这种策略通常用于保护设备免受过热,确保设备的可靠性和寿命。
    if (result)
        return result;

    return thermal_gov_power_allocator_register();//注册电源分配器热管理策略的函数。电源分配器策略是一种热管理策略,它通过分配电源使用来控制设备的温度。这种策略通常用于平衡设备的性能和功耗,以提高整体的热效率。
}

以step_wise为例

1.1.1 thermal_gov_step_wise_register注册函数

int thermal_gov_step_wise_register(void)
{
    return thermal_register_governor(&thermal_gov_step_wise);
}

1.1.1.1 thermal_gov_step_wise定义如下:

static struct thermal_governor thermal_gov_step_wise = {
    .name        = "step_wise",
    .throttle    = step_wise_throttle,
};

static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
{
    struct thermal_instance *instance;

    thermal_zone_trip_update(tz, trip);//thermal zone 触发点更新

    if (tz->forced_passive)//判断是否强制更新
        thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE);

    mutex_lock(&tz->lock);

    list_for_each_entry(instance, &tz->thermal_instances, tz_node)
        thermal_cdev_update(instance->cdev);//更新每一个cooling 设备

    mutex_unlock(&tz->lock);

    return 0;
}

1.1.1.1.1 thermal_zone_trip_update函数

static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
{
    int trip_temp, hyst_temp;
    enum thermal_trip_type trip_type;
    enum thermal_trend trend;
    struct thermal_instance *instance;
    bool throttle = false;
    int old_target;

    if (trip == THERMAL_TRIPS_NONE) {
        hyst_temp = trip_temp = tz->forced_passive;
        trip_type = THERMAL_TRIPS_NONE;
    } else {
        tz->ops->get_trip_temp(tz, trip, &trip_temp);//获取触发点温度,此回调函数来自of_thermal_ops
        if (tz->ops->get_trip_hyst) {
            tz->ops->get_trip_hyst(tz, trip, &hyst_temp);//获取trip_hyst,此值用于温度低于trip_temp-hysteresis值时恢复状态,此回调函数来自of_thermal_ops
            hyst_temp = trip_temp - hyst_temp;
        } else {
            hyst_temp = trip_temp;
        }
        tz->ops->get_trip_type(tz, trip, &trip_type);//获取触发类型,此回调函数来自of_thermal_ops
    }

    trend = get_tz_trend(tz, trip);//判断温度是在增加、下降还是不变

    dev_dbg(&tz->device,
        "Trip%d[type=%d,temp=%d,hyst=%d]:trend=%d,throttle=%d\n",
        trip, trip_type, trip_temp, hyst_temp, trend, throttle);

    mutex_lock(&tz->lock);

    list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
        if (instance->trip != trip)
            continue;

        old_target = instance->target;
        /*
         * Step wise has to lower the mitigation only if the
         * temperature goes below the hysteresis temperature.
         * Atleast, it has to hold on to mitigation device lower
         * limit if the temperature is above the hysteresis
         * temperature.
         */
        if (tz->temperature >= trip_temp ||
            (tz->temperature > hyst_temp &&
             old_target != THERMAL_NO_TARGET))//判断温度是不是达到触发点
            throttle = true;
        else
            throttle = false;

        instance->target = get_target_state(instance, trend, throttle);//得到目标值
        dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
                    old_target, (int)instance->target);

        if (instance->initialized && old_target == instance->target)
            continue;

        if (!instance->initialized) {//如果没有初始化
            if (instance->target != THERMAL_NO_TARGET) {
                trace_thermal_zone_trip(tz, trip, trip_type,
                            true);
                update_passive_instance(tz, trip_type, 1);//更新passive值用于在monitor_thermal_zone函数中是否使用passive_delay循环
            }
        } else {
            /* Activate a passive thermal instance */
            if (old_target == THERMAL_NO_TARGET &&
                instance->target != THERMAL_NO_TARGET) {
                trace_thermal_zone_trip(tz, trip, trip_type,
                            true);
                update_passive_instance(tz, trip_type, 1);//更新passive值用于在monitor_thermal_zone函数中是否使用passive_delay循环
            /* Deactivate a passive thermal instance */
            } else if (old_target != THERMAL_NO_TARGET &&
                instance->target == THERMAL_NO_TARGET) {
                trace_thermal_zone_trip(tz, trip, trip_type,
                            false);
                update_passive_instance(tz, trip_type, -1);//更新passive值用于在monitor_thermal_zone函数中是否使用passive_delay循环
            }
        }

        instance->initialized = true;
        mutex_lock(&instance->cdev->lock);
        instance->cdev->updated = false; /* cdev needs update */
        mutex_unlock(&instance->cdev->lock);
    }

    mutex_unlock(&tz->lock);
}
 

1.1.1.1.2  thermal_cdev_update函数

void thermal_cdev_update(struct thermal_cooling_device *cdev)
{
    struct thermal_instance *instance;
    unsigned long current_target = 0, min_target = ULONG_MAX;

    mutex_lock(&cdev->lock);
    /* cooling device is updated*/
    if (cdev->updated) {
        mutex_unlock(&cdev->lock);
        return;
    }

    /* Make sure cdev enters the deepest cooling state */
    current_target = cdev->sysfs_cur_state_req;//获取当前的状态
    min_target = cdev->sysfs_min_state_req;
    list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {//遍历每一个cooling device
        dev_dbg(&cdev->device, "zone%d->target=%lu\n",
                instance->tz->id, instance->target);
        if (instance->target == THERMAL_NO_TARGET)
            continue;
        if (instance->tz->governor->min_state_throttle) {//用于low_limits governor
            if (instance->target < min_target)
                min_target = instance->target;
        } else {
            if (instance->target > current_target)//目标状态如果大于当前状态,更新当前状态值
                current_target = instance->target;
        }
    }
    trace_cdev_update_start(cdev);
    cdev->ops->set_cur_state(cdev, current_target);//设置当前状态值到cooling device
    if (cdev->ops->set_min_state)
        cdev->ops->set_min_state(cdev, min_target);//设置最小的状态值
    cdev->updated = true;//更新完成
    mutex_unlock(&cdev->lock);
    trace_cdev_update(cdev, current_target, min_target);
    dev_dbg(&cdev->device, "set to state %lu min state %lu\n",
                current_target, min_target);
}

1.1.1.2 thermal_register_governor函数

int thermal_register_governor(struct thermal_governor *governor)
{
    int err;
    const char *name;
    struct thermal_zone_device *pos;

    if (!governor)
        return -EINVAL;

    mutex_lock(&thermal_governor_lock);

    err = -EBUSY;
    if (__find_governor(governor->name) == NULL) {//__find_governor查找governor是否已经存在thermal_governor_list链表中,若不在,将governor添加到thermal_governor_list中
        err = 0;
        list_add(&governor->governor_list, &thermal_governor_list);
        if (!def_governor && !strncmp(governor->name,
            DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
            def_governor = governor;
    }

    mutex_lock(&thermal_list_lock);

    list_for_each_entry(pos, &thermal_tz_list, node) {//遍历链表thermal_tz_list
        /*
         * only thermal zones with specified tz->tzp->governor_name
         * may run with tz->govenor unset
         */
        if (pos->governor)
            continue;

        name = pos->tzp->governor_name;

        if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {//如果governor name一样,则进行绑定
            int ret;

            ret = thermal_set_governor(pos, governor);//绑定governor和thermal_zone_device
            if (ret)
                dev_err(&pos->device,
                    "Failed to set governor %s for thermal zone %s: %d\n",
                    governor->name, pos->type, ret);
        }
    }

    mutex_unlock(&thermal_list_lock);
    mutex_unlock(&thermal_governor_lock);

    return err;
}
 

static int thermal_set_governor(struct thermal_zone_device *tz,
                struct thermal_governor *new_gov)
{
    int ret = 0;

    if (tz->governor && tz->governor->unbind_from_tz)
        tz->governor->unbind_from_tz(tz);

    if (new_gov && new_gov->bind_to_tz) {
        ret = new_gov->bind_to_tz(tz);
        if (ret) {
            bind_previous_governor(tz, new_gov->name);

            return ret;
        }
    }

    tz->governor = new_gov;//帮governor给thermal zone device

    return ret;
}

1.2 解析thermal zone数据 of_parse_thermal_zones

函数原型如下:

int __init of_parse_thermal_zones(void)
{
    struct device_node *np, *child;
    struct __thermal_zone *tz;
    struct thermal_zone_device_ops *ops;

    np = of_find_node_by_name(NULL, "thermal-zones");//从设备树中解析thermal-zones
    if (!np) {
        pr_debug("unable to find thermal zones\n");
        return 0; /* Run successfully on systems without thermal DT */
    }

    for_each_available_child_of_node(np, child) {//遍历thermal-zones中每一个孩子
        struct thermal_zone_device *zone;
        struct thermal_zone_params *tzp;
        int i, mask = 0;
        u32 prop;
        const char *governor_name;

        tz = thermal_of_build_thermal_zone(child);//建立__thermal_zone
        if (IS_ERR(tz)) {
            pr_err("failed to build thermal zone %s: %ld\n",
                   child->name,
                   PTR_ERR(tz));
            continue;
        }

        ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);//复制of_thermal_ops,提供各种回调函数
        if (!ops)
            goto exit_free;

        tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
        if (!tzp) {
            kfree(ops);
            goto exit_free;
        }

        /* No hwmon because there might be hwmon drivers registering */
        tzp->no_hwmon = true;

        if (!of_property_read_string(child, "thermal-governor",
                        &governor_name))//从设备树中读取"thermal-governor"值
            strlcpy(tzp->governor_name, governor_name,
                    THERMAL_NAME_LENGTH);

        if (!of_property_read_u32(child, "sustainable-power", &prop))//从设备树中读取"sustainable-power"值
            tzp->sustainable_power = prop;

        for (i = 0; i < tz->ntrips; i++)//根据触发个数设置触发点的可写的字符位。
            mask |= 1 << i;

        /* these two are left for temperature drivers to use */
        tzp->slope = tz->slope;
        tzp->offset = tz->offset;

        if (of_property_read_bool(child, "tracks-low"))//从设备树中读取"tracks-low"值
            tzp->tracks_low = true;

        zone = thermal_zone_device_register(child->name, tz->ntrips,
                            mask, tz,
                            ops, tzp,
                            tz->passive_delay,
                            tz->polling_delay);//注册新的热区设备
        if (IS_ERR(zone)) {//失败处理
            pr_err("Failed to build %s zone %ld\n", child->name,
                   PTR_ERR(zone));
            kfree(tzp);
            kfree(ops);
            of_thermal_free_zone(tz);
            /* attempting to build remaining zones still */
            continue;
        }
        tz->tzd = zone;
    }
    of_node_put(np);

    return 0;

exit_free:
    of_node_put(child);
    of_node_put(np);
    of_thermal_free_zone(tz);

    /* no memory available, so free what we have built */
    of_thermal_destroy_zones();

    return -ENOMEM;
}

1.2.1 of_thermal_ops结构

static struct thermal_zone_device_ops of_thermal_ops = {
    .get_mode = of_thermal_get_mode,
    .set_mode = of_thermal_set_mode,

    .get_trip_type = of_thermal_get_trip_type,
    .get_trip_temp = of_thermal_get_trip_temp,
    .set_trip_temp = of_thermal_set_trip_temp,
    .get_trip_hyst = of_thermal_get_trip_hyst,
    .set_trip_hyst = of_thermal_set_trip_hyst,
    .get_crit_temp = of_thermal_get_crit_temp,

    .bind = of_thermal_bind,
    .unbind = of_thermal_unbind,

    .is_wakeable = of_thermal_is_wakeable,
};


1.2.2 建立__thermal_zone

thermal_of_build_thermal_zone函数原型如下:

static struct __thermal_zone
__init *thermal_of_build_thermal_zone(struct device_node *np)
{
    struct device_node *child = NULL, *gchild;
    struct __thermal_zone *tz;
    int ret, i;
    u32 prop, coef[2];

    if (!np) {
        pr_err("no thermal zone np\n");
        return ERR_PTR(-EINVAL);
    }

    tz = kzalloc(sizeof(*tz), GFP_KERNEL);//分配空间
    if (!tz)
        return ERR_PTR(-ENOMEM);

    INIT_LIST_HEAD(&tz->list);
    ret = of_property_read_u32(np, "polling-delay-passive", &prop);//读取设备树参数,用于passive轮询间隔时间
    if (ret < 0) {
        pr_err("missing polling-delay-passive property\n");
        goto free_tz;
    }
    tz->passive_delay = prop;

    ret = of_property_read_u32(np, "polling-delay", &prop);//读取设备树参数,用于轮询间隔时间
    if (ret < 0) {
        pr_err("missing polling-delay property\n");
        goto free_tz;
    }
    tz->polling_delay = prop;

    tz->default_disable = of_property_read_bool(np,
                    "disable-thermal-zone");//读取设备树参数,用于默认关闭

    tz->is_wakeable = of_property_read_bool(np,
                    "wake-capable-sensor");//读取设备树参数,用于唤醒系统的传感器
    /*
     * REVIST: 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.
     */
    ret = of_property_read_u32_array(np, "coefficients", coef, 2);
    if (ret == 0) {
        tz->slope = coef[0];
        tz->offset = coef[1];
    } else {
        tz->slope = 1;
        tz->offset = 0;
    }

    /* trips */
    child = of_get_child_by_name(np, "trips");//触发点

    /* No trips provided */
    if (!child)
        goto finish;

    tz->ntrips = of_get_child_count(child);//得到触发点的个数
    if (tz->ntrips == 0) /* must have at least one child */
        goto finish;

    tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);//分配触发点的空间
    if (!tz->trips) {
        ret = -ENOMEM;
        goto free_tz;
    }

    i = 0;
    for_each_child_of_node(child, gchild) {
        ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);//得到每个触发点的参数
        if (ret)
            goto free_trips;
    }

    of_node_put(child);

    /* cooling-maps */
    child = of_get_child_by_name(np, "cooling-maps");//从设备节点中读取cooling-maps

    /* cooling-maps not provided */
    if (!child)
        goto finish;

    tz->num_tbps = of_get_child_count(child);//得到cooling-maps孩子的个数
    if (tz->num_tbps == 0)
        goto finish;

    tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);//分配空间
    if (!tz->tbps) {
        ret = -ENOMEM;
        goto free_trips;
    }

    i = 0;
    for_each_child_of_node(child, gchild) {//遍历cooling-maps中每一个孩子
        ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
                              tz->trips, tz->ntrips);//从设备树中解析和填充cooling-maps数据
        if (ret)
            goto free_tbps;
    }

finish:
    of_node_put(child);
    tz->mode = THERMAL_DEVICE_DISABLED;

    return tz;

free_tbps:
    for (i = i - 1; i >= 0; i--)
        of_node_put(tz->tbps[i].cooling_device);
    kfree(tz->tbps);
free_trips:
    for (i = 0; i < tz->ntrips; i++)
        of_node_put(tz->trips[i].np);
    kfree(tz->trips);
    of_node_put(gchild);
free_tz:
    kfree(tz);
    of_node_put(child);

    return ERR_PTR(ret);
}

1.2.2.1 解析和填充cooling-maps数据

static int thermal_of_populate_bind_params(struct device_node *np,
                       struct __thermal_bind_params *__tbp,
                       struct thermal_trip *trips,
                       int ntrips)
{
    struct of_phandle_args cooling_spec;
    struct device_node *trip;
    int ret, i;
    u32 prop;

    /* Default weight. Usage is optional */
    __tbp->usage = THERMAL_WEIGHT_DEFAULT;
    ret = of_property_read_u32(np, "contribution", &prop);
    if (ret == 0)
        __tbp->usage = prop;

    trip = of_parse_phandle(np, "trip", 0);//从设备树中读取trip值
    if (!trip) {
        pr_err("missing trip property\n");
        return -ENODEV;
    }

    /* match using device_node */
    for (i = 0; i < ntrips; i++)//和设备节点进行匹配
        if (trip == trips[i].np) {
            __tbp->trip_id = i;
            break;
        }

    if (i == ntrips) {
        ret = -ENODEV;
        goto end;
    }

    ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
                     0, &cooling_spec);//从cooling-maps的孩子中解析cooling-device,并读取对应参数
    if (ret < 0) {
        pr_err("missing cooling_device property\n");
        goto end;
    }
    __tbp->cooling_device = cooling_spec.np;
    if (cooling_spec.args_count >= 2) { /* at least min and max */
        __tbp->min = cooling_spec.args[0];
        __tbp->max = cooling_spec.args[1];
    } else {
        pr_err("wrong reference to cooling device, missing limits\n");
    }

end:
    of_node_put(trip);

    return ret;
}

1.2.3 注册新的热区设备

thermal_zone_device_register函数用于注册新的热区设备,原型如下:

struct thermal_zone_device *thermal_zone_device_register(const char *type,
    int trips, int mask, void *devdata,
    struct thermal_zone_device_ops *ops,
    struct thermal_zone_params *tzp,
    int passive_delay, int polling_delay)
{
    struct thermal_zone_device *tz;
    enum thermal_trip_type trip_type;
    int trip_temp;
    int result;
    int count;
    int passive = 0;
    struct thermal_governor *governor;

//参数异常检测

    if (type && strlen(type) >= THERMAL_NAME_LENGTH)
        return ERR_PTR(-EINVAL);

    if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips)
        return ERR_PTR(-EINVAL);

    if (!ops)
        return ERR_PTR(-EINVAL);

    if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
        return ERR_PTR(-EINVAL);

    tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
    if (!tz)
        return ERR_PTR(-ENOMEM);

    INIT_LIST_HEAD(&tz->thermal_instances);//初始化链表头
    idr_init(&tz->idr);
    mutex_init(&tz->lock);
    result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
    if (result) {
        kfree(tz);
        return ERR_PTR(result);
    }

    strlcpy(tz->type, type ? : "", sizeof(tz->type));
    tz->ops = ops;
    tz->tzp = tzp;
    tz->device.class = &thermal_class;
    tz->devdata = devdata;
    tz->trips = trips;
    tz->passive_delay = passive_delay;//从设备树中读取的参数
    tz->polling_delay = polling_delay;//从设备树中读取的参数
    /* A new thermal zone needs to be updated anyway. */
    atomic_set(&tz->need_update, 1);

    dev_set_name(&tz->device, "thermal_zone%d", tz->id);
    result = device_register(&tz->device);//注册设备,名字为thermal_zone*,可以在/sys/class/thermal/路径下查看到
    if (result) {
        release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
        kfree(tz);
        return ERR_PTR(result);
    }

    /* sys I/F */
    if (type) {
        result = device_create_file(&tz->device, &dev_attr_type);//创建获取type节点
        if (result)
            goto unregister;
    }

    result = device_create_file(&tz->device, &dev_attr_temp);//创建获取温度的节点
    if (result)
        goto unregister;

    if (ops->get_mode) {
        result = device_create_file(&tz->device, &dev_attr_mode);//创建获取温度的节点
        if (result)
            goto unregister;
    }

    result = create_trip_attrs(tz, mask);//创建触发点的属性
    if (result)
        goto unregister;

    for (count = 0; count < trips; count++) {
        if (tz->ops->get_trip_type(tz, count, &trip_type))//获取触发点的类型,触发点的类型在enum thermal_trip_type中定义
            set_bit(count, &tz->trips_disabled);
        if (trip_type == THERMAL_TRIP_PASSIVE)
            passive = 1;
        if (tz->ops->get_trip_temp(tz, count, &trip_temp))//获取触发温度
            set_bit(count, &tz->trips_disabled);
        /* Check for bogus trip points */
        if (trip_temp == 0)
            set_bit(count, &tz->trips_disabled);
    }

    if (!passive) {
        result = device_create_file(&tz->device, &dev_attr_passive);//创建passive的节点
        if (result)
            goto unregister;
    }
    result = device_create_file(&tz->device, &dev_attr_passive_delay);//创建passive_delay的节点
    if (result)
        goto unregister;

    result = device_create_file(&tz->device, &dev_attr_polling_delay);//创建polling_delay的节点
    if (result)
        goto unregister;

    if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) {
        result = device_create_file(&tz->device, &dev_attr_emul_temp);
        if (result)
            goto unregister;
    }

    /* Create policy attribute */
    result = device_create_file(&tz->device, &dev_attr_policy);创建policy的节点
    if (result)
        goto unregister;

    /* Add thermal zone params */
    result = create_tzp_attrs(&tz->device);//创建热区参数节点属性
    if (result)
        goto unregister;

    /* Create available_policies attribute */
    result = device_create_file(&tz->device, &dev_attr_available_policies);//创建available_policies的节点
    if (result)
        goto unregister;

    /* Update 'this' zone's governor information */
    mutex_lock(&thermal_governor_lock);

    if (tz->tzp)
        governor = __find_governor(tz->tzp->governor_name);//查找governor
    else
        governor = def_governor;

    result = thermal_set_governor(tz, governor);thermal zone 和governor 进行绑定
    if (result) {
        mutex_unlock(&thermal_governor_lock);
        goto unregister;
    }

    mutex_unlock(&thermal_governor_lock);

    if (!tz->tzp || !tz->tzp->no_hwmon) {
        result = thermal_add_hwmon_sysfs(tz);
        if (result)
            goto unregister;
    }

    mutex_lock(&thermal_list_lock);
    list_add_tail(&tz->node, &thermal_tz_list);//thermal zone添加到链表
    mutex_unlock(&thermal_list_lock);

    /* Bind cooling devices for this zone */
    bind_tz(tz);

    INIT_DEFERRABLE_WORK(&(tz->poll_queue), thermal_zone_device_check);//初始化work,用于循环check温度状态

    thermal_zone_device_reset(tz);//thermal zone 设备复位
    /* Update the new thermal zone and mark it as already updated. */
    if (atomic_cmpxchg(&tz->need_update, 1, 0))//此函数返回&tz->need_update的counter值来判断是否需要更新,如果&tz->need_update的counter值等于1,&tz->need_update的counter值将更新为0
        thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);//thermal zone 设备更新

    return tz;

unregister:
    release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
    device_unregister(&tz->device);
    return ERR_PTR(result);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_register);

1.2.3.0 bind_tz函数

static void bind_tz(struct thermal_zone_device *tz)
{
    int i, ret;
    struct thermal_cooling_device *pos = NULL;
    const struct thermal_zone_params *tzp = tz->tzp;

    if (!tzp && !tz->ops->bind)
        return;

    mutex_lock(&thermal_list_lock);

    /* If there is ops->bind, try to use ops->bind */
    if (tz->ops->bind) {//来自结构of_thermal_ops
        list_for_each_entry(pos, &thermal_cdev_list, node) {//遍历每一个cooling device和此  thermal_zone_device进行bind
            ret = tz->ops->bind(tz, pos);
            if (ret)
                print_bind_err_msg(tz, pos, ret);
        }
        goto exit;
    }

    if (!tzp || !tzp->tbp)
        goto exit;

    list_for_each_entry(pos, &thermal_cdev_list, node) {
        for (i = 0; i < tzp->num_tbps; i++) {
            if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
                continue;
            if (tzp->tbp[i].match(tz, pos))
                continue;
            tzp->tbp[i].cdev = pos;
            __bind(tz, tzp->tbp[i].trip_mask, pos,
                   tzp->tbp[i].binding_limits,
                   tzp->tbp[i].weight);
        }
    }
exit:
    mutex_unlock(&thermal_list_lock);
}
 

1.2.3.1 thermal zone 设备更新函数thermal_zone_device_update

thermal zone 设备更新通过thermal_zone_device_update,并由此建立启动循环检测状态

void thermal_zone_device_update(struct thermal_zone_device *tz,
                enum thermal_notify_event event)
{
    int count;

    if (atomic_read(&in_suspend) && (!tz->ops->is_wakeable ||
        !(tz->ops->is_wakeable(tz))))状态判断
        return;

    if (!tz->ops->get_temp)//判断获取温度接口是否存在
        return;

    trace_thermal_device_update(tz, event);//trace 使用
    update_temperature(tz);//更新温度

    thermal_zone_set_trips(tz);//找到合适的触发点温度设置

    tz->notify_event = event;

    for (count = 0; count < tz->trips; count++)
        handle_thermal_trip(tz, count);//处理热阈值的函数,根据温度的情况进行相应的热处理
}

1.2.3.1.1  更新温度函数update_temperature

static void update_temperature(struct thermal_zone_device *tz)
{
    int temp, ret;

    ret = thermal_zone_get_temp(tz, &temp);//获取温度
    if (ret) {
        if (ret != -EAGAIN)
            dev_warn(&tz->device,
                 "failed to read out thermal zone (%d)\n",
                 ret);
        return;
    }
    store_temperature(tz, temp);//保存温度
}

1.2.3.1.1.1 获取温度函数thermal_zone_get_temp

int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
    int ret = -EINVAL;
    int count;
    int crit_temp = INT_MAX;
    enum thermal_trip_type type;

    if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
        goto exit;

    mutex_lock(&tz->lock);

    ret = tz->ops->get_temp(tz, temp);//获取温度,此回调函数有thermal_zone_of_sensor_register注册sensor时赋值

    if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
        for (count = 0; count < tz->trips; count++) {
            ret = tz->ops->get_trip_type(tz, count, &type);//来自of_thermal_ops
            if (!ret && type == THERMAL_TRIP_CRITICAL) {
                ret = tz->ops->get_trip_temp(tz, count,
                        &crit_temp);//来自of_thermal_ops
                break;
            }
        }

        /*
         * Only allow emulating a temperature when the real temperature
         * is below the critical temperature so that the emulation code
         * cannot hide critical conditions.
         */
        if (!ret && *temp < crit_temp)
            *temp = tz->emul_temperature;
    }
    trace_thermal_query_temp(tz, *temp);
    mutex_unlock(&tz->lock);
exit:
    return ret;
}

1.2.3.1.1.2 保存温度函数store_temperature

static void store_temperature(struct thermal_zone_device *tz, int temp)
{
    mutex_lock(&tz->lock);
    tz->last_temperature = tz->temperature;
    tz->temperature = temp;//将温度保存到thermal zone 设备中
    mutex_unlock(&tz->lock);

    trace_thermal_temperature(tz);
    if (tz->last_temperature == THERMAL_TEMP_INVALID ||
        tz->last_temperature == THERMAL_TEMP_INVALID_LOW)
        dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n",
            tz->temperature);
    else
        dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
            tz->last_temperature, tz->temperature);
}

1.2.3.1.2 thermal_zone_set_trips

void thermal_zone_set_trips(struct thermal_zone_device *tz)
{
    int low = -INT_MAX;
    int high = INT_MAX;
    int trip_temp, hysteresis;
    int i, ret;

    mutex_lock(&tz->lock);

    if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
        goto exit;

  //此for循环用于找到最接近temp且小于temp的trip temp

 for (i = 0; i < tz->trips; i++) {
        int trip_low;

        tz->ops->get_trip_temp(tz, i, &trip_temp);//获取触发点温度 来自of_thermal_ops
        tz->ops->get_trip_hyst(tz, i, &hysteresis);//此值用于温度低于trip_temp-hysteresis值时恢复状态,来自of_thermal_ops

        trip_low = trip_temp - hysteresis;

        if (trip_low < tz->temperature && trip_low > low)
            low = trip_low;

        if (trip_temp > tz->temperature && trip_temp < high)
            high = trip_temp;
    }

    tz->prev_low_trip = low;
    tz->prev_high_trip = high;

    dev_dbg(&tz->device,
        "new temperature boundaries: %d < x < %d\n", low, high);

    /*
     * Set a temperature window. When this window is left the driver
     * must inform the thermal core via thermal_zone_device_update.
     */
    ret = tz->ops->set_trips(tz, low, high);//设置触发点,此回调函数有thermal_zone_of_sensor_register注册sensor时赋值
    if (ret)
        dev_err(&tz->device, "Failed to set trips: %d\n", ret);
    trace_thermal_set_trip(tz);

exit:
    mutex_unlock(&tz->lock);
}

1.2.3.1.3 处理热阈值函数handle_thermal_trip

static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
{
    enum thermal_trip_type type;

    /* Ignore disabled trip points */
    if (test_bit(trip, &tz->trips_disabled))
        return;

    tz->ops->get_trip_type(tz, trip, &type);//获取触发类型,来自of_thermal_ops

    if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
        handle_critical_trips(tz, trip, type);//如果是关键触发点
    else
        handle_non_critical_trips(tz, trip, type);//如果不是关键的触发点,走governor处理
    /*
     * Alright, we handled this trip successfully.
     * So, start monitoring again.
     */
    monitor_thermal_zone(tz);//循环处理
    trace_thermal_handle_trip(tz, trip);
}

1.2.3.1.3.1 handle_critical_trips函数

static void handle_critical_trips(struct thermal_zone_device *tz,
                int trip, enum thermal_trip_type trip_type)
{
    int trip_temp;

    tz->ops->get_trip_temp(tz, trip, &trip_temp);//获取触发点温度

    /* If we have not crossed the trip_temp, we do not care. */
    if (trip_temp <= 0 || tz->temperature < trip_temp)
        return;

    trace_thermal_zone_trip(tz, trip, trip_type, true);

    if (tz->ops->notify)//定义了通知回调,调用通知回调函数
        tz->ops->notify(tz, trip, trip_type);

    if (trip_type == THERMAL_TRIP_CRITICAL) {//如果是critical类型,关机
        dev_emerg(&tz->device,
              "critical temperature reached(%d C),shutting down\n",
              tz->temperature / 1000);
        orderly_poweroff(true);
    }
}

1.2.3.1.3.2 handle_non_critical_trips函数

static void handle_non_critical_trips(struct thermal_zone_device *tz,
            int trip, enum thermal_trip_type trip_type)
{
    tz->governor ? tz->governor->throttle(tz, trip) :
               def_governor->throttle(tz, trip);//调用governor 回调
}

governor->throttle回调见以step_wise为例

1.2.3.1.3.3  monitor_thermal_zone函数

static void monitor_thermal_zone(struct thermal_zone_device *tz)
{
    mutex_lock(&tz->lock);

    if (tz->passive)
        thermal_zone_device_set_polling(thermal_passive_wq,
                        tz, tz->passive_delay);//继续延迟polling
    else if (tz->polling_delay)
        thermal_zone_device_set_polling(
                system_freezable_power_efficient_wq,
                tz, tz->polling_delay);//继续延迟polling
    else
        thermal_zone_device_set_polling(NULL, tz, 0);//取消polling

    mutex_unlock(&tz->lock);
}

static void thermal_zone_device_set_polling(struct workqueue_struct *queue,
                        struct thermal_zone_device *tz,
                        int delay)
{
    if (delay > 1000)
        mod_delayed_work(queue, &tz->poll_queue,
                 round_jiffies(msecs_to_jiffies(delay)));
    else if (delay)
        mod_delayed_work(queue, &tz->poll_queue,
                 msecs_to_jiffies(delay));
    else
        cancel_delayed_work(&tz->poll_queue);
}

延时时间到了后会调用thermal_zone_device_check函数

static void thermal_zone_device_check(struct work_struct *work)
{
    struct thermal_zone_device *tz = container_of(work, struct
                              thermal_zone_device,
                              poll_queue.work);
    thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);//此函数回到1.2.3.1将完成循环
}

  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值