brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · f0e504f Raw
291 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  thermal_hwmon.c - Generic Thermal Management hwmon support.4 *5 *  Code based on Intel thermal_core.c. Copyrights of the original code:6 *  Copyright (C) 2008 Intel Corp7 *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>8 *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>9 *10 *  Copyright (C) 2013 Texas Instruments11 *  Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>12 */13#include <linux/err.h>14#include <linux/export.h>15#include <linux/hwmon.h>16#include <linux/slab.h>17#include <linux/thermal.h>18 19#include "thermal_hwmon.h"20#include "thermal_core.h"21 22/* hwmon sys I/F */23/* thermal zone devices with the same type share one hwmon device */24struct thermal_hwmon_device {25	char type[THERMAL_NAME_LENGTH];26	struct device *device;27	int count;28	struct list_head tz_list;29	struct list_head node;30};31 32struct thermal_hwmon_attr {33	struct device_attribute attr;34	char name[16];35};36 37/* one temperature input for each thermal zone */38struct thermal_hwmon_temp {39	struct list_head hwmon_node;40	struct thermal_zone_device *tz;41	struct thermal_hwmon_attr temp_input;	/* hwmon sys attr */42	struct thermal_hwmon_attr temp_crit;	/* hwmon sys attr */43};44 45static LIST_HEAD(thermal_hwmon_list);46 47static DEFINE_MUTEX(thermal_hwmon_list_lock);48 49static ssize_t50temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)51{52	int temperature;53	int ret;54	struct thermal_hwmon_attr *hwmon_attr55			= container_of(attr, struct thermal_hwmon_attr, attr);56	struct thermal_hwmon_temp *temp57			= container_of(hwmon_attr, struct thermal_hwmon_temp,58				       temp_input);59	struct thermal_zone_device *tz = temp->tz;60 61	ret = thermal_zone_get_temp(tz, &temperature);62 63	if (ret)64		return ret;65 66	return sprintf(buf, "%d\n", temperature);67}68 69static ssize_t70temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)71{72	struct thermal_hwmon_attr *hwmon_attr73			= container_of(attr, struct thermal_hwmon_attr, attr);74	struct thermal_hwmon_temp *temp75			= container_of(hwmon_attr, struct thermal_hwmon_temp,76				       temp_crit);77	struct thermal_zone_device *tz = temp->tz;78	int temperature;79	int ret;80 81	mutex_lock(&tz->lock);82 83	ret = tz->ops.get_crit_temp(tz, &temperature);84 85	mutex_unlock(&tz->lock);86 87	if (ret)88		return ret;89 90	return sprintf(buf, "%d\n", temperature);91}92 93 94static struct thermal_hwmon_device *95thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)96{97	struct thermal_hwmon_device *hwmon;98	char type[THERMAL_NAME_LENGTH];99 100	mutex_lock(&thermal_hwmon_list_lock);101	list_for_each_entry(hwmon, &thermal_hwmon_list, node) {102		strcpy(type, tz->type);103		strreplace(type, '-', '_');104		if (!strcmp(hwmon->type, type)) {105			mutex_unlock(&thermal_hwmon_list_lock);106			return hwmon;107		}108	}109	mutex_unlock(&thermal_hwmon_list_lock);110 111	return NULL;112}113 114/* Find the temperature input matching a given thermal zone */115static struct thermal_hwmon_temp *116thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,117			  const struct thermal_zone_device *tz)118{119	struct thermal_hwmon_temp *temp;120 121	mutex_lock(&thermal_hwmon_list_lock);122	list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)123		if (temp->tz == tz) {124			mutex_unlock(&thermal_hwmon_list_lock);125			return temp;126		}127	mutex_unlock(&thermal_hwmon_list_lock);128 129	return NULL;130}131 132static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)133{134	int temp;135	return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp);136}137 138int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)139{140	struct thermal_hwmon_device *hwmon;141	struct thermal_hwmon_temp *temp;142	int new_hwmon_device = 1;143	int result;144 145	hwmon = thermal_hwmon_lookup_by_type(tz);146	if (hwmon) {147		new_hwmon_device = 0;148		goto register_sys_interface;149	}150 151	hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);152	if (!hwmon)153		return -ENOMEM;154 155	INIT_LIST_HEAD(&hwmon->tz_list);156	strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);157	strreplace(hwmon->type, '-', '_');158	hwmon->device = hwmon_device_register_for_thermal(&tz->device,159							  hwmon->type, hwmon);160	if (IS_ERR(hwmon->device)) {161		result = PTR_ERR(hwmon->device);162		goto free_mem;163	}164 165 register_sys_interface:166	temp = kzalloc(sizeof(*temp), GFP_KERNEL);167	if (!temp) {168		result = -ENOMEM;169		goto unregister_name;170	}171 172	temp->tz = tz;173	hwmon->count++;174 175	snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),176		 "temp%d_input", hwmon->count);177	temp->temp_input.attr.attr.name = temp->temp_input.name;178	temp->temp_input.attr.attr.mode = 0444;179	temp->temp_input.attr.show = temp_input_show;180	sysfs_attr_init(&temp->temp_input.attr.attr);181	result = device_create_file(hwmon->device, &temp->temp_input.attr);182	if (result)183		goto free_temp_mem;184 185	if (thermal_zone_crit_temp_valid(tz)) {186		snprintf(temp->temp_crit.name,187				sizeof(temp->temp_crit.name),188				"temp%d_crit", hwmon->count);189		temp->temp_crit.attr.attr.name = temp->temp_crit.name;190		temp->temp_crit.attr.attr.mode = 0444;191		temp->temp_crit.attr.show = temp_crit_show;192		sysfs_attr_init(&temp->temp_crit.attr.attr);193		result = device_create_file(hwmon->device,194					    &temp->temp_crit.attr);195		if (result)196			goto unregister_input;197	}198 199	mutex_lock(&thermal_hwmon_list_lock);200	if (new_hwmon_device)201		list_add_tail(&hwmon->node, &thermal_hwmon_list);202	list_add_tail(&temp->hwmon_node, &hwmon->tz_list);203	mutex_unlock(&thermal_hwmon_list_lock);204 205	return 0;206 207 unregister_input:208	device_remove_file(hwmon->device, &temp->temp_input.attr);209 free_temp_mem:210	kfree(temp);211 unregister_name:212	if (new_hwmon_device)213		hwmon_device_unregister(hwmon->device);214 free_mem:215	kfree(hwmon);216 217	return result;218}219EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);220 221void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)222{223	struct thermal_hwmon_device *hwmon;224	struct thermal_hwmon_temp *temp;225 226	hwmon = thermal_hwmon_lookup_by_type(tz);227	if (unlikely(!hwmon)) {228		/* Should never happen... */229		dev_dbg(&tz->device, "hwmon device lookup failed!\n");230		return;231	}232 233	temp = thermal_hwmon_lookup_temp(hwmon, tz);234	if (unlikely(!temp)) {235		/* Should never happen... */236		dev_dbg(&tz->device, "temperature input lookup failed!\n");237		return;238	}239 240	device_remove_file(hwmon->device, &temp->temp_input.attr);241	if (thermal_zone_crit_temp_valid(tz))242		device_remove_file(hwmon->device, &temp->temp_crit.attr);243 244	mutex_lock(&thermal_hwmon_list_lock);245	list_del(&temp->hwmon_node);246	kfree(temp);247	if (!list_empty(&hwmon->tz_list)) {248		mutex_unlock(&thermal_hwmon_list_lock);249		return;250	}251	list_del(&hwmon->node);252	mutex_unlock(&thermal_hwmon_list_lock);253 254	hwmon_device_unregister(hwmon->device);255	kfree(hwmon);256}257EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs);258 259static void devm_thermal_hwmon_release(struct device *dev, void *res)260{261	thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res);262}263 264int devm_thermal_add_hwmon_sysfs(struct device *dev, struct thermal_zone_device *tz)265{266	struct thermal_zone_device **ptr;267	int ret;268 269	ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr),270			   GFP_KERNEL);271	if (!ptr) {272		dev_warn(dev, "Failed to allocate device resource data\n");273		return -ENOMEM;274	}275 276	ret = thermal_add_hwmon_sysfs(tz);277	if (ret) {278		dev_warn(dev, "Failed to add hwmon sysfs attributes\n");279		devres_free(ptr);280		return ret;281	}282 283	*ptr = tz;284	devres_add(dev, ptr);285 286	return ret;287}288EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);289 290MODULE_IMPORT_NS(HWMON_THERMAL);291