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 @@ ...@@ -4,8 +4,8 @@
# #
obj-$(CONFIG_THERMAL) += thermal_sys.o obj-$(CONFIG_THERMAL) += thermal_sys.o
thermal_sys-y += thermal_core.o thermal_sysfs.o \ thermal_sys-y += thermal_core.o thermal_sysfs.o
thermal_helpers.o thermal_sys-y += thermal_trip.o thermal_helpers.o
# netlink interface to manage the thermal framework # netlink interface to manage the thermal framework
thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
......
...@@ -229,10 +229,9 @@ int thermal_build_list_of_policies(char *buf) ...@@ -229,10 +229,9 @@ int thermal_build_list_of_policies(char *buf)
mutex_lock(&thermal_governor_lock); mutex_lock(&thermal_governor_lock);
list_for_each_entry(pos, &thermal_governor_list, governor_list) { list_for_each_entry(pos, &thermal_governor_list, governor_list) {
count += scnprintf(buf + count, PAGE_SIZE - count, "%s ", count += sysfs_emit_at(buf, count, "%s ", pos->name);
pos->name);
} }
count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); count += sysfs_emit_at(buf, count, "\n");
mutex_unlock(&thermal_governor_lock); mutex_unlock(&thermal_governor_lock);
...@@ -770,14 +769,14 @@ static void thermal_release(struct device *dev) ...@@ -770,14 +769,14 @@ static void thermal_release(struct device *dev)
} else if (!strncmp(dev_name(dev), "cooling_device", } else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) { sizeof("cooling_device") - 1)) {
cdev = to_cooling_device(dev); cdev = to_cooling_device(dev);
thermal_cooling_device_destroy_sysfs(cdev);
kfree(cdev->type);
ida_free(&thermal_cdev_ida, cdev->id);
kfree(cdev); kfree(cdev);
} }
} }
static struct class thermal_class = { static struct class *thermal_class;
.name = "thermal",
.dev_release = thermal_release,
};
static inline static inline
void print_bind_err_msg(struct thermal_zone_device *tz, void print_bind_err_msg(struct thermal_zone_device *tz,
...@@ -880,6 +879,9 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -880,6 +879,9 @@ __thermal_cooling_device_register(struct device_node *np,
!ops->set_cur_state) !ops->set_cur_state)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (!thermal_class)
return ERR_PTR(-ENODEV);
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev) if (!cdev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -901,27 +903,25 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -901,27 +903,25 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->np = np; cdev->np = np;
cdev->ops = ops; cdev->ops = ops;
cdev->updated = false; cdev->updated = false;
cdev->device.class = &thermal_class; cdev->device.class = thermal_class;
cdev->devdata = devdata; cdev->devdata = devdata;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state); ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
if (ret) { if (ret)
kfree(cdev->type); goto out_cdev_type;
goto out_ida_remove;
}
thermal_cooling_device_setup_sysfs(cdev); thermal_cooling_device_setup_sysfs(cdev);
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
if (ret) { if (ret)
kfree(cdev->type); goto out_cooling_dev;
thermal_cooling_device_destroy_sysfs(cdev);
goto out_ida_remove;
}
ret = device_register(&cdev->device); ret = device_register(&cdev->device);
if (ret) if (ret) {
goto out_kfree_type; /* thermal_release() handles rest of the cleanup */
put_device(&cdev->device);
return ERR_PTR(ret);
}
/* Add 'this' new cdev to the global cdev list */ /* Add 'this' new cdev to the global cdev list */
mutex_lock(&thermal_list_lock); mutex_lock(&thermal_list_lock);
...@@ -940,13 +940,10 @@ __thermal_cooling_device_register(struct device_node *np, ...@@ -940,13 +940,10 @@ __thermal_cooling_device_register(struct device_node *np,
return cdev; return cdev;
out_kfree_type: out_cooling_dev:
thermal_cooling_device_destroy_sysfs(cdev); thermal_cooling_device_destroy_sysfs(cdev);
out_cdev_type:
kfree(cdev->type); kfree(cdev->type);
put_device(&cdev->device);
/* thermal_release() takes care of the rest */
cdev = NULL;
out_ida_remove: out_ida_remove:
ida_free(&thermal_cdev_ida, id); ida_free(&thermal_cdev_ida, id);
out_kfree_cdev: out_kfree_cdev:
...@@ -1107,11 +1104,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) ...@@ -1107,11 +1104,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
mutex_unlock(&thermal_list_lock); mutex_unlock(&thermal_list_lock);
ida_free(&thermal_cdev_ida, cdev->id); device_unregister(&cdev->device);
device_del(&cdev->device);
thermal_cooling_device_destroy_sysfs(cdev);
kfree(cdev->type);
put_device(&cdev->device);
} }
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
...@@ -1162,12 +1155,6 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms ...@@ -1162,12 +1155,6 @@ static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms
*delay_jiffies = round_jiffies(*delay_jiffies); *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 thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp)
{ {
int i, ret = -EINVAL; int i, ret = -EINVAL;
...@@ -1194,87 +1181,6 @@ int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) ...@@ -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); 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 * thermal_zone_device_register_with_trips() - register a new thermal zone device
* @type: the thermal zone device type * @type: the thermal zone device type
...@@ -1349,6 +1255,9 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t ...@@ -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) if (num_trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp) && !trips)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (!thermal_class)
return ERR_PTR(-ENODEV);
tz = kzalloc(sizeof(*tz), GFP_KERNEL); tz = kzalloc(sizeof(*tz), GFP_KERNEL);
if (!tz) if (!tz)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -1370,7 +1279,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t ...@@ -1370,7 +1279,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
tz->ops = ops; tz->ops = ops;
tz->tzp = tzp; tz->tzp = tzp;
tz->device.class = &thermal_class; tz->device.class = thermal_class;
tz->devdata = devdata; tz->devdata = devdata;
tz->trips = trips; tz->trips = trips;
tz->num_trips = num_trips; tz->num_trips = num_trips;
...@@ -1613,11 +1522,23 @@ static int __init thermal_init(void) ...@@ -1613,11 +1522,23 @@ static int __init thermal_init(void)
result = thermal_register_governors(); result = thermal_register_governors();
if (result) if (result)
goto error; goto unregister_netlink;
result = class_register(&thermal_class); thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL);
if (result) if (!thermal_class) {
result = -ENOMEM;
goto unregister_governors; 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); result = register_pm_notifier(&thermal_pm_nb);
if (result) if (result)
...@@ -1628,9 +1549,9 @@ static int __init thermal_init(void) ...@@ -1628,9 +1549,9 @@ static int __init thermal_init(void)
unregister_governors: unregister_governors:
thermal_unregister_governors(); thermal_unregister_governors();
unregister_netlink:
thermal_netlink_exit();
error: error:
ida_destroy(&thermal_tz_ida);
ida_destroy(&thermal_cdev_ida);
mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_list_lock);
mutex_destroy(&thermal_governor_lock); mutex_destroy(&thermal_governor_lock);
return result; return result;
......
...@@ -52,6 +52,10 @@ int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, ...@@ -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 *), int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *),
void *thermal_governor); 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_zone_device *thermal_zone_get_by_id(int id);
struct thermal_attr { struct thermal_attr {
......
...@@ -146,68 +146,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) ...@@ -146,68 +146,6 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
} }
EXPORT_SYMBOL_GPL(thermal_zone_get_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, static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
int target) int target)
{ {
......
...@@ -699,3 +699,8 @@ int __init thermal_netlink_init(void) ...@@ -699,3 +699,8 @@ int __init thermal_netlink_init(void)
{ {
return genl_register_family(&thermal_gnl_family); 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 { ...@@ -13,6 +13,7 @@ struct thermal_genl_cpu_caps {
/* Netlink notification function */ /* Netlink notification function */
#ifdef CONFIG_THERMAL_NETLINK #ifdef CONFIG_THERMAL_NETLINK
int __init thermal_netlink_init(void); 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_create(int tz_id, const char *name);
int thermal_notify_tz_delete(int tz_id); int thermal_notify_tz_delete(int tz_id);
int thermal_notify_tz_enable(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 ...@@ -115,4 +116,6 @@ static inline int thermal_genl_cpu_capability_event(int count, struct thermal_ge
return 0; return 0;
} }
static inline void __init thermal_netlink_exit(void) {}
#endif /* CONFIG_THERMAL_NETLINK */ #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