Commit c3bd6d53 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'thermal-core'

Merge thermal control core changes for 6.3-rc1:

 - Clean up thermal device unregistration code (Viresh Kumar).

 - Fix and clean up thermal control core initialization error code
   paths (Daniel Lezcano).

 - Relocate the trip points handling code into a separate file (Daniel
   Lezcano).

 - Make the thermal core fail registration of thermal zones and cooling
   devices if the thermal class has not been registered (Rafael Wysocki).

 - Make the core thermal control code use sysfs_emit_at() instead of
   scnprintf() where applicable (ye xingchen).

* thermal-core:
  thermal: core: Use sysfs_emit_at() instead of scnprintf()
  thermal: Fail object registration if thermal class is not registered
  thermal/core: Move the thermal trip code to a dedicated file
  thermal/core: Remove unneeded ida_destroy()
  thermal/core: Fix unregistering netlink at thermal init time
  thermal: core: Use device_unregister() instead of device_del/put()
  thermal: core: Move cdev cleanup to thermal_release()
parents f364beb5 5bbafd43
......@@ -4,8 +4,8 @@
#
obj-$(CONFIG_THERMAL) += thermal_sys.o
thermal_sys-y += thermal_core.o thermal_sysfs.o \
thermal_helpers.o
thermal_sys-y += thermal_core.o thermal_sysfs.o
thermal_sys-y += thermal_trip.o thermal_helpers.o
# netlink interface to manage the thermal framework
thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
......
......@@ -229,10 +229,9 @@ int thermal_build_list_of_policies(char *buf)
mutex_lock(&thermal_governor_lock);
list_for_each_entry(pos, &thermal_governor_list, governor_list) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%s ",
pos->name);
count += sysfs_emit_at(buf, count, "%s ", pos->name);
}
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
count += sysfs_emit_at(buf, count, "\n");
mutex_unlock(&thermal_governor_lock);
......@@ -770,14 +769,14 @@ static void thermal_release(struct device *dev)
} else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) {
cdev = to_cooling_device(dev);
thermal_cooling_device_destroy_sysfs(cdev);
kfree(cdev->type);
ida_free(&thermal_cdev_ida, cdev->id);
kfree(cdev);
}
}
static struct class thermal_class = {
.name = "thermal",
.dev_release = thermal_release,
};
static struct class *thermal_class;
static inline
void print_bind_err_msg(struct thermal_zone_device *tz,
......@@ -880,6 +879,9 @@ __thermal_cooling_device_register(struct device_node *np,
!ops->set_cur_state)
return ERR_PTR(-EINVAL);
if (!thermal_class)
return ERR_PTR(-ENODEV);
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return ERR_PTR(-ENOMEM);
......@@ -901,27 +903,25 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->np = np;
cdev->ops = ops;
cdev->updated = false;
cdev->device.class = &thermal_class;
cdev->device.class = thermal_class;
cdev->devdata = devdata;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
if (ret) {
kfree(cdev->type);
goto out_ida_remove;
}
if (ret)
goto out_cdev_type;
thermal_cooling_device_setup_sysfs(cdev);
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
if (ret) {
kfree(cdev->type);
thermal_cooling_device_destroy_sysfs(cdev);
goto out_ida_remove;
}
if (ret)
goto out_cooling_dev;
ret = device_register(&cdev->device);
if (ret)
goto out_kfree_type;
if (ret) {
/* thermal_release() handles rest of the cleanup */
put_device(&cdev->device);
return ERR_PTR(ret);
}
/* Add 'this' new cdev to the global cdev list */
mutex_lock(&thermal_list_lock);
......@@ -940,13 +940,10 @@ __thermal_cooling_device_register(struct device_node *np,
return cdev;
out_kfree_type:
out_cooling_dev:
thermal_cooling_device_destroy_sysfs(cdev);
out_cdev_type:
kfree(cdev->type);
put_device(&cdev->device);
/* thermal_release() takes care of the rest */
cdev = NULL;
out_ida_remove:
ida_free(&thermal_cdev_ida, id);
out_kfree_cdev:
......@@ -1107,11 +1104,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
mutex_unlock(&thermal_list_lock);
ida_free(&thermal_cdev_ida, cdev->id);
device_del(&cdev->device);
thermal_cooling_device_destroy_sysfs(cdev);
kfree(cdev->type);
put_device(&cdev->device);
device_unregister(&cdev->device);
}
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
......@@ -1162,12 +1155,6 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
*delay_jiffies = round_jiffies(*delay_jiffies);
}
int thermal_zone_get_num_trips(struct thermal_zone_device *tz)
{
return tz->num_trips;
}
EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
{
int i, ret = -EINVAL;
......@@ -1194,87 +1181,6 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
}
EXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp);
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
int ret;
if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL;
if (tz->trips) {
*trip = tz->trips[trip_id];
return 0;
}
if (tz->ops->get_trip_hyst) {
ret = tz->ops->get_trip_hyst(tz, trip_id, &trip->hysteresis);
if (ret)
return ret;
} else {
trip->hysteresis = 0;
}
ret = tz->ops->get_trip_temp(tz, trip_id, &trip->temperature);
if (ret)
return ret;
return tz->ops->get_trip_type(tz, trip_id, &trip->type);
}
EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
int ret;
mutex_lock(&tz->lock);
ret = __thermal_zone_get_trip(tz, trip_id, trip);
mutex_unlock(&tz->lock);
return ret;
}
EXPORT_SYMBOL_GPL(thermal_zone_get_trip);
int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id,
const struct thermal_trip *trip)
{
struct thermal_trip t;
int ret;
if (!tz->ops->set_trip_temp && !tz->ops->set_trip_hyst && !tz->trips)
return -EINVAL;
ret = __thermal_zone_get_trip(tz, trip_id, &t);
if (ret)
return ret;
if (t.type != trip->type)
return -EINVAL;
if (t.temperature != trip->temperature && tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip_id, trip->temperature);
if (ret)
return ret;
}
if (t.hysteresis != trip->hysteresis && tz->ops->set_trip_hyst) {
ret = tz->ops->set_trip_hyst(tz, trip_id, trip->hysteresis);
if (ret)
return ret;
}
if (tz->trips && (t.temperature != trip->temperature || t.hysteresis != trip->hysteresis))
tz->trips[trip_id] = *trip;
thermal_notify_tz_trip_change(tz->id, trip_id, trip->type,
trip->temperature, trip->hysteresis);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
return 0;
}
/**
* thermal_zone_device_register_with_trips() - register a new thermal zone device
* @type: the thermal zone device type
......@@ -1349,6 +1255,9 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
if (num_trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp) && !trips)
return ERR_PTR(-EINVAL);
if (!thermal_class)
return ERR_PTR(-ENODEV);
tz = kzalloc(sizeof(*tz), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);
......@@ -1370,7 +1279,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
tz->ops = ops;
tz->tzp = tzp;
tz->device.class = &thermal_class;
tz->device.class = thermal_class;
tz->devdata = devdata;
tz->trips = trips;
tz->num_trips = num_trips;
......@@ -1613,11 +1522,23 @@ static int __init thermal_init(void)
result = thermal_register_governors();
if (result)
goto error;
goto unregister_netlink;
result = class_register(&thermal_class);
if (result)
thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL);
if (!thermal_class) {
result = -ENOMEM;
goto unregister_governors;
}
thermal_class->name = "thermal";
thermal_class->dev_release = thermal_release;
result = class_register(thermal_class);
if (result) {
kfree(thermal_class);
thermal_class = NULL;
goto unregister_governors;
}
result = register_pm_notifier(&thermal_pm_nb);
if (result)
......@@ -1628,9 +1549,9 @@ static int __init thermal_init(void)
unregister_governors:
thermal_unregister_governors();
unregister_netlink:
thermal_netlink_exit();
error:
ida_destroy(&thermal_tz_ida);
ida_destroy(&thermal_cdev_ida);
mutex_destroy(&thermal_list_lock);
mutex_destroy(&thermal_governor_lock);
return result;
......
......@@ -52,6 +52,10 @@ int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *,
int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
void *thermal_governor);
int __for_each_thermal_trip(struct thermal_zone_device *,
int (*cb)(struct thermal_trip *, void *),
void *);
struct thermal_zone_device *thermal_zone_get_by_id(int id);
struct thermal_attr {
......
......@@ -146,68 +146,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
}
EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
/**
* __thermal_zone_set_trips - Computes the next trip points for the driver
* @tz: a pointer to a thermal zone device structure
*
* The function computes the next temperature boundaries by browsing
* the trip points. The result is the closer low and high trip points
* to the current temperature. These values are passed to the backend
* driver to let it set its own notification mechanism (usually an
* interrupt).
*
* This function must be called with tz->lock held. Both tz and tz->ops
* must be valid pointers.
*
* It does not return a value
*/
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
{
struct thermal_trip trip;
int low = -INT_MAX, high = INT_MAX;
int i, ret;
lockdep_assert_held(&tz->lock);
if (!tz->ops->set_trips)
return;
for (i = 0; i < tz->num_trips; i++) {
int trip_low;
ret = __thermal_zone_get_trip(tz, i , &trip);
if (ret)
return;
trip_low = trip.temperature - trip.hysteresis;
if (trip_low < tz->temperature && trip_low > low)
low = trip_low;
if (trip.temperature > tz->temperature &&
trip.temperature < high)
high = trip.temperature;
}
/* No need to change trip points */
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
return;
tz->prev_low_trip = low;
tz->prev_high_trip = high;
dev_dbg(&tz->device,
"new temperature boundaries: %d < x < %d\n", low, high);
/*
* Set a temperature window. When this window is left the driver
* must inform the thermal core via thermal_zone_device_update.
*/
ret = tz->ops->set_trips(tz, low, high);
if (ret)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
}
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
int target)
{
......
......@@ -699,3 +699,8 @@ int __init thermal_netlink_init(void)
{
return genl_register_family(&thermal_gnl_family);
}
void __init thermal_netlink_exit(void)
{
genl_unregister_family(&thermal_gnl_family);
}
......@@ -13,6 +13,7 @@ struct thermal_genl_cpu_caps {
/* Netlink notification function */
#ifdef CONFIG_THERMAL_NETLINK
int __init thermal_netlink_init(void);
void __init thermal_netlink_exit(void);
int thermal_notify_tz_create(int tz_id, const char *name);
int thermal_notify_tz_delete(int tz_id);
int thermal_notify_tz_enable(int tz_id);
......@@ -115,4 +116,6 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge
return 0;
}
static inline void __init thermal_netlink_exit(void) {}
#endif /* CONFIG_THERMAL_NETLINK */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2008 Intel Corp
* Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
* Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
* Copyright 2022 Linaro Limited
*
* Thermal trips handling
*/
#include "thermal_core.h"
int __for_each_thermal_trip(struct thermal_zone_device *tz,
int (*cb)(struct thermal_trip *, void *),
void *data)
{
int i, ret;
struct thermal_trip trip;
lockdep_assert_held(&tz->lock);
for (i = 0; i < tz->num_trips; i++) {
ret = __thermal_zone_get_trip(tz, i, &trip);
if (ret)
return ret;
ret = cb(&trip, data);
if (ret)
return ret;
}
return 0;
}
int thermal_zone_get_num_trips(struct thermal_zone_device *tz)
{
return tz->num_trips;
}
EXPORT_SYMBOL_GPL(thermal_zone_get_num_trips);
/**
* __thermal_zone_set_trips - Computes the next trip points for the driver
* @tz: a pointer to a thermal zone device structure
*
* The function computes the next temperature boundaries by browsing
* the trip points. The result is the closer low and high trip points
* to the current temperature. These values are passed to the backend
* driver to let it set its own notification mechanism (usually an
* interrupt).
*
* This function must be called with tz->lock held. Both tz and tz->ops
* must be valid pointers.
*
* It does not return a value
*/
void __thermal_zone_set_trips(struct thermal_zone_device *tz)
{
struct thermal_trip trip;
int low = -INT_MAX, high = INT_MAX;
int i, ret;
lockdep_assert_held(&tz->lock);
if (!tz->ops->set_trips)
return;
for (i = 0; i < tz->num_trips; i++) {
int trip_low;
ret = __thermal_zone_get_trip(tz, i , &trip);
if (ret)
return;
trip_low = trip.temperature - trip.hysteresis;
if (trip_low < tz->temperature && trip_low > low)
low = trip_low;
if (trip.temperature > tz->temperature &&
trip.temperature < high)
high = trip.temperature;
}
/* No need to change trip points */
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
return;
tz->prev_low_trip = low;
tz->prev_high_trip = high;
dev_dbg(&tz->device,
"new temperature boundaries: %d < x < %d\n", low, high);
/*
* Set a temperature window. When this window is left the driver
* must inform the thermal core via thermal_zone_device_update.
*/
ret = tz->ops->set_trips(tz, low, high);
if (ret)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
}
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
int ret;
if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip)
return -EINVAL;
if (tz->trips) {
*trip = tz->trips[trip_id];
return 0;
}
if (tz->ops->get_trip_hyst) {
ret = tz->ops->get_trip_hyst(tz, trip_id, &trip->hysteresis);
if (ret)
return ret;
} else {
trip->hysteresis = 0;
}
ret = tz->ops->get_trip_temp(tz, trip_id, &trip->temperature);
if (ret)
return ret;
return tz->ops->get_trip_type(tz, trip_id, &trip->type);
}
EXPORT_SYMBOL_GPL(__thermal_zone_get_trip);
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip)
{
int ret;
mutex_lock(&tz->lock);
ret = __thermal_zone_get_trip(tz, trip_id, trip);
mutex_unlock(&tz->lock);
return ret;
}
EXPORT_SYMBOL_GPL(thermal_zone_get_trip);
int thermal_zone_set_trip(struct thermal_zone_device *tz, int trip_id,
const struct thermal_trip *trip)
{
struct thermal_trip t;
int ret;
if (!tz->ops->set_trip_temp && !tz->ops->set_trip_hyst && !tz->trips)
return -EINVAL;
ret = __thermal_zone_get_trip(tz, trip_id, &t);
if (ret)
return ret;
if (t.type != trip->type)
return -EINVAL;
if (t.temperature != trip->temperature && tz->ops->set_trip_temp) {
ret = tz->ops->set_trip_temp(tz, trip_id, trip->temperature);
if (ret)
return ret;
}
if (t.hysteresis != trip->hysteresis && tz->ops->set_trip_hyst) {
ret = tz->ops->set_trip_hyst(tz, trip_id, trip->hysteresis);
if (ret)
return ret;
}
if (tz->trips && (t.temperature != trip->temperature || t.hysteresis != trip->hysteresis))
tz->trips[trip_id] = *trip;
thermal_notify_tz_trip_change(tz->id, trip_id, trip->type,
trip->temperature, trip->hysteresis);
__thermal_zone_device_update(tz, THERMAL_TRIP_CHANGED);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment