diff options
Diffstat (limited to 'drivers/thermal/int340x_thermal/int340x_thermal_zone.c')
-rw-r--r-- | drivers/thermal/int340x_thermal/int340x_thermal_zone.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c new file mode 100644 index 000000000000..f88b08877025 --- /dev/null +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c | |||
@@ -0,0 +1,276 @@ | |||
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, | ||
23 | unsigned long *temp) | ||
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 | |||
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); | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone, | ||
52 | int trip, unsigned long *temp) | ||
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, | ||
117 | int trip, unsigned long temp) | ||
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, | ||
139 | int trip, unsigned long *temp) | ||
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, | ||
166 | unsigned long *temp) | ||
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; | ||
211 | goto free_mem; | ||
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 | } | ||
239 | int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table( | ||
240 | adev->handle); | ||
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); | ||
251 | goto free_lpat; | ||
252 | } | ||
253 | |||
254 | return int34x_thermal_zone; | ||
255 | |||
256 | free_lpat: | ||
257 | acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); | ||
258 | free_mem: | ||
259 | kfree(int34x_thermal_zone); | ||
260 | return ERR_PTR(ret); | ||
261 | } | ||
262 | EXPORT_SYMBOL_GPL(int340x_thermal_zone_add); | ||
263 | |||
264 | void int340x_thermal_zone_remove(struct int34x_thermal_zone | ||
265 | *int34x_thermal_zone) | ||
266 | { | ||
267 | thermal_zone_device_unregister(int34x_thermal_zone->zone); | ||
268 | acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); | ||
269 | kfree(int34x_thermal_zone); | ||
270 | } | ||
271 | EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove); | ||
272 | |||
273 | MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>"); | ||
274 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
275 | MODULE_DESCRIPTION("Intel INT340x common thermal zone handler"); | ||
276 | MODULE_LICENSE("GPL v2"); | ||