cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

thermal_hwmon.c (7181B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      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 Corp
      7 *  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 Instruments
     11 *  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
     21/* hwmon sys I/F */
     22/* thermal zone devices with the same type share one hwmon device */
     23struct thermal_hwmon_device {
     24	char type[THERMAL_NAME_LENGTH];
     25	struct device *device;
     26	int count;
     27	struct list_head tz_list;
     28	struct list_head node;
     29};
     30
     31struct thermal_hwmon_attr {
     32	struct device_attribute attr;
     33	char name[16];
     34};
     35
     36/* one temperature input for each thermal zone */
     37struct thermal_hwmon_temp {
     38	struct list_head hwmon_node;
     39	struct thermal_zone_device *tz;
     40	struct thermal_hwmon_attr temp_input;	/* hwmon sys attr */
     41	struct thermal_hwmon_attr temp_crit;	/* hwmon sys attr */
     42};
     43
     44static LIST_HEAD(thermal_hwmon_list);
     45
     46static DEFINE_MUTEX(thermal_hwmon_list_lock);
     47
     48static ssize_t
     49temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
     50{
     51	int temperature;
     52	int ret;
     53	struct thermal_hwmon_attr *hwmon_attr
     54			= container_of(attr, struct thermal_hwmon_attr, attr);
     55	struct thermal_hwmon_temp *temp
     56			= container_of(hwmon_attr, struct thermal_hwmon_temp,
     57				       temp_input);
     58	struct thermal_zone_device *tz = temp->tz;
     59
     60	ret = thermal_zone_get_temp(tz, &temperature);
     61
     62	if (ret)
     63		return ret;
     64
     65	return sprintf(buf, "%d\n", temperature);
     66}
     67
     68static ssize_t
     69temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
     70{
     71	struct thermal_hwmon_attr *hwmon_attr
     72			= container_of(attr, struct thermal_hwmon_attr, attr);
     73	struct thermal_hwmon_temp *temp
     74			= container_of(hwmon_attr, struct thermal_hwmon_temp,
     75				       temp_crit);
     76	struct thermal_zone_device *tz = temp->tz;
     77	int temperature;
     78	int ret;
     79
     80	ret = tz->ops->get_crit_temp(tz, &temperature);
     81	if (ret)
     82		return ret;
     83
     84	return sprintf(buf, "%d\n", temperature);
     85}
     86
     87
     88static struct thermal_hwmon_device *
     89thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
     90{
     91	struct thermal_hwmon_device *hwmon;
     92	char type[THERMAL_NAME_LENGTH];
     93
     94	mutex_lock(&thermal_hwmon_list_lock);
     95	list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
     96		strcpy(type, tz->type);
     97		strreplace(type, '-', '_');
     98		if (!strcmp(hwmon->type, type)) {
     99			mutex_unlock(&thermal_hwmon_list_lock);
    100			return hwmon;
    101		}
    102	}
    103	mutex_unlock(&thermal_hwmon_list_lock);
    104
    105	return NULL;
    106}
    107
    108/* Find the temperature input matching a given thermal zone */
    109static struct thermal_hwmon_temp *
    110thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
    111			  const struct thermal_zone_device *tz)
    112{
    113	struct thermal_hwmon_temp *temp;
    114
    115	mutex_lock(&thermal_hwmon_list_lock);
    116	list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
    117		if (temp->tz == tz) {
    118			mutex_unlock(&thermal_hwmon_list_lock);
    119			return temp;
    120		}
    121	mutex_unlock(&thermal_hwmon_list_lock);
    122
    123	return NULL;
    124}
    125
    126static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
    127{
    128	int temp;
    129	return tz->ops->get_crit_temp && !tz->ops->get_crit_temp(tz, &temp);
    130}
    131
    132int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
    133{
    134	struct thermal_hwmon_device *hwmon;
    135	struct thermal_hwmon_temp *temp;
    136	int new_hwmon_device = 1;
    137	int result;
    138
    139	hwmon = thermal_hwmon_lookup_by_type(tz);
    140	if (hwmon) {
    141		new_hwmon_device = 0;
    142		goto register_sys_interface;
    143	}
    144
    145	hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);
    146	if (!hwmon)
    147		return -ENOMEM;
    148
    149	INIT_LIST_HEAD(&hwmon->tz_list);
    150	strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
    151	strreplace(hwmon->type, '-', '_');
    152	hwmon->device = hwmon_device_register_for_thermal(&tz->device,
    153							  hwmon->type, hwmon);
    154	if (IS_ERR(hwmon->device)) {
    155		result = PTR_ERR(hwmon->device);
    156		goto free_mem;
    157	}
    158
    159 register_sys_interface:
    160	temp = kzalloc(sizeof(*temp), GFP_KERNEL);
    161	if (!temp) {
    162		result = -ENOMEM;
    163		goto unregister_name;
    164	}
    165
    166	temp->tz = tz;
    167	hwmon->count++;
    168
    169	snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
    170		 "temp%d_input", hwmon->count);
    171	temp->temp_input.attr.attr.name = temp->temp_input.name;
    172	temp->temp_input.attr.attr.mode = 0444;
    173	temp->temp_input.attr.show = temp_input_show;
    174	sysfs_attr_init(&temp->temp_input.attr.attr);
    175	result = device_create_file(hwmon->device, &temp->temp_input.attr);
    176	if (result)
    177		goto free_temp_mem;
    178
    179	if (thermal_zone_crit_temp_valid(tz)) {
    180		snprintf(temp->temp_crit.name,
    181				sizeof(temp->temp_crit.name),
    182				"temp%d_crit", hwmon->count);
    183		temp->temp_crit.attr.attr.name = temp->temp_crit.name;
    184		temp->temp_crit.attr.attr.mode = 0444;
    185		temp->temp_crit.attr.show = temp_crit_show;
    186		sysfs_attr_init(&temp->temp_crit.attr.attr);
    187		result = device_create_file(hwmon->device,
    188					    &temp->temp_crit.attr);
    189		if (result)
    190			goto unregister_input;
    191	}
    192
    193	mutex_lock(&thermal_hwmon_list_lock);
    194	if (new_hwmon_device)
    195		list_add_tail(&hwmon->node, &thermal_hwmon_list);
    196	list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
    197	mutex_unlock(&thermal_hwmon_list_lock);
    198
    199	return 0;
    200
    201 unregister_input:
    202	device_remove_file(hwmon->device, &temp->temp_input.attr);
    203 free_temp_mem:
    204	kfree(temp);
    205 unregister_name:
    206	if (new_hwmon_device)
    207		hwmon_device_unregister(hwmon->device);
    208 free_mem:
    209	kfree(hwmon);
    210
    211	return result;
    212}
    213EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
    214
    215void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
    216{
    217	struct thermal_hwmon_device *hwmon;
    218	struct thermal_hwmon_temp *temp;
    219
    220	hwmon = thermal_hwmon_lookup_by_type(tz);
    221	if (unlikely(!hwmon)) {
    222		/* Should never happen... */
    223		dev_dbg(&tz->device, "hwmon device lookup failed!\n");
    224		return;
    225	}
    226
    227	temp = thermal_hwmon_lookup_temp(hwmon, tz);
    228	if (unlikely(!temp)) {
    229		/* Should never happen... */
    230		dev_dbg(&tz->device, "temperature input lookup failed!\n");
    231		return;
    232	}
    233
    234	device_remove_file(hwmon->device, &temp->temp_input.attr);
    235	if (thermal_zone_crit_temp_valid(tz))
    236		device_remove_file(hwmon->device, &temp->temp_crit.attr);
    237
    238	mutex_lock(&thermal_hwmon_list_lock);
    239	list_del(&temp->hwmon_node);
    240	kfree(temp);
    241	if (!list_empty(&hwmon->tz_list)) {
    242		mutex_unlock(&thermal_hwmon_list_lock);
    243		return;
    244	}
    245	list_del(&hwmon->node);
    246	mutex_unlock(&thermal_hwmon_list_lock);
    247
    248	hwmon_device_unregister(hwmon->device);
    249	kfree(hwmon);
    250}
    251EXPORT_SYMBOL_GPL(thermal_remove_hwmon_sysfs);
    252
    253static void devm_thermal_hwmon_release(struct device *dev, void *res)
    254{
    255	thermal_remove_hwmon_sysfs(*(struct thermal_zone_device **)res);
    256}
    257
    258int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
    259{
    260	struct thermal_zone_device **ptr;
    261	int ret;
    262
    263	ptr = devres_alloc(devm_thermal_hwmon_release, sizeof(*ptr),
    264			   GFP_KERNEL);
    265	if (!ptr)
    266		return -ENOMEM;
    267
    268	ret = thermal_add_hwmon_sysfs(tz);
    269	if (ret) {
    270		devres_free(ptr);
    271		return ret;
    272	}
    273
    274	*ptr = tz;
    275	devres_add(&tz->device, ptr);
    276
    277	return ret;
    278}
    279EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);
    280
    281MODULE_IMPORT_NS(HWMON_THERMAL);