diff options
Diffstat (limited to 'drivers/thermal/thermal_helpers.c')
| -rw-r--r-- | drivers/thermal/thermal_helpers.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c new file mode 100644 index 000000000..b1152ad7a --- /dev/null +++ b/drivers/thermal/thermal_helpers.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * thermal_helpers.c - helper functions to handle thermal devices + * + * Copyright (C) 2016 Eduardo Valentin <edubezval@gmail.com> + * + * Highly based on original thermal_core.c + * Copyright (C) 2008 Intel Corp + * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> + * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/sysfs.h> + +#include "thermal_core.h" +#include "thermal_trace.h" + +int get_tz_trend(struct thermal_zone_device *tz, const struct thermal_trip *trip) +{ + enum thermal_trend trend; + + if (tz->emul_temperature || !tz->ops.get_trend || + tz->ops.get_trend(tz, trip, &trend)) { + if (tz->temperature > tz->last_temperature) + trend = THERMAL_TREND_RAISING; + else if (tz->temperature < tz->last_temperature) + trend = THERMAL_TREND_DROPPING; + else + trend = THERMAL_TREND_STABLE; + } + + return trend; +} + +static bool thermal_instance_present(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, + const struct thermal_trip *trip) +{ + const struct thermal_trip_desc *td = trip_to_trip_desc(trip); + struct thermal_instance *ti; + + list_for_each_entry(ti, &td->thermal_instances, trip_node) { + if (ti->cdev == cdev) + return true; + } + + return false; +} + +bool thermal_trip_is_bound_to_cdev(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev) +{ + guard(thermal_zone)(tz); + guard(cooling_dev)(cdev); + + return thermal_instance_present(tz, cdev, trip); +} +EXPORT_SYMBOL_GPL(thermal_trip_is_bound_to_cdev); + +/** + * __thermal_zone_get_temp() - returns the temperature of a thermal zone + * @tz: a valid pointer to a struct thermal_zone_device + * @temp: a valid pointer to where to store the resulting temperature. + * + * When a valid thermal zone reference is passed, it will fetch its + * temperature and fill @temp. + * + * Both tz and tz->ops must be valid pointers when calling this function, + * and the tz->ops.get_temp callback must be provided. + * The function must be called under tz->lock. + * + * Return: On success returns 0, an error code otherwise + */ +int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) +{ + const struct thermal_trip_desc *td; + int crit_temp = INT_MAX; + int ret = -EINVAL; + + lockdep_assert_held(&tz->lock); + + ret = tz->ops.get_temp(tz, temp); + + if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { + for_each_trip_desc(tz, td) { + const struct thermal_trip *trip = &td->trip; + + if (trip->type == THERMAL_TRIP_CRITICAL) { + crit_temp = trip->temperature; + 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; + } + + if (ret) + dev_dbg(&tz->device, "Failed to get temperature: %d\n", ret); + + return ret; +} + +/** + * thermal_zone_get_temp() - returns the temperature of a thermal zone + * @tz: a valid pointer to a struct thermal_zone_device + * @temp: a valid pointer to where to store the resulting temperature. + * + * When a valid thermal zone reference is passed, it will fetch its + * temperature and fill @temp. + * + * Return: On success returns 0, an error code otherwise + */ +int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) +{ + int ret; + + if (IS_ERR_OR_NULL(tz)) + return -EINVAL; + + guard(thermal_zone)(tz); + + if (!tz->ops.get_temp) + return -EINVAL; + + ret = __thermal_zone_get_temp(tz, temp); + if (!ret && *temp <= THERMAL_TEMP_INVALID) + return -ENODATA; + + return ret; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_temp); + +static int thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev, int state) +{ + int ret; + + /* + * No check is needed for the ops->set_cur_state as the + * registering function checked the ops are correctly set + */ + ret = cdev->ops->set_cur_state(cdev, state); + if (ret) + return ret; + + thermal_notify_cdev_state_update(cdev, state); + thermal_cooling_device_stats_update(cdev, state); + thermal_debug_cdev_state_update(cdev, state); + + return 0; +} + +void __thermal_cdev_update(struct thermal_cooling_device *cdev) +{ + struct thermal_instance *instance; + unsigned long target = 0; + + /* Make sure cdev enters the deepest cooling state */ + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { + if (instance->target == THERMAL_NO_TARGET) + continue; + if (instance->target > target) + target = instance->target; + } + + thermal_cdev_set_cur_state(cdev, target); + + trace_cdev_update(cdev, target); + dev_dbg(&cdev->device, "set to state %lu\n", target); +} + +/** + * thermal_cdev_update - update cooling device state if needed + * @cdev: pointer to struct thermal_cooling_device + * + * Update the cooling device state if there is a need. + */ +void thermal_cdev_update(struct thermal_cooling_device *cdev) +{ + guard(cooling_dev)(cdev); + + if (!cdev->updated) { + __thermal_cdev_update(cdev); + cdev->updated = true; + } +} + +/** + * thermal_cdev_update_nocheck() - Unconditionally update cooling device state + * @cdev: Target cooling device. + */ +void thermal_cdev_update_nocheck(struct thermal_cooling_device *cdev) +{ + guard(cooling_dev)(cdev); + + __thermal_cdev_update(cdev); +} + +/** + * thermal_zone_get_slope - return the slope attribute of the thermal zone + * @tz: thermal zone device with the slope attribute + * + * Return: If the thermal zone device has a slope attribute, return it, else + * return 1. + */ +int thermal_zone_get_slope(struct thermal_zone_device *tz) +{ + if (tz && tz->tzp) + return tz->tzp->slope; + return 1; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_slope); + +/** + * thermal_zone_get_offset - return the offset attribute of the thermal zone + * @tz: thermal zone device with the offset attribute + * + * Return: If the thermal zone device has a offset attribute, return it, else + * return 0. + */ +int thermal_zone_get_offset(struct thermal_zone_device *tz) +{ + if (tz && tz->tzp) + return tz->tzp->offset; + return 0; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_offset); |
