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