123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * thermal_hwmon.c - Generic Thermal Management hwmon support.
- *
- * Code based on Intel thermal_core.c. Copyrights of the original code:
- * Copyright (C) 2008 Intel Corp
- * Copyright (C) 2008 Zhang Rui <[email protected]>
- * Copyright (C) 2008 Sujith Thomas <[email protected]>
- *
- * Copyright (C) 2013 Texas Instruments
- * Copyright (C) 2013 Eduardo Valentin <[email protected]>
- */
- #include <linux/err.h>
- #include <linux/export.h>
- #include <linux/hwmon.h>
- #include <linux/slab.h>
- #include <linux/thermal.h>
- #include "thermal_hwmon.h"
- /* hwmon sys I/F */
- /* thermal zone devices with the same type share one hwmon device */
- struct thermal_hwmon_device {
- char type[THERMAL_NAME_LENGTH];
- struct device *device;
- int count;
- struct list_head tz_list;
- struct list_head node;
- };
- struct thermal_hwmon_attr {
- struct device_attribute attr;
- char name[16];
- };
- /* one temperature input for each thermal zone */
- struct thermal_hwmon_temp {
- struct list_head hwmon_node;
- struct thermal_zone_device *tz;
- struct thermal_hwmon_attr temp_input; /* hwmon sys attr */
- struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */
- };
- static LIST_HEAD(thermal_hwmon_list);
- static DEFINE_MUTEX(thermal_hwmon_list_lock);
- static ssize_t
- temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- int temperature;
- int ret;
- struct thermal_hwmon_attr *hwmon_attr
- = container_of(attr, struct thermal_hwmon_attr, attr);
- struct thermal_hwmon_temp *temp
- = container_of(hwmon_attr, struct thermal_hwmon_temp,
- temp_input);
- struct thermal_zone_device *tz = temp->tz;
- ret = thermal_zone_get_temp(tz, &temperature);
- if (ret)
- return ret;
- return sprintf(buf, "%d\n", temperature);
- }
- static ssize_t
- temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- struct thermal_hwmon_attr *hwmon_attr
- = container_of(attr, struct thermal_hwmon_attr, attr);
- struct thermal_hwmon_temp *temp
- = container_of(hwmon_attr, struct thermal_hwmon_temp,
- temp_crit);
- struct thermal_zone_device *tz = temp->tz;
- int temperature;
- int ret;
- ret = tz->ops->get_crit_temp(tz, &temperature);
- if (ret)
- return ret;
- return sprintf(buf, "%d\n", temperature);
- }
- static struct thermal_hwmon_device *
- thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
- {
- struct thermal_hwmon_device *hwmon;
- char type[THERMAL_NAME_LENGTH];
- mutex_lock(&thermal_hwmon_list_lock);
- list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
- strcpy(type, tz->type);
- strreplace(type, '-', '_');
- if (!strcmp(hwmon->type, type)) {
- mutex_unlock(&thermal_hwmon_list_lock);
- return hwmon;
- }
- }
- mutex_unlock(&thermal_hwmon_list_lock);
- return NULL;
- }
- /* Find the temperature input matching a given thermal zone */
- static struct thermal_hwmon_temp *
- thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
- const struct thermal_zone_device *tz)
- {
- struct thermal_hwmon_temp *temp;
- mutex_lock(&thermal_hwmon_list_lock);
- list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
- if (temp->tz == tz) {
- mutex_unlock(&thermal_hwmon_list_lock);
- return temp;
- }
- mutex_unlock(&thermal_hwmon_list_lock);
- return NULL;
- }
- static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
- {
- int temp;
- return tz->ops->get_crit_temp && !tz->ops->get_crit_temp(tz, &temp);
- }
- int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
- {
- struct thermal_hwmon_device *hwmon;
- struct thermal_hwmon_temp *temp;
- int new_hwmon_device = 1;
- int result;
- hwmon = thermal_hwmon_lookup_by_type(tz);
- if (hwmon) {
- new_hwmon_device = 0;
- goto register_sys_interface;
- }
- hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);
- if (!hwmon)
- return -ENOMEM;
- INIT_LIST_HEAD(&hwmon->tz_list);
- strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
- strreplace(hwmon->type, '-', '_');
- hwmon->device = hwmon_device_register_for_thermal(&tz->device,
- hwmon->type, hwmon);
- if (IS_ERR(hwmon->device)) {
- result = PTR_ERR(hwmon->device);
- goto free_mem;
- }
- register_sys_interface:
- temp = kzalloc(sizeof(*temp), GFP_KERNEL);
- if (!temp) {
- result = -ENOMEM;
- goto unregister_name;
- }
- temp->tz = tz;
- hwmon->count++;
- snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
- "temp%d_input", hwmon->count);
- temp->temp_input.attr.attr.name = temp->temp_input.name;
- temp->temp_input.attr.attr.mode = 0444;
- temp->temp_input.attr.show = temp_input_show;
- sysfs_attr_init(&temp->temp_input.attr.attr);
- result = device_create_file(hwmon->device, &temp->temp_input.attr);
- if (result)
- goto free_temp_mem;
- if (thermal_zone_crit_temp_valid(tz)) {
- snprintf(temp->temp_crit.name,
- sizeof(temp->temp_crit.name),
- "temp%d_crit", hwmon->count);
- temp->temp_crit.attr.attr.name = temp->temp_crit.name;
- temp->temp_crit.attr.attr.mode = 0444;
- temp->temp_crit.attr.show = temp_crit_show;
- sysfs_attr_init(&temp->temp_crit.attr.attr);
- result = device_create_file(hwmon->device,
- &temp->temp_crit.attr);
- if (result)
- goto unregister_input;
- }
- mutex_lock(&thermal_hwmon_list_lock);
- if (new_hwmon_device)
- list_add_tail(&hwmon->node, &thermal_hwmon_list);
- list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
- mutex_unlock(&thermal_hwmon_list_lock);
- return 0;
- unregister_input:
- device_remove_file(hwmon->device, &temp->temp_input.attr);
- free_temp_mem:
- kfree(temp);
- unregister_name:
- if (new_hwmon_device)
- hwmon_device_unregister(hwmon->device);
- free_mem:
- kfree(hwmon);
- return result;
- }
- EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
- void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
- {
- struct thermal_hwmon_device *hwmon;
- struct thermal_hwmon_temp *temp;
- hwmon = thermal_hwmon_lookup_by_type(tz);
- if (unlikely(!hwmon)) {
- /* Should never happen... */
- dev_dbg(&tz->device, "hwmon device lookup failed!\n");
- return;
- }
- temp = thermal_hwmon_lookup_temp(hwmon, tz);
- if (unlikely(!temp)) {
- /* Should never happen... */
- dev_dbg(&tz->device, "temperature input lookup failed!\n");
- return;
- }
- device_remove_file(hwmon->device, &temp->temp_input.attr);
- if (thermal_zone_crit_temp_valid(tz))
- device_remove_file(hwmon->device, &temp->temp_crit.attr);
- mutex_lock(&thermal_hwmon_list_lock);
- list_del(&temp->hwmon_node);
- kfree(temp);
- if (!list_empty(&hwmon->tz_list)) {
- mutex_unlock(&thermal_hwmon_list_lock);
- return;
- }
- list_del(&hwmon->node);
- mutex_unlock(&thermal_hwmon_list_lock);
- hwmon_device_unregister(hwmon->device);
- kfree(hwmon);
- }
- EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs);
- static void devm_thermal_hwmon_release(struct device *dev, void *res)
- {
- thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res);
- }
- int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
- {
- struct thermal_zone_device **ptr;
- int ret;
- ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr),
- GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
- ret = thermal_add_hwmon_sysfs(tz);
- if (ret) {
- devres_free(ptr);
- return ret;
- }
- *ptr = tz;
- devres_add(&tz->device, ptr);
- return ret;
- }
- EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);
- MODULE_IMPORT_NS(HWMON_THERMAL);
|