162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * thermal.c - Generic Thermal Management Sysfs support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Intel Corp 662306a36Sopenharmony_ci * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> 762306a36Sopenharmony_ci * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/kdev_t.h> 1762306a36Sopenharmony_ci#include <linux/idr.h> 1862306a36Sopenharmony_ci#include <linux/thermal.h> 1962306a36Sopenharmony_ci#include <linux/reboot.h> 2062306a36Sopenharmony_ci#include <linux/string.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/suspend.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 2562306a36Sopenharmony_ci#include "thermal_trace.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "thermal_core.h" 2862306a36Sopenharmony_ci#include "thermal_hwmon.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic DEFINE_IDA(thermal_tz_ida); 3162306a36Sopenharmony_cistatic DEFINE_IDA(thermal_cdev_ida); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic LIST_HEAD(thermal_tz_list); 3462306a36Sopenharmony_cistatic LIST_HEAD(thermal_cdev_list); 3562306a36Sopenharmony_cistatic LIST_HEAD(thermal_governor_list); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic DEFINE_MUTEX(thermal_list_lock); 3862306a36Sopenharmony_cistatic DEFINE_MUTEX(thermal_governor_lock); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct thermal_governor *def_governor; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Governor section: set of functions to handle thermal governors 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Functions to help in the life cycle of thermal governors within 4662306a36Sopenharmony_ci * the thermal core and by the thermal governor code. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct thermal_governor *__find_governor(const char *name) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct thermal_governor *pos; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!name || !name[0]) 5462306a36Sopenharmony_ci return def_governor; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_governor_list, governor_list) 5762306a36Sopenharmony_ci if (!strncasecmp(name, pos->name, THERMAL_NAME_LENGTH)) 5862306a36Sopenharmony_ci return pos; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * bind_previous_governor() - bind the previous governor of the thermal zone 6562306a36Sopenharmony_ci * @tz: a valid pointer to a struct thermal_zone_device 6662306a36Sopenharmony_ci * @failed_gov_name: the name of the governor that failed to register 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Register the previous governor of the thermal zone after a new 6962306a36Sopenharmony_ci * governor has failed to be bound. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistatic void bind_previous_governor(struct thermal_zone_device *tz, 7262306a36Sopenharmony_ci const char *failed_gov_name) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci if (tz->governor && tz->governor->bind_to_tz) { 7562306a36Sopenharmony_ci if (tz->governor->bind_to_tz(tz)) { 7662306a36Sopenharmony_ci dev_err(&tz->device, 7762306a36Sopenharmony_ci "governor %s failed to bind and the previous one (%s) failed to bind again, thermal zone %s has no governor\n", 7862306a36Sopenharmony_ci failed_gov_name, tz->governor->name, tz->type); 7962306a36Sopenharmony_ci tz->governor = NULL; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * thermal_set_governor() - Switch to another governor 8662306a36Sopenharmony_ci * @tz: a valid pointer to a struct thermal_zone_device 8762306a36Sopenharmony_ci * @new_gov: pointer to the new governor 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Change the governor of thermal zone @tz. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Return: 0 on success, an error if the new governor's bind_to_tz() failed. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic int thermal_set_governor(struct thermal_zone_device *tz, 9462306a36Sopenharmony_ci struct thermal_governor *new_gov) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int ret = 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (tz->governor && tz->governor->unbind_from_tz) 9962306a36Sopenharmony_ci tz->governor->unbind_from_tz(tz); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (new_gov && new_gov->bind_to_tz) { 10262306a36Sopenharmony_ci ret = new_gov->bind_to_tz(tz); 10362306a36Sopenharmony_ci if (ret) { 10462306a36Sopenharmony_ci bind_previous_governor(tz, new_gov->name); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci tz->governor = new_gov; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint thermal_register_governor(struct thermal_governor *governor) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int err; 11862306a36Sopenharmony_ci const char *name; 11962306a36Sopenharmony_ci struct thermal_zone_device *pos; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (!governor) 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mutex_lock(&thermal_governor_lock); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci err = -EBUSY; 12762306a36Sopenharmony_ci if (!__find_governor(governor->name)) { 12862306a36Sopenharmony_ci bool match_default; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci err = 0; 13162306a36Sopenharmony_ci list_add(&governor->governor_list, &thermal_governor_list); 13262306a36Sopenharmony_ci match_default = !strncmp(governor->name, 13362306a36Sopenharmony_ci DEFAULT_THERMAL_GOVERNOR, 13462306a36Sopenharmony_ci THERMAL_NAME_LENGTH); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (!def_governor && match_default) 13762306a36Sopenharmony_ci def_governor = governor; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_tz_list, node) { 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * only thermal zones with specified tz->tzp->governor_name 14562306a36Sopenharmony_ci * may run with tz->govenor unset 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (pos->governor) 14862306a36Sopenharmony_ci continue; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci name = pos->tzp->governor_name; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) { 15362306a36Sopenharmony_ci int ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = thermal_set_governor(pos, governor); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci dev_err(&pos->device, 15862306a36Sopenharmony_ci "Failed to set governor %s for thermal zone %s: %d\n", 15962306a36Sopenharmony_ci governor->name, pos->type, ret); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 16462306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return err; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid thermal_unregister_governor(struct thermal_governor *governor) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct thermal_zone_device *pos; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!governor) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mutex_lock(&thermal_governor_lock); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!__find_governor(governor->name)) 17962306a36Sopenharmony_ci goto exit; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_tz_list, node) { 18462306a36Sopenharmony_ci if (!strncasecmp(pos->governor->name, governor->name, 18562306a36Sopenharmony_ci THERMAL_NAME_LENGTH)) 18662306a36Sopenharmony_ci thermal_set_governor(pos, NULL); 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 19062306a36Sopenharmony_ci list_del(&governor->governor_list); 19162306a36Sopenharmony_ciexit: 19262306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint thermal_zone_device_set_policy(struct thermal_zone_device *tz, 19662306a36Sopenharmony_ci char *policy) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct thermal_governor *gov; 19962306a36Sopenharmony_ci int ret = -EINVAL; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci mutex_lock(&thermal_governor_lock); 20262306a36Sopenharmony_ci mutex_lock(&tz->lock); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!device_is_registered(&tz->device)) 20562306a36Sopenharmony_ci goto exit; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci gov = __find_governor(strim(policy)); 20862306a36Sopenharmony_ci if (!gov) 20962306a36Sopenharmony_ci goto exit; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = thermal_set_governor(tz, gov); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciexit: 21462306a36Sopenharmony_ci mutex_unlock(&tz->lock); 21562306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci thermal_notify_tz_gov_change(tz->id, policy); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciint thermal_build_list_of_policies(char *buf) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct thermal_governor *pos; 22562306a36Sopenharmony_ci ssize_t count = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci mutex_lock(&thermal_governor_lock); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_governor_list, governor_list) { 23062306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "%s ", pos->name); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "\n"); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return count; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void __init thermal_unregister_governors(void) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct thermal_governor **governor; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for_each_governor_table(governor) 24462306a36Sopenharmony_ci thermal_unregister_governor(*governor); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int __init thermal_register_governors(void) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int ret = 0; 25062306a36Sopenharmony_ci struct thermal_governor **governor; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for_each_governor_table(governor) { 25362306a36Sopenharmony_ci ret = thermal_register_governor(*governor); 25462306a36Sopenharmony_ci if (ret) { 25562306a36Sopenharmony_ci pr_err("Failed to register governor: '%s'", 25662306a36Sopenharmony_ci (*governor)->name); 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci pr_info("Registered thermal governor '%s'", 26162306a36Sopenharmony_ci (*governor)->name); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (ret) { 26562306a36Sopenharmony_ci struct thermal_governor **gov; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for_each_governor_table(gov) { 26862306a36Sopenharmony_ci if (gov == governor) 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci thermal_unregister_governor(*gov); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return ret; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Zone update section: main control loop applied to each zone while monitoring 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * in polling mode. The monitoring is done using a workqueue. 28162306a36Sopenharmony_ci * Same update may be done on a zone by calling thermal_zone_device_update(). 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * An update means: 28462306a36Sopenharmony_ci * - Non-critical trips will invoke the governor responsible for that zone; 28562306a36Sopenharmony_ci * - Hot trips will produce a notification to userspace; 28662306a36Sopenharmony_ci * - Critical trip point will cause a system shutdown. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_cistatic void thermal_zone_device_set_polling(struct thermal_zone_device *tz, 28962306a36Sopenharmony_ci unsigned long delay) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci if (delay) 29262306a36Sopenharmony_ci mod_delayed_work(system_freezable_power_efficient_wq, 29362306a36Sopenharmony_ci &tz->poll_queue, delay); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci cancel_delayed_work(&tz->poll_queue); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void monitor_thermal_zone(struct thermal_zone_device *tz) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (tz->mode != THERMAL_DEVICE_ENABLED) 30162306a36Sopenharmony_ci thermal_zone_device_set_polling(tz, 0); 30262306a36Sopenharmony_ci else if (tz->passive) 30362306a36Sopenharmony_ci thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies); 30462306a36Sopenharmony_ci else if (tz->polling_delay_jiffies) 30562306a36Sopenharmony_ci thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void handle_non_critical_trips(struct thermal_zone_device *tz, int trip) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci tz->governor ? tz->governor->throttle(tz, trip) : 31162306a36Sopenharmony_ci def_governor->throttle(tz, trip); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_civoid thermal_zone_device_critical(struct thermal_zone_device *tz) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci /* 31762306a36Sopenharmony_ci * poweroff_delay_ms must be a carefully profiled positive value. 31862306a36Sopenharmony_ci * Its a must for forced_emergency_poweroff_work to be scheduled. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dev_emerg(&tz->device, "%s: critical temperature reached, " 32362306a36Sopenharmony_ci "shutting down\n", tz->type); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci hw_protection_shutdown("Temperature too high", poweroff_delay_ms); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ciEXPORT_SYMBOL(thermal_zone_device_critical); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void handle_critical_trips(struct thermal_zone_device *tz, 33062306a36Sopenharmony_ci int trip, int trip_temp, enum thermal_trip_type trip_type) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci /* If we have not crossed the trip_temp, we do not care. */ 33362306a36Sopenharmony_ci if (trip_temp <= 0 || tz->temperature < trip_temp) 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci trace_thermal_zone_trip(tz, trip, trip_type); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (trip_type == THERMAL_TRIP_HOT && tz->ops->hot) 33962306a36Sopenharmony_ci tz->ops->hot(tz); 34062306a36Sopenharmony_ci else if (trip_type == THERMAL_TRIP_CRITICAL) 34162306a36Sopenharmony_ci tz->ops->critical(tz); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic void handle_thermal_trip(struct thermal_zone_device *tz, int trip_id) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct thermal_trip trip; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Ignore disabled trip points */ 34962306a36Sopenharmony_ci if (test_bit(trip_id, &tz->trips_disabled)) 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci __thermal_zone_get_trip(tz, trip_id, &trip); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (trip.temperature == THERMAL_TEMP_INVALID) 35562306a36Sopenharmony_ci return; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (tz->last_temperature != THERMAL_TEMP_INVALID) { 35862306a36Sopenharmony_ci if (tz->last_temperature < trip.temperature && 35962306a36Sopenharmony_ci tz->temperature >= trip.temperature) 36062306a36Sopenharmony_ci thermal_notify_tz_trip_up(tz->id, trip_id, 36162306a36Sopenharmony_ci tz->temperature); 36262306a36Sopenharmony_ci if (tz->last_temperature >= trip.temperature && 36362306a36Sopenharmony_ci tz->temperature < (trip.temperature - trip.hysteresis)) 36462306a36Sopenharmony_ci thermal_notify_tz_trip_down(tz->id, trip_id, 36562306a36Sopenharmony_ci tz->temperature); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (trip.type == THERMAL_TRIP_CRITICAL || trip.type == THERMAL_TRIP_HOT) 36962306a36Sopenharmony_ci handle_critical_trips(tz, trip_id, trip.temperature, trip.type); 37062306a36Sopenharmony_ci else 37162306a36Sopenharmony_ci handle_non_critical_trips(tz, trip_id); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void update_temperature(struct thermal_zone_device *tz) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci int temp, ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = __thermal_zone_get_temp(tz, &temp); 37962306a36Sopenharmony_ci if (ret) { 38062306a36Sopenharmony_ci if (ret != -EAGAIN) 38162306a36Sopenharmony_ci dev_warn(&tz->device, 38262306a36Sopenharmony_ci "failed to read out thermal zone (%d)\n", 38362306a36Sopenharmony_ci ret); 38462306a36Sopenharmony_ci return; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci tz->last_temperature = tz->temperature; 38862306a36Sopenharmony_ci tz->temperature = temp; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci trace_thermal_temperature(tz); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci thermal_genl_sampling_temp(tz->id, temp); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void thermal_zone_device_init(struct thermal_zone_device *tz) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct thermal_instance *pos; 39862306a36Sopenharmony_ci tz->temperature = THERMAL_TEMP_INVALID; 39962306a36Sopenharmony_ci tz->prev_low_trip = -INT_MAX; 40062306a36Sopenharmony_ci tz->prev_high_trip = INT_MAX; 40162306a36Sopenharmony_ci list_for_each_entry(pos, &tz->thermal_instances, tz_node) 40262306a36Sopenharmony_ci pos->initialized = false; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_civoid __thermal_zone_device_update(struct thermal_zone_device *tz, 40662306a36Sopenharmony_ci enum thermal_notify_event event) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int count; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (tz->suspended) 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (WARN_ONCE(!tz->ops->get_temp, 41462306a36Sopenharmony_ci "'%s' must not be called without 'get_temp' ops set\n", 41562306a36Sopenharmony_ci __func__)) 41662306a36Sopenharmony_ci return; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!thermal_zone_device_is_enabled(tz)) 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci update_temperature(tz); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci __thermal_zone_set_trips(tz); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci tz->notify_event = event; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci for (count = 0; count < tz->num_trips; count++) 42862306a36Sopenharmony_ci handle_thermal_trip(tz, count); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci monitor_thermal_zone(tz); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int thermal_zone_device_set_mode(struct thermal_zone_device *tz, 43462306a36Sopenharmony_ci enum thermal_device_mode mode) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int ret = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci mutex_lock(&tz->lock); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* do nothing if mode isn't changing */ 44162306a36Sopenharmony_ci if (mode == tz->mode) { 44262306a36Sopenharmony_ci mutex_unlock(&tz->lock); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!device_is_registered(&tz->device)) { 44862306a36Sopenharmony_ci mutex_unlock(&tz->lock); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return -ENODEV; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (tz->ops->change_mode) 45462306a36Sopenharmony_ci ret = tz->ops->change_mode(tz, mode); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!ret) 45762306a36Sopenharmony_ci tz->mode = mode; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci mutex_unlock(&tz->lock); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (mode == THERMAL_DEVICE_ENABLED) 46462306a36Sopenharmony_ci thermal_notify_tz_enable(tz->id); 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci thermal_notify_tz_disable(tz->id); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciint thermal_zone_device_enable(struct thermal_zone_device *tz) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_ENABLED); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_enable); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ciint thermal_zone_device_disable(struct thermal_zone_device *tz) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci return thermal_zone_device_set_mode(tz, THERMAL_DEVICE_DISABLED); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_disable); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciint thermal_zone_device_is_enabled(struct thermal_zone_device *tz) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci lockdep_assert_held(&tz->lock); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return tz->mode == THERMAL_DEVICE_ENABLED; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_civoid thermal_zone_device_update(struct thermal_zone_device *tz, 49162306a36Sopenharmony_ci enum thermal_notify_event event) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci mutex_lock(&tz->lock); 49462306a36Sopenharmony_ci if (device_is_registered(&tz->device)) 49562306a36Sopenharmony_ci __thermal_zone_device_update(tz, event); 49662306a36Sopenharmony_ci mutex_unlock(&tz->lock); 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_update); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/** 50162306a36Sopenharmony_ci * thermal_zone_device_exec - Run a callback under the zone lock. 50262306a36Sopenharmony_ci * @tz: Thermal zone. 50362306a36Sopenharmony_ci * @cb: Callback to run. 50462306a36Sopenharmony_ci * @data: Data to pass to the callback. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_civoid thermal_zone_device_exec(struct thermal_zone_device *tz, 50762306a36Sopenharmony_ci void (*cb)(struct thermal_zone_device *, 50862306a36Sopenharmony_ci unsigned long), 50962306a36Sopenharmony_ci unsigned long data) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci mutex_lock(&tz->lock); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci cb(tz, data); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci mutex_unlock(&tz->lock); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_exec); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void thermal_zone_device_check(struct work_struct *work) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct thermal_zone_device *tz = container_of(work, struct 52262306a36Sopenharmony_ci thermal_zone_device, 52362306a36Sopenharmony_ci poll_queue.work); 52462306a36Sopenharmony_ci thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ciint for_each_thermal_governor(int (*cb)(struct thermal_governor *, void *), 52862306a36Sopenharmony_ci void *data) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct thermal_governor *gov; 53162306a36Sopenharmony_ci int ret = 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mutex_lock(&thermal_governor_lock); 53462306a36Sopenharmony_ci list_for_each_entry(gov, &thermal_governor_list, governor_list) { 53562306a36Sopenharmony_ci ret = cb(gov, data); 53662306a36Sopenharmony_ci if (ret) 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ciint for_each_thermal_cooling_device(int (*cb)(struct thermal_cooling_device *, 54562306a36Sopenharmony_ci void *), void *data) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 54862306a36Sopenharmony_ci int ret = 0; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 55162306a36Sopenharmony_ci list_for_each_entry(cdev, &thermal_cdev_list, node) { 55262306a36Sopenharmony_ci ret = cb(cdev, data); 55362306a36Sopenharmony_ci if (ret) 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciint for_each_thermal_zone(int (*cb)(struct thermal_zone_device *, void *), 56262306a36Sopenharmony_ci void *data) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct thermal_zone_device *tz; 56562306a36Sopenharmony_ci int ret = 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 56862306a36Sopenharmony_ci list_for_each_entry(tz, &thermal_tz_list, node) { 56962306a36Sopenharmony_ci ret = cb(tz, data); 57062306a36Sopenharmony_ci if (ret) 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return ret; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistruct thermal_zone_device *thermal_zone_get_by_id(int id) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct thermal_zone_device *tz, *match = NULL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 58362306a36Sopenharmony_ci list_for_each_entry(tz, &thermal_tz_list, node) { 58462306a36Sopenharmony_ci if (tz->id == id) { 58562306a36Sopenharmony_ci match = tz; 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return match; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/* 59562306a36Sopenharmony_ci * Device management section: cooling devices, zones devices, and binding 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * Set of functions provided by the thermal core for: 59862306a36Sopenharmony_ci * - cooling devices lifecycle: registration, unregistration, 59962306a36Sopenharmony_ci * binding, and unbinding. 60062306a36Sopenharmony_ci * - thermal zone devices lifecycle: registration, unregistration, 60162306a36Sopenharmony_ci * binding, and unbinding. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/** 60562306a36Sopenharmony_ci * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone 60662306a36Sopenharmony_ci * @tz: pointer to struct thermal_zone_device 60762306a36Sopenharmony_ci * @trip_index: indicates which trip point the cooling devices is 60862306a36Sopenharmony_ci * associated with in this thermal zone. 60962306a36Sopenharmony_ci * @cdev: pointer to struct thermal_cooling_device 61062306a36Sopenharmony_ci * @upper: the Maximum cooling state for this trip point. 61162306a36Sopenharmony_ci * THERMAL_NO_LIMIT means no upper limit, 61262306a36Sopenharmony_ci * and the cooling device can be in max_state. 61362306a36Sopenharmony_ci * @lower: the Minimum cooling state can be used for this trip point. 61462306a36Sopenharmony_ci * THERMAL_NO_LIMIT means no lower limit, 61562306a36Sopenharmony_ci * and the cooling device can be in cooling state 0. 61662306a36Sopenharmony_ci * @weight: The weight of the cooling device to be bound to the 61762306a36Sopenharmony_ci * thermal zone. Use THERMAL_WEIGHT_DEFAULT for the 61862306a36Sopenharmony_ci * default value 61962306a36Sopenharmony_ci * 62062306a36Sopenharmony_ci * This interface function bind a thermal cooling device to the certain trip 62162306a36Sopenharmony_ci * point of a thermal zone device. 62262306a36Sopenharmony_ci * This function is usually called in the thermal zone device .bind callback. 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * Return: 0 on success, the proper error value otherwise. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ciint thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, 62762306a36Sopenharmony_ci int trip_index, 62862306a36Sopenharmony_ci struct thermal_cooling_device *cdev, 62962306a36Sopenharmony_ci unsigned long upper, unsigned long lower, 63062306a36Sopenharmony_ci unsigned int weight) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct thermal_instance *dev; 63362306a36Sopenharmony_ci struct thermal_instance *pos; 63462306a36Sopenharmony_ci struct thermal_zone_device *pos1; 63562306a36Sopenharmony_ci struct thermal_cooling_device *pos2; 63662306a36Sopenharmony_ci const struct thermal_trip *trip; 63762306a36Sopenharmony_ci bool upper_no_limit; 63862306a36Sopenharmony_ci int result; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (trip_index >= tz->num_trips || trip_index < 0) 64162306a36Sopenharmony_ci return -EINVAL; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci trip = &tz->trips[trip_index]; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci list_for_each_entry(pos1, &thermal_tz_list, node) { 64662306a36Sopenharmony_ci if (pos1 == tz) 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci list_for_each_entry(pos2, &thermal_cdev_list, node) { 65062306a36Sopenharmony_ci if (pos2 == cdev) 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (tz != pos1 || cdev != pos2) 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* lower default 0, upper default max_state */ 65862306a36Sopenharmony_ci lower = lower == THERMAL_NO_LIMIT ? 0 : lower; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (upper == THERMAL_NO_LIMIT) { 66162306a36Sopenharmony_ci upper = cdev->max_state; 66262306a36Sopenharmony_ci upper_no_limit = true; 66362306a36Sopenharmony_ci } else { 66462306a36Sopenharmony_ci upper_no_limit = false; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (lower > upper || upper > cdev->max_state) 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 67162306a36Sopenharmony_ci if (!dev) 67262306a36Sopenharmony_ci return -ENOMEM; 67362306a36Sopenharmony_ci dev->tz = tz; 67462306a36Sopenharmony_ci dev->cdev = cdev; 67562306a36Sopenharmony_ci dev->trip = trip; 67662306a36Sopenharmony_ci dev->upper = upper; 67762306a36Sopenharmony_ci dev->upper_no_limit = upper_no_limit; 67862306a36Sopenharmony_ci dev->lower = lower; 67962306a36Sopenharmony_ci dev->target = THERMAL_NO_TARGET; 68062306a36Sopenharmony_ci dev->weight = weight; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci result = ida_alloc(&tz->ida, GFP_KERNEL); 68362306a36Sopenharmony_ci if (result < 0) 68462306a36Sopenharmony_ci goto free_mem; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci dev->id = result; 68762306a36Sopenharmony_ci sprintf(dev->name, "cdev%d", dev->id); 68862306a36Sopenharmony_ci result = 68962306a36Sopenharmony_ci sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name); 69062306a36Sopenharmony_ci if (result) 69162306a36Sopenharmony_ci goto release_ida; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci snprintf(dev->attr_name, sizeof(dev->attr_name), "cdev%d_trip_point", 69462306a36Sopenharmony_ci dev->id); 69562306a36Sopenharmony_ci sysfs_attr_init(&dev->attr.attr); 69662306a36Sopenharmony_ci dev->attr.attr.name = dev->attr_name; 69762306a36Sopenharmony_ci dev->attr.attr.mode = 0444; 69862306a36Sopenharmony_ci dev->attr.show = trip_point_show; 69962306a36Sopenharmony_ci result = device_create_file(&tz->device, &dev->attr); 70062306a36Sopenharmony_ci if (result) 70162306a36Sopenharmony_ci goto remove_symbol_link; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci snprintf(dev->weight_attr_name, sizeof(dev->weight_attr_name), 70462306a36Sopenharmony_ci "cdev%d_weight", dev->id); 70562306a36Sopenharmony_ci sysfs_attr_init(&dev->weight_attr.attr); 70662306a36Sopenharmony_ci dev->weight_attr.attr.name = dev->weight_attr_name; 70762306a36Sopenharmony_ci dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO; 70862306a36Sopenharmony_ci dev->weight_attr.show = weight_show; 70962306a36Sopenharmony_ci dev->weight_attr.store = weight_store; 71062306a36Sopenharmony_ci result = device_create_file(&tz->device, &dev->weight_attr); 71162306a36Sopenharmony_ci if (result) 71262306a36Sopenharmony_ci goto remove_trip_file; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mutex_lock(&tz->lock); 71562306a36Sopenharmony_ci mutex_lock(&cdev->lock); 71662306a36Sopenharmony_ci list_for_each_entry(pos, &tz->thermal_instances, tz_node) 71762306a36Sopenharmony_ci if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { 71862306a36Sopenharmony_ci result = -EEXIST; 71962306a36Sopenharmony_ci break; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci if (!result) { 72262306a36Sopenharmony_ci list_add_tail(&dev->tz_node, &tz->thermal_instances); 72362306a36Sopenharmony_ci list_add_tail(&dev->cdev_node, &cdev->thermal_instances); 72462306a36Sopenharmony_ci atomic_set(&tz->need_update, 1); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 72762306a36Sopenharmony_ci mutex_unlock(&tz->lock); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (!result) 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci device_remove_file(&tz->device, &dev->weight_attr); 73362306a36Sopenharmony_ciremove_trip_file: 73462306a36Sopenharmony_ci device_remove_file(&tz->device, &dev->attr); 73562306a36Sopenharmony_ciremove_symbol_link: 73662306a36Sopenharmony_ci sysfs_remove_link(&tz->device.kobj, dev->name); 73762306a36Sopenharmony_cirelease_ida: 73862306a36Sopenharmony_ci ida_free(&tz->ida, dev->id); 73962306a36Sopenharmony_cifree_mem: 74062306a36Sopenharmony_ci kfree(dev); 74162306a36Sopenharmony_ci return result; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/** 74662306a36Sopenharmony_ci * thermal_zone_unbind_cooling_device() - unbind a cooling device from a 74762306a36Sopenharmony_ci * thermal zone. 74862306a36Sopenharmony_ci * @tz: pointer to a struct thermal_zone_device. 74962306a36Sopenharmony_ci * @trip_index: indicates which trip point the cooling devices is 75062306a36Sopenharmony_ci * associated with in this thermal zone. 75162306a36Sopenharmony_ci * @cdev: pointer to a struct thermal_cooling_device. 75262306a36Sopenharmony_ci * 75362306a36Sopenharmony_ci * This interface function unbind a thermal cooling device from the certain 75462306a36Sopenharmony_ci * trip point of a thermal zone device. 75562306a36Sopenharmony_ci * This function is usually called in the thermal zone device .unbind callback. 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * Return: 0 on success, the proper error value otherwise. 75862306a36Sopenharmony_ci */ 75962306a36Sopenharmony_ciint thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, 76062306a36Sopenharmony_ci int trip_index, 76162306a36Sopenharmony_ci struct thermal_cooling_device *cdev) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct thermal_instance *pos, *next; 76462306a36Sopenharmony_ci const struct thermal_trip *trip; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci mutex_lock(&tz->lock); 76762306a36Sopenharmony_ci mutex_lock(&cdev->lock); 76862306a36Sopenharmony_ci trip = &tz->trips[trip_index]; 76962306a36Sopenharmony_ci list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { 77062306a36Sopenharmony_ci if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { 77162306a36Sopenharmony_ci list_del(&pos->tz_node); 77262306a36Sopenharmony_ci list_del(&pos->cdev_node); 77362306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 77462306a36Sopenharmony_ci mutex_unlock(&tz->lock); 77562306a36Sopenharmony_ci goto unbind; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 77962306a36Sopenharmony_ci mutex_unlock(&tz->lock); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return -ENODEV; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ciunbind: 78462306a36Sopenharmony_ci device_remove_file(&tz->device, &pos->weight_attr); 78562306a36Sopenharmony_ci device_remove_file(&tz->device, &pos->attr); 78662306a36Sopenharmony_ci sysfs_remove_link(&tz->device.kobj, pos->name); 78762306a36Sopenharmony_ci ida_free(&tz->ida, pos->id); 78862306a36Sopenharmony_ci kfree(pos); 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void thermal_release(struct device *dev) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct thermal_zone_device *tz; 79662306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (!strncmp(dev_name(dev), "thermal_zone", 79962306a36Sopenharmony_ci sizeof("thermal_zone") - 1)) { 80062306a36Sopenharmony_ci tz = to_thermal_zone(dev); 80162306a36Sopenharmony_ci thermal_zone_destroy_device_groups(tz); 80262306a36Sopenharmony_ci mutex_destroy(&tz->lock); 80362306a36Sopenharmony_ci kfree(tz); 80462306a36Sopenharmony_ci } else if (!strncmp(dev_name(dev), "cooling_device", 80562306a36Sopenharmony_ci sizeof("cooling_device") - 1)) { 80662306a36Sopenharmony_ci cdev = to_cooling_device(dev); 80762306a36Sopenharmony_ci thermal_cooling_device_destroy_sysfs(cdev); 80862306a36Sopenharmony_ci kfree(cdev->type); 80962306a36Sopenharmony_ci ida_free(&thermal_cdev_ida, cdev->id); 81062306a36Sopenharmony_ci kfree(cdev); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic struct class *thermal_class; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic inline 81762306a36Sopenharmony_civoid print_bind_err_msg(struct thermal_zone_device *tz, 81862306a36Sopenharmony_ci struct thermal_cooling_device *cdev, int ret) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", 82162306a36Sopenharmony_ci tz->type, cdev->type, ret); 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic void bind_cdev(struct thermal_cooling_device *cdev) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci struct thermal_zone_device *pos = NULL; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_tz_list, node) { 83062306a36Sopenharmony_ci if (pos->ops->bind) { 83162306a36Sopenharmony_ci ret = pos->ops->bind(pos, cdev); 83262306a36Sopenharmony_ci if (ret) 83362306a36Sopenharmony_ci print_bind_err_msg(pos, cdev, ret); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci/** 83962306a36Sopenharmony_ci * __thermal_cooling_device_register() - register a new thermal cooling device 84062306a36Sopenharmony_ci * @np: a pointer to a device tree node. 84162306a36Sopenharmony_ci * @type: the thermal cooling device type. 84262306a36Sopenharmony_ci * @devdata: device private data. 84362306a36Sopenharmony_ci * @ops: standard thermal cooling devices callbacks. 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...) 84662306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself 84762306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time. 84862306a36Sopenharmony_ci * It also gives the opportunity to link the cooling device to a device tree 84962306a36Sopenharmony_ci * node, so that it can be bound to a thermal zone created out of device tree. 85062306a36Sopenharmony_ci * 85162306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an 85262306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers. 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_cistatic struct thermal_cooling_device * 85562306a36Sopenharmony_ci__thermal_cooling_device_register(struct device_node *np, 85662306a36Sopenharmony_ci const char *type, void *devdata, 85762306a36Sopenharmony_ci const struct thermal_cooling_device_ops *ops) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 86062306a36Sopenharmony_ci struct thermal_zone_device *pos = NULL; 86162306a36Sopenharmony_ci int id, ret; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (!ops || !ops->get_max_state || !ops->get_cur_state || 86462306a36Sopenharmony_ci !ops->set_cur_state) 86562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (!thermal_class) 86862306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 87162306a36Sopenharmony_ci if (!cdev) 87262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL); 87562306a36Sopenharmony_ci if (ret < 0) 87662306a36Sopenharmony_ci goto out_kfree_cdev; 87762306a36Sopenharmony_ci cdev->id = ret; 87862306a36Sopenharmony_ci id = ret; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci cdev->type = kstrdup(type ? type : "", GFP_KERNEL); 88162306a36Sopenharmony_ci if (!cdev->type) { 88262306a36Sopenharmony_ci ret = -ENOMEM; 88362306a36Sopenharmony_ci goto out_ida_remove; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci mutex_init(&cdev->lock); 88762306a36Sopenharmony_ci INIT_LIST_HEAD(&cdev->thermal_instances); 88862306a36Sopenharmony_ci cdev->np = np; 88962306a36Sopenharmony_ci cdev->ops = ops; 89062306a36Sopenharmony_ci cdev->updated = false; 89162306a36Sopenharmony_ci cdev->device.class = thermal_class; 89262306a36Sopenharmony_ci cdev->devdata = devdata; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ret = cdev->ops->get_max_state(cdev, &cdev->max_state); 89562306a36Sopenharmony_ci if (ret) 89662306a36Sopenharmony_ci goto out_cdev_type; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci thermal_cooling_device_setup_sysfs(cdev); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id); 90162306a36Sopenharmony_ci if (ret) 90262306a36Sopenharmony_ci goto out_cooling_dev; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci ret = device_register(&cdev->device); 90562306a36Sopenharmony_ci if (ret) { 90662306a36Sopenharmony_ci /* thermal_release() handles rest of the cleanup */ 90762306a36Sopenharmony_ci put_device(&cdev->device); 90862306a36Sopenharmony_ci return ERR_PTR(ret); 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* Add 'this' new cdev to the global cdev list */ 91262306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci list_add(&cdev->node, &thermal_cdev_list); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Update binding information for 'this' new cdev */ 91762306a36Sopenharmony_ci bind_cdev(cdev); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_tz_list, node) 92062306a36Sopenharmony_ci if (atomic_cmpxchg(&pos->need_update, 1, 0)) 92162306a36Sopenharmony_ci thermal_zone_device_update(pos, 92262306a36Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return cdev; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ciout_cooling_dev: 92962306a36Sopenharmony_ci thermal_cooling_device_destroy_sysfs(cdev); 93062306a36Sopenharmony_ciout_cdev_type: 93162306a36Sopenharmony_ci kfree(cdev->type); 93262306a36Sopenharmony_ciout_ida_remove: 93362306a36Sopenharmony_ci ida_free(&thermal_cdev_ida, id); 93462306a36Sopenharmony_ciout_kfree_cdev: 93562306a36Sopenharmony_ci kfree(cdev); 93662306a36Sopenharmony_ci return ERR_PTR(ret); 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/** 94062306a36Sopenharmony_ci * thermal_cooling_device_register() - register a new thermal cooling device 94162306a36Sopenharmony_ci * @type: the thermal cooling device type. 94262306a36Sopenharmony_ci * @devdata: device private data. 94362306a36Sopenharmony_ci * @ops: standard thermal cooling devices callbacks. 94462306a36Sopenharmony_ci * 94562306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...) 94662306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself 94762306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time. 94862306a36Sopenharmony_ci * 94962306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an 95062306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers. 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_cistruct thermal_cooling_device * 95362306a36Sopenharmony_cithermal_cooling_device_register(const char *type, void *devdata, 95462306a36Sopenharmony_ci const struct thermal_cooling_device_ops *ops) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci return __thermal_cooling_device_register(NULL, type, devdata, ops); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_cooling_device_register); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci/** 96162306a36Sopenharmony_ci * thermal_of_cooling_device_register() - register an OF thermal cooling device 96262306a36Sopenharmony_ci * @np: a pointer to a device tree node. 96362306a36Sopenharmony_ci * @type: the thermal cooling device type. 96462306a36Sopenharmony_ci * @devdata: device private data. 96562306a36Sopenharmony_ci * @ops: standard thermal cooling devices callbacks. 96662306a36Sopenharmony_ci * 96762306a36Sopenharmony_ci * This function will register a cooling device with device tree node reference. 96862306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...) 96962306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself 97062306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time. 97162306a36Sopenharmony_ci * 97262306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an 97362306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_cistruct thermal_cooling_device * 97662306a36Sopenharmony_cithermal_of_cooling_device_register(struct device_node *np, 97762306a36Sopenharmony_ci const char *type, void *devdata, 97862306a36Sopenharmony_ci const struct thermal_cooling_device_ops *ops) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci return __thermal_cooling_device_register(np, type, devdata, ops); 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic void thermal_cooling_device_release(struct device *dev, void *res) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci thermal_cooling_device_unregister( 98762306a36Sopenharmony_ci *(struct thermal_cooling_device **)res); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci/** 99162306a36Sopenharmony_ci * devm_thermal_of_cooling_device_register() - register an OF thermal cooling 99262306a36Sopenharmony_ci * device 99362306a36Sopenharmony_ci * @dev: a valid struct device pointer of a sensor device. 99462306a36Sopenharmony_ci * @np: a pointer to a device tree node. 99562306a36Sopenharmony_ci * @type: the thermal cooling device type. 99662306a36Sopenharmony_ci * @devdata: device private data. 99762306a36Sopenharmony_ci * @ops: standard thermal cooling devices callbacks. 99862306a36Sopenharmony_ci * 99962306a36Sopenharmony_ci * This function will register a cooling device with device tree node reference. 100062306a36Sopenharmony_ci * This interface function adds a new thermal cooling device (fan/processor/...) 100162306a36Sopenharmony_ci * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself 100262306a36Sopenharmony_ci * to all the thermal zone devices registered at the same time. 100362306a36Sopenharmony_ci * 100462306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_cooling_device or an 100562306a36Sopenharmony_ci * ERR_PTR. Caller must check return value with IS_ERR*() helpers. 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_cistruct thermal_cooling_device * 100862306a36Sopenharmony_cidevm_thermal_of_cooling_device_register(struct device *dev, 100962306a36Sopenharmony_ci struct device_node *np, 101062306a36Sopenharmony_ci char *type, void *devdata, 101162306a36Sopenharmony_ci const struct thermal_cooling_device_ops *ops) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct thermal_cooling_device **ptr, *tcd; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ptr = devres_alloc(thermal_cooling_device_release, sizeof(*ptr), 101662306a36Sopenharmony_ci GFP_KERNEL); 101762306a36Sopenharmony_ci if (!ptr) 101862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci tcd = __thermal_cooling_device_register(np, type, devdata, ops); 102162306a36Sopenharmony_ci if (IS_ERR(tcd)) { 102262306a36Sopenharmony_ci devres_free(ptr); 102362306a36Sopenharmony_ci return tcd; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci *ptr = tcd; 102762306a36Sopenharmony_ci devres_add(dev, ptr); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return tcd; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_thermal_of_cooling_device_register); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic bool thermal_cooling_device_present(struct thermal_cooling_device *cdev) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct thermal_cooling_device *pos = NULL; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_cdev_list, node) { 103862306a36Sopenharmony_ci if (pos == cdev) 103962306a36Sopenharmony_ci return true; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return false; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci/** 104662306a36Sopenharmony_ci * thermal_cooling_device_update - Update a cooling device object 104762306a36Sopenharmony_ci * @cdev: Target cooling device. 104862306a36Sopenharmony_ci * 104962306a36Sopenharmony_ci * Update @cdev to reflect a change of the underlying hardware or platform. 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * Must be called when the maximum cooling state of @cdev becomes invalid and so 105262306a36Sopenharmony_ci * its .get_max_state() callback needs to be run to produce the new maximum 105362306a36Sopenharmony_ci * cooling state value. 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_civoid thermal_cooling_device_update(struct thermal_cooling_device *cdev) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct thermal_instance *ti; 105862306a36Sopenharmony_ci unsigned long state; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (IS_ERR_OR_NULL(cdev)) 106162306a36Sopenharmony_ci return; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* 106462306a36Sopenharmony_ci * Hold thermal_list_lock throughout the update to prevent the device 106562306a36Sopenharmony_ci * from going away while being updated. 106662306a36Sopenharmony_ci */ 106762306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (!thermal_cooling_device_present(cdev)) 107062306a36Sopenharmony_ci goto unlock_list; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * Update under the cdev lock to prevent the state from being set beyond 107462306a36Sopenharmony_ci * the new limit concurrently. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci mutex_lock(&cdev->lock); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (cdev->ops->get_max_state(cdev, &cdev->max_state)) 107962306a36Sopenharmony_ci goto unlock; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci thermal_cooling_device_stats_reinit(cdev); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci list_for_each_entry(ti, &cdev->thermal_instances, cdev_node) { 108462306a36Sopenharmony_ci if (ti->upper == cdev->max_state) 108562306a36Sopenharmony_ci continue; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (ti->upper < cdev->max_state) { 108862306a36Sopenharmony_ci if (ti->upper_no_limit) 108962306a36Sopenharmony_ci ti->upper = cdev->max_state; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci continue; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci ti->upper = cdev->max_state; 109562306a36Sopenharmony_ci if (ti->lower > ti->upper) 109662306a36Sopenharmony_ci ti->lower = ti->upper; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (ti->target == THERMAL_NO_TARGET) 109962306a36Sopenharmony_ci continue; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (ti->target > ti->upper) 110262306a36Sopenharmony_ci ti->target = ti->upper; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (cdev->ops->get_cur_state(cdev, &state) || state > cdev->max_state) 110662306a36Sopenharmony_ci goto unlock; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci thermal_cooling_device_stats_update(cdev, state); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ciunlock: 111162306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ciunlock_list: 111462306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_cooling_device_update); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci/** 111962306a36Sopenharmony_ci * thermal_cooling_device_unregister - removes a thermal cooling device 112062306a36Sopenharmony_ci * @cdev: the thermal cooling device to remove. 112162306a36Sopenharmony_ci * 112262306a36Sopenharmony_ci * thermal_cooling_device_unregister() must be called when a registered 112362306a36Sopenharmony_ci * thermal cooling device is no longer needed. 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_civoid thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci struct thermal_zone_device *tz; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (!cdev) 113062306a36Sopenharmony_ci return; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (!thermal_cooling_device_present(cdev)) { 113562306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 113662306a36Sopenharmony_ci return; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci list_del(&cdev->node); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Unbind all thermal zones associated with 'this' cdev */ 114262306a36Sopenharmony_ci list_for_each_entry(tz, &thermal_tz_list, node) { 114362306a36Sopenharmony_ci if (tz->ops->unbind) 114462306a36Sopenharmony_ci tz->ops->unbind(tz, cdev); 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci device_unregister(&cdev->device); 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic void bind_tz(struct thermal_zone_device *tz) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci int ret; 115662306a36Sopenharmony_ci struct thermal_cooling_device *pos = NULL; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (!tz->ops->bind) 115962306a36Sopenharmony_ci return; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_cdev_list, node) { 116462306a36Sopenharmony_ci ret = tz->ops->bind(tz, pos); 116562306a36Sopenharmony_ci if (ret) 116662306a36Sopenharmony_ci print_bind_err_msg(tz, pos, ret); 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic void thermal_set_delay_jiffies(unsigned long *delay_jiffies, int delay_ms) 117362306a36Sopenharmony_ci{ 117462306a36Sopenharmony_ci *delay_jiffies = msecs_to_jiffies(delay_ms); 117562306a36Sopenharmony_ci if (delay_ms > 1000) 117662306a36Sopenharmony_ci *delay_jiffies = round_jiffies(*delay_jiffies); 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ciint thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci int i, ret = -EINVAL; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (tz->ops->get_crit_temp) 118462306a36Sopenharmony_ci return tz->ops->get_crit_temp(tz, temp); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (!tz->trips) 118762306a36Sopenharmony_ci return -EINVAL; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci mutex_lock(&tz->lock); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci for (i = 0; i < tz->num_trips; i++) { 119262306a36Sopenharmony_ci if (tz->trips[i].type == THERMAL_TRIP_CRITICAL) { 119362306a36Sopenharmony_ci *temp = tz->trips[i].temperature; 119462306a36Sopenharmony_ci ret = 0; 119562306a36Sopenharmony_ci break; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci mutex_unlock(&tz->lock); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return ret; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_get_crit_temp); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci/** 120662306a36Sopenharmony_ci * thermal_zone_device_register_with_trips() - register a new thermal zone device 120762306a36Sopenharmony_ci * @type: the thermal zone device type 120862306a36Sopenharmony_ci * @trips: a pointer to an array of thermal trips 120962306a36Sopenharmony_ci * @num_trips: the number of trip points the thermal zone support 121062306a36Sopenharmony_ci * @mask: a bit string indicating the writeablility of trip points 121162306a36Sopenharmony_ci * @devdata: private device data 121262306a36Sopenharmony_ci * @ops: standard thermal zone device callbacks 121362306a36Sopenharmony_ci * @tzp: thermal zone platform parameters 121462306a36Sopenharmony_ci * @passive_delay: number of milliseconds to wait between polls when 121562306a36Sopenharmony_ci * performing passive cooling 121662306a36Sopenharmony_ci * @polling_delay: number of milliseconds to wait between polls when checking 121762306a36Sopenharmony_ci * whether trip points have been crossed (0 for interrupt 121862306a36Sopenharmony_ci * driven systems) 121962306a36Sopenharmony_ci * 122062306a36Sopenharmony_ci * This interface function adds a new thermal zone device (sensor) to 122162306a36Sopenharmony_ci * /sys/class/thermal folder as thermal_zone[0-*]. It tries to bind all the 122262306a36Sopenharmony_ci * thermal cooling devices registered at the same time. 122362306a36Sopenharmony_ci * thermal_zone_device_unregister() must be called when the device is no 122462306a36Sopenharmony_ci * longer needed. The passive cooling depends on the .get_trend() return value. 122562306a36Sopenharmony_ci * 122662306a36Sopenharmony_ci * Return: a pointer to the created struct thermal_zone_device or an 122762306a36Sopenharmony_ci * in case of error, an ERR_PTR. Caller must check return value with 122862306a36Sopenharmony_ci * IS_ERR*() helpers. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_cistruct thermal_zone_device * 123162306a36Sopenharmony_cithermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips, int mask, 123262306a36Sopenharmony_ci void *devdata, struct thermal_zone_device_ops *ops, 123362306a36Sopenharmony_ci const struct thermal_zone_params *tzp, int passive_delay, 123462306a36Sopenharmony_ci int polling_delay) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci struct thermal_zone_device *tz; 123762306a36Sopenharmony_ci int id; 123862306a36Sopenharmony_ci int result; 123962306a36Sopenharmony_ci int count; 124062306a36Sopenharmony_ci struct thermal_governor *governor; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (!type || strlen(type) == 0) { 124362306a36Sopenharmony_ci pr_err("No thermal zone type defined\n"); 124462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci if (strlen(type) >= THERMAL_NAME_LENGTH) { 124862306a36Sopenharmony_ci pr_err("Thermal zone name (%s) too long, should be under %d chars\n", 124962306a36Sopenharmony_ci type, THERMAL_NAME_LENGTH); 125062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* 125462306a36Sopenharmony_ci * Max trip count can't exceed 31 as the "mask >> num_trips" condition. 125562306a36Sopenharmony_ci * For example, shifting by 32 will result in compiler warning: 125662306a36Sopenharmony_ci * warning: right shift count >= width of type [-Wshift-count- overflow] 125762306a36Sopenharmony_ci * 125862306a36Sopenharmony_ci * Also "mask >> num_trips" will always be true with 32 bit shift. 125962306a36Sopenharmony_ci * E.g. mask = 0x80000000 for trip id 31 to be RW. Then 126062306a36Sopenharmony_ci * mask >> 32 = 0x80000000 126162306a36Sopenharmony_ci * This will result in failure for the below condition. 126262306a36Sopenharmony_ci * 126362306a36Sopenharmony_ci * Check will be true when the bit 31 of the mask is set. 126462306a36Sopenharmony_ci * 32 bit shift will cause overflow of 4 byte integer. 126562306a36Sopenharmony_ci */ 126662306a36Sopenharmony_ci if (num_trips > (BITS_PER_TYPE(int) - 1) || num_trips < 0 || mask >> num_trips) { 126762306a36Sopenharmony_ci pr_err("Incorrect number of thermal trips\n"); 126862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (!ops) { 127262306a36Sopenharmony_ci pr_err("Thermal zone device ops not defined\n"); 127362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci if (num_trips > 0 && !trips) 127762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (!thermal_class) 128062306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci tz = kzalloc(sizeof(*tz), GFP_KERNEL); 128362306a36Sopenharmony_ci if (!tz) 128462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (tzp) { 128762306a36Sopenharmony_ci tz->tzp = kmemdup(tzp, sizeof(*tzp), GFP_KERNEL); 128862306a36Sopenharmony_ci if (!tz->tzp) { 128962306a36Sopenharmony_ci result = -ENOMEM; 129062306a36Sopenharmony_ci goto free_tz; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci INIT_LIST_HEAD(&tz->thermal_instances); 129562306a36Sopenharmony_ci ida_init(&tz->ida); 129662306a36Sopenharmony_ci mutex_init(&tz->lock); 129762306a36Sopenharmony_ci id = ida_alloc(&thermal_tz_ida, GFP_KERNEL); 129862306a36Sopenharmony_ci if (id < 0) { 129962306a36Sopenharmony_ci result = id; 130062306a36Sopenharmony_ci goto free_tzp; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci tz->id = id; 130462306a36Sopenharmony_ci strscpy(tz->type, type, sizeof(tz->type)); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (!ops->critical) 130762306a36Sopenharmony_ci ops->critical = thermal_zone_device_critical; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci tz->ops = ops; 131062306a36Sopenharmony_ci tz->device.class = thermal_class; 131162306a36Sopenharmony_ci tz->devdata = devdata; 131262306a36Sopenharmony_ci tz->trips = trips; 131362306a36Sopenharmony_ci tz->num_trips = num_trips; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci thermal_set_delay_jiffies(&tz->passive_delay_jiffies, passive_delay); 131662306a36Sopenharmony_ci thermal_set_delay_jiffies(&tz->polling_delay_jiffies, polling_delay); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* sys I/F */ 131962306a36Sopenharmony_ci /* Add nodes that are always present via .groups */ 132062306a36Sopenharmony_ci result = thermal_zone_create_device_groups(tz, mask); 132162306a36Sopenharmony_ci if (result) 132262306a36Sopenharmony_ci goto remove_id; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* A new thermal zone needs to be updated anyway. */ 132562306a36Sopenharmony_ci atomic_set(&tz->need_update, 1); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci result = dev_set_name(&tz->device, "thermal_zone%d", tz->id); 132862306a36Sopenharmony_ci if (result) { 132962306a36Sopenharmony_ci thermal_zone_destroy_device_groups(tz); 133062306a36Sopenharmony_ci goto remove_id; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci result = device_register(&tz->device); 133362306a36Sopenharmony_ci if (result) 133462306a36Sopenharmony_ci goto release_device; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci for (count = 0; count < num_trips; count++) { 133762306a36Sopenharmony_ci struct thermal_trip trip; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci result = thermal_zone_get_trip(tz, count, &trip); 134062306a36Sopenharmony_ci if (result || !trip.temperature) 134162306a36Sopenharmony_ci set_bit(count, &tz->trips_disabled); 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci /* Update 'this' zone's governor information */ 134562306a36Sopenharmony_ci mutex_lock(&thermal_governor_lock); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (tz->tzp) 134862306a36Sopenharmony_ci governor = __find_governor(tz->tzp->governor_name); 134962306a36Sopenharmony_ci else 135062306a36Sopenharmony_ci governor = def_governor; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci result = thermal_set_governor(tz, governor); 135362306a36Sopenharmony_ci if (result) { 135462306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 135562306a36Sopenharmony_ci goto unregister; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci mutex_unlock(&thermal_governor_lock); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci if (!tz->tzp || !tz->tzp->no_hwmon) { 136162306a36Sopenharmony_ci result = thermal_add_hwmon_sysfs(tz); 136262306a36Sopenharmony_ci if (result) 136362306a36Sopenharmony_ci goto unregister; 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 136762306a36Sopenharmony_ci list_add_tail(&tz->node, &thermal_tz_list); 136862306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci /* Bind cooling devices for this zone */ 137162306a36Sopenharmony_ci bind_tz(tz); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci thermal_zone_device_init(tz); 137662306a36Sopenharmony_ci /* Update the new thermal zone and mark it as already updated. */ 137762306a36Sopenharmony_ci if (atomic_cmpxchg(&tz->need_update, 1, 0)) 137862306a36Sopenharmony_ci thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci thermal_notify_tz_create(tz->id, tz->type); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci return tz; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ciunregister: 138562306a36Sopenharmony_ci device_del(&tz->device); 138662306a36Sopenharmony_cirelease_device: 138762306a36Sopenharmony_ci put_device(&tz->device); 138862306a36Sopenharmony_ciremove_id: 138962306a36Sopenharmony_ci ida_free(&thermal_tz_ida, id); 139062306a36Sopenharmony_cifree_tzp: 139162306a36Sopenharmony_ci kfree(tz->tzp); 139262306a36Sopenharmony_cifree_tz: 139362306a36Sopenharmony_ci kfree(tz); 139462306a36Sopenharmony_ci return ERR_PTR(result); 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_register_with_trips); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistruct thermal_zone_device *thermal_tripless_zone_device_register( 139962306a36Sopenharmony_ci const char *type, 140062306a36Sopenharmony_ci void *devdata, 140162306a36Sopenharmony_ci struct thermal_zone_device_ops *ops, 140262306a36Sopenharmony_ci const struct thermal_zone_params *tzp) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci return thermal_zone_device_register_with_trips(type, NULL, 0, 0, devdata, 140562306a36Sopenharmony_ci ops, tzp, 0, 0); 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_tripless_zone_device_register); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_civoid *thermal_zone_device_priv(struct thermal_zone_device *tzd) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci return tzd->devdata; 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_priv); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ciconst char *thermal_zone_device_type(struct thermal_zone_device *tzd) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci return tzd->type; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_type); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ciint thermal_zone_device_id(struct thermal_zone_device *tzd) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci return tzd->id; 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_id); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistruct device *thermal_zone_device(struct thermal_zone_device *tzd) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci return &tzd->device; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci/** 143462306a36Sopenharmony_ci * thermal_zone_device_unregister - removes the registered thermal zone device 143562306a36Sopenharmony_ci * @tz: the thermal zone device to remove 143662306a36Sopenharmony_ci */ 143762306a36Sopenharmony_civoid thermal_zone_device_unregister(struct thermal_zone_device *tz) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci int tz_id; 144062306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 144162306a36Sopenharmony_ci struct thermal_zone_device *pos = NULL; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (!tz) 144462306a36Sopenharmony_ci return; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci tz_id = tz->id; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 144962306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_tz_list, node) 145062306a36Sopenharmony_ci if (pos == tz) 145162306a36Sopenharmony_ci break; 145262306a36Sopenharmony_ci if (pos != tz) { 145362306a36Sopenharmony_ci /* thermal zone device not found */ 145462306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 145562306a36Sopenharmony_ci return; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci list_del(&tz->node); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* Unbind all cdevs associated with 'this' thermal zone */ 146062306a36Sopenharmony_ci list_for_each_entry(cdev, &thermal_cdev_list, node) 146162306a36Sopenharmony_ci if (tz->ops->unbind) 146262306a36Sopenharmony_ci tz->ops->unbind(tz, cdev); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci cancel_delayed_work_sync(&tz->poll_queue); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci thermal_set_governor(tz, NULL); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci thermal_remove_hwmon_sysfs(tz); 147162306a36Sopenharmony_ci ida_free(&thermal_tz_ida, tz->id); 147262306a36Sopenharmony_ci ida_destroy(&tz->ida); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci mutex_lock(&tz->lock); 147562306a36Sopenharmony_ci device_del(&tz->device); 147662306a36Sopenharmony_ci mutex_unlock(&tz->lock); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci kfree(tz->tzp); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci put_device(&tz->device); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci thermal_notify_tz_delete(tz_id); 148362306a36Sopenharmony_ci} 148462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_device_unregister); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci/** 148762306a36Sopenharmony_ci * thermal_zone_get_zone_by_name() - search for a zone and returns its ref 148862306a36Sopenharmony_ci * @name: thermal zone name to fetch the temperature 148962306a36Sopenharmony_ci * 149062306a36Sopenharmony_ci * When only one zone is found with the passed name, returns a reference to it. 149162306a36Sopenharmony_ci * 149262306a36Sopenharmony_ci * Return: On success returns a reference to an unique thermal zone with 149362306a36Sopenharmony_ci * matching name equals to @name, an ERR_PTR otherwise (-EINVAL for invalid 149462306a36Sopenharmony_ci * paramenters, -ENODEV for not found and -EEXIST for multiple matches). 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_cistruct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct thermal_zone_device *pos = NULL, *ref = ERR_PTR(-EINVAL); 149962306a36Sopenharmony_ci unsigned int found = 0; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (!name) 150262306a36Sopenharmony_ci goto exit; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 150562306a36Sopenharmony_ci list_for_each_entry(pos, &thermal_tz_list, node) 150662306a36Sopenharmony_ci if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) { 150762306a36Sopenharmony_ci found++; 150862306a36Sopenharmony_ci ref = pos; 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* nothing has been found, thus an error code for it */ 151362306a36Sopenharmony_ci if (found == 0) 151462306a36Sopenharmony_ci ref = ERR_PTR(-ENODEV); 151562306a36Sopenharmony_ci else if (found > 1) 151662306a36Sopenharmony_ci /* Success only when an unique zone is found */ 151762306a36Sopenharmony_ci ref = ERR_PTR(-EEXIST); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ciexit: 152062306a36Sopenharmony_ci return ref; 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic int thermal_pm_notify(struct notifier_block *nb, 152562306a36Sopenharmony_ci unsigned long mode, void *_unused) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci struct thermal_zone_device *tz; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci switch (mode) { 153062306a36Sopenharmony_ci case PM_HIBERNATION_PREPARE: 153162306a36Sopenharmony_ci case PM_RESTORE_PREPARE: 153262306a36Sopenharmony_ci case PM_SUSPEND_PREPARE: 153362306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci list_for_each_entry(tz, &thermal_tz_list, node) { 153662306a36Sopenharmony_ci mutex_lock(&tz->lock); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci tz->suspended = true; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci mutex_unlock(&tz->lock); 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 154462306a36Sopenharmony_ci break; 154562306a36Sopenharmony_ci case PM_POST_HIBERNATION: 154662306a36Sopenharmony_ci case PM_POST_RESTORE: 154762306a36Sopenharmony_ci case PM_POST_SUSPEND: 154862306a36Sopenharmony_ci mutex_lock(&thermal_list_lock); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci list_for_each_entry(tz, &thermal_tz_list, node) { 155162306a36Sopenharmony_ci mutex_lock(&tz->lock); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci tz->suspended = false; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci thermal_zone_device_init(tz); 155662306a36Sopenharmony_ci __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci mutex_unlock(&tz->lock); 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci mutex_unlock(&thermal_list_lock); 156262306a36Sopenharmony_ci break; 156362306a36Sopenharmony_ci default: 156462306a36Sopenharmony_ci break; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci return 0; 156762306a36Sopenharmony_ci} 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_cistatic struct notifier_block thermal_pm_nb = { 157062306a36Sopenharmony_ci .notifier_call = thermal_pm_notify, 157162306a36Sopenharmony_ci}; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic int __init thermal_init(void) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci int result; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci result = thermal_netlink_init(); 157862306a36Sopenharmony_ci if (result) 157962306a36Sopenharmony_ci goto error; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci result = thermal_register_governors(); 158262306a36Sopenharmony_ci if (result) 158362306a36Sopenharmony_ci goto unregister_netlink; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci thermal_class = kzalloc(sizeof(*thermal_class), GFP_KERNEL); 158662306a36Sopenharmony_ci if (!thermal_class) { 158762306a36Sopenharmony_ci result = -ENOMEM; 158862306a36Sopenharmony_ci goto unregister_governors; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci thermal_class->name = "thermal"; 159262306a36Sopenharmony_ci thermal_class->dev_release = thermal_release; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci result = class_register(thermal_class); 159562306a36Sopenharmony_ci if (result) { 159662306a36Sopenharmony_ci kfree(thermal_class); 159762306a36Sopenharmony_ci thermal_class = NULL; 159862306a36Sopenharmony_ci goto unregister_governors; 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci result = register_pm_notifier(&thermal_pm_nb); 160262306a36Sopenharmony_ci if (result) 160362306a36Sopenharmony_ci pr_warn("Thermal: Can not register suspend notifier, return %d\n", 160462306a36Sopenharmony_ci result); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci return 0; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ciunregister_governors: 160962306a36Sopenharmony_ci thermal_unregister_governors(); 161062306a36Sopenharmony_ciunregister_netlink: 161162306a36Sopenharmony_ci thermal_netlink_exit(); 161262306a36Sopenharmony_cierror: 161362306a36Sopenharmony_ci mutex_destroy(&thermal_list_lock); 161462306a36Sopenharmony_ci mutex_destroy(&thermal_governor_lock); 161562306a36Sopenharmony_ci return result; 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_cipostcore_initcall(thermal_init); 1618