xref: /linux/drivers/acpi/thermal_lib.c (revision e6a901a00822659181c93c86d8bbc2a17779fddc)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2023 Linaro Limited
4  * Copyright 2023 Intel Corporation
5  *
6  * Library routines for retrieving trip point temperature values from the
7  * platform firmware via ACPI.
8  */
9 #include <linux/acpi.h>
10 #include <linux/units.h>
11 #include <linux/thermal.h>
12 #include "internal.h"
13 
14 /*
15  * Minimum temperature for full military grade is 218°K (-55°C) and
16  * max temperature is 448°K (175°C). We can consider those values as
17  * the boundaries for the [trips] temperature returned by the
18  * firmware. Any values out of these boundaries may be considered
19  * bogus and we can assume the firmware has no data to provide.
20  */
21 #define TEMP_MIN_DECIK	2180ULL
22 #define TEMP_MAX_DECIK	4480ULL
23 
24 static int acpi_trip_temp(struct acpi_device *adev, char *obj_name,
25 			  int *ret_temp)
26 {
27 	unsigned long long temp;
28 	acpi_status status;
29 
30 	status = acpi_evaluate_integer(adev->handle, obj_name, NULL, &temp);
31 	if (ACPI_FAILURE(status)) {
32 		acpi_handle_debug(adev->handle, "%s evaluation failed\n", obj_name);
33 		return -ENODATA;
34 	}
35 
36 	if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) {
37 		*ret_temp = temp;
38 	} else {
39 		acpi_handle_debug(adev->handle, "%s result %llu out of range\n",
40 				  obj_name, temp);
41 		*ret_temp = THERMAL_TEMP_INVALID;
42 	}
43 
44 	return 0;
45 }
46 
47 int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
48 {
49 	char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'};
50 
51 	if (id < 0 || id > 9)
52 		return -EINVAL;
53 
54 	return acpi_trip_temp(adev, obj_name, ret_temp);
55 }
56 EXPORT_SYMBOL_NS_GPL(acpi_active_trip_temp, ACPI_THERMAL);
57 
58 int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
59 {
60 	return acpi_trip_temp(adev, "_PSV", ret_temp);
61 }
62 EXPORT_SYMBOL_NS_GPL(acpi_passive_trip_temp, ACPI_THERMAL);
63 
64 int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
65 {
66 	return acpi_trip_temp(adev, "_HOT", ret_temp);
67 }
68 EXPORT_SYMBOL_NS_GPL(acpi_hot_trip_temp, ACPI_THERMAL);
69 
70 int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
71 {
72 	return acpi_trip_temp(adev, "_CRT", ret_temp);
73 }
74 EXPORT_SYMBOL_NS_GPL(acpi_critical_trip_temp, ACPI_THERMAL);
75 
76 static int thermal_temp(int error, int temp_decik, int *ret_temp)
77 {
78 	if (error)
79 		return error;
80 
81 	if (temp_decik == THERMAL_TEMP_INVALID)
82 		*ret_temp = THERMAL_TEMP_INVALID;
83 	else
84 		*ret_temp = deci_kelvin_to_millicelsius(temp_decik);
85 
86 	return 0;
87 }
88 
89 /**
90  * thermal_acpi_active_trip_temp - Retrieve active trip point temperature
91  * @adev: Target thermal zone ACPI device object.
92  * @id: Active cooling level (0 - 9).
93  * @ret_temp: Address to store the retrieved temperature value on success.
94  *
95  * Evaluate the _ACx object for the thermal zone represented by @adev to obtain
96  * the temperature of the active cooling trip point corresponding to the active
97  * cooling level given by @id.
98  *
99  * Return 0 on success or a negative error value on failure.
100  */
101 int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
102 {
103 	int temp_decik = 0;
104 	int ret = acpi_active_trip_temp(adev, id, &temp_decik);
105 
106 	return thermal_temp(ret, temp_decik, ret_temp);
107 }
108 EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp);
109 
110 /**
111  * thermal_acpi_passive_trip_temp - Retrieve passive trip point temperature
112  * @adev: Target thermal zone ACPI device object.
113  * @ret_temp: Address to store the retrieved temperature value on success.
114  *
115  * Evaluate the _PSV object for the thermal zone represented by @adev to obtain
116  * the temperature of the passive cooling trip point.
117  *
118  * Return 0 on success or -ENODATA on failure.
119  */
120 int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
121 {
122 	int temp_decik = 0;
123 	int ret = acpi_passive_trip_temp(adev, &temp_decik);
124 
125 	return thermal_temp(ret, temp_decik, ret_temp);
126 }
127 EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp);
128 
129 /**
130  * thermal_acpi_hot_trip_temp - Retrieve hot trip point temperature
131  * @adev: Target thermal zone ACPI device object.
132  * @ret_temp: Address to store the retrieved temperature value on success.
133  *
134  * Evaluate the _HOT object for the thermal zone represented by @adev to obtain
135  * the temperature of the trip point at which the system is expected to be put
136  * into the S4 sleep state.
137  *
138  * Return 0 on success or -ENODATA on failure.
139  */
140 int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
141 {
142 	int temp_decik = 0;
143 	int ret = acpi_hot_trip_temp(adev, &temp_decik);
144 
145 	return thermal_temp(ret, temp_decik, ret_temp);
146 }
147 EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp);
148 
149 /**
150  * thermal_acpi_critical_trip_temp - Retrieve critical trip point temperature
151  * @adev: Target thermal zone ACPI device object.
152  * @ret_temp: Address to store the retrieved temperature value on success.
153  *
154  * Evaluate the _CRT object for the thermal zone represented by @adev to obtain
155  * the temperature of the critical cooling trip point.
156  *
157  * Return 0 on success or -ENODATA on failure.
158  */
159 int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
160 {
161 	int temp_decik = 0;
162 	int ret = acpi_critical_trip_temp(adev, &temp_decik);
163 
164 	return thermal_temp(ret, temp_decik, ret_temp);
165 }
166 EXPORT_SYMBOL_GPL(thermal_acpi_critical_trip_temp);
167