2 * ACPI INT3403 thermal driver
3 * Copyright (c) 2013, Intel Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/types.h>
19 #include <linux/acpi.h>
20 #include <linux/thermal.h>
21 #include <linux/platform_device.h>
23 #define INT3403_TYPE_SENSOR 0x03
24 #define INT3403_TYPE_CHARGER 0x0B
25 #define INT3403_TYPE_BATTERY 0x0C
26 #define INT3403_PERF_CHANGED_EVENT 0x80
27 #define INT3403_THERMAL_EVENT 0x90
29 #define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
30 #define KELVIN_OFFSET 2732
31 #define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
33 struct int3403_sensor
{
34 struct thermal_zone_device
*tzone
;
35 unsigned long *thresholds
;
36 unsigned long crit_temp
;
38 unsigned long psv_temp
;
43 struct int3403_performance_state
{
55 struct thermal_cooling_device
*cdev
;
56 unsigned long max_state
;
60 struct platform_device
*pdev
;
61 struct acpi_device
*adev
;
62 unsigned long long type
;
66 static int sys_get_curr_temp(struct thermal_zone_device
*tzone
,
69 struct int3403_priv
*priv
= tzone
->devdata
;
70 struct acpi_device
*device
= priv
->adev
;
71 unsigned long long tmp
;
74 status
= acpi_evaluate_integer(device
->handle
, "_TMP", NULL
, &tmp
);
75 if (ACPI_FAILURE(status
))
78 *temp
= DECI_KELVIN_TO_MILLI_CELSIUS(tmp
, KELVIN_OFFSET
);
83 static int sys_get_trip_hyst(struct thermal_zone_device
*tzone
,
84 int trip
, unsigned long *temp
)
86 struct int3403_priv
*priv
= tzone
->devdata
;
87 struct acpi_device
*device
= priv
->adev
;
88 unsigned long long hyst
;
91 status
= acpi_evaluate_integer(device
->handle
, "GTSH", NULL
, &hyst
);
92 if (ACPI_FAILURE(status
))
96 * Thermal hysteresis represents a temperature difference.
97 * Kelvin and Celsius have same degree size. So the
98 * conversion here between tenths of degree Kelvin unit
99 * and Milli-Celsius unit is just to multiply 100.
106 static int sys_get_trip_temp(struct thermal_zone_device
*tzone
,
107 int trip
, unsigned long *temp
)
109 struct int3403_priv
*priv
= tzone
->devdata
;
110 struct int3403_sensor
*obj
= priv
->priv
;
112 if (priv
->type
!= INT3403_TYPE_SENSOR
|| !obj
)
115 if (trip
== obj
->crit_trip_id
)
116 *temp
= obj
->crit_temp
;
117 else if (trip
== obj
->psv_trip_id
)
118 *temp
= obj
->psv_temp
;
121 * get_trip_temp is a mandatory callback but
122 * PATx method doesn't return any value, so return
123 * cached value, which was last set from user space
125 *temp
= obj
->thresholds
[trip
];
131 static int sys_get_trip_type(struct thermal_zone_device
*thermal
,
132 int trip
, enum thermal_trip_type
*type
)
134 struct int3403_priv
*priv
= thermal
->devdata
;
135 struct int3403_sensor
*obj
= priv
->priv
;
137 /* Mandatory callback, may not mean much here */
138 if (trip
== obj
->crit_trip_id
)
139 *type
= THERMAL_TRIP_CRITICAL
;
141 *type
= THERMAL_TRIP_PASSIVE
;
146 int sys_set_trip_temp(struct thermal_zone_device
*tzone
, int trip
,
149 struct int3403_priv
*priv
= tzone
->devdata
;
150 struct acpi_device
*device
= priv
->adev
;
151 struct int3403_sensor
*obj
= priv
->priv
;
156 snprintf(name
, sizeof(name
), "PAT%d", trip
);
157 if (acpi_has_method(device
->handle
, name
)) {
158 status
= acpi_execute_simple_method(device
->handle
, name
,
159 MILLI_CELSIUS_TO_DECI_KELVIN(temp
,
161 if (ACPI_FAILURE(status
))
164 obj
->thresholds
[trip
] = temp
;
167 dev_err(&device
->dev
, "sys_set_trip_temp: method not found\n");
173 static struct thermal_zone_device_ops tzone_ops
= {
174 .get_temp
= sys_get_curr_temp
,
175 .get_trip_temp
= sys_get_trip_temp
,
176 .get_trip_type
= sys_get_trip_type
,
177 .set_trip_temp
= sys_set_trip_temp
,
178 .get_trip_hyst
= sys_get_trip_hyst
,
181 static struct thermal_zone_params int3403_thermal_params
= {
182 .governor_name
= "user_space",
186 static void int3403_notify(acpi_handle handle
,
187 u32 event
, void *data
)
189 struct int3403_priv
*priv
= data
;
190 struct int3403_sensor
*obj
;
196 if (priv
->type
!= INT3403_TYPE_SENSOR
|| !obj
)
200 case INT3403_PERF_CHANGED_EVENT
:
202 case INT3403_THERMAL_EVENT
:
203 thermal_zone_device_update(obj
->tzone
);
206 dev_err(&priv
->pdev
->dev
, "Unsupported event [0x%x]\n", event
);
211 static int sys_get_trip_crt(struct acpi_device
*device
, unsigned long *temp
)
213 unsigned long long crt
;
216 status
= acpi_evaluate_integer(device
->handle
, "_CRT", NULL
, &crt
);
217 if (ACPI_FAILURE(status
))
220 *temp
= DECI_KELVIN_TO_MILLI_CELSIUS(crt
, KELVIN_OFFSET
);
225 static int sys_get_trip_psv(struct acpi_device
*device
, unsigned long *temp
)
227 unsigned long long psv
;
230 status
= acpi_evaluate_integer(device
->handle
, "_PSV", NULL
, &psv
);
231 if (ACPI_FAILURE(status
))
234 *temp
= DECI_KELVIN_TO_MILLI_CELSIUS(psv
, KELVIN_OFFSET
);
239 static int int3403_sensor_add(struct int3403_priv
*priv
)
243 struct int3403_sensor
*obj
;
244 unsigned long long trip_cnt
;
247 obj
= devm_kzalloc(&priv
->pdev
->dev
, sizeof(*obj
), GFP_KERNEL
);
253 status
= acpi_evaluate_integer(priv
->adev
->handle
, "PATC", NULL
,
255 if (ACPI_FAILURE(status
))
259 /* We have to cache, thresholds can't be readback */
260 obj
->thresholds
= devm_kzalloc(&priv
->pdev
->dev
,
261 sizeof(*obj
->thresholds
) * trip_cnt
,
263 if (!obj
->thresholds
) {
267 trip_mask
= BIT(trip_cnt
) - 1;
270 obj
->psv_trip_id
= -1;
271 if (!sys_get_trip_psv(priv
->adev
, &obj
->psv_temp
))
272 obj
->psv_trip_id
= trip_cnt
++;
274 obj
->crit_trip_id
= -1;
275 if (!sys_get_trip_crt(priv
->adev
, &obj
->crit_temp
))
276 obj
->crit_trip_id
= trip_cnt
++;
278 obj
->tzone
= thermal_zone_device_register(acpi_device_bid(priv
->adev
),
279 trip_cnt
, trip_mask
, priv
, &tzone_ops
,
280 &int3403_thermal_params
, 0, 0);
281 if (IS_ERR(obj
->tzone
)) {
282 result
= PTR_ERR(obj
->tzone
);
287 result
= acpi_install_notify_handler(priv
->adev
->handle
,
288 ACPI_DEVICE_NOTIFY
, int3403_notify
,
297 thermal_zone_device_unregister(obj
->tzone
);
301 static int int3403_sensor_remove(struct int3403_priv
*priv
)
303 struct int3403_sensor
*obj
= priv
->priv
;
305 thermal_zone_device_unregister(obj
->tzone
);
309 /* INT3403 Cooling devices */
310 static int int3403_get_max_state(struct thermal_cooling_device
*cdev
,
311 unsigned long *state
)
313 struct int3403_priv
*priv
= cdev
->devdata
;
314 struct int3403_cdev
*obj
= priv
->priv
;
316 *state
= obj
->max_state
;
320 static int int3403_get_cur_state(struct thermal_cooling_device
*cdev
,
321 unsigned long *state
)
323 struct int3403_priv
*priv
= cdev
->devdata
;
324 unsigned long long level
;
327 status
= acpi_evaluate_integer(priv
->adev
->handle
, "PPPC", NULL
, &level
);
328 if (ACPI_SUCCESS(status
)) {
336 int3403_set_cur_state(struct thermal_cooling_device
*cdev
, unsigned long state
)
338 struct int3403_priv
*priv
= cdev
->devdata
;
341 status
= acpi_execute_simple_method(priv
->adev
->handle
, "SPPC", state
);
342 if (ACPI_SUCCESS(status
))
348 static const struct thermal_cooling_device_ops int3403_cooling_ops
= {
349 .get_max_state
= int3403_get_max_state
,
350 .get_cur_state
= int3403_get_cur_state
,
351 .set_cur_state
= int3403_set_cur_state
,
354 static int int3403_cdev_add(struct int3403_priv
*priv
)
358 struct int3403_cdev
*obj
;
359 struct acpi_buffer buf
= { ACPI_ALLOCATE_BUFFER
, NULL
};
360 union acpi_object
*p
;
362 obj
= devm_kzalloc(&priv
->pdev
->dev
, sizeof(*obj
), GFP_KERNEL
);
366 status
= acpi_evaluate_object(priv
->adev
->handle
, "PPSS", NULL
, &buf
);
367 if (ACPI_FAILURE(status
))
371 if (!p
|| (p
->type
!= ACPI_TYPE_PACKAGE
)) {
372 printk(KERN_WARNING
"Invalid PPSS data\n");
376 obj
->max_state
= p
->package
.count
- 1;
378 thermal_cooling_device_register(acpi_device_bid(priv
->adev
),
379 priv
, &int3403_cooling_ops
);
380 if (IS_ERR(obj
->cdev
))
381 result
= PTR_ERR(obj
->cdev
);
385 /* TODO: add ACPI notification support */
390 static int int3403_cdev_remove(struct int3403_priv
*priv
)
392 struct int3403_cdev
*obj
= priv
->priv
;
394 thermal_cooling_device_unregister(obj
->cdev
);
398 static int int3403_add(struct platform_device
*pdev
)
400 struct int3403_priv
*priv
;
404 priv
= devm_kzalloc(&pdev
->dev
, sizeof(struct int3403_priv
),
410 priv
->adev
= ACPI_COMPANION(&(pdev
->dev
));
416 status
= acpi_evaluate_integer(priv
->adev
->handle
, "PTYP",
418 if (ACPI_FAILURE(status
)) {
423 platform_set_drvdata(pdev
, priv
);
424 switch (priv
->type
) {
425 case INT3403_TYPE_SENSOR
:
426 result
= int3403_sensor_add(priv
);
428 case INT3403_TYPE_CHARGER
:
429 case INT3403_TYPE_BATTERY
:
430 result
= int3403_cdev_add(priv
);
444 static int int3403_remove(struct platform_device
*pdev
)
446 struct int3403_priv
*priv
= platform_get_drvdata(pdev
);
448 switch (priv
->type
) {
449 case INT3403_TYPE_SENSOR
:
450 int3403_sensor_remove(priv
);
452 case INT3403_TYPE_CHARGER
:
453 case INT3403_TYPE_BATTERY
:
454 int3403_cdev_remove(priv
);
463 static const struct acpi_device_id int3403_device_ids
[] = {
467 MODULE_DEVICE_TABLE(acpi
, int3403_device_ids
);
469 static struct platform_driver int3403_driver
= {
470 .probe
= int3403_add
,
471 .remove
= int3403_remove
,
473 .name
= "int3403 thermal",
474 .owner
= THIS_MODULE
,
475 .acpi_match_table
= int3403_device_ids
,
479 module_platform_driver(int3403_driver
);
481 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
482 MODULE_LICENSE("GPL v2");
483 MODULE_DESCRIPTION("ACPI INT3403 thermal driver");