2 * INT3400 thermal driver
4 * Copyright (C) 2014, Intel Corporation
5 * Authors: Zhang Rui <rui.zhang@intel.com>
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.
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/acpi.h>
16 #include <linux/thermal.h>
17 #include "acpi_thermal_rel.h"
19 #define INT3400_THERMAL_TABLE_CHANGED 0x83
21 enum int3400_thermal_uuid
{
22 INT3400_THERMAL_PASSIVE_1
,
23 INT3400_THERMAL_ACTIVE
,
24 INT3400_THERMAL_CRITICAL
,
25 INT3400_THERMAL_ADAPTIVE_PERFORMANCE
,
26 INT3400_THERMAL_EMERGENCY_CALL_MODE
,
27 INT3400_THERMAL_PASSIVE_2
,
28 INT3400_THERMAL_POWER_BOSS
,
29 INT3400_THERMAL_VIRTUAL_SENSOR
,
30 INT3400_THERMAL_COOLING_MODE
,
31 INT3400_THERMAL_HARDWARE_DUTY_CYCLING
,
32 INT3400_THERMAL_MAXIMUM_UUID
,
35 static char *int3400_thermal_uuids
[INT3400_THERMAL_MAXIMUM_UUID
] = {
36 "42A441D6-AE6A-462b-A84B-4A8CE79027D3",
37 "3A95C389-E4B8-4629-A526-C52C88626BAE",
38 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
39 "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D",
40 "5349962F-71E6-431D-9AE8-0A635B710AEE",
41 "9E04115A-AE87-4D1C-9500-0F3E340BFE75",
42 "F5A35014-C209-46A4-993A-EB56DE7530A1",
43 "6ED722A7-9240-48A5-B479-31EEF723D7CF",
44 "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531",
45 "BE84BABF-C4D4-403D-B495-3128FD44dAC1",
48 struct int3400_thermal_priv
{
49 struct acpi_device
*adev
;
50 struct thermal_zone_device
*thermal
;
58 int current_uuid_index
;
61 static ssize_t
available_uuids_show(struct device
*dev
,
62 struct device_attribute
*attr
,
65 struct int3400_thermal_priv
*priv
= dev_get_drvdata(dev
);
69 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; i
++) {
70 if (priv
->uuid_bitmap
& (1 << i
))
71 if (PAGE_SIZE
- length
> 0)
72 length
+= snprintf(&buf
[length
],
75 int3400_thermal_uuids
[i
]);
81 static ssize_t
current_uuid_show(struct device
*dev
,
82 struct device_attribute
*devattr
, char *buf
)
84 struct int3400_thermal_priv
*priv
= dev_get_drvdata(dev
);
86 if (priv
->uuid_bitmap
& (1 << priv
->current_uuid_index
))
87 return sprintf(buf
, "%s\n",
88 int3400_thermal_uuids
[priv
->current_uuid_index
]);
90 return sprintf(buf
, "INVALID\n");
93 static ssize_t
current_uuid_store(struct device
*dev
,
94 struct device_attribute
*attr
,
95 const char *buf
, size_t count
)
97 struct int3400_thermal_priv
*priv
= dev_get_drvdata(dev
);
100 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; ++i
) {
101 if ((priv
->uuid_bitmap
& (1 << i
)) &&
102 !(strncmp(buf
, int3400_thermal_uuids
[i
],
103 sizeof(int3400_thermal_uuids
[i
]) - 1))) {
104 priv
->current_uuid_index
= i
;
112 static DEVICE_ATTR_RW(current_uuid
);
113 static DEVICE_ATTR_RO(available_uuids
);
114 static struct attribute
*uuid_attrs
[] = {
115 &dev_attr_available_uuids
.attr
,
116 &dev_attr_current_uuid
.attr
,
120 static const struct attribute_group uuid_attribute_group
= {
125 static int int3400_thermal_get_uuids(struct int3400_thermal_priv
*priv
)
127 struct acpi_buffer buf
= { ACPI_ALLOCATE_BUFFER
, NULL
};
128 union acpi_object
*obja
, *objb
;
133 status
= acpi_evaluate_object(priv
->adev
->handle
, "IDSP", NULL
, &buf
);
134 if (ACPI_FAILURE(status
))
137 obja
= (union acpi_object
*)buf
.pointer
;
138 if (obja
->type
!= ACPI_TYPE_PACKAGE
) {
143 for (i
= 0; i
< obja
->package
.count
; i
++) {
144 objb
= &obja
->package
.elements
[i
];
145 if (objb
->type
!= ACPI_TYPE_BUFFER
) {
150 /* UUID must be 16 bytes */
151 if (objb
->buffer
.length
!= 16) {
156 for (j
= 0; j
< INT3400_THERMAL_MAXIMUM_UUID
; j
++) {
159 guid_parse(int3400_thermal_uuids
[j
], &guid
);
160 if (guid_equal((guid_t
*)objb
->buffer
.pointer
, &guid
)) {
161 priv
->uuid_bitmap
|= (1 << j
);
172 static int int3400_thermal_run_osc(acpi_handle handle
,
173 enum int3400_thermal_uuid uuid
, bool enable
)
178 struct acpi_osc_context context
= {
179 .uuid_str
= int3400_thermal_uuids
[uuid
],
184 buf
[OSC_QUERY_DWORD
] = 0;
185 buf
[OSC_SUPPORT_DWORD
] = enable
;
187 context
.cap
.pointer
= buf
;
189 status
= acpi_run_osc(handle
, &context
);
190 if (ACPI_SUCCESS(status
)) {
191 ret
= *((u32
*)(context
.ret
.pointer
+ 4));
197 kfree(context
.ret
.pointer
);
201 static void int3400_notify(acpi_handle handle
,
205 struct int3400_thermal_priv
*priv
= data
;
206 char *thermal_prop
[5];
212 case INT3400_THERMAL_TABLE_CHANGED
:
213 thermal_prop
[0] = kasprintf(GFP_KERNEL
, "NAME=%s",
214 priv
->thermal
->type
);
215 thermal_prop
[1] = kasprintf(GFP_KERNEL
, "TEMP=%d",
216 priv
->thermal
->temperature
);
217 thermal_prop
[2] = kasprintf(GFP_KERNEL
, "TRIP=");
218 thermal_prop
[3] = kasprintf(GFP_KERNEL
, "EVENT=%d",
219 THERMAL_TABLE_CHANGED
);
220 thermal_prop
[4] = NULL
;
221 kobject_uevent_env(&priv
->thermal
->device
.kobj
, KOBJ_CHANGE
,
225 /* Ignore unknown notification codes sent to INT3400 device */
230 static int int3400_thermal_get_temp(struct thermal_zone_device
*thermal
,
233 *temp
= 20 * 1000; /* faked temp sensor with 20C */
237 static int int3400_thermal_get_mode(struct thermal_zone_device
*thermal
,
238 enum thermal_device_mode
*mode
)
240 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
250 static int int3400_thermal_set_mode(struct thermal_zone_device
*thermal
,
251 enum thermal_device_mode mode
)
253 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
260 if (mode
== THERMAL_DEVICE_ENABLED
)
262 else if (mode
== THERMAL_DEVICE_DISABLED
)
267 if (enable
!= priv
->mode
) {
269 result
= int3400_thermal_run_osc(priv
->adev
->handle
,
270 priv
->current_uuid_index
,
276 static struct thermal_zone_device_ops int3400_thermal_ops
= {
277 .get_temp
= int3400_thermal_get_temp
,
280 static struct thermal_zone_params int3400_thermal_params
= {
281 .governor_name
= "user_space",
285 static int int3400_thermal_probe(struct platform_device
*pdev
)
287 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
288 struct int3400_thermal_priv
*priv
;
294 priv
= kzalloc(sizeof(struct int3400_thermal_priv
), GFP_KERNEL
);
300 result
= int3400_thermal_get_uuids(priv
);
304 result
= acpi_parse_art(priv
->adev
->handle
, &priv
->art_count
,
307 dev_dbg(&pdev
->dev
, "_ART table parsing error\n");
309 result
= acpi_parse_trt(priv
->adev
->handle
, &priv
->trt_count
,
312 dev_dbg(&pdev
->dev
, "_TRT table parsing error\n");
314 platform_set_drvdata(pdev
, priv
);
316 int3400_thermal_ops
.get_mode
= int3400_thermal_get_mode
;
317 int3400_thermal_ops
.set_mode
= int3400_thermal_set_mode
;
319 priv
->thermal
= thermal_zone_device_register("INT3400 Thermal", 0, 0,
320 priv
, &int3400_thermal_ops
,
321 &int3400_thermal_params
, 0, 0);
322 if (IS_ERR(priv
->thermal
)) {
323 result
= PTR_ERR(priv
->thermal
);
327 priv
->rel_misc_dev_res
= acpi_thermal_rel_misc_device_add(
330 result
= sysfs_create_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
334 result
= acpi_install_notify_handler(
335 priv
->adev
->handle
, ACPI_DEVICE_NOTIFY
, int3400_notify
,
343 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
345 if (!priv
->rel_misc_dev_res
)
346 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
347 thermal_zone_device_unregister(priv
->thermal
);
356 static int int3400_thermal_remove(struct platform_device
*pdev
)
358 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
360 acpi_remove_notify_handler(
361 priv
->adev
->handle
, ACPI_DEVICE_NOTIFY
,
364 if (!priv
->rel_misc_dev_res
)
365 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
367 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
368 thermal_zone_device_unregister(priv
->thermal
);
375 static const struct acpi_device_id int3400_thermal_match
[] = {
380 MODULE_DEVICE_TABLE(acpi
, int3400_thermal_match
);
382 static struct platform_driver int3400_thermal_driver
= {
383 .probe
= int3400_thermal_probe
,
384 .remove
= int3400_thermal_remove
,
386 .name
= "int3400 thermal",
387 .acpi_match_table
= ACPI_PTR(int3400_thermal_match
),
391 module_platform_driver(int3400_thermal_driver
);
393 MODULE_DESCRIPTION("INT3400 Thermal driver");
394 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
395 MODULE_LICENSE("GPL");