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 platform_device
*pdev
= to_platform_device(dev
);
66 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
70 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; i
++) {
71 if (priv
->uuid_bitmap
& (1 << i
))
72 if (PAGE_SIZE
- length
> 0)
73 length
+= snprintf(&buf
[length
],
76 int3400_thermal_uuids
[i
]);
82 static ssize_t
current_uuid_show(struct device
*dev
,
83 struct device_attribute
*devattr
, char *buf
)
85 struct platform_device
*pdev
= to_platform_device(dev
);
86 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
88 if (priv
->uuid_bitmap
& (1 << priv
->current_uuid_index
))
89 return sprintf(buf
, "%s\n",
90 int3400_thermal_uuids
[priv
->current_uuid_index
]);
92 return sprintf(buf
, "INVALID\n");
95 static ssize_t
current_uuid_store(struct device
*dev
,
96 struct device_attribute
*attr
,
97 const char *buf
, size_t count
)
99 struct platform_device
*pdev
= to_platform_device(dev
);
100 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
103 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; ++i
) {
104 if ((priv
->uuid_bitmap
& (1 << i
)) &&
105 !(strncmp(buf
, int3400_thermal_uuids
[i
],
106 sizeof(int3400_thermal_uuids
[i
]) - 1))) {
107 priv
->current_uuid_index
= i
;
115 static DEVICE_ATTR_RW(current_uuid
);
116 static DEVICE_ATTR_RO(available_uuids
);
117 static struct attribute
*uuid_attrs
[] = {
118 &dev_attr_available_uuids
.attr
,
119 &dev_attr_current_uuid
.attr
,
123 static const struct attribute_group uuid_attribute_group
= {
128 static int int3400_thermal_get_uuids(struct int3400_thermal_priv
*priv
)
130 struct acpi_buffer buf
= { ACPI_ALLOCATE_BUFFER
, NULL
};
131 union acpi_object
*obja
, *objb
;
136 status
= acpi_evaluate_object(priv
->adev
->handle
, "IDSP", NULL
, &buf
);
137 if (ACPI_FAILURE(status
))
140 obja
= (union acpi_object
*)buf
.pointer
;
141 if (obja
->type
!= ACPI_TYPE_PACKAGE
) {
146 for (i
= 0; i
< obja
->package
.count
; i
++) {
147 objb
= &obja
->package
.elements
[i
];
148 if (objb
->type
!= ACPI_TYPE_BUFFER
) {
153 /* UUID must be 16 bytes */
154 if (objb
->buffer
.length
!= 16) {
159 for (j
= 0; j
< INT3400_THERMAL_MAXIMUM_UUID
; j
++) {
162 guid_parse(int3400_thermal_uuids
[j
], &guid
);
163 if (guid_equal((guid_t
*)objb
->buffer
.pointer
, &guid
)) {
164 priv
->uuid_bitmap
|= (1 << j
);
175 static int int3400_thermal_run_osc(acpi_handle handle
,
176 enum int3400_thermal_uuid uuid
, bool enable
)
181 struct acpi_osc_context context
= {
182 .uuid_str
= int3400_thermal_uuids
[uuid
],
187 buf
[OSC_QUERY_DWORD
] = 0;
188 buf
[OSC_SUPPORT_DWORD
] = enable
;
190 context
.cap
.pointer
= buf
;
192 status
= acpi_run_osc(handle
, &context
);
193 if (ACPI_SUCCESS(status
)) {
194 ret
= *((u32
*)(context
.ret
.pointer
+ 4));
200 kfree(context
.ret
.pointer
);
204 static void int3400_notify(acpi_handle handle
,
208 struct int3400_thermal_priv
*priv
= data
;
209 char *thermal_prop
[5];
215 case INT3400_THERMAL_TABLE_CHANGED
:
216 thermal_prop
[0] = kasprintf(GFP_KERNEL
, "NAME=%s",
217 priv
->thermal
->type
);
218 thermal_prop
[1] = kasprintf(GFP_KERNEL
, "TEMP=%d",
219 priv
->thermal
->temperature
);
220 thermal_prop
[2] = kasprintf(GFP_KERNEL
, "TRIP=");
221 thermal_prop
[3] = kasprintf(GFP_KERNEL
, "EVENT=%d",
222 THERMAL_TABLE_CHANGED
);
223 thermal_prop
[4] = NULL
;
224 kobject_uevent_env(&priv
->thermal
->device
.kobj
, KOBJ_CHANGE
,
228 /* Ignore unknown notification codes sent to INT3400 device */
233 static int int3400_thermal_get_temp(struct thermal_zone_device
*thermal
,
236 *temp
= 20 * 1000; /* faked temp sensor with 20C */
240 static int int3400_thermal_get_mode(struct thermal_zone_device
*thermal
,
241 enum thermal_device_mode
*mode
)
243 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
253 static int int3400_thermal_set_mode(struct thermal_zone_device
*thermal
,
254 enum thermal_device_mode mode
)
256 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
263 if (mode
== THERMAL_DEVICE_ENABLED
)
265 else if (mode
== THERMAL_DEVICE_DISABLED
)
270 if (enable
!= priv
->mode
) {
272 result
= int3400_thermal_run_osc(priv
->adev
->handle
,
273 priv
->current_uuid_index
,
279 static struct thermal_zone_device_ops int3400_thermal_ops
= {
280 .get_temp
= int3400_thermal_get_temp
,
283 static struct thermal_zone_params int3400_thermal_params
= {
284 .governor_name
= "user_space",
288 static int int3400_thermal_probe(struct platform_device
*pdev
)
290 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
291 struct int3400_thermal_priv
*priv
;
297 priv
= kzalloc(sizeof(struct int3400_thermal_priv
), GFP_KERNEL
);
303 result
= int3400_thermal_get_uuids(priv
);
307 result
= acpi_parse_art(priv
->adev
->handle
, &priv
->art_count
,
310 dev_dbg(&pdev
->dev
, "_ART table parsing error\n");
312 result
= acpi_parse_trt(priv
->adev
->handle
, &priv
->trt_count
,
315 dev_dbg(&pdev
->dev
, "_TRT table parsing error\n");
317 platform_set_drvdata(pdev
, priv
);
319 int3400_thermal_ops
.get_mode
= int3400_thermal_get_mode
;
320 int3400_thermal_ops
.set_mode
= int3400_thermal_set_mode
;
322 priv
->thermal
= thermal_zone_device_register("INT3400 Thermal", 0, 0,
323 priv
, &int3400_thermal_ops
,
324 &int3400_thermal_params
, 0, 0);
325 if (IS_ERR(priv
->thermal
)) {
326 result
= PTR_ERR(priv
->thermal
);
330 priv
->rel_misc_dev_res
= acpi_thermal_rel_misc_device_add(
333 result
= sysfs_create_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
337 result
= acpi_install_notify_handler(
338 priv
->adev
->handle
, ACPI_DEVICE_NOTIFY
, int3400_notify
,
346 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
348 if (!priv
->rel_misc_dev_res
)
349 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
350 thermal_zone_device_unregister(priv
->thermal
);
359 static int int3400_thermal_remove(struct platform_device
*pdev
)
361 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
363 acpi_remove_notify_handler(
364 priv
->adev
->handle
, ACPI_DEVICE_NOTIFY
,
367 if (!priv
->rel_misc_dev_res
)
368 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
370 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
371 thermal_zone_device_unregister(priv
->thermal
);
378 static const struct acpi_device_id int3400_thermal_match
[] = {
383 MODULE_DEVICE_TABLE(acpi
, int3400_thermal_match
);
385 static struct platform_driver int3400_thermal_driver
= {
386 .probe
= int3400_thermal_probe
,
387 .remove
= int3400_thermal_remove
,
389 .name
= "int3400 thermal",
390 .acpi_match_table
= ACPI_PTR(int3400_thermal_match
),
394 module_platform_driver(int3400_thermal_driver
);
396 MODULE_DESCRIPTION("INT3400 Thermal driver");
397 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
398 MODULE_LICENSE("GPL");