// SPDX-License-Identifier: GPL-2.0
/*
* thermal.c - sysfs interface of 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/container_of.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/jiffies.h>
#include "thermal_core.h"
/* sys I/F for thermal zone */
static ssize_t
type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
return sprintf(buf, "%s\n", tz->type);
}
static ssize_t
temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int temperature, ret;
ret = thermal_zone_get_temp(tz, &temperature);
if (!ret)
return sprintf(buf, "%d\n", temperature);
if (ret == -EAGAIN)
return -ENODATA;
return ret;
}
static ssize_t
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
guard(thermal_zone)(tz);
if (tz->mode == THERMAL_DEVICE_ENABLED)
return sprintf(buf, "enabled\n");
return sprintf(buf, "disabled\n");
}
static ssize_t
mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int result;
if (!strncmp(buf, "enabled", sizeof("enabled") - 1))
result = thermal_zone_device_enable(tz);
else if (!strncmp(buf, "disabled", sizeof("disabled") - 1))
result = thermal_zone_device_disable(tz);
else
result = -EINVAL;
if (result)
return result;
return count;
}
#define thermal_trip_of_attr(_ptr_, _attr_) \
({ \
struct thermal_trip_desc *td; \
\
td = container_of(_ptr_, struct thermal_trip_desc, \
trip_attrs._attr_.attr); \
&td->trip; \
})
static ssize_t
trip_point_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, type);
return sprintf(buf, "%s\n", thermal_trip_type_name(trip->type));
}
static ssize_t
trip_point_temp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
struct thermal_zone_device *tz = to_thermal_zone(dev);
int temp;
if (kstrtoint(buf, 10, &temp))
return -EINVAL;
guard(thermal_zone)(tz);
if (temp == trip->temperature)
return count;
/* Arrange the condition to avoid integer overflows. */
if (temp != THERMAL_TEMP_INVALID &&
temp <= trip->hysteresis + THERMAL_TEMP_INVALID)
return -EINVAL;
if (tz->ops.set_trip_temp) {
int ret;
ret = tz->ops.set_trip_temp(tz, trip, temp);
if (ret)
return ret;
}
thermal_zone_set_trip_temp(tz, trip, temp);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
return count;
}
static ssize_t
trip_point_temp_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, temp);
return sprintf(buf, "%d\n", READ_ONCE(trip->temperature));
}
static ssize_t
trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_trip *trip = thermal_trip_of_attr(attr, hyst);
struct thermal_zone_device *tz = to_thermal_zone(dev);
int hyst;
if (kstrtoint(buf, 10, &hyst) || hyst < 0)
return -EINVAL;
guard(thermal_zone)(tz);
if (hyst == trip->hysteresis)
return count;
/*
* Allow the hysteresis to be updated when the temperature is invalid
* to allow user space to avoid having to adjust hysteresis after a
* valid temperature has been set, but in that case just change the
* value and do nothing else.
*/
if (trip->temperature == THERMAL_TEMP_INVALID) {
WRITE_ONCE(trip->hysteresis, hyst);
return count