e8182f0929d3ddec43bdf8fb12bcd6f1d5a09d0e
[linux-2.6-microblaze.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 = thermal_zone_device_priv(zone);
18         unsigned long long tmp;
19         acpi_status status;
20
21         status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
22         if (ACPI_FAILURE(status))
23                 return -EIO;
24
25         if (d->lpat_table) {
26                 int conv_temp;
27
28                 conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
29                 if (conv_temp < 0)
30                         return conv_temp;
31
32                 *temp = conv_temp * 10;
33         } else {
34                 /* _TMP returns the temperature in tenths of degrees Kelvin */
35                 *temp = deci_kelvin_to_millicelsius(tmp);
36         }
37
38         return 0;
39 }
40
41 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
42                                          int trip, int temp)
43 {
44         struct int34x_thermal_zone *d = thermal_zone_device_priv(zone);
45         char name[] = {'P', 'A', 'T', '0' + trip, '\0'};
46         acpi_status status;
47
48         if (trip > 9)
49                 return -EINVAL;
50
51         status = acpi_execute_simple_method(d->adev->handle, name,
52                                             millicelsius_to_deci_kelvin(temp));
53         if (ACPI_FAILURE(status))
54                 return -EIO;
55
56         return 0;
57 }
58
59 static void int340x_thermal_critical(struct thermal_zone_device *zone)
60 {
61         dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type);
62 }
63
64 static inline void *int_to_trip_priv(int i)
65 {
66         return (void *)(long)i;
67 }
68
69 static inline int trip_priv_to_int(const struct thermal_trip *trip)
70 {
71         return (long)trip->priv;
72 }
73
74 static int int340x_thermal_read_trips(struct acpi_device *zone_adev,
75                                       struct thermal_trip *zone_trips,
76                                       int trip_cnt)
77 {
78         int i, ret;
79
80         ret = thermal_acpi_critical_trip_temp(zone_adev,
81                                               &zone_trips[trip_cnt].temperature);
82         if (!ret) {
83                 zone_trips[trip_cnt].type = THERMAL_TRIP_CRITICAL;
84                 trip_cnt++;
85         }
86
87         ret = thermal_acpi_hot_trip_temp(zone_adev,
88                                          &zone_trips[trip_cnt].temperature);
89         if (!ret) {
90                 zone_trips[trip_cnt].type = THERMAL_TRIP_HOT;
91                 trip_cnt++;
92         }
93
94         ret = thermal_acpi_passive_trip_temp(zone_adev,
95                                              &zone_trips[trip_cnt].temperature);
96         if (!ret) {
97                 zone_trips[trip_cnt].type = THERMAL_TRIP_PASSIVE;
98                 trip_cnt++;
99         }
100
101         for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
102                 ret = thermal_acpi_active_trip_temp(zone_adev, i,
103                                                     &zone_trips[trip_cnt].temperature);
104                 if (ret)
105                         break;
106
107                 zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE;
108                 zone_trips[trip_cnt].priv = int_to_trip_priv(i);
109                 trip_cnt++;
110         }
111
112         return trip_cnt;
113 }
114
115 static struct thermal_zone_params int340x_thermal_params = {
116         .governor_name = "user_space",
117         .no_hwmon = true,
118 };
119
120 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
121                                                      int (*get_temp) (struct thermal_zone_device *, int *))
122 {
123         const struct thermal_zone_device_ops zone_ops = {
124                 .set_trip_temp = int340x_thermal_set_trip_temp,
125                 .critical = int340x_thermal_critical,
126                 .get_temp = get_temp ? get_temp : int340x_thermal_get_zone_temp,
127         };
128         struct int34x_thermal_zone *int34x_zone;
129         struct thermal_trip *zone_trips;
130         unsigned long long trip_cnt = 0;
131         unsigned long long hyst;
132         acpi_status status;
133         int i, ret;
134
135         int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL);
136         if (!int34x_zone)
137                 return ERR_PTR(-ENOMEM);
138
139         int34x_zone->adev = adev;
140
141         status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
142         if (ACPI_SUCCESS(status))
143                 int34x_zone->aux_trip_nr = trip_cnt;
144
145         zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT),
146                              GFP_KERNEL);
147         if (!zone_trips) {
148                 ret = -ENOMEM;
149                 goto err_trips_alloc;
150         }
151
152         for (i = 0; i < trip_cnt; i++) {
153                 zone_trips[i].type = THERMAL_TRIP_PASSIVE;
154                 zone_trips[i].temperature = THERMAL_TEMP_INVALID;
155                 zone_trips[i].flags |= THERMAL_TRIP_FLAG_RW_TEMP;
156         }
157
158         trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt);
159
160         status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst);
161         if (ACPI_SUCCESS(status))
162                 hyst *= 100;
163         else
164                 hyst = 0;
165
166         for (i = 0; i < trip_cnt; ++i)
167                 zone_trips[i].hysteresis = hyst;
168
169         int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle);
170
171         int34x_zone->zone = thermal_zone_device_register_with_trips(
172                                                         acpi_device_bid(adev),
173                                                         zone_trips, trip_cnt,
174                                                         int34x_zone,
175                                                         &zone_ops,
176                                                         &int340x_thermal_params,
177                                                         0, 0);
178         kfree(zone_trips);
179
180         if (IS_ERR(int34x_zone->zone)) {
181                 ret = PTR_ERR(int34x_zone->zone);
182                 goto err_thermal_zone;
183         }
184         ret = thermal_zone_device_enable(int34x_zone->zone);
185         if (ret)
186                 goto err_enable;
187
188         return int34x_zone;
189
190 err_enable:
191         thermal_zone_device_unregister(int34x_zone->zone);
192 err_thermal_zone:
193         acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
194 err_trips_alloc:
195         kfree(int34x_zone);
196         return ERR_PTR(ret);
197 }
198 EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
199
200 void int340x_thermal_zone_remove(struct int34x_thermal_zone *int34x_zone)
201 {
202         thermal_zone_device_unregister(int34x_zone->zone);
203         acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
204         kfree(int34x_zone);
205 }
206 EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
207
208 static int int340x_update_one_trip(struct thermal_trip *trip, void *arg)
209 {
210         struct int34x_thermal_zone *int34x_zone = arg;
211         struct acpi_device *zone_adev = int34x_zone->adev;
212         int temp, err;
213
214         switch (trip->type) {
215         case THERMAL_TRIP_CRITICAL:
216                 err = thermal_acpi_critical_trip_temp(zone_adev, &temp);
217                 break;
218         case THERMAL_TRIP_HOT:
219                 err = thermal_acpi_hot_trip_temp(zone_adev, &temp);
220                 break;
221         case THERMAL_TRIP_PASSIVE:
222                 err = thermal_acpi_passive_trip_temp(zone_adev, &temp);
223                 break;
224         case THERMAL_TRIP_ACTIVE:
225                 err = thermal_acpi_active_trip_temp(zone_adev,
226                                                     trip_priv_to_int(trip),
227                                                     &temp);
228                 break;
229         default:
230                 err = -ENODEV;
231         }
232         if (err)
233                 temp = THERMAL_TEMP_INVALID;
234
235         thermal_zone_set_trip_temp(int34x_zone->zone, trip, temp);
236
237         return 0;
238 }
239
240 void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone)
241 {
242         thermal_zone_for_each_trip(int34x_zone->zone, int340x_update_one_trip,
243                                    int34x_zone);
244 }
245 EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);
246
247 MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
248 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
249 MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
250 MODULE_LICENSE("GPL v2");