// SPDX-License-Identifier: GPL-2.0 /* * thermal.c - Generic Thermal Management Sysfs support. * * Copyright (C) 2008 Intel Corp * Copyright (C) 2008 Zhang Rui <[email protected]> * Copyright (C) 2008 Sujith Thomas <[email protected]> */ #define pr_fmt(fmt) … #include <linux/device.h> #include <linux/err.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/kdev_t.h> #include <linux/idr.h> #include <linux/list_sort.h> #include <linux/thermal.h> #include <linux/reboot.h> #include <linux/string.h> #include <linux/of.h> #include <linux/suspend.h> #define CREATE_TRACE_POINTS #include "thermal_trace.h" #include "thermal_core.h" #include "thermal_hwmon.h" static DEFINE_IDA(thermal_tz_ida); static DEFINE_IDA(thermal_cdev_ida); static LIST_HEAD(thermal_tz_list); static LIST_HEAD(thermal_cdev_list); static LIST_HEAD(thermal_governor_list); static DEFINE_MUTEX(thermal_list_lock); static DEFINE_MUTEX(thermal_governor_lock); static struct thermal_governor *def_governor; /* * Governor section: set of functions to handle thermal governors * * Functions to help in the life cycle of thermal governors within * the thermal core and by the thermal governor code. */ static struct thermal_governor *__find_governor(const char *name) { … } /** * bind_previous_governor() - bind the previous governor of the thermal zone * @tz: a valid pointer to a struct thermal_zone_device * @failed_gov_name: the name of the governor that failed to register * * Register the previous governor of the thermal zone after a new * governor has failed to be bound. */ static void bind_previous_governor(struct thermal_zone_device *tz, const char *failed_gov_name) { … } /** * thermal_set_governor() - Switch to another governor * @tz: a valid pointer to a struct thermal_zone_device * @new_gov: pointer to the new governor * * Change the governor of thermal zone @tz. * * Return: 0 on success, an error if the new governor's bind_to_tz() failed. */ static int thermal_set_governor(struct thermal_zone_device *tz, struct thermal_governor *new_gov) { … } int thermal_register_governor(struct thermal_governor *governor) { … } void thermal_unregister_governor(struct thermal_governor *governor) { … } int thermal_zone_device_set_policy(struct thermal_zone_device *tz, char *policy) { … } int thermal_build_list_of_policies(char *buf) { … } static void __init thermal_unregister_governors(void) { … } static int __init thermal_register_governors(void) { … } static int __thermal_zone_device_set_mode(struct thermal_zone_device *tz, enum thermal_device_mode mode) { … } static void thermal_zone_broken_disable(struct thermal_zone_device *tz) { … } /* * Zone update section: main control loop applied to each zone while monitoring * in polling mode. The monitoring is done using a workqueue. * Same update may be done on a zone by calling thermal_zone_device_update(). * * An update means: * - Non-critical trips will invoke the governor responsible for that zone; * - Hot trips will produce a notification to userspace; * - Critical trip point will cause a system shutdown. */ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, unsigned long delay) { … } static void thermal_zone_recheck(struct thermal_zone_device *tz, int error) { … } static void monitor_thermal_zone(struct thermal_zone_device *tz) { … } static struct thermal_governor *thermal_get_tz_governor(struct thermal_zone_device *tz) { … } void thermal_governor_update_tz(struct thermal_zone_device *tz, enum thermal_notify_event reason) { … } static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdown) { … } void thermal_zone_device_critical(struct thermal_zone_device *tz) { … } EXPORT_SYMBOL(…); void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz) { … } static void handle_critical_trips(struct thermal_zone_device *tz, const struct thermal_trip *trip) { … } static void handle_thermal_trip(struct thermal_zone_device *tz, struct thermal_trip_desc *td, struct list_head *way_up_list, struct list_head *way_down_list) { … } static void thermal_zone_device_check(struct work_struct *work) { … } static void thermal_zone_device_init(struct thermal_zone_device *tz) { … } static void thermal_governor_trip_crossed(struct thermal_governor *governor, struct thermal_zone_device *tz, const struct thermal_trip *trip, bool crossed_up) { … } static void thermal_trip_crossed(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_governor *governor, bool crossed_up) { … } static int thermal_trip_notify_cmp(void *not_used, const struct list_head *a, const struct list_head *b) { … } void __thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { … } static int thermal_zone_device_set_mode(struct thermal_zone_device *tz, enum thermal_device_mode mode) { … } int thermal_zone_device_enable(struct thermal_zone_device *tz) { … } EXPORT_SYMBOL_GPL(…); int thermal_zone_device_disable(struct thermal_zone_device *tz) { … } EXPORT_SYMBOL_GPL(…); int thermal_zone_device_is_enabled(struct thermal_zone_device *tz) { … } static bool thermal_zone_is_present(struct thermal_zone_device *tz) { … } void thermal_zone_device_update(struct thermal_zone_device *tz, enum thermal_notify_event event) { … } EXPORT_SYMBOL_GPL(…); void thermal_zone_trip_down(struct thermal_zone_device *tz, const struct thermal_trip *trip) { … } int for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), void *data) { … } int for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, void *), void *data) { … } int for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), void *data) { … } struct thermal_zone_device *thermal_zone_get_by_id(int id) { … } /* * Device management section: cooling devices, zones devices, and binding * * Set of functions provided by the thermal core for: * - cooling devices lifecycle: registration, unregistration, * binding, and unbinding. * - thermal zone devices lifecycle: registration, unregistration, * binding, and unbinding. */ /** * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone * @tz: pointer to struct thermal_zone_device * @trip: trip point the cooling devices is associated with in this zone. * @cdev: pointer to struct thermal_cooling_device * @upper: the Maximum cooling state for this trip point. * THERMAL_NO_LIMIT means no upper limit, * and the cooling device can be in max_state. * @lower: the Minimum cooling state can be used for this trip point. * THERMAL_NO_LIMIT means no lower limit, * and the cooling device can be in cooling state 0. * @weight: The weight of the cooling device to be bound to the * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the * default value * * This interface function bind a thermal cooling device to the certain trip * point of a thermal zone device. * This function is usually called in the thermal zone device .bind callback. * * Return: 0 on success, the proper error value otherwise. */ int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_cooling_device *cdev, unsigned long upper, unsigned long lower, unsigned int weight) { … } EXPORT_SYMBOL_GPL(…); int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, int trip_index, struct thermal_cooling_device *cdev, unsigned long upper, unsigned long lower, unsigned int weight) { … } EXPORT_SYMBOL_GPL(…); /** * thermal_unbind_cdev_from_trip - unbind a cooling device from a thermal zone. * @tz: pointer to a struct thermal_zone_device. * @trip: trip point the cooling devices is associated with in this zone. * @cdev: pointer to a struct thermal_cooling_device. * * This interface function unbind a thermal cooling device from the certain * trip point of a thermal zone device. * This function is usually called in the thermal zone device .unbind callback. * * Return: 0 on success, the proper error value otherwise. */ int thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz, const struct thermal_trip *trip, struct thermal_cooling_device *cdev) { … } EXPORT_SYMBOL_GPL(…); int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, int trip_index, struct thermal_cooling_device *cdev) { … } EXPORT_SYMBOL_GPL(…); static void thermal_release(struct device *dev) { … } static struct class *thermal_class; static inline void print_bind_err_msg(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev, int ret) { … } static void bind_cdev(struct thermal_cooling_device *cdev) { … } /** * __thermal_cooling_device_register() - register a new thermal cooling device * @np: a pointer to a device tree node. * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. * * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. * It also gives the opportunity to link the cooling device to a device tree * node, so that it can be bound to a thermal zone created out of device tree. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ static struct thermal_cooling_device * __thermal_cooling_device_register(struct device_node *np, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { … } /** * thermal_cooling_device_register() - register a new thermal cooling device * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. * * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * thermal_cooling_device_register(const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { … } EXPORT_SYMBOL_GPL(…); /** * thermal_of_cooling_device_register() - register an OF thermal cooling device * @np: a pointer to a device tree node. * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. * * This function will register a cooling device with device tree node reference. * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * thermal_of_cooling_device_register(struct device_node *np, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { … } EXPORT_SYMBOL_GPL(…); static void thermal_cooling_device_release(struct device *dev, void *res) { … } /** * devm_thermal_of_cooling_device_register() - register an OF thermal cooling * device * @dev: a valid struct device pointer of a sensor device. * @np: a pointer to a device tree node. * @type: the thermal cooling device type. * @devdata: device private data. * @ops: standard thermal cooling devices callbacks. * * This function will register a cooling device with device tree node reference. * This interface function adds a new thermal cooling device (fan/processor/...) * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself * to all the thermal zone devices registered at the same time. * * Return: a pointer to the created struct thermal_cooling_device or an * ERR_PTR. Caller must check return value with IS_ERR*() helpers. */ struct thermal_cooling_device * devm_thermal_of_cooling_device_register(struct device *dev, struct device_node *np, const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { … } EXPORT_SYMBOL_GPL(…); static bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) { … } /** * thermal_cooling_device_update - Update a cooling device object * @cdev: Target cooling device. * * Update @cdev to reflect a change of the underlying hardware or platform. * * Must be called when the maximum cooling state of @cdev becomes invalid and so * its .get_max_state() callback needs to be run to produce the new maximum * cooling state value. */ void thermal_cooling_device_update(struct thermal_cooling_device *cdev) { … } EXPORT_SYMBOL_GPL(…); /** * thermal_cooling_device_unregister - removes a thermal cooling device * @cdev: the thermal cooling device to remove. * * thermal_cooling_device_unregister() must be called when a registered * thermal cooling device is no longer needed. */ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) { … } EXPORT_SYMBOL_GPL(…); static void bind_tz(struct thermal_zone_device *tz) { … } static void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) { … } int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) { … } EXPORT_SYMBOL_GPL(…); /** * thermal_zone_device_register_with_trips() - register a new thermal zone device * @type: the thermal zone device type * @trips: a pointer to an array of thermal trips * @num_trips: the number of trip points the thermal zone support * @devdata: private device data * @ops: standard thermal zone device callbacks * @tzp: thermal zone platform parameters * @passive_delay: number of milliseconds to wait between polls when * performing passive cooling * @polling_delay: number of milliseconds to wait between polls when checking * whether trip points have been crossed (0 for interrupt * driven systems) * * This interface function adds a new thermal zone device (sensor) to * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the * thermal cooling devices registered at the same time. * thermal_zone_device_unregister() must be called when the device is no * longer needed. The passive cooling depends on the .get_trend() return value. * * Return: a pointer to the created struct thermal_zone_device or an * in case of error, an ERR_PTR. Caller must check return value with * IS_ERR*() helpers. */ struct thermal_zone_device * thermal_zone_device_register_with_trips(const char *type, const struct thermal_trip *trips, int num_trips, void *devdata, const struct thermal_zone_device_ops *ops, const struct thermal_zone_params *tzp, unsigned int passive_delay, unsigned int polling_delay) { … } EXPORT_SYMBOL_GPL(…); struct thermal_zone_device *thermal_tripless_zone_device_register( const char *type, void *devdata, const struct thermal_zone_device_ops *ops, const struct thermal_zone_params *tzp) { … } EXPORT_SYMBOL_GPL(…); void *thermal_zone_device_priv(struct thermal_zone_device *tzd) { … } EXPORT_SYMBOL_GPL(…); const char *thermal_zone_device_type(struct thermal_zone_device *tzd) { … } EXPORT_SYMBOL_GPL(…); int thermal_zone_device_id(struct thermal_zone_device *tzd) { … } EXPORT_SYMBOL_GPL(…); struct device *thermal_zone_device(struct thermal_zone_device *tzd) { … } EXPORT_SYMBOL_GPL(…); /** * thermal_zone_device_unregister - removes the registered thermal zone device * @tz: the thermal zone device to remove */ void thermal_zone_device_unregister(struct thermal_zone_device *tz) { … } EXPORT_SYMBOL_GPL(…); /** * thermal_zone_get_zone_by_name() - search for a zone and returns its ref * @name: thermal zone name to fetch the temperature * * When only one zone is found with the passed name, returns a reference to it. * * Return: On success returns a reference to an unique thermal zone with * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid * paramenters, -ENODEV for not found and -EEXIST for multiple matches). */ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) { … } EXPORT_SYMBOL_GPL(…); static void thermal_zone_device_resume(struct work_struct *work) { … } static int thermal_pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused) { … } static struct notifier_block thermal_pm_nb = …; static int __init thermal_init(void) { … } postcore_initcall(thermal_init);