xref: /kernel/linux/linux-5.10/drivers/acpi/thermal.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
68c2ecf20Sopenharmony_ci *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  This driver fully implements the ACPI thermal policy as described in the
98c2ecf20Sopenharmony_ci *  ACPI 2.0 Specification.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *  TBD: 1. Implement passive cooling hysteresis.
128c2ecf20Sopenharmony_ci *       2. Enhance passive cooling (CPU) states/limit interface to support
138c2ecf20Sopenharmony_ci *          concepts of 'multiple limiters', upper/lower limits, etc.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/dmi.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/types.h>
228c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
238c2ecf20Sopenharmony_ci#include <linux/kmod.h>
248c2ecf20Sopenharmony_ci#include <linux/reboot.h>
258c2ecf20Sopenharmony_ci#include <linux/device.h>
268c2ecf20Sopenharmony_ci#include <linux/thermal.h>
278c2ecf20Sopenharmony_ci#include <linux/acpi.h>
288c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
308c2ecf20Sopenharmony_ci#include <linux/units.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define PREFIX "ACPI: "
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define ACPI_THERMAL_CLASS		"thermal_zone"
358c2ecf20Sopenharmony_ci#define ACPI_THERMAL_DEVICE_NAME	"Thermal Zone"
368c2ecf20Sopenharmony_ci#define ACPI_THERMAL_NOTIFY_TEMPERATURE	0x80
378c2ecf20Sopenharmony_ci#define ACPI_THERMAL_NOTIFY_THRESHOLDS	0x81
388c2ecf20Sopenharmony_ci#define ACPI_THERMAL_NOTIFY_DEVICES	0x82
398c2ecf20Sopenharmony_ci#define ACPI_THERMAL_NOTIFY_CRITICAL	0xF0
408c2ecf20Sopenharmony_ci#define ACPI_THERMAL_NOTIFY_HOT		0xF1
418c2ecf20Sopenharmony_ci#define ACPI_THERMAL_MODE_ACTIVE	0x00
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define ACPI_THERMAL_MAX_ACTIVE	10
448c2ecf20Sopenharmony_ci#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define _COMPONENT		ACPI_THERMAL_COMPONENT
478c2ecf20Sopenharmony_ciACPI_MODULE_NAME("thermal");
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Diefenbaugh");
508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI Thermal Zone Driver");
518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int act;
548c2ecf20Sopenharmony_cimodule_param(act, int, 0644);
558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(act, "Disable or override all lowest active trip points.");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int crt;
588c2ecf20Sopenharmony_cimodule_param(crt, int, 0644);
598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(crt, "Disable or lower all critical trip points.");
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int tzp;
628c2ecf20Sopenharmony_cimodule_param(tzp, int, 0444);
638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int nocrt;
668c2ecf20Sopenharmony_cimodule_param(nocrt, int, 0);
678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nocrt, "Set to take no action upon ACPI thermal zone critical trips points.");
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int off;
708c2ecf20Sopenharmony_cimodule_param(off, int, 0);
718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(off, "Set to disable ACPI thermal support.");
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int psv;
748c2ecf20Sopenharmony_cimodule_param(psv, int, 0644);
758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(psv, "Disable or override all passive trip points.");
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct workqueue_struct *acpi_thermal_pm_queue;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int acpi_thermal_add(struct acpi_device *device);
808c2ecf20Sopenharmony_cistatic int acpi_thermal_remove(struct acpi_device *device);
818c2ecf20Sopenharmony_cistatic void acpi_thermal_notify(struct acpi_device *device, u32 event);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct acpi_device_id  thermal_device_ids[] = {
848c2ecf20Sopenharmony_ci	{ACPI_THERMAL_HID, 0},
858c2ecf20Sopenharmony_ci	{"", 0},
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, thermal_device_ids);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
908c2ecf20Sopenharmony_cistatic int acpi_thermal_suspend(struct device *dev);
918c2ecf20Sopenharmony_cistatic int acpi_thermal_resume(struct device *dev);
928c2ecf20Sopenharmony_ci#else
938c2ecf20Sopenharmony_ci#define acpi_thermal_suspend NULL
948c2ecf20Sopenharmony_ci#define acpi_thermal_resume NULL
958c2ecf20Sopenharmony_ci#endif
968c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_thermal_driver = {
998c2ecf20Sopenharmony_ci	.name = "thermal",
1008c2ecf20Sopenharmony_ci	.class = ACPI_THERMAL_CLASS,
1018c2ecf20Sopenharmony_ci	.ids = thermal_device_ids,
1028c2ecf20Sopenharmony_ci	.ops = {
1038c2ecf20Sopenharmony_ci		.add = acpi_thermal_add,
1048c2ecf20Sopenharmony_ci		.remove = acpi_thermal_remove,
1058c2ecf20Sopenharmony_ci		.notify = acpi_thermal_notify,
1068c2ecf20Sopenharmony_ci		},
1078c2ecf20Sopenharmony_ci	.drv.pm = &acpi_thermal_pm,
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistruct acpi_thermal_state {
1118c2ecf20Sopenharmony_ci	u8 critical:1;
1128c2ecf20Sopenharmony_ci	u8 hot:1;
1138c2ecf20Sopenharmony_ci	u8 passive:1;
1148c2ecf20Sopenharmony_ci	u8 active:1;
1158c2ecf20Sopenharmony_ci	u8 reserved:4;
1168c2ecf20Sopenharmony_ci	int active_index;
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct acpi_thermal_state_flags {
1208c2ecf20Sopenharmony_ci	u8 valid:1;
1218c2ecf20Sopenharmony_ci	u8 enabled:1;
1228c2ecf20Sopenharmony_ci	u8 reserved:6;
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct acpi_thermal_critical {
1268c2ecf20Sopenharmony_ci	struct acpi_thermal_state_flags flags;
1278c2ecf20Sopenharmony_ci	unsigned long temperature;
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistruct acpi_thermal_hot {
1318c2ecf20Sopenharmony_ci	struct acpi_thermal_state_flags flags;
1328c2ecf20Sopenharmony_ci	unsigned long temperature;
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistruct acpi_thermal_passive {
1368c2ecf20Sopenharmony_ci	struct acpi_thermal_state_flags flags;
1378c2ecf20Sopenharmony_ci	unsigned long temperature;
1388c2ecf20Sopenharmony_ci	unsigned long tc1;
1398c2ecf20Sopenharmony_ci	unsigned long tc2;
1408c2ecf20Sopenharmony_ci	unsigned long tsp;
1418c2ecf20Sopenharmony_ci	struct acpi_handle_list devices;
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistruct acpi_thermal_active {
1458c2ecf20Sopenharmony_ci	struct acpi_thermal_state_flags flags;
1468c2ecf20Sopenharmony_ci	unsigned long temperature;
1478c2ecf20Sopenharmony_ci	struct acpi_handle_list devices;
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct acpi_thermal_trips {
1518c2ecf20Sopenharmony_ci	struct acpi_thermal_critical critical;
1528c2ecf20Sopenharmony_ci	struct acpi_thermal_hot hot;
1538c2ecf20Sopenharmony_ci	struct acpi_thermal_passive passive;
1548c2ecf20Sopenharmony_ci	struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE];
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistruct acpi_thermal_flags {
1588c2ecf20Sopenharmony_ci	u8 cooling_mode:1;	/* _SCP */
1598c2ecf20Sopenharmony_ci	u8 devices:1;		/* _TZD */
1608c2ecf20Sopenharmony_ci	u8 reserved:6;
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistruct acpi_thermal {
1648c2ecf20Sopenharmony_ci	struct acpi_device * device;
1658c2ecf20Sopenharmony_ci	acpi_bus_id name;
1668c2ecf20Sopenharmony_ci	unsigned long temperature;
1678c2ecf20Sopenharmony_ci	unsigned long last_temperature;
1688c2ecf20Sopenharmony_ci	unsigned long polling_frequency;
1698c2ecf20Sopenharmony_ci	volatile u8 zombie;
1708c2ecf20Sopenharmony_ci	struct acpi_thermal_flags flags;
1718c2ecf20Sopenharmony_ci	struct acpi_thermal_state state;
1728c2ecf20Sopenharmony_ci	struct acpi_thermal_trips trips;
1738c2ecf20Sopenharmony_ci	struct acpi_handle_list devices;
1748c2ecf20Sopenharmony_ci	struct thermal_zone_device *thermal_zone;
1758c2ecf20Sopenharmony_ci	int kelvin_offset;	/* in millidegrees */
1768c2ecf20Sopenharmony_ci	struct work_struct thermal_check_work;
1778c2ecf20Sopenharmony_ci	struct mutex thermal_check_lock;
1788c2ecf20Sopenharmony_ci	refcount_t thermal_check_count;
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------
1828c2ecf20Sopenharmony_ci                             Thermal Zone Management
1838c2ecf20Sopenharmony_ci   -------------------------------------------------------------------------- */
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int acpi_thermal_get_temperature(struct acpi_thermal *tz)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
1888c2ecf20Sopenharmony_ci	unsigned long long tmp;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (!tz)
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	tz->last_temperature = tz->temperature;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);
1968c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
1978c2ecf20Sopenharmony_ci		return -ENODEV;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	tz->temperature = tmp;
2008c2ecf20Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n",
2018c2ecf20Sopenharmony_ci			  tz->temperature));
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
2098c2ecf20Sopenharmony_ci	unsigned long long tmp;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (!tz)
2128c2ecf20Sopenharmony_ci		return -EINVAL;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(tz->device->handle, "_TZP", NULL, &tmp);
2158c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
2168c2ecf20Sopenharmony_ci		return -ENODEV;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	tz->polling_frequency = tmp;
2198c2ecf20Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n",
2208c2ecf20Sopenharmony_ci			  tz->polling_frequency));
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (!tz)
2288c2ecf20Sopenharmony_ci		return -EINVAL;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle,
2318c2ecf20Sopenharmony_ci						    "_SCP", mode)))
2328c2ecf20Sopenharmony_ci		return -ENODEV;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci#define ACPI_TRIPS_CRITICAL	0x01
2388c2ecf20Sopenharmony_ci#define ACPI_TRIPS_HOT		0x02
2398c2ecf20Sopenharmony_ci#define ACPI_TRIPS_PASSIVE	0x04
2408c2ecf20Sopenharmony_ci#define ACPI_TRIPS_ACTIVE	0x08
2418c2ecf20Sopenharmony_ci#define ACPI_TRIPS_DEVICES	0x10
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci#define ACPI_TRIPS_REFRESH_THRESHOLDS	(ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
2448c2ecf20Sopenharmony_ci#define ACPI_TRIPS_REFRESH_DEVICES	ACPI_TRIPS_DEVICES
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci#define ACPI_TRIPS_INIT      (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT |	\
2478c2ecf20Sopenharmony_ci			      ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE |	\
2488c2ecf20Sopenharmony_ci			      ACPI_TRIPS_DEVICES)
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * This exception is thrown out in two cases:
2528c2ecf20Sopenharmony_ci * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
2538c2ecf20Sopenharmony_ci *   when re-evaluating the AML code.
2548c2ecf20Sopenharmony_ci * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
2558c2ecf20Sopenharmony_ci *   We need to re-bind the cooling devices of a thermal zone when this occurs.
2568c2ecf20Sopenharmony_ci */
2578c2ecf20Sopenharmony_ci#define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str)	\
2588c2ecf20Sopenharmony_cido {	\
2598c2ecf20Sopenharmony_ci	if (flags != ACPI_TRIPS_INIT)	\
2608c2ecf20Sopenharmony_ci		ACPI_EXCEPTION((AE_INFO, AE_ERROR,	\
2618c2ecf20Sopenharmony_ci		"ACPI thermal trip point %s changed\n"	\
2628c2ecf20Sopenharmony_ci		"Please send acpidump to linux-acpi@vger.kernel.org", str)); \
2638c2ecf20Sopenharmony_ci} while (0)
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
2688c2ecf20Sopenharmony_ci	unsigned long long tmp;
2698c2ecf20Sopenharmony_ci	struct acpi_handle_list devices;
2708c2ecf20Sopenharmony_ci	int valid = 0;
2718c2ecf20Sopenharmony_ci	int i;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Critical Shutdown */
2748c2ecf20Sopenharmony_ci	if (flag & ACPI_TRIPS_CRITICAL) {
2758c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(tz->device->handle,
2768c2ecf20Sopenharmony_ci				"_CRT", NULL, &tmp);
2778c2ecf20Sopenharmony_ci		tz->trips.critical.temperature = tmp;
2788c2ecf20Sopenharmony_ci		/*
2798c2ecf20Sopenharmony_ci		 * Treat freezing temperatures as invalid as well; some
2808c2ecf20Sopenharmony_ci		 * BIOSes return really low values and cause reboots at startup.
2818c2ecf20Sopenharmony_ci		 * Below zero (Celsius) values clearly aren't right for sure..
2828c2ecf20Sopenharmony_ci		 * ... so lets discard those as invalid.
2838c2ecf20Sopenharmony_ci		 */
2848c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status)) {
2858c2ecf20Sopenharmony_ci			tz->trips.critical.flags.valid = 0;
2868c2ecf20Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
2878c2ecf20Sopenharmony_ci					  "No critical threshold\n"));
2888c2ecf20Sopenharmony_ci		} else if (tmp <= 2732) {
2898c2ecf20Sopenharmony_ci			pr_warn(FW_BUG "Invalid critical threshold (%llu)\n",
2908c2ecf20Sopenharmony_ci				tmp);
2918c2ecf20Sopenharmony_ci			tz->trips.critical.flags.valid = 0;
2928c2ecf20Sopenharmony_ci		} else {
2938c2ecf20Sopenharmony_ci			tz->trips.critical.flags.valid = 1;
2948c2ecf20Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
2958c2ecf20Sopenharmony_ci					  "Found critical threshold [%lu]\n",
2968c2ecf20Sopenharmony_ci					  tz->trips.critical.temperature));
2978c2ecf20Sopenharmony_ci		}
2988c2ecf20Sopenharmony_ci		if (tz->trips.critical.flags.valid == 1) {
2998c2ecf20Sopenharmony_ci			if (crt == -1) {
3008c2ecf20Sopenharmony_ci				tz->trips.critical.flags.valid = 0;
3018c2ecf20Sopenharmony_ci			} else if (crt > 0) {
3028c2ecf20Sopenharmony_ci				unsigned long crt_k = celsius_to_deci_kelvin(crt);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci				/*
3058c2ecf20Sopenharmony_ci				 * Allow override critical threshold
3068c2ecf20Sopenharmony_ci				 */
3078c2ecf20Sopenharmony_ci				if (crt_k > tz->trips.critical.temperature)
3088c2ecf20Sopenharmony_ci					pr_warn(PREFIX "Critical threshold %d C\n",
3098c2ecf20Sopenharmony_ci						crt);
3108c2ecf20Sopenharmony_ci				tz->trips.critical.temperature = crt_k;
3118c2ecf20Sopenharmony_ci			}
3128c2ecf20Sopenharmony_ci		}
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* Critical Sleep (optional) */
3168c2ecf20Sopenharmony_ci	if (flag & ACPI_TRIPS_HOT) {
3178c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(tz->device->handle,
3188c2ecf20Sopenharmony_ci				"_HOT", NULL, &tmp);
3198c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status)) {
3208c2ecf20Sopenharmony_ci			tz->trips.hot.flags.valid = 0;
3218c2ecf20Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
3228c2ecf20Sopenharmony_ci					"No hot threshold\n"));
3238c2ecf20Sopenharmony_ci		} else {
3248c2ecf20Sopenharmony_ci			tz->trips.hot.temperature = tmp;
3258c2ecf20Sopenharmony_ci			tz->trips.hot.flags.valid = 1;
3268c2ecf20Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
3278c2ecf20Sopenharmony_ci					"Found hot threshold [%lu]\n",
3288c2ecf20Sopenharmony_ci					tz->trips.hot.temperature));
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Passive (optional) */
3338c2ecf20Sopenharmony_ci	if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) ||
3348c2ecf20Sopenharmony_ci		(flag == ACPI_TRIPS_INIT)) {
3358c2ecf20Sopenharmony_ci		valid = tz->trips.passive.flags.valid;
3368c2ecf20Sopenharmony_ci		if (psv == -1) {
3378c2ecf20Sopenharmony_ci			status = AE_SUPPORT;
3388c2ecf20Sopenharmony_ci		} else if (psv > 0) {
3398c2ecf20Sopenharmony_ci			tmp = celsius_to_deci_kelvin(psv);
3408c2ecf20Sopenharmony_ci			status = AE_OK;
3418c2ecf20Sopenharmony_ci		} else {
3428c2ecf20Sopenharmony_ci			status = acpi_evaluate_integer(tz->device->handle,
3438c2ecf20Sopenharmony_ci				"_PSV", NULL, &tmp);
3448c2ecf20Sopenharmony_ci		}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
3478c2ecf20Sopenharmony_ci			tz->trips.passive.flags.valid = 0;
3488c2ecf20Sopenharmony_ci		else {
3498c2ecf20Sopenharmony_ci			tz->trips.passive.temperature = tmp;
3508c2ecf20Sopenharmony_ci			tz->trips.passive.flags.valid = 1;
3518c2ecf20Sopenharmony_ci			if (flag == ACPI_TRIPS_INIT) {
3528c2ecf20Sopenharmony_ci				status = acpi_evaluate_integer(
3538c2ecf20Sopenharmony_ci						tz->device->handle, "_TC1",
3548c2ecf20Sopenharmony_ci						NULL, &tmp);
3558c2ecf20Sopenharmony_ci				if (ACPI_FAILURE(status))
3568c2ecf20Sopenharmony_ci					tz->trips.passive.flags.valid = 0;
3578c2ecf20Sopenharmony_ci				else
3588c2ecf20Sopenharmony_ci					tz->trips.passive.tc1 = tmp;
3598c2ecf20Sopenharmony_ci				status = acpi_evaluate_integer(
3608c2ecf20Sopenharmony_ci						tz->device->handle, "_TC2",
3618c2ecf20Sopenharmony_ci						NULL, &tmp);
3628c2ecf20Sopenharmony_ci				if (ACPI_FAILURE(status))
3638c2ecf20Sopenharmony_ci					tz->trips.passive.flags.valid = 0;
3648c2ecf20Sopenharmony_ci				else
3658c2ecf20Sopenharmony_ci					tz->trips.passive.tc2 = tmp;
3668c2ecf20Sopenharmony_ci				status = acpi_evaluate_integer(
3678c2ecf20Sopenharmony_ci						tz->device->handle, "_TSP",
3688c2ecf20Sopenharmony_ci						NULL, &tmp);
3698c2ecf20Sopenharmony_ci				if (ACPI_FAILURE(status))
3708c2ecf20Sopenharmony_ci					tz->trips.passive.flags.valid = 0;
3718c2ecf20Sopenharmony_ci				else
3728c2ecf20Sopenharmony_ci					tz->trips.passive.tsp = tmp;
3738c2ecf20Sopenharmony_ci			}
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci	if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
3778c2ecf20Sopenharmony_ci		memset(&devices, 0, sizeof(struct acpi_handle_list));
3788c2ecf20Sopenharmony_ci		status = acpi_evaluate_reference(tz->device->handle, "_PSL",
3798c2ecf20Sopenharmony_ci							NULL, &devices);
3808c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status)) {
3818c2ecf20Sopenharmony_ci			pr_warn(PREFIX "Invalid passive threshold\n");
3828c2ecf20Sopenharmony_ci			tz->trips.passive.flags.valid = 0;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci		else
3858c2ecf20Sopenharmony_ci			tz->trips.passive.flags.valid = 1;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		if (memcmp(&tz->trips.passive.devices, &devices,
3888c2ecf20Sopenharmony_ci				sizeof(struct acpi_handle_list))) {
3898c2ecf20Sopenharmony_ci			memcpy(&tz->trips.passive.devices, &devices,
3908c2ecf20Sopenharmony_ci				sizeof(struct acpi_handle_list));
3918c2ecf20Sopenharmony_ci			ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
3958c2ecf20Sopenharmony_ci		if (valid != tz->trips.passive.flags.valid)
3968c2ecf20Sopenharmony_ci				ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Active (optional) */
4008c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
4018c2ecf20Sopenharmony_ci		char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
4028c2ecf20Sopenharmony_ci		valid = tz->trips.active[i].flags.valid;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		if (act == -1)
4058c2ecf20Sopenharmony_ci			break; /* disable all active trip points */
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) &&
4088c2ecf20Sopenharmony_ci			tz->trips.active[i].flags.valid)) {
4098c2ecf20Sopenharmony_ci			status = acpi_evaluate_integer(tz->device->handle,
4108c2ecf20Sopenharmony_ci							name, NULL, &tmp);
4118c2ecf20Sopenharmony_ci			if (ACPI_FAILURE(status)) {
4128c2ecf20Sopenharmony_ci				tz->trips.active[i].flags.valid = 0;
4138c2ecf20Sopenharmony_ci				if (i == 0)
4148c2ecf20Sopenharmony_ci					break;
4158c2ecf20Sopenharmony_ci				if (act <= 0)
4168c2ecf20Sopenharmony_ci					break;
4178c2ecf20Sopenharmony_ci				if (i == 1)
4188c2ecf20Sopenharmony_ci					tz->trips.active[0].temperature =
4198c2ecf20Sopenharmony_ci						celsius_to_deci_kelvin(act);
4208c2ecf20Sopenharmony_ci				else
4218c2ecf20Sopenharmony_ci					/*
4228c2ecf20Sopenharmony_ci					 * Don't allow override higher than
4238c2ecf20Sopenharmony_ci					 * the next higher trip point
4248c2ecf20Sopenharmony_ci					 */
4258c2ecf20Sopenharmony_ci					tz->trips.active[i - 1].temperature =
4268c2ecf20Sopenharmony_ci						(tz->trips.active[i - 2].temperature <
4278c2ecf20Sopenharmony_ci						celsius_to_deci_kelvin(act) ?
4288c2ecf20Sopenharmony_ci						tz->trips.active[i - 2].temperature :
4298c2ecf20Sopenharmony_ci						celsius_to_deci_kelvin(act));
4308c2ecf20Sopenharmony_ci				break;
4318c2ecf20Sopenharmony_ci			} else {
4328c2ecf20Sopenharmony_ci				tz->trips.active[i].temperature = tmp;
4338c2ecf20Sopenharmony_ci				tz->trips.active[i].flags.valid = 1;
4348c2ecf20Sopenharmony_ci			}
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		name[2] = 'L';
4388c2ecf20Sopenharmony_ci		if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
4398c2ecf20Sopenharmony_ci			memset(&devices, 0, sizeof(struct acpi_handle_list));
4408c2ecf20Sopenharmony_ci			status = acpi_evaluate_reference(tz->device->handle,
4418c2ecf20Sopenharmony_ci						name, NULL, &devices);
4428c2ecf20Sopenharmony_ci			if (ACPI_FAILURE(status)) {
4438c2ecf20Sopenharmony_ci				pr_warn(PREFIX "Invalid active%d threshold\n",
4448c2ecf20Sopenharmony_ci					i);
4458c2ecf20Sopenharmony_ci				tz->trips.active[i].flags.valid = 0;
4468c2ecf20Sopenharmony_ci			}
4478c2ecf20Sopenharmony_ci			else
4488c2ecf20Sopenharmony_ci				tz->trips.active[i].flags.valid = 1;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci			if (memcmp(&tz->trips.active[i].devices, &devices,
4518c2ecf20Sopenharmony_ci					sizeof(struct acpi_handle_list))) {
4528c2ecf20Sopenharmony_ci				memcpy(&tz->trips.active[i].devices, &devices,
4538c2ecf20Sopenharmony_ci					sizeof(struct acpi_handle_list));
4548c2ecf20Sopenharmony_ci				ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
4558c2ecf20Sopenharmony_ci			}
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci		if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
4588c2ecf20Sopenharmony_ci			if (valid != tz->trips.active[i].flags.valid)
4598c2ecf20Sopenharmony_ci				ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci		if (!tz->trips.active[i].flags.valid)
4628c2ecf20Sopenharmony_ci			break;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (flag & ACPI_TRIPS_DEVICES) {
4668c2ecf20Sopenharmony_ci		memset(&devices, 0, sizeof(devices));
4678c2ecf20Sopenharmony_ci		status = acpi_evaluate_reference(tz->device->handle, "_TZD",
4688c2ecf20Sopenharmony_ci						NULL, &devices);
4698c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status)
4708c2ecf20Sopenharmony_ci		    && memcmp(&tz->devices, &devices, sizeof(devices))) {
4718c2ecf20Sopenharmony_ci			tz->devices = devices;
4728c2ecf20Sopenharmony_ci			ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	int i, valid, ret = acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (ret)
4848c2ecf20Sopenharmony_ci		return ret;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	valid = tz->trips.critical.flags.valid |
4878c2ecf20Sopenharmony_ci		tz->trips.hot.flags.valid |
4888c2ecf20Sopenharmony_ci		tz->trips.passive.flags.valid;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
4918c2ecf20Sopenharmony_ci		valid |= tz->trips.active[i].flags.valid;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (!valid) {
4948c2ecf20Sopenharmony_ci		pr_warn(FW_BUG "No valid trip found\n");
4958c2ecf20Sopenharmony_ci		return -ENODEV;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/* sys I/F for generic thermal sysfs support */
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
5058c2ecf20Sopenharmony_ci	int result;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (!tz)
5088c2ecf20Sopenharmony_ci		return -EINVAL;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	result = acpi_thermal_get_temperature(tz);
5118c2ecf20Sopenharmony_ci	if (result)
5128c2ecf20Sopenharmony_ci		return result;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	*temp = deci_kelvin_to_millicelsius_with_offset(tz->temperature,
5158c2ecf20Sopenharmony_ci							tz->kelvin_offset);
5168c2ecf20Sopenharmony_ci	return 0;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int thermal_get_trip_type(struct thermal_zone_device *thermal,
5208c2ecf20Sopenharmony_ci				 int trip, enum thermal_trip_type *type)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
5238c2ecf20Sopenharmony_ci	int i;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (!tz || trip < 0)
5268c2ecf20Sopenharmony_ci		return -EINVAL;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (tz->trips.critical.flags.valid) {
5298c2ecf20Sopenharmony_ci		if (!trip) {
5308c2ecf20Sopenharmony_ci			*type = THERMAL_TRIP_CRITICAL;
5318c2ecf20Sopenharmony_ci			return 0;
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci		trip--;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (tz->trips.hot.flags.valid) {
5378c2ecf20Sopenharmony_ci		if (!trip) {
5388c2ecf20Sopenharmony_ci			*type = THERMAL_TRIP_HOT;
5398c2ecf20Sopenharmony_ci			return 0;
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci		trip--;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (tz->trips.passive.flags.valid) {
5458c2ecf20Sopenharmony_ci		if (!trip) {
5468c2ecf20Sopenharmony_ci			*type = THERMAL_TRIP_PASSIVE;
5478c2ecf20Sopenharmony_ci			return 0;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci		trip--;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
5538c2ecf20Sopenharmony_ci		tz->trips.active[i].flags.valid; i++) {
5548c2ecf20Sopenharmony_ci		if (!trip) {
5558c2ecf20Sopenharmony_ci			*type = THERMAL_TRIP_ACTIVE;
5568c2ecf20Sopenharmony_ci			return 0;
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci		trip--;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	return -EINVAL;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int thermal_get_trip_temp(struct thermal_zone_device *thermal,
5658c2ecf20Sopenharmony_ci				 int trip, int *temp)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
5688c2ecf20Sopenharmony_ci	int i;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (!tz || trip < 0)
5718c2ecf20Sopenharmony_ci		return -EINVAL;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (tz->trips.critical.flags.valid) {
5748c2ecf20Sopenharmony_ci		if (!trip) {
5758c2ecf20Sopenharmony_ci			*temp = deci_kelvin_to_millicelsius_with_offset(
5768c2ecf20Sopenharmony_ci				tz->trips.critical.temperature,
5778c2ecf20Sopenharmony_ci				tz->kelvin_offset);
5788c2ecf20Sopenharmony_ci			return 0;
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci		trip--;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (tz->trips.hot.flags.valid) {
5848c2ecf20Sopenharmony_ci		if (!trip) {
5858c2ecf20Sopenharmony_ci			*temp = deci_kelvin_to_millicelsius_with_offset(
5868c2ecf20Sopenharmony_ci				tz->trips.hot.temperature,
5878c2ecf20Sopenharmony_ci				tz->kelvin_offset);
5888c2ecf20Sopenharmony_ci			return 0;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci		trip--;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	if (tz->trips.passive.flags.valid) {
5948c2ecf20Sopenharmony_ci		if (!trip) {
5958c2ecf20Sopenharmony_ci			*temp = deci_kelvin_to_millicelsius_with_offset(
5968c2ecf20Sopenharmony_ci				tz->trips.passive.temperature,
5978c2ecf20Sopenharmony_ci				tz->kelvin_offset);
5988c2ecf20Sopenharmony_ci			return 0;
5998c2ecf20Sopenharmony_ci		}
6008c2ecf20Sopenharmony_ci		trip--;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
6048c2ecf20Sopenharmony_ci		tz->trips.active[i].flags.valid; i++) {
6058c2ecf20Sopenharmony_ci		if (!trip) {
6068c2ecf20Sopenharmony_ci			*temp = deci_kelvin_to_millicelsius_with_offset(
6078c2ecf20Sopenharmony_ci				tz->trips.active[i].temperature,
6088c2ecf20Sopenharmony_ci				tz->kelvin_offset);
6098c2ecf20Sopenharmony_ci			return 0;
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci		trip--;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return -EINVAL;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic int thermal_get_crit_temp(struct thermal_zone_device *thermal,
6188c2ecf20Sopenharmony_ci				int *temperature)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (tz->trips.critical.flags.valid) {
6238c2ecf20Sopenharmony_ci		*temperature = deci_kelvin_to_millicelsius_with_offset(
6248c2ecf20Sopenharmony_ci				tz->trips.critical.temperature,
6258c2ecf20Sopenharmony_ci				tz->kelvin_offset);
6268c2ecf20Sopenharmony_ci		return 0;
6278c2ecf20Sopenharmony_ci	} else
6288c2ecf20Sopenharmony_ci		return -EINVAL;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int thermal_get_trend(struct thermal_zone_device *thermal,
6328c2ecf20Sopenharmony_ci				int trip, enum thermal_trend *trend)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
6358c2ecf20Sopenharmony_ci	enum thermal_trip_type type;
6368c2ecf20Sopenharmony_ci	int i;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (thermal_get_trip_type(thermal, trip, &type))
6398c2ecf20Sopenharmony_ci		return -EINVAL;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (type == THERMAL_TRIP_ACTIVE) {
6428c2ecf20Sopenharmony_ci		int trip_temp;
6438c2ecf20Sopenharmony_ci		int temp = deci_kelvin_to_millicelsius_with_offset(
6448c2ecf20Sopenharmony_ci					tz->temperature, tz->kelvin_offset);
6458c2ecf20Sopenharmony_ci		if (thermal_get_trip_temp(thermal, trip, &trip_temp))
6468c2ecf20Sopenharmony_ci			return -EINVAL;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci		if (temp > trip_temp) {
6498c2ecf20Sopenharmony_ci			*trend = THERMAL_TREND_RAISING;
6508c2ecf20Sopenharmony_ci			return 0;
6518c2ecf20Sopenharmony_ci		} else {
6528c2ecf20Sopenharmony_ci			/* Fall back on default trend */
6538c2ecf20Sopenharmony_ci			return -EINVAL;
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/*
6588c2ecf20Sopenharmony_ci	 * tz->temperature has already been updated by generic thermal layer,
6598c2ecf20Sopenharmony_ci	 * before this callback being invoked
6608c2ecf20Sopenharmony_ci	 */
6618c2ecf20Sopenharmony_ci	i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature))
6628c2ecf20Sopenharmony_ci		+ (tz->trips.passive.tc2
6638c2ecf20Sopenharmony_ci		* (tz->temperature - tz->trips.passive.temperature));
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (i > 0)
6668c2ecf20Sopenharmony_ci		*trend = THERMAL_TREND_RAISING;
6678c2ecf20Sopenharmony_ci	else if (i < 0)
6688c2ecf20Sopenharmony_ci		*trend = THERMAL_TREND_DROPPING;
6698c2ecf20Sopenharmony_ci	else
6708c2ecf20Sopenharmony_ci		*trend = THERMAL_TREND_STABLE;
6718c2ecf20Sopenharmony_ci	return 0;
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic int thermal_notify(struct thermal_zone_device *thermal, int trip,
6768c2ecf20Sopenharmony_ci			   enum thermal_trip_type trip_type)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	u8 type = 0;
6798c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (trip_type == THERMAL_TRIP_CRITICAL)
6828c2ecf20Sopenharmony_ci		type = ACPI_THERMAL_NOTIFY_CRITICAL;
6838c2ecf20Sopenharmony_ci	else if (trip_type == THERMAL_TRIP_HOT)
6848c2ecf20Sopenharmony_ci		type = ACPI_THERMAL_NOTIFY_HOT;
6858c2ecf20Sopenharmony_ci	else
6868c2ecf20Sopenharmony_ci		return 0;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
6898c2ecf20Sopenharmony_ci					dev_name(&tz->device->dev), type, 1);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
6928c2ecf20Sopenharmony_ci		return 1;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	return 0;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
6988c2ecf20Sopenharmony_ci					struct thermal_cooling_device *cdev,
6998c2ecf20Sopenharmony_ci					bool bind)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	struct acpi_device *device = cdev->devdata;
7028c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = thermal->devdata;
7038c2ecf20Sopenharmony_ci	struct acpi_device *dev;
7048c2ecf20Sopenharmony_ci	acpi_status status;
7058c2ecf20Sopenharmony_ci	acpi_handle handle;
7068c2ecf20Sopenharmony_ci	int i;
7078c2ecf20Sopenharmony_ci	int j;
7088c2ecf20Sopenharmony_ci	int trip = -1;
7098c2ecf20Sopenharmony_ci	int result = 0;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (tz->trips.critical.flags.valid)
7128c2ecf20Sopenharmony_ci		trip++;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (tz->trips.hot.flags.valid)
7158c2ecf20Sopenharmony_ci		trip++;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (tz->trips.passive.flags.valid) {
7188c2ecf20Sopenharmony_ci		trip++;
7198c2ecf20Sopenharmony_ci		for (i = 0; i < tz->trips.passive.devices.count;
7208c2ecf20Sopenharmony_ci		    i++) {
7218c2ecf20Sopenharmony_ci			handle = tz->trips.passive.devices.handles[i];
7228c2ecf20Sopenharmony_ci			status = acpi_bus_get_device(handle, &dev);
7238c2ecf20Sopenharmony_ci			if (ACPI_FAILURE(status) || dev != device)
7248c2ecf20Sopenharmony_ci				continue;
7258c2ecf20Sopenharmony_ci			if (bind)
7268c2ecf20Sopenharmony_ci				result =
7278c2ecf20Sopenharmony_ci					thermal_zone_bind_cooling_device
7288c2ecf20Sopenharmony_ci					(thermal, trip, cdev,
7298c2ecf20Sopenharmony_ci					 THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
7308c2ecf20Sopenharmony_ci					 THERMAL_WEIGHT_DEFAULT);
7318c2ecf20Sopenharmony_ci			else
7328c2ecf20Sopenharmony_ci				result =
7338c2ecf20Sopenharmony_ci					thermal_zone_unbind_cooling_device
7348c2ecf20Sopenharmony_ci					(thermal, trip, cdev);
7358c2ecf20Sopenharmony_ci			if (result)
7368c2ecf20Sopenharmony_ci				goto failed;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
7418c2ecf20Sopenharmony_ci		if (!tz->trips.active[i].flags.valid)
7428c2ecf20Sopenharmony_ci			break;
7438c2ecf20Sopenharmony_ci		trip++;
7448c2ecf20Sopenharmony_ci		for (j = 0;
7458c2ecf20Sopenharmony_ci		    j < tz->trips.active[i].devices.count;
7468c2ecf20Sopenharmony_ci		    j++) {
7478c2ecf20Sopenharmony_ci			handle = tz->trips.active[i].devices.handles[j];
7488c2ecf20Sopenharmony_ci			status = acpi_bus_get_device(handle, &dev);
7498c2ecf20Sopenharmony_ci			if (ACPI_FAILURE(status) || dev != device)
7508c2ecf20Sopenharmony_ci				continue;
7518c2ecf20Sopenharmony_ci			if (bind)
7528c2ecf20Sopenharmony_ci				result = thermal_zone_bind_cooling_device
7538c2ecf20Sopenharmony_ci					(thermal, trip, cdev,
7548c2ecf20Sopenharmony_ci					 THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
7558c2ecf20Sopenharmony_ci					 THERMAL_WEIGHT_DEFAULT);
7568c2ecf20Sopenharmony_ci			else
7578c2ecf20Sopenharmony_ci				result = thermal_zone_unbind_cooling_device
7588c2ecf20Sopenharmony_ci					(thermal, trip, cdev);
7598c2ecf20Sopenharmony_ci			if (result)
7608c2ecf20Sopenharmony_ci				goto failed;
7618c2ecf20Sopenharmony_ci		}
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	for (i = 0; i < tz->devices.count; i++) {
7658c2ecf20Sopenharmony_ci		handle = tz->devices.handles[i];
7668c2ecf20Sopenharmony_ci		status = acpi_bus_get_device(handle, &dev);
7678c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status) && (dev == device)) {
7688c2ecf20Sopenharmony_ci			if (bind)
7698c2ecf20Sopenharmony_ci				result = thermal_zone_bind_cooling_device
7708c2ecf20Sopenharmony_ci						(thermal, THERMAL_TRIPS_NONE,
7718c2ecf20Sopenharmony_ci						 cdev, THERMAL_NO_LIMIT,
7728c2ecf20Sopenharmony_ci						 THERMAL_NO_LIMIT,
7738c2ecf20Sopenharmony_ci						 THERMAL_WEIGHT_DEFAULT);
7748c2ecf20Sopenharmony_ci			else
7758c2ecf20Sopenharmony_ci				result = thermal_zone_unbind_cooling_device
7768c2ecf20Sopenharmony_ci						(thermal, THERMAL_TRIPS_NONE,
7778c2ecf20Sopenharmony_ci						 cdev);
7788c2ecf20Sopenharmony_ci			if (result)
7798c2ecf20Sopenharmony_ci				goto failed;
7808c2ecf20Sopenharmony_ci		}
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_cifailed:
7848c2ecf20Sopenharmony_ci	return result;
7858c2ecf20Sopenharmony_ci}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cistatic int
7888c2ecf20Sopenharmony_ciacpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
7898c2ecf20Sopenharmony_ci					struct thermal_cooling_device *cdev)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	return acpi_thermal_cooling_device_cb(thermal, cdev, true);
7928c2ecf20Sopenharmony_ci}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cistatic int
7958c2ecf20Sopenharmony_ciacpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
7968c2ecf20Sopenharmony_ci					struct thermal_cooling_device *cdev)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	return acpi_thermal_cooling_device_cb(thermal, cdev, false);
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic struct thermal_zone_device_ops acpi_thermal_zone_ops = {
8028c2ecf20Sopenharmony_ci	.bind = acpi_thermal_bind_cooling_device,
8038c2ecf20Sopenharmony_ci	.unbind	= acpi_thermal_unbind_cooling_device,
8048c2ecf20Sopenharmony_ci	.get_temp = thermal_get_temp,
8058c2ecf20Sopenharmony_ci	.get_trip_type = thermal_get_trip_type,
8068c2ecf20Sopenharmony_ci	.get_trip_temp = thermal_get_trip_temp,
8078c2ecf20Sopenharmony_ci	.get_crit_temp = thermal_get_crit_temp,
8088c2ecf20Sopenharmony_ci	.get_trend = thermal_get_trend,
8098c2ecf20Sopenharmony_ci	.notify = thermal_notify,
8108c2ecf20Sopenharmony_ci};
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	int trips = 0;
8158c2ecf20Sopenharmony_ci	int result;
8168c2ecf20Sopenharmony_ci	acpi_status status;
8178c2ecf20Sopenharmony_ci	int i;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	if (tz->trips.critical.flags.valid)
8208c2ecf20Sopenharmony_ci		trips++;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (tz->trips.hot.flags.valid)
8238c2ecf20Sopenharmony_ci		trips++;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	if (tz->trips.passive.flags.valid)
8268c2ecf20Sopenharmony_ci		trips++;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
8298c2ecf20Sopenharmony_ci			tz->trips.active[i].flags.valid; i++, trips++);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	if (tz->trips.passive.flags.valid)
8328c2ecf20Sopenharmony_ci		tz->thermal_zone =
8338c2ecf20Sopenharmony_ci			thermal_zone_device_register("acpitz", trips, 0, tz,
8348c2ecf20Sopenharmony_ci						&acpi_thermal_zone_ops, NULL,
8358c2ecf20Sopenharmony_ci						     tz->trips.passive.tsp*100,
8368c2ecf20Sopenharmony_ci						     tz->polling_frequency*100);
8378c2ecf20Sopenharmony_ci	else
8388c2ecf20Sopenharmony_ci		tz->thermal_zone =
8398c2ecf20Sopenharmony_ci			thermal_zone_device_register("acpitz", trips, 0, tz,
8408c2ecf20Sopenharmony_ci						&acpi_thermal_zone_ops, NULL,
8418c2ecf20Sopenharmony_ci						0, tz->polling_frequency*100);
8428c2ecf20Sopenharmony_ci	if (IS_ERR(tz->thermal_zone))
8438c2ecf20Sopenharmony_ci		return -ENODEV;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	result = sysfs_create_link(&tz->device->dev.kobj,
8468c2ecf20Sopenharmony_ci				   &tz->thermal_zone->device.kobj, "thermal_zone");
8478c2ecf20Sopenharmony_ci	if (result)
8488c2ecf20Sopenharmony_ci		goto unregister_tzd;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	result = sysfs_create_link(&tz->thermal_zone->device.kobj,
8518c2ecf20Sopenharmony_ci				   &tz->device->dev.kobj, "device");
8528c2ecf20Sopenharmony_ci	if (result)
8538c2ecf20Sopenharmony_ci		goto remove_tz_link;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	status =  acpi_bus_attach_private_data(tz->device->handle,
8568c2ecf20Sopenharmony_ci					       tz->thermal_zone);
8578c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
8588c2ecf20Sopenharmony_ci		result = -ENODEV;
8598c2ecf20Sopenharmony_ci		goto remove_dev_link;
8608c2ecf20Sopenharmony_ci	}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	result = thermal_zone_device_enable(tz->thermal_zone);
8638c2ecf20Sopenharmony_ci	if (result)
8648c2ecf20Sopenharmony_ci		goto acpi_bus_detach;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
8678c2ecf20Sopenharmony_ci		 tz->thermal_zone->id);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return 0;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ciacpi_bus_detach:
8728c2ecf20Sopenharmony_ci	acpi_bus_detach_private_data(tz->device->handle);
8738c2ecf20Sopenharmony_ciremove_dev_link:
8748c2ecf20Sopenharmony_ci	sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
8758c2ecf20Sopenharmony_ciremove_tz_link:
8768c2ecf20Sopenharmony_ci	sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
8778c2ecf20Sopenharmony_ciunregister_tzd:
8788c2ecf20Sopenharmony_ci	thermal_zone_device_unregister(tz->thermal_zone);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	return result;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
8868c2ecf20Sopenharmony_ci	sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
8878c2ecf20Sopenharmony_ci	thermal_zone_device_unregister(tz->thermal_zone);
8888c2ecf20Sopenharmony_ci	tz->thermal_zone = NULL;
8898c2ecf20Sopenharmony_ci	acpi_bus_detach_private_data(tz->device->handle);
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------
8948c2ecf20Sopenharmony_ci                                 Driver Interface
8958c2ecf20Sopenharmony_ci   -------------------------------------------------------------------------- */
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic void acpi_queue_thermal_check(struct acpi_thermal *tz)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	if (!work_pending(&tz->thermal_check_work))
9008c2ecf20Sopenharmony_ci		queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic void acpi_thermal_notify(struct acpi_device *device, u32 event)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = acpi_driver_data(device);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	if (!tz)
9098c2ecf20Sopenharmony_ci		return;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	switch (event) {
9128c2ecf20Sopenharmony_ci	case ACPI_THERMAL_NOTIFY_TEMPERATURE:
9138c2ecf20Sopenharmony_ci		acpi_queue_thermal_check(tz);
9148c2ecf20Sopenharmony_ci		break;
9158c2ecf20Sopenharmony_ci	case ACPI_THERMAL_NOTIFY_THRESHOLDS:
9168c2ecf20Sopenharmony_ci		acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
9178c2ecf20Sopenharmony_ci		acpi_queue_thermal_check(tz);
9188c2ecf20Sopenharmony_ci		acpi_bus_generate_netlink_event(device->pnp.device_class,
9198c2ecf20Sopenharmony_ci						  dev_name(&device->dev), event, 0);
9208c2ecf20Sopenharmony_ci		break;
9218c2ecf20Sopenharmony_ci	case ACPI_THERMAL_NOTIFY_DEVICES:
9228c2ecf20Sopenharmony_ci		acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
9238c2ecf20Sopenharmony_ci		acpi_queue_thermal_check(tz);
9248c2ecf20Sopenharmony_ci		acpi_bus_generate_netlink_event(device->pnp.device_class,
9258c2ecf20Sopenharmony_ci						  dev_name(&device->dev), event, 0);
9268c2ecf20Sopenharmony_ci		break;
9278c2ecf20Sopenharmony_ci	default:
9288c2ecf20Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
9298c2ecf20Sopenharmony_ci				  "Unsupported event [0x%x]\n", event));
9308c2ecf20Sopenharmony_ci		break;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci/*
9358c2ecf20Sopenharmony_ci * On some platforms, the AML code has dependency about
9368c2ecf20Sopenharmony_ci * the evaluating order of _TMP and _CRT/_HOT/_PSV/_ACx.
9378c2ecf20Sopenharmony_ci * 1. On HP Pavilion G4-1016tx, _TMP must be invoked after
9388c2ecf20Sopenharmony_ci *    /_CRT/_HOT/_PSV/_ACx, or else system will be power off.
9398c2ecf20Sopenharmony_ci * 2. On HP Compaq 6715b/6715s, the return value of _PSV is 0
9408c2ecf20Sopenharmony_ci *    if _TMP has never been evaluated.
9418c2ecf20Sopenharmony_ci *
9428c2ecf20Sopenharmony_ci * As this dependency is totally transparent to OS, evaluate
9438c2ecf20Sopenharmony_ci * all of them once, in the order of _CRT/_HOT/_PSV/_ACx,
9448c2ecf20Sopenharmony_ci * _TMP, before they are actually used.
9458c2ecf20Sopenharmony_ci */
9468c2ecf20Sopenharmony_cistatic void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	acpi_handle handle = tz->device->handle;
9498c2ecf20Sopenharmony_ci	unsigned long long value;
9508c2ecf20Sopenharmony_ci	int i;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	acpi_evaluate_integer(handle, "_CRT", NULL, &value);
9538c2ecf20Sopenharmony_ci	acpi_evaluate_integer(handle, "_HOT", NULL, &value);
9548c2ecf20Sopenharmony_ci	acpi_evaluate_integer(handle, "_PSV", NULL, &value);
9558c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
9568c2ecf20Sopenharmony_ci		char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
9578c2ecf20Sopenharmony_ci		acpi_status status;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(handle, name, NULL, &value);
9608c2ecf20Sopenharmony_ci		if (status == AE_NOT_FOUND)
9618c2ecf20Sopenharmony_ci			break;
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci	acpi_evaluate_integer(handle, "_TMP", NULL, &value);
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic int acpi_thermal_get_info(struct acpi_thermal *tz)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	int result = 0;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	if (!tz)
9728c2ecf20Sopenharmony_ci		return -EINVAL;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	acpi_thermal_aml_dependency_fix(tz);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* Get trip points [_CRT, _PSV, etc.] (required) */
9778c2ecf20Sopenharmony_ci	result = acpi_thermal_get_trip_points(tz);
9788c2ecf20Sopenharmony_ci	if (result)
9798c2ecf20Sopenharmony_ci		return result;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* Get temperature [_TMP] (required) */
9828c2ecf20Sopenharmony_ci	result = acpi_thermal_get_temperature(tz);
9838c2ecf20Sopenharmony_ci	if (result)
9848c2ecf20Sopenharmony_ci		return result;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	/* Set the cooling mode [_SCP] to active cooling (default) */
9878c2ecf20Sopenharmony_ci	result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE);
9888c2ecf20Sopenharmony_ci	if (!result)
9898c2ecf20Sopenharmony_ci		tz->flags.cooling_mode = 1;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* Get default polling frequency [_TZP] (optional) */
9928c2ecf20Sopenharmony_ci	if (tzp)
9938c2ecf20Sopenharmony_ci		tz->polling_frequency = tzp;
9948c2ecf20Sopenharmony_ci	else
9958c2ecf20Sopenharmony_ci		acpi_thermal_get_polling_frequency(tz);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	return 0;
9988c2ecf20Sopenharmony_ci}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci/*
10018c2ecf20Sopenharmony_ci * The exact offset between Kelvin and degree Celsius is 273.15. However ACPI
10028c2ecf20Sopenharmony_ci * handles temperature values with a single decimal place. As a consequence,
10038c2ecf20Sopenharmony_ci * some implementations use an offset of 273.1 and others use an offset of
10048c2ecf20Sopenharmony_ci * 273.2. Try to find out which one is being used, to present the most
10058c2ecf20Sopenharmony_ci * accurate and visually appealing number.
10068c2ecf20Sopenharmony_ci *
10078c2ecf20Sopenharmony_ci * The heuristic below should work for all ACPI thermal zones which have a
10088c2ecf20Sopenharmony_ci * critical trip point with a value being a multiple of 0.5 degree Celsius.
10098c2ecf20Sopenharmony_ci */
10108c2ecf20Sopenharmony_cistatic void acpi_thermal_guess_offset(struct acpi_thermal *tz)
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	if (tz->trips.critical.flags.valid &&
10138c2ecf20Sopenharmony_ci	    (tz->trips.critical.temperature % 5) == 1)
10148c2ecf20Sopenharmony_ci		tz->kelvin_offset = 273100;
10158c2ecf20Sopenharmony_ci	else
10168c2ecf20Sopenharmony_ci		tz->kelvin_offset = 273200;
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic void acpi_thermal_check_fn(struct work_struct *work)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = container_of(work, struct acpi_thermal,
10228c2ecf20Sopenharmony_ci					       thermal_check_work);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/*
10258c2ecf20Sopenharmony_ci	 * In general, it is not sufficient to check the pending bit, because
10268c2ecf20Sopenharmony_ci	 * subsequent instances of this function may be queued after one of them
10278c2ecf20Sopenharmony_ci	 * has started running (e.g. if _TMP sleeps).  Avoid bailing out if just
10288c2ecf20Sopenharmony_ci	 * one of them is running, though, because it may have done the actual
10298c2ecf20Sopenharmony_ci	 * check some time ago, so allow at least one of them to block on the
10308c2ecf20Sopenharmony_ci	 * mutex while another one is running the update.
10318c2ecf20Sopenharmony_ci	 */
10328c2ecf20Sopenharmony_ci	if (!refcount_dec_not_one(&tz->thermal_check_count))
10338c2ecf20Sopenharmony_ci		return;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	mutex_lock(&tz->thermal_check_lock);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	thermal_zone_device_update(tz->thermal_zone, THERMAL_EVENT_UNSPECIFIED);
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	refcount_inc(&tz->thermal_check_count);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	mutex_unlock(&tz->thermal_check_lock);
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic int acpi_thermal_add(struct acpi_device *device)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	int result = 0;
10478c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = NULL;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	if (!device)
10518c2ecf20Sopenharmony_ci		return -EINVAL;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
10548c2ecf20Sopenharmony_ci	if (!tz)
10558c2ecf20Sopenharmony_ci		return -ENOMEM;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	tz->device = device;
10588c2ecf20Sopenharmony_ci	strcpy(tz->name, device->pnp.bus_id);
10598c2ecf20Sopenharmony_ci	strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME);
10608c2ecf20Sopenharmony_ci	strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
10618c2ecf20Sopenharmony_ci	device->driver_data = tz;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	result = acpi_thermal_get_info(tz);
10648c2ecf20Sopenharmony_ci	if (result)
10658c2ecf20Sopenharmony_ci		goto free_memory;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	acpi_thermal_guess_offset(tz);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	result = acpi_thermal_register_thermal_zone(tz);
10708c2ecf20Sopenharmony_ci	if (result)
10718c2ecf20Sopenharmony_ci		goto free_memory;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	refcount_set(&tz->thermal_check_count, 3);
10748c2ecf20Sopenharmony_ci	mutex_init(&tz->thermal_check_lock);
10758c2ecf20Sopenharmony_ci	INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device),
10788c2ecf20Sopenharmony_ci		acpi_device_bid(device), deci_kelvin_to_celsius(tz->temperature));
10798c2ecf20Sopenharmony_ci	goto end;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cifree_memory:
10828c2ecf20Sopenharmony_ci	kfree(tz);
10838c2ecf20Sopenharmony_ciend:
10848c2ecf20Sopenharmony_ci	return result;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic int acpi_thermal_remove(struct acpi_device *device)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct acpi_thermal *tz = NULL;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	if (!device || !acpi_driver_data(device))
10928c2ecf20Sopenharmony_ci		return -EINVAL;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	flush_workqueue(acpi_thermal_pm_queue);
10958c2ecf20Sopenharmony_ci	tz = acpi_driver_data(device);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	acpi_thermal_unregister_thermal_zone(tz);
10988c2ecf20Sopenharmony_ci	kfree(tz);
10998c2ecf20Sopenharmony_ci	return 0;
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
11038c2ecf20Sopenharmony_cistatic int acpi_thermal_suspend(struct device *dev)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	/* Make sure the previously queued thermal check work has been done */
11068c2ecf20Sopenharmony_ci	flush_workqueue(acpi_thermal_pm_queue);
11078c2ecf20Sopenharmony_ci	return 0;
11088c2ecf20Sopenharmony_ci}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_cistatic int acpi_thermal_resume(struct device *dev)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	struct acpi_thermal *tz;
11138c2ecf20Sopenharmony_ci	int i, j, power_state, result;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	if (!dev)
11168c2ecf20Sopenharmony_ci		return -EINVAL;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	tz = acpi_driver_data(to_acpi_device(dev));
11198c2ecf20Sopenharmony_ci	if (!tz)
11208c2ecf20Sopenharmony_ci		return -EINVAL;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
11238c2ecf20Sopenharmony_ci		if (!tz->trips.active[i].flags.valid)
11248c2ecf20Sopenharmony_ci			break;
11258c2ecf20Sopenharmony_ci		tz->trips.active[i].flags.enabled = 1;
11268c2ecf20Sopenharmony_ci		for (j = 0; j < tz->trips.active[i].devices.count; j++) {
11278c2ecf20Sopenharmony_ci			result = acpi_bus_update_power(
11288c2ecf20Sopenharmony_ci					tz->trips.active[i].devices.handles[j],
11298c2ecf20Sopenharmony_ci					&power_state);
11308c2ecf20Sopenharmony_ci			if (result || (power_state != ACPI_STATE_D0)) {
11318c2ecf20Sopenharmony_ci				tz->trips.active[i].flags.enabled = 0;
11328c2ecf20Sopenharmony_ci				break;
11338c2ecf20Sopenharmony_ci			}
11348c2ecf20Sopenharmony_ci		}
11358c2ecf20Sopenharmony_ci		tz->state.active |= tz->trips.active[i].flags.enabled;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	acpi_queue_thermal_check(tz);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	return AE_OK;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci#endif
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_cistatic int thermal_act(const struct dmi_system_id *d) {
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	if (act == 0) {
11478c2ecf20Sopenharmony_ci		pr_notice(PREFIX "%s detected: "
11488c2ecf20Sopenharmony_ci			  "disabling all active thermal trip points\n", d->ident);
11498c2ecf20Sopenharmony_ci		act = -1;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci	return 0;
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_cistatic int thermal_nocrt(const struct dmi_system_id *d) {
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	pr_notice(PREFIX "%s detected: "
11568c2ecf20Sopenharmony_ci		  "disabling all critical thermal trip point actions.\n", d->ident);
11578c2ecf20Sopenharmony_ci	nocrt = 1;
11588c2ecf20Sopenharmony_ci	return 0;
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_cistatic int thermal_tzp(const struct dmi_system_id *d) {
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	if (tzp == 0) {
11638c2ecf20Sopenharmony_ci		pr_notice(PREFIX "%s detected: "
11648c2ecf20Sopenharmony_ci			  "enabling thermal zone polling\n", d->ident);
11658c2ecf20Sopenharmony_ci		tzp = 300;	/* 300 dS = 30 Seconds */
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci	return 0;
11688c2ecf20Sopenharmony_ci}
11698c2ecf20Sopenharmony_cistatic int thermal_psv(const struct dmi_system_id *d) {
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	if (psv == 0) {
11728c2ecf20Sopenharmony_ci		pr_notice(PREFIX "%s detected: "
11738c2ecf20Sopenharmony_ci			  "disabling all passive thermal trip points\n", d->ident);
11748c2ecf20Sopenharmony_ci		psv = -1;
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci	return 0;
11778c2ecf20Sopenharmony_ci}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_cistatic const struct dmi_system_id thermal_dmi_table[] __initconst = {
11808c2ecf20Sopenharmony_ci	/*
11818c2ecf20Sopenharmony_ci	 * Award BIOS on this AOpen makes thermal control almost worthless.
11828c2ecf20Sopenharmony_ci	 * http://bugzilla.kernel.org/show_bug.cgi?id=8842
11838c2ecf20Sopenharmony_ci	 */
11848c2ecf20Sopenharmony_ci	{
11858c2ecf20Sopenharmony_ci	 .callback = thermal_act,
11868c2ecf20Sopenharmony_ci	 .ident = "AOpen i915GMm-HFS",
11878c2ecf20Sopenharmony_ci	 .matches = {
11888c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
11898c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
11908c2ecf20Sopenharmony_ci		},
11918c2ecf20Sopenharmony_ci	},
11928c2ecf20Sopenharmony_ci	{
11938c2ecf20Sopenharmony_ci	 .callback = thermal_psv,
11948c2ecf20Sopenharmony_ci	 .ident = "AOpen i915GMm-HFS",
11958c2ecf20Sopenharmony_ci	 .matches = {
11968c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
11978c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
11988c2ecf20Sopenharmony_ci		},
11998c2ecf20Sopenharmony_ci	},
12008c2ecf20Sopenharmony_ci	{
12018c2ecf20Sopenharmony_ci	 .callback = thermal_tzp,
12028c2ecf20Sopenharmony_ci	 .ident = "AOpen i915GMm-HFS",
12038c2ecf20Sopenharmony_ci	 .matches = {
12048c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
12058c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
12068c2ecf20Sopenharmony_ci		},
12078c2ecf20Sopenharmony_ci	},
12088c2ecf20Sopenharmony_ci	{
12098c2ecf20Sopenharmony_ci	 .callback = thermal_nocrt,
12108c2ecf20Sopenharmony_ci	 .ident = "Gigabyte GA-7ZX",
12118c2ecf20Sopenharmony_ci	 .matches = {
12128c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
12138c2ecf20Sopenharmony_ci		DMI_MATCH(DMI_BOARD_NAME, "7ZX"),
12148c2ecf20Sopenharmony_ci		},
12158c2ecf20Sopenharmony_ci	},
12168c2ecf20Sopenharmony_ci	{}
12178c2ecf20Sopenharmony_ci};
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic int __init acpi_thermal_init(void)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	int result = 0;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	dmi_check_system(thermal_dmi_table);
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	if (off) {
12268c2ecf20Sopenharmony_ci		pr_notice(PREFIX "thermal control disabled\n");
12278c2ecf20Sopenharmony_ci		return -ENODEV;
12288c2ecf20Sopenharmony_ci	}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm",
12318c2ecf20Sopenharmony_ci						WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
12328c2ecf20Sopenharmony_ci	if (!acpi_thermal_pm_queue)
12338c2ecf20Sopenharmony_ci		return -ENODEV;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	result = acpi_bus_register_driver(&acpi_thermal_driver);
12368c2ecf20Sopenharmony_ci	if (result < 0) {
12378c2ecf20Sopenharmony_ci		destroy_workqueue(acpi_thermal_pm_queue);
12388c2ecf20Sopenharmony_ci		return -ENODEV;
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	return 0;
12428c2ecf20Sopenharmony_ci}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cistatic void __exit acpi_thermal_exit(void)
12458c2ecf20Sopenharmony_ci{
12468c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&acpi_thermal_driver);
12478c2ecf20Sopenharmony_ci	destroy_workqueue(acpi_thermal_pm_queue);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	return;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_cimodule_init(acpi_thermal_init);
12538c2ecf20Sopenharmony_cimodule_exit(acpi_thermal_exit);
1254