Thermal: int340x_thermal: expose acpi thermal relationship tables
[deliverable/linux.git] / drivers / thermal / int340x_thermal / int3400_thermal.c
CommitLineData
816cab93
ZR
1/*
2 * INT3400 thermal driver
3 *
4 * Copyright (C) 2014, Intel Corporation
5 * Authors: Zhang Rui <rui.zhang@intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/acpi.h>
0ab15365 16#include <linux/thermal.h>
816cab93
ZR
17
18struct art {
19 acpi_handle source;
20 acpi_handle target;
21 u64 weight;
22 u64 ac0_max;
23 u64 ac1_max;
24 u64 ac2_max;
25 u64 ac3_max;
26 u64 ac4_max;
27 u64 ac5_max;
28 u64 ac6_max;
29 u64 ac7_max;
30 u64 ac8_max;
31 u64 ac9_max;
32};
33
34struct trt {
35 acpi_handle source;
36 acpi_handle target;
37 u64 influence;
38 u64 sampling_period;
39 u64 reverved1;
40 u64 reverved2;
41 u64 reverved3;
42 u64 reverved4;
43};
44
c5738ddd
ZR
45enum int3400_thermal_uuid {
46 INT3400_THERMAL_PASSIVE_1,
47 INT3400_THERMAL_PASSIVE_2,
48 INT3400_THERMAL_ACTIVE,
49 INT3400_THERMAL_CRITICAL,
50 INT3400_THERMAL_COOLING_MODE,
51 INT3400_THERMAL_MAXIMUM_UUID,
52};
53
54static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
55 "42A441D6-AE6A-462b-A84B-4A8CE79027D3",
56 "9E04115A-AE87-4D1C-9500-0F3E340BFE75",
57 "3A95C389-E4B8-4629-A526-C52C88626BAE",
58 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
59 "16CAF1B7-DD38-40ed-B1C1-1B8A1913D531",
60};
61
816cab93
ZR
62struct int3400_thermal_priv {
63 struct acpi_device *adev;
0ab15365
ZR
64 struct thermal_zone_device *thermal;
65 int mode;
816cab93
ZR
66 int art_count;
67 struct art *arts;
68 int trt_count;
69 struct trt *trts;
c5738ddd 70 u8 uuid_bitmap;
816cab93
ZR
71};
72
c5738ddd
ZR
73static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
74{
75 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
76 union acpi_object *obja, *objb;
77 int i, j;
78 int result = 0;
79 acpi_status status;
80
81 status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
82 if (ACPI_FAILURE(status))
83 return -ENODEV;
84
85 obja = (union acpi_object *)buf.pointer;
86 if (obja->type != ACPI_TYPE_PACKAGE) {
87 result = -EINVAL;
88 goto end;
89 }
90
91 for (i = 0; i < obja->package.count; i++) {
92 objb = &obja->package.elements[i];
93 if (objb->type != ACPI_TYPE_BUFFER) {
94 result = -EINVAL;
95 goto end;
96 }
97
98 /* UUID must be 16 bytes */
99 if (objb->buffer.length != 16) {
100 result = -EINVAL;
101 goto end;
102 }
103
104 for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
105 u8 uuid[16];
106
107 acpi_str_to_uuid(int3400_thermal_uuids[j], uuid);
108 if (!strncmp(uuid, objb->buffer.pointer, 16)) {
109 priv->uuid_bitmap |= (1 << j);
110 break;
111 }
112 }
113 }
114
115end:
116 kfree(buf.pointer);
117 return result;
118}
119
0ab15365
ZR
120static int int3400_thermal_run_osc(acpi_handle handle,
121 enum int3400_thermal_uuid uuid, bool enable)
122{
123 u32 ret, buf[2];
124 acpi_status status;
125 int result = 0;
126 struct acpi_osc_context context = {
127 .uuid_str = int3400_thermal_uuids[uuid],
128 .rev = 1,
129 .cap.length = 8,
130 };
131
132 buf[OSC_QUERY_DWORD] = 0;
133 buf[OSC_SUPPORT_DWORD] = enable;
134
135 context.cap.pointer = buf;
136
137 status = acpi_run_osc(handle, &context);
138 if (ACPI_SUCCESS(status)) {
139 ret = *((u32 *)(context.ret.pointer + 4));
140 if (ret != enable)
141 result = -EPERM;
142 } else
143 result = -EPERM;
144
145 kfree(context.ret.pointer);
146 return result;
147}
148
149
816cab93
ZR
150static int parse_art(struct int3400_thermal_priv *priv)
151{
152 acpi_handle handle = priv->adev->handle;
153 acpi_status status;
154 int result = 0;
155 int i;
156 struct acpi_device *adev;
157 union acpi_object *p;
158 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
159 struct acpi_buffer element = { 0, NULL };
160 struct acpi_buffer art_format = {
161 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
162
163 if (!acpi_has_method(handle, "_ART"))
164 return 0;
165
166 status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
167 if (ACPI_FAILURE(status))
168 return -ENODEV;
169
170 p = buffer.pointer;
171 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
172 pr_err("Invalid _ART data\n");
173 result = -EFAULT;
174 goto end;
175 }
176
177 /* ignore p->package.elements[0], as this is _ART Revision field */
178 priv->art_count = p->package.count - 1;
179 priv->arts = kzalloc(sizeof(struct art) * priv->art_count, GFP_KERNEL);
180 if (!priv->arts) {
181 result = -ENOMEM;
182 goto end;
183 }
184
185 for (i = 0; i < priv->art_count; i++) {
186 struct art *art = &(priv->arts[i]);
187
188 element.length = sizeof(struct art);
189 element.pointer = art;
190
191 status = acpi_extract_package(&(p->package.elements[i + 1]),
192 &art_format, &element);
193 if (ACPI_FAILURE(status)) {
194 pr_err("Invalid _ART data");
195 result = -EFAULT;
196 kfree(priv->arts);
197 goto end;
198 }
199 result = acpi_bus_get_device(art->source, &adev);
200 if (!result)
201 acpi_create_platform_device(adev, NULL);
202 else
203 pr_warn("Failed to get source ACPI device\n");
204 result = acpi_bus_get_device(art->target, &adev);
205 if (!result)
206 acpi_create_platform_device(adev, NULL);
207 else
208 pr_warn("Failed to get source ACPI device\n");
209 }
210end:
211 kfree(buffer.pointer);
212 return result;
213}
214
215static int parse_trt(struct int3400_thermal_priv *priv)
216{
217 acpi_handle handle = priv->adev->handle;
218 acpi_status status;
219 int result = 0;
220 int i;
221 struct acpi_device *adev;
222 union acpi_object *p;
223 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
224 struct acpi_buffer element = { 0, NULL };
225 struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
226
227 if (!acpi_has_method(handle, "_TRT"))
228 return 0;
229
230 status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
231 if (ACPI_FAILURE(status))
232 return -ENODEV;
233
234 p = buffer.pointer;
235 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
236 pr_err("Invalid _TRT data\n");
237 result = -EFAULT;
238 goto end;
239 }
240
241 priv->trt_count = p->package.count;
242 priv->trts = kzalloc(sizeof(struct trt) * priv->trt_count, GFP_KERNEL);
243 if (!priv->trts) {
244 result = -ENOMEM;
245 goto end;
246 }
247
248 for (i = 0; i < priv->trt_count; i++) {
249 struct trt *trt = &(priv->trts[i]);
250
251 element.length = sizeof(struct trt);
252 element.pointer = trt;
253
254 status = acpi_extract_package(&(p->package.elements[i]),
255 &trt_format, &element);
256 if (ACPI_FAILURE(status)) {
257 pr_err("Invalid _ART data");
258 result = -EFAULT;
259 kfree(priv->trts);
260 goto end;
261 }
262
263 result = acpi_bus_get_device(trt->source, &adev);
264 if (!result)
265 acpi_create_platform_device(adev, NULL);
266 else
267 pr_warn("Failed to get source ACPI device\n");
268 result = acpi_bus_get_device(trt->target, &adev);
269 if (!result)
270 acpi_create_platform_device(adev, NULL);
271 else
272 pr_warn("Failed to get target ACPI device\n");
273 }
274end:
275 kfree(buffer.pointer);
276 return result;
277}
278
0ab15365
ZR
279static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
280 unsigned long *temp)
281{
282 *temp = 20 * 1000; /* faked temp sensor with 20C */
283 return 0;
284}
285
286static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
287 enum thermal_device_mode *mode)
288{
289 struct int3400_thermal_priv *priv = thermal->devdata;
290
291 if (!priv)
292 return -EINVAL;
293
294 *mode = priv->mode;
295
296 return 0;
297}
298
299static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
300 enum thermal_device_mode mode)
301{
302 struct int3400_thermal_priv *priv = thermal->devdata;
303 bool enable;
304 int result = 0;
305
306 if (!priv)
307 return -EINVAL;
308
309 if (mode == THERMAL_DEVICE_ENABLED)
310 enable = true;
311 else if (mode == THERMAL_DEVICE_DISABLED)
312 enable = false;
313 else
314 return -EINVAL;
315
316 if (enable != priv->mode) {
317 priv->mode = enable;
318 /* currently, only PASSIVE COOLING is supported */
319 result = int3400_thermal_run_osc(priv->adev->handle,
320 INT3400_THERMAL_PASSIVE_1, enable);
321 }
322 return result;
323}
324
325static struct thermal_zone_device_ops int3400_thermal_ops = {
326 .get_temp = int3400_thermal_get_temp,
327};
328
329static struct thermal_zone_params int3400_thermal_params = {
330 .governor_name = "user_space",
331 .no_hwmon = true,
332};
333
816cab93
ZR
334static int int3400_thermal_probe(struct platform_device *pdev)
335{
336 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
337 struct int3400_thermal_priv *priv;
338 int result;
339
340 if (!adev)
341 return -ENODEV;
342
343 priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
344 if (!priv)
345 return -ENOMEM;
346
347 priv->adev = adev;
348
c5738ddd
ZR
349 result = int3400_thermal_get_uuids(priv);
350 if (result)
351 goto free_priv;
352
816cab93
ZR
353 result = parse_art(priv);
354 if (result)
355 goto free_priv;
356
357 result = parse_trt(priv);
358 if (result)
359 goto free_art;
360
361 platform_set_drvdata(pdev, priv);
362
0ab15365
ZR
363 if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) {
364 int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
365 int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
366 }
367 priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
368 priv, &int3400_thermal_ops,
369 &int3400_thermal_params, 0, 0);
370 if (IS_ERR(priv->thermal)) {
371 result = PTR_ERR(priv->thermal);
372 goto free_trt;
373 }
374
816cab93 375 return 0;
0ab15365
ZR
376free_trt:
377 kfree(priv->trts);
816cab93
ZR
378free_art:
379 kfree(priv->arts);
380free_priv:
381 kfree(priv);
382 return result;
383}
384
385static int int3400_thermal_remove(struct platform_device *pdev)
386{
387 struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
388
0ab15365 389 thermal_zone_device_unregister(priv->thermal);
816cab93
ZR
390 kfree(priv->trts);
391 kfree(priv->arts);
392 kfree(priv);
393 return 0;
394}
395
396static const struct acpi_device_id int3400_thermal_match[] = {
397 {"INT3400", 0},
398 {}
399};
400
401MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
402
403static struct platform_driver int3400_thermal_driver = {
404 .probe = int3400_thermal_probe,
405 .remove = int3400_thermal_remove,
406 .driver = {
407 .name = "int3400 thermal",
408 .owner = THIS_MODULE,
409 .acpi_match_table = ACPI_PTR(int3400_thermal_match),
410 },
411};
412
413module_platform_driver(int3400_thermal_driver);
414
415MODULE_DESCRIPTION("INT3400 Thermal driver");
416MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
417MODULE_LICENSE("GPL");
This page took 0.039297 seconds and 5 git commands to generate.