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 enum int3400_thermal_uuid
{
20 INT3400_THERMAL_PASSIVE_1
,
21 INT3400_THERMAL_ACTIVE
,
22 INT3400_THERMAL_CRITICAL
,
23 INT3400_THERMAL_MAXIMUM_UUID
,
26 static u8
*int3400_thermal_uuids
[INT3400_THERMAL_MAXIMUM_UUID
] = {
27 "42A441D6-AE6A-462b-A84B-4A8CE79027D3",
28 "3A95C389-E4B8-4629-A526-C52C88626BAE",
29 "97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
32 struct int3400_thermal_priv
{
33 struct acpi_device
*adev
;
34 struct thermal_zone_device
*thermal
;
42 int current_uuid_index
;
45 static ssize_t
available_uuids_show(struct device
*dev
,
46 struct device_attribute
*attr
,
49 struct platform_device
*pdev
= to_platform_device(dev
);
50 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
54 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; i
++) {
55 if (priv
->uuid_bitmap
& (1 << i
))
56 if (PAGE_SIZE
- length
> 0)
57 length
+= snprintf(&buf
[length
],
60 int3400_thermal_uuids
[i
]);
66 static ssize_t
current_uuid_show(struct device
*dev
,
67 struct device_attribute
*devattr
, char *buf
)
69 struct platform_device
*pdev
= to_platform_device(dev
);
70 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
72 if (priv
->uuid_bitmap
& (1 << priv
->current_uuid_index
))
73 return sprintf(buf
, "%s\n",
74 int3400_thermal_uuids
[priv
->current_uuid_index
]);
76 return sprintf(buf
, "INVALID\n");
79 static ssize_t
current_uuid_store(struct device
*dev
,
80 struct device_attribute
*attr
,
81 const char *buf
, size_t count
)
83 struct platform_device
*pdev
= to_platform_device(dev
);
84 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
87 for (i
= 0; i
< INT3400_THERMAL_MAXIMUM_UUID
; ++i
) {
88 if ((priv
->uuid_bitmap
& (1 << i
)) &&
89 !(strncmp(buf
, int3400_thermal_uuids
[i
],
90 sizeof(int3400_thermal_uuids
[i
]) - 1))) {
91 priv
->current_uuid_index
= i
;
99 static DEVICE_ATTR(current_uuid
, 0644, current_uuid_show
, current_uuid_store
);
100 static DEVICE_ATTR_RO(available_uuids
);
101 static struct attribute
*uuid_attrs
[] = {
102 &dev_attr_available_uuids
.attr
,
103 &dev_attr_current_uuid
.attr
,
107 static struct attribute_group uuid_attribute_group
= {
112 static int int3400_thermal_get_uuids(struct int3400_thermal_priv
*priv
)
114 struct acpi_buffer buf
= { ACPI_ALLOCATE_BUFFER
, NULL
};
115 union acpi_object
*obja
, *objb
;
120 status
= acpi_evaluate_object(priv
->adev
->handle
, "IDSP", NULL
, &buf
);
121 if (ACPI_FAILURE(status
))
124 obja
= (union acpi_object
*)buf
.pointer
;
125 if (obja
->type
!= ACPI_TYPE_PACKAGE
) {
130 for (i
= 0; i
< obja
->package
.count
; i
++) {
131 objb
= &obja
->package
.elements
[i
];
132 if (objb
->type
!= ACPI_TYPE_BUFFER
) {
137 /* UUID must be 16 bytes */
138 if (objb
->buffer
.length
!= 16) {
143 for (j
= 0; j
< INT3400_THERMAL_MAXIMUM_UUID
; j
++) {
146 acpi_str_to_uuid(int3400_thermal_uuids
[j
], uuid
);
147 if (!strncmp(uuid
, objb
->buffer
.pointer
, 16)) {
148 priv
->uuid_bitmap
|= (1 << j
);
159 static int int3400_thermal_run_osc(acpi_handle handle
,
160 enum int3400_thermal_uuid uuid
, bool enable
)
165 struct acpi_osc_context context
= {
166 .uuid_str
= int3400_thermal_uuids
[uuid
],
171 buf
[OSC_QUERY_DWORD
] = 0;
172 buf
[OSC_SUPPORT_DWORD
] = enable
;
174 context
.cap
.pointer
= buf
;
176 status
= acpi_run_osc(handle
, &context
);
177 if (ACPI_SUCCESS(status
)) {
178 ret
= *((u32
*)(context
.ret
.pointer
+ 4));
184 kfree(context
.ret
.pointer
);
188 static int int3400_thermal_get_temp(struct thermal_zone_device
*thermal
,
191 *temp
= 20 * 1000; /* faked temp sensor with 20C */
195 static int int3400_thermal_get_mode(struct thermal_zone_device
*thermal
,
196 enum thermal_device_mode
*mode
)
198 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
208 static int int3400_thermal_set_mode(struct thermal_zone_device
*thermal
,
209 enum thermal_device_mode mode
)
211 struct int3400_thermal_priv
*priv
= thermal
->devdata
;
218 if (mode
== THERMAL_DEVICE_ENABLED
)
220 else if (mode
== THERMAL_DEVICE_DISABLED
)
225 if (enable
!= priv
->mode
) {
227 result
= int3400_thermal_run_osc(priv
->adev
->handle
,
228 priv
->current_uuid_index
,
234 static struct thermal_zone_device_ops int3400_thermal_ops
= {
235 .get_temp
= int3400_thermal_get_temp
,
238 static struct thermal_zone_params int3400_thermal_params
= {
239 .governor_name
= "user_space",
243 static int int3400_thermal_probe(struct platform_device
*pdev
)
245 struct acpi_device
*adev
= ACPI_COMPANION(&pdev
->dev
);
246 struct int3400_thermal_priv
*priv
;
252 priv
= kzalloc(sizeof(struct int3400_thermal_priv
), GFP_KERNEL
);
258 result
= int3400_thermal_get_uuids(priv
);
262 result
= acpi_parse_art(priv
->adev
->handle
, &priv
->art_count
,
265 dev_dbg(&pdev
->dev
, "_ART table parsing error\n");
267 result
= acpi_parse_trt(priv
->adev
->handle
, &priv
->trt_count
,
270 dev_dbg(&pdev
->dev
, "_TRT table parsing error\n");
272 platform_set_drvdata(pdev
, priv
);
274 if (priv
->uuid_bitmap
& 1 << INT3400_THERMAL_PASSIVE_1
) {
275 int3400_thermal_ops
.get_mode
= int3400_thermal_get_mode
;
276 int3400_thermal_ops
.set_mode
= int3400_thermal_set_mode
;
278 priv
->thermal
= thermal_zone_device_register("INT3400 Thermal", 0, 0,
279 priv
, &int3400_thermal_ops
,
280 &int3400_thermal_params
, 0, 0);
281 if (IS_ERR(priv
->thermal
)) {
282 result
= PTR_ERR(priv
->thermal
);
286 priv
->rel_misc_dev_res
= acpi_thermal_rel_misc_device_add(
289 result
= sysfs_create_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
296 thermal_zone_device_unregister(priv
->thermal
);
305 static int int3400_thermal_remove(struct platform_device
*pdev
)
307 struct int3400_thermal_priv
*priv
= platform_get_drvdata(pdev
);
309 if (!priv
->rel_misc_dev_res
)
310 acpi_thermal_rel_misc_device_remove(priv
->adev
->handle
);
312 sysfs_remove_group(&pdev
->dev
.kobj
, &uuid_attribute_group
);
313 thermal_zone_device_unregister(priv
->thermal
);
320 static const struct acpi_device_id int3400_thermal_match
[] = {
325 MODULE_DEVICE_TABLE(acpi
, int3400_thermal_match
);
327 static struct platform_driver int3400_thermal_driver
= {
328 .probe
= int3400_thermal_probe
,
329 .remove
= int3400_thermal_remove
,
331 .name
= "int3400 thermal",
332 .acpi_match_table
= ACPI_PTR(int3400_thermal_match
),
336 module_platform_driver(int3400_thermal_driver
);
338 MODULE_DESCRIPTION("INT3400 Thermal driver");
339 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
340 MODULE_LICENSE("GPL");