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_MAXIMUM_UUID
,
28 static char *int3400_thermal_uuids
[INT3400_THERMAL_MAXIMUM_UUID
] = {
29 "42A441D6-AE6A-462b-A84B-4A8CE79027D3",
30 "3A95C389-E4B8-4629-A526-C52C88626BAE",
31 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
34 struct int3400_thermal_priv
{
35 struct acpi_device
*adev
;
36 struct thermal_zone_device
*thermal
;
44 int current_uuid_index
;
47 static ssize_t
available_uuids_show(struct device
*dev
,
48 struct device_attribute
*attr
,
51 struct platform_device
*pdev
= to_platform_device(dev
);
52 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
56 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; i
++) {
57 if (priv
->uuid_bitmap
& (1 << i
))
58 if (PAGE_SIZE
- length
> 0)
59 length
+= snprintf(&buf
[length
],
62 int3400_thermal_uuids
[i
]);
68 static ssize_t
current_uuid_show(struct device
*dev
,
69 struct device_attribute
*devattr
, char *buf
)
71 struct platform_device
*pdev
= to_platform_device(dev
);
72 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
74 if (priv
->uuid_bitmap
& (1 << priv
->current_uuid_index
))
75 return sprintf(buf
, "%s\n",
76 int3400_thermal_uuids
[priv
->current_uuid_index
]);
78 return sprintf(buf
, "INVALID\n");
81 static ssize_t
current_uuid_store(struct device
*dev
,
82 struct device_attribute
*attr
,
83 const char *buf
, size_t count
)
85 struct platform_device
*pdev
= to_platform_device(dev
);
86 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
89 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; ++i
) {
90 if ((priv
->uuid_bitmap
& (1 << i
)) &&
91 !(strncmp(buf
, int3400_thermal_uuids
[i
],
92 sizeof(int3400_thermal_uuids
[i
]) - 1))) {
93 priv
->current_uuid_index
= i
;
101 static DEVICE_ATTR_RW(current_uuid
);
102 static DEVICE_ATTR_RO(available_uuids
);
103 static struct attribute
*uuid_attrs
[] = {
104 &dev_attr_available_uuids
.attr
,
105 &dev_attr_current_uuid
.attr
,
109 static const struct attribute_group uuid_attribute_group
= {
114 static int int3400_thermal_get_uuids(struct int3400_thermal_priv
*priv
)
116 struct acpi_buffer buf
= { ACPI_ALLOCATE_BUFFER
, NULL
};
117 union acpi_object
*obja
, *objb
;
122 status
= acpi_evaluate_object(priv
->adev
->handle
, "IDSP", NULL
, &buf
);
123 if (ACPI_FAILURE(status
))
126 obja
= (union acpi_object
*)buf
.pointer
;
127 if (obja
->type
!= ACPI_TYPE_PACKAGE
) {
132 for (i
= 0; i
< obja
->package
.count
; i
++) {
133 objb
= &obja
->package
.elements
[i
];
134 if (objb
->type
!= ACPI_TYPE_BUFFER
) {
139 /* UUID must be 16 bytes */
140 if (objb
->buffer
.length
!= 16) {
145 for (j
= 0; j
< INT3400_THERMAL_MAXIMUM_UUID
; j
++) {
148 guid_parse(int3400_thermal_uuids
[j
], &guid
);
149 if (guid_equal((guid_t
*)objb
->buffer
.pointer
, &guid
)) {
150 priv
->uuid_bitmap
|= (1 << j
);
161 static int int3400_thermal_run_osc(acpi_handle handle
,
162 enum int3400_thermal_uuid uuid
, bool enable
)
167 struct acpi_osc_context context
= {
168 .uuid_str
= int3400_thermal_uuids
[uuid
],
173 buf
[OSC_QUERY_DWORD
] = 0;
174 buf
[OSC_SUPPORT_DWORD
] = enable
;
176 context
.cap
.pointer
= buf
;
178 status
= acpi_run_osc(handle
, &context
);
179 if (ACPI_SUCCESS(status
)) {
180 ret
= *((u32
*)(context
.ret
.pointer
+ 4));
186 kfree(context
.ret
.pointer
);
190 static void int3400_notify(acpi_handle handle
,
194 struct int3400_thermal_priv
*priv
= data
;
195 char *thermal_prop
[5];
201 case INT3400_THERMAL_TABLE_CHANGED
:
202 thermal_prop
[0] = kasprintf(GFP_KERNEL
, "NAME=%s",
203 priv
->thermal
->type
);
204 thermal_prop
[1] = kasprintf(GFP_KERNEL
, "TEMP=%d",
205 priv
->thermal
->temperature
);
206 thermal_prop
[2] = kasprintf(GFP_KERNEL
, "TRIP=");
207 thermal_prop
[3] = kasprintf(GFP_KERNEL
, "EVENT=%d",
208 THERMAL_TABLE_CHANGED
);
209 thermal_prop
[4] = NULL
;
210 kobject_uevent_env(&priv
->thermal
->device
.kobj
, KOBJ_CHANGE
,
214 /* Ignore unknown notification codes sent to INT3400 device */
219 static int int3400_thermal_get_temp(struct thermal_zone_device
*thermal
,
222 *temp
= 20 * 1000; /* faked temp sensor with 20C */
226 static int int3400_thermal_get_mode(struct thermal_zone_device
*thermal
,
227 enum thermal_device_mode
*mode
)
229 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
239 static int int3400_thermal_set_mode(struct thermal_zone_device
*thermal
,
240 enum thermal_device_mode mode
)
242 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
249 if (mode
== THERMAL_DEVICE_ENABLED
)
251 else if (mode
== THERMAL_DEVICE_DISABLED
)
256 if (enable
!= priv
->mode
) {
258 result
= int3400_thermal_run_osc(priv
->adev
->handle
,
259 priv
->current_uuid_index
,
265 static struct thermal_zone_device_ops int3400_thermal_ops
= {
266 .get_temp
= int3400_thermal_get_temp
,
269 static struct thermal_zone_params int3400_thermal_params
= {
270 .governor_name
= "user_space",
274 static int int3400_thermal_probe(struct platform_device
*pdev
)
276 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
277 struct int3400_thermal_priv
*priv
;
283 priv
= kzalloc(sizeof(struct int3400_thermal_priv
), GFP_KERNEL
);
289 result
= int3400_thermal_get_uuids(priv
);
293 result
= acpi_parse_art(priv
->adev
->handle
, &priv
->art_count
,
296 dev_dbg(&pdev
->dev
, "_ART table parsing error\n");
298 result
= acpi_parse_trt(priv
->adev
->handle
, &priv
->trt_count
,
301 dev_dbg(&pdev
->dev
, "_TRT table parsing error\n");
303 platform_set_drvdata(pdev
, priv
);
305 if (priv
->uuid_bitmap
& 1 << INT3400_THERMAL_PASSIVE_1
) {
306 int3400_thermal_ops
.get_mode
= int3400_thermal_get_mode
;
307 int3400_thermal_ops
.set_mode
= int3400_thermal_set_mode
;
309 priv
->thermal
= thermal_zone_device_register("INT3400 Thermal", 0, 0,
310 priv
, &int3400_thermal_ops
,
311 &int3400_thermal_params
, 0, 0);
312 if (IS_ERR(priv
->thermal
)) {
313 result
= PTR_ERR(priv
->thermal
);
317 priv
->rel_misc_dev_res
= acpi_thermal_rel_misc_device_add(
320 result
= sysfs_create_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
324 result
= acpi_install_notify_handler(
325 priv
->adev
->handle
, ACPI_DEVICE_NOTIFY
, int3400_notify
,
333 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
335 if (!priv
->rel_misc_dev_res
)
336 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
337 thermal_zone_device_unregister(priv
->thermal
);
346 static int int3400_thermal_remove(struct platform_device
*pdev
)
348 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
350 acpi_remove_notify_handler(
351 priv
->adev
->handle
, ACPI_DEVICE_NOTIFY
,
354 if (!priv
->rel_misc_dev_res
)
355 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
357 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
358 thermal_zone_device_unregister(priv
->thermal
);
365 static const struct acpi_device_id int3400_thermal_match
[] = {
370 MODULE_DEVICE_TABLE(acpi
, int3400_thermal_match
);
372 static struct platform_driver int3400_thermal_driver
= {
373 .probe
= int3400_thermal_probe
,
374 .remove
= int3400_thermal_remove
,
376 .name
= "int3400 thermal",
377 .acpi_match_table
= ACPI_PTR(int3400_thermal_match
),
381 module_platform_driver(int3400_thermal_driver
);
383 MODULE_DESCRIPTION("INT3400 Thermal driver");
384 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
385 MODULE_LICENSE("GPL");