• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * int340x_thermal_zone.c
4  * Copyright (c) 2015, Intel Corporation.
5  */
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/init.h>
9 #include <linux/acpi.h>
10 #include <linux/thermal.h>
11 #include "int340x_thermal_zone.h"
12 
int340x_thermal_get_zone_temp(struct thermal_zone_device * zone,int * temp)13 static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
14 					 int *temp)
15 {
16 	struct int34x_thermal_zone *d = zone->devdata;
17 	unsigned long long tmp;
18 	acpi_status status;
19 
20 	if (d->override_ops && d->override_ops->get_temp)
21 		return d->override_ops->get_temp(zone, temp);
22 
23 	status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
24 	if (ACPI_FAILURE(status))
25 		return -EIO;
26 
27 	if (d->lpat_table) {
28 		int conv_temp;
29 
30 		conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
31 		if (conv_temp < 0)
32 			return conv_temp;
33 
34 		*temp = (unsigned long)conv_temp * 10;
35 	} else
36 		/* _TMP returns the temperature in tenths of degrees Kelvin */
37 		*temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
38 
39 	return 0;
40 }
41 
int340x_thermal_get_trip_temp(struct thermal_zone_device * zone,int trip,int * temp)42 static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
43 					 int trip, int *temp)
44 {
45 	struct int34x_thermal_zone *d = zone->devdata;
46 	int i, ret = 0;
47 
48 	if (d->override_ops && d->override_ops->get_trip_temp)
49 		return d->override_ops->get_trip_temp(zone, trip, temp);
50 
51 	mutex_lock(&d->trip_mutex);
52 
53 	if (trip < d->aux_trip_nr)
54 		*temp = d->aux_trips[trip];
55 	else if (trip == d->crt_trip_id)
56 		*temp = d->crt_temp;
57 	else if (trip == d->psv_trip_id)
58 		*temp = d->psv_temp;
59 	else if (trip == d->hot_trip_id)
60 		*temp = d->hot_temp;
61 	else {
62 		for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
63 			if (d->act_trips[i].valid &&
64 			    d->act_trips[i].id == trip) {
65 				*temp = d->act_trips[i].temp;
66 				break;
67 			}
68 		}
69 		if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
70 			ret = -EINVAL;
71 	}
72 
73 	mutex_unlock(&d->trip_mutex);
74 
75 	return ret;
76 }
77 
int340x_thermal_get_trip_type(struct thermal_zone_device * zone,int trip,enum thermal_trip_type * type)78 static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
79 					 int trip,
80 					 enum thermal_trip_type *type)
81 {
82 	struct int34x_thermal_zone *d = zone->devdata;
83 	int i, ret = 0;
84 
85 	if (d->override_ops && d->override_ops->get_trip_type)
86 		return d->override_ops->get_trip_type(zone, trip, type);
87 
88 	mutex_lock(&d->trip_mutex);
89 
90 	if (trip < d->aux_trip_nr)
91 		*type = THERMAL_TRIP_PASSIVE;
92 	else if (trip == d->crt_trip_id)
93 		*type = THERMAL_TRIP_CRITICAL;
94 	else if (trip == d->hot_trip_id)
95 		*type = THERMAL_TRIP_HOT;
96 	else if (trip == d->psv_trip_id)
97 		*type = THERMAL_TRIP_PASSIVE;
98 	else {
99 		for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
100 			if (d->act_trips[i].valid &&
101 			    d->act_trips[i].id == trip) {
102 				*type = THERMAL_TRIP_ACTIVE;
103 				break;
104 			}
105 		}
106 		if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
107 			ret = -EINVAL;
108 	}
109 
110 	mutex_unlock(&d->trip_mutex);
111 
112 	return ret;
113 }
114 
int340x_thermal_set_trip_temp(struct thermal_zone_device * zone,int trip,int temp)115 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
116 				      int trip, int temp)
117 {
118 	struct int34x_thermal_zone *d = zone->devdata;
119 	acpi_status status;
120 	char name[10];
121 
122 	if (d->override_ops && d->override_ops->set_trip_temp)
123 		return d->override_ops->set_trip_temp(zone, trip, temp);
124 
125 	snprintf(name, sizeof(name), "PAT%d", trip);
126 	status = acpi_execute_simple_method(d->adev->handle, name,
127 			MILLICELSIUS_TO_DECI_KELVIN(temp));
128 	if (ACPI_FAILURE(status))
129 		return -EIO;
130 
131 	d->aux_trips[trip] = temp;
132 
133 	return 0;
134 }
135 
136 
int340x_thermal_get_trip_hyst(struct thermal_zone_device * zone,int trip,int * temp)137 static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
138 		int trip, int *temp)
139 {
140 	struct int34x_thermal_zone *d = zone->devdata;
141 	acpi_status status;
142 	unsigned long long hyst;
143 
144 	if (d->override_ops && d->override_ops->get_trip_hyst)
145 		return d->override_ops->get_trip_hyst(zone, trip, temp);
146 
147 	status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
148 	if (ACPI_FAILURE(status))
149 		*temp = 0;
150 	else
151 		*temp = hyst * 100;
152 
153 	return 0;
154 }
155 
156 static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
157 	.get_temp       = int340x_thermal_get_zone_temp,
158 	.get_trip_temp	= int340x_thermal_get_trip_temp,
159 	.get_trip_type	= int340x_thermal_get_trip_type,
160 	.set_trip_temp	= int340x_thermal_set_trip_temp,
161 	.get_trip_hyst =  int340x_thermal_get_trip_hyst,
162 };
163 
int340x_thermal_get_trip_config(acpi_handle handle,char * name,int * temp)164 static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
165 				      int *temp)
166 {
167 	unsigned long long r;
168 	acpi_status status;
169 
170 	status = acpi_evaluate_integer(handle, name, NULL, &r);
171 	if (ACPI_FAILURE(status))
172 		return -EIO;
173 
174 	*temp = DECI_KELVIN_TO_MILLICELSIUS(r);
175 
176 	return 0;
177 }
178 
int340x_thermal_read_trips(struct int34x_thermal_zone * int34x_zone)179 int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
180 {
181 	int trip_cnt = int34x_zone->aux_trip_nr;
182 	int i;
183 
184 	mutex_lock(&int34x_zone->trip_mutex);
185 
186 	int34x_zone->crt_trip_id = -1;
187 	if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
188 					     &int34x_zone->crt_temp))
189 		int34x_zone->crt_trip_id = trip_cnt++;
190 
191 	int34x_zone->hot_trip_id = -1;
192 	if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
193 					     &int34x_zone->hot_temp))
194 		int34x_zone->hot_trip_id = trip_cnt++;
195 
196 	int34x_zone->psv_trip_id = -1;
197 	if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
198 					     &int34x_zone->psv_temp))
199 		int34x_zone->psv_trip_id = trip_cnt++;
200 
201 	for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
202 		char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
203 
204 		if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
205 					name,
206 					&int34x_zone->act_trips[i].temp))
207 			break;
208 
209 		int34x_zone->act_trips[i].id = trip_cnt++;
210 		int34x_zone->act_trips[i].valid = true;
211 	}
212 
213 	mutex_unlock(&int34x_zone->trip_mutex);
214 
215 	return trip_cnt;
216 }
217 EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
218 
219 static struct thermal_zone_params int340x_thermal_params = {
220 	.governor_name = "user_space",
221 	.no_hwmon = true,
222 };
223 
int340x_thermal_zone_add(struct acpi_device * adev,struct thermal_zone_device_ops * override_ops)224 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
225 				struct thermal_zone_device_ops *override_ops)
226 {
227 	struct int34x_thermal_zone *int34x_thermal_zone;
228 	acpi_status status;
229 	unsigned long long trip_cnt;
230 	int trip_mask = 0;
231 	int ret;
232 
233 	int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
234 				      GFP_KERNEL);
235 	if (!int34x_thermal_zone)
236 		return ERR_PTR(-ENOMEM);
237 
238 	mutex_init(&int34x_thermal_zone->trip_mutex);
239 
240 	int34x_thermal_zone->adev = adev;
241 	int34x_thermal_zone->override_ops = override_ops;
242 
243 	status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
244 	if (ACPI_FAILURE(status))
245 		trip_cnt = 0;
246 	else {
247 		int i;
248 
249 		int34x_thermal_zone->aux_trips =
250 			kcalloc(trip_cnt,
251 				sizeof(*int34x_thermal_zone->aux_trips),
252 				GFP_KERNEL);
253 		if (!int34x_thermal_zone->aux_trips) {
254 			ret = -ENOMEM;
255 			goto err_trip_alloc;
256 		}
257 		trip_mask = BIT(trip_cnt) - 1;
258 		int34x_thermal_zone->aux_trip_nr = trip_cnt;
259 		for (i = 0; i < trip_cnt; ++i)
260 			int34x_thermal_zone->aux_trips[i] = THERMAL_TEMP_INVALID;
261 	}
262 
263 	trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
264 
265 	int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
266 								adev->handle);
267 
268 	int34x_thermal_zone->zone = thermal_zone_device_register(
269 						acpi_device_bid(adev),
270 						trip_cnt,
271 						trip_mask, int34x_thermal_zone,
272 						&int340x_thermal_zone_ops,
273 						&int340x_thermal_params,
274 						0, 0);
275 	if (IS_ERR(int34x_thermal_zone->zone)) {
276 		ret = PTR_ERR(int34x_thermal_zone->zone);
277 		goto err_thermal_zone;
278 	}
279 
280 	return int34x_thermal_zone;
281 
282 err_thermal_zone:
283 	acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
284 	kfree(int34x_thermal_zone->aux_trips);
285 err_trip_alloc:
286 	mutex_destroy(&int34x_thermal_zone->trip_mutex);
287 	kfree(int34x_thermal_zone);
288 	return ERR_PTR(ret);
289 }
290 EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
291 
int340x_thermal_zone_remove(struct int34x_thermal_zone * int34x_thermal_zone)292 void int340x_thermal_zone_remove(struct int34x_thermal_zone
293 				 *int34x_thermal_zone)
294 {
295 	thermal_zone_device_unregister(int34x_thermal_zone->zone);
296 	acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
297 	kfree(int34x_thermal_zone->aux_trips);
298 	mutex_destroy(&int34x_thermal_zone->trip_mutex);
299 	kfree(int34x_thermal_zone);
300 }
301 EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
302 
303 MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
304 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
305 MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
306 MODULE_LICENSE("GPL v2");
307