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