diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2023-02-15 19:08:30 +0300 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2023-02-15 19:08:30 +0300 |
commit | c3bd6d539f34784b235c7070edba978f67516372 (patch) | |
tree | 52330b48f668dfbe33711756fe9cb57622d48401 /drivers/thermal | |
parent | f364beb5b6734cefbfa01a79aacdf6c086082e6a (diff) | |
parent | 5bbafd436266136a67224b98e6ad864c4ddd762d (diff) | |
download | linux-c3bd6d539f34784b235c7070edba978f67516372.tar.xz |
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()
Diffstat (limited to 'drivers/thermal')
-rw-r--r-- | drivers/thermal/Makefile | 4 | ||||
-rw-r--r-- | drivers/thermal/thermal_core.c | 165 | ||||
-rw-r--r-- | drivers/thermal/thermal_core.h | 4 | ||||
-rw-r--r-- | drivers/thermal/thermal_helpers.c | 62 | ||||
-rw-r--r-- | drivers/thermal/thermal_netlink.c | 5 | ||||
-rw-r--r-- | drivers/thermal/thermal_netlink.h | 3 | ||||
-rw-r--r-- | drivers/thermal/thermal_trip.c | 182 |
7 files changed, 239 insertions, 186 deletions
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 60f0dfa9aae2..b9b5dc86f5fd 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -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 diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index c686da8b4c68..55679fd86505 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -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; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 26350206a98d..7af54382e915 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -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 { diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index 8977d5ddc23c..0f648131b0b5 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -146,68 +146,6 @@ unlock: } 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) { diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index 75943b06dbe7..08bc46c3ec7b 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -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); +} diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index 1052f523188d..0a9987c3bc57 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -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 */ diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c new file mode 100644 index 000000000000..907f3a4d7bc8 --- /dev/null +++ b/drivers/thermal/thermal_trip.c @@ -0,0 +1,182 @@ +// 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; +} |