mirror of https://gitee.com/openkylin/linux.git
Merge branch 'int340x-thermal' of .git into next
This commit is contained in:
commit
6ceaf58abe
|
@ -144,7 +144,7 @@ config ACPI_VIDEO
|
|||
|
||||
config ACPI_FAN
|
||||
tristate "Fan"
|
||||
select THERMAL
|
||||
depends on THERMAL
|
||||
default y
|
||||
help
|
||||
This driver supports ACPI fan devices, allowing user-mode
|
||||
|
|
|
@ -43,6 +43,7 @@ acpi-y += pci_root.o pci_link.o pci_irq.o
|
|||
acpi-y += acpi_lpss.o
|
||||
acpi-y += acpi_platform.o
|
||||
acpi-y += acpi_pnp.o
|
||||
acpi-y += int340x_thermal.o
|
||||
acpi-y += power.o
|
||||
acpi-y += event.o
|
||||
acpi-y += sysfs.o
|
||||
|
|
|
@ -113,3 +113,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
|
|||
kfree(resources);
|
||||
return pdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_create_platform_device);
|
||||
|
|
|
@ -343,6 +343,7 @@ int acpi_device_update_power(struct acpi_device *device, int *state_p)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_device_update_power);
|
||||
|
||||
int acpi_bus_update_power(acpi_handle handle, int *state_p)
|
||||
{
|
||||
|
|
|
@ -30,24 +30,19 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define PREFIX "ACPI: "
|
||||
|
||||
#define ACPI_FAN_CLASS "fan"
|
||||
#define ACPI_FAN_FILE_STATE "state"
|
||||
|
||||
#define _COMPONENT ACPI_FAN_COMPONENT
|
||||
ACPI_MODULE_NAME("fan");
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
MODULE_AUTHOR("Paul Diefenbaugh");
|
||||
MODULE_DESCRIPTION("ACPI Fan Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int acpi_fan_add(struct acpi_device *device);
|
||||
static int acpi_fan_remove(struct acpi_device *device);
|
||||
static int acpi_fan_probe(struct platform_device *pdev);
|
||||
static int acpi_fan_remove(struct platform_device *pdev);
|
||||
|
||||
static const struct acpi_device_id fan_device_ids[] = {
|
||||
{"PNP0C0B", 0},
|
||||
{"INT3404", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, fan_device_ids);
|
||||
|
@ -66,37 +61,100 @@ static struct dev_pm_ops acpi_fan_pm = {
|
|||
#define FAN_PM_OPS_PTR NULL
|
||||
#endif
|
||||
|
||||
static struct acpi_driver acpi_fan_driver = {
|
||||
.name = "fan",
|
||||
.class = ACPI_FAN_CLASS,
|
||||
.ids = fan_device_ids,
|
||||
.ops = {
|
||||
.add = acpi_fan_add,
|
||||
.remove = acpi_fan_remove,
|
||||
},
|
||||
.drv.pm = FAN_PM_OPS_PTR,
|
||||
struct acpi_fan_fps {
|
||||
u64 control;
|
||||
u64 trip_point;
|
||||
u64 speed;
|
||||
u64 noise_level;
|
||||
u64 power;
|
||||
};
|
||||
|
||||
struct acpi_fan_fif {
|
||||
u64 revision;
|
||||
u64 fine_grain_ctrl;
|
||||
u64 step_size;
|
||||
u64 low_speed_notification;
|
||||
};
|
||||
|
||||
struct acpi_fan {
|
||||
bool acpi4;
|
||||
struct acpi_fan_fif fif;
|
||||
struct acpi_fan_fps *fps;
|
||||
int fps_count;
|
||||
struct thermal_cooling_device *cdev;
|
||||
};
|
||||
|
||||
static struct platform_driver acpi_fan_driver = {
|
||||
.probe = acpi_fan_probe,
|
||||
.remove = acpi_fan_remove,
|
||||
.driver = {
|
||||
.name = "acpi-fan",
|
||||
.acpi_match_table = fan_device_ids,
|
||||
.pm = FAN_PM_OPS_PTR,
|
||||
},
|
||||
};
|
||||
|
||||
/* thermal cooling device callbacks */
|
||||
static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
|
||||
*state)
|
||||
{
|
||||
/* ACPI fan device only support two states: ON/OFF */
|
||||
*state = 1;
|
||||
struct acpi_device *device = cdev->devdata;
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
|
||||
if (fan->acpi4)
|
||||
*state = fan->fps_count - 1;
|
||||
else
|
||||
*state = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
|
||||
*state)
|
||||
static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int control, i;
|
||||
|
||||
status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(&device->dev, "Get fan state failed\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
obj = buffer.pointer;
|
||||
if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
|
||||
obj->package.count != 3 ||
|
||||
obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
|
||||
dev_err(&device->dev, "Invalid _FST data\n");
|
||||
status = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
control = obj->package.elements[1].integer.value;
|
||||
for (i = 0; i < fan->fps_count; i++) {
|
||||
if (control == fan->fps[i].control)
|
||||
break;
|
||||
}
|
||||
if (i == fan->fps_count) {
|
||||
dev_dbg(&device->dev, "Invalid control value returned\n");
|
||||
status = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
*state = i;
|
||||
|
||||
err:
|
||||
kfree(obj);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int fan_get_state(struct acpi_device *device, unsigned long *state)
|
||||
{
|
||||
struct acpi_device *device = cdev->devdata;
|
||||
int result;
|
||||
int acpi_state = ACPI_STATE_D0;
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
result = acpi_bus_update_power(device->handle, &acpi_state);
|
||||
result = acpi_device_update_power(device, &acpi_state);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
|
@ -105,20 +163,56 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
|
||||
*state)
|
||||
{
|
||||
struct acpi_device *device = cdev->devdata;
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
|
||||
if (fan->acpi4)
|
||||
return fan_get_state_acpi4(device, state);
|
||||
else
|
||||
return fan_get_state(device, state);
|
||||
}
|
||||
|
||||
static int fan_set_state(struct acpi_device *device, unsigned long state)
|
||||
{
|
||||
if (state != 0 && state != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return acpi_device_set_power(device,
|
||||
state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
|
||||
}
|
||||
|
||||
static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
|
||||
{
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
acpi_status status;
|
||||
|
||||
if (state >= fan->fps_count)
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_execute_simple_method(device->handle, "_FSL",
|
||||
fan->fps[state].control);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_dbg(&device->dev, "Failed to set state by _FSL\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
|
||||
{
|
||||
struct acpi_device *device = cdev->devdata;
|
||||
int result;
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
|
||||
if (!device || (state != 0 && state != 1))
|
||||
return -EINVAL;
|
||||
|
||||
result = acpi_bus_set_power(device->handle,
|
||||
state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
|
||||
|
||||
return result;
|
||||
}
|
||||
if (fan->acpi4)
|
||||
return fan_set_state_acpi4(device, state);
|
||||
else
|
||||
return fan_set_state(device, state);
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops fan_cooling_ops = {
|
||||
.get_max_state = fan_get_max_state,
|
||||
|
@ -130,21 +224,125 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = {
|
|||
Driver Interface
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
static int acpi_fan_add(struct acpi_device *device)
|
||||
static bool acpi_fan_is_acpi4(struct acpi_device *device)
|
||||
{
|
||||
return acpi_has_method(device->handle, "_FIF") &&
|
||||
acpi_has_method(device->handle, "_FPS") &&
|
||||
acpi_has_method(device->handle, "_FSL") &&
|
||||
acpi_has_method(device->handle, "_FST");
|
||||
}
|
||||
|
||||
static int acpi_fan_get_fif(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
|
||||
struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = buffer.pointer;
|
||||
if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
|
||||
dev_err(&device->dev, "Invalid _FIF data\n");
|
||||
status = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
status = acpi_extract_package(obj, &format, &fif);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(&device->dev, "Invalid _FIF element\n");
|
||||
status = -EINVAL;
|
||||
}
|
||||
|
||||
err:
|
||||
kfree(obj);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acpi_fan_speed_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct acpi_fan_fps *fps1 = a;
|
||||
const struct acpi_fan_fps *fps2 = b;
|
||||
return fps1->speed - fps2->speed;
|
||||
}
|
||||
|
||||
static int acpi_fan_get_fps(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_fan *fan = acpi_driver_data(device);
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int i;
|
||||
|
||||
status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = buffer.pointer;
|
||||
if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
|
||||
dev_err(&device->dev, "Invalid _FPS data\n");
|
||||
status = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
fan->fps_count = obj->package.count - 1; /* minus revision field */
|
||||
fan->fps = devm_kzalloc(&device->dev,
|
||||
fan->fps_count * sizeof(struct acpi_fan_fps),
|
||||
GFP_KERNEL);
|
||||
if (!fan->fps) {
|
||||
dev_err(&device->dev, "Not enough memory\n");
|
||||
status = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
for (i = 0; i < fan->fps_count; i++) {
|
||||
struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
|
||||
struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] };
|
||||
status = acpi_extract_package(&obj->package.elements[i + 1],
|
||||
&format, &fps);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
dev_err(&device->dev, "Invalid _FPS element\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the state array according to fan speed in increase order */
|
||||
sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
|
||||
acpi_fan_speed_cmp, NULL);
|
||||
|
||||
err:
|
||||
kfree(obj);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int acpi_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
int result = 0;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct acpi_fan *fan;
|
||||
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
|
||||
if (!fan) {
|
||||
dev_err(&device->dev, "No memory for fan\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
device->driver_data = fan;
|
||||
platform_set_drvdata(pdev, fan);
|
||||
|
||||
strcpy(acpi_device_name(device), "Fan");
|
||||
strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
|
||||
|
||||
result = acpi_bus_update_power(device->handle, NULL);
|
||||
if (result) {
|
||||
printk(KERN_ERR PREFIX "Setting initial power state\n");
|
||||
goto end;
|
||||
if (acpi_fan_is_acpi4(device)) {
|
||||
if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device))
|
||||
goto end;
|
||||
fan->acpi4 = true;
|
||||
} else {
|
||||
result = acpi_device_update_power(device, NULL);
|
||||
if (result) {
|
||||
dev_err(&device->dev, "Setting initial power state\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
cdev = thermal_cooling_device_register("Fan", device,
|
||||
|
@ -154,45 +352,34 @@ static int acpi_fan_add(struct acpi_device *device)
|
|||
goto end;
|
||||
}
|
||||
|
||||
dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
|
||||
dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
|
||||
|
||||
device->driver_data = cdev;
|
||||
result = sysfs_create_link(&device->dev.kobj,
|
||||
fan->cdev = cdev;
|
||||
result = sysfs_create_link(&pdev->dev.kobj,
|
||||
&cdev->device.kobj,
|
||||
"thermal_cooling");
|
||||
if (result)
|
||||
dev_err(&device->dev, "Failed to create sysfs link "
|
||||
dev_err(&pdev->dev, "Failed to create sysfs link "
|
||||
"'thermal_cooling'\n");
|
||||
|
||||
result = sysfs_create_link(&cdev->device.kobj,
|
||||
&device->dev.kobj,
|
||||
&pdev->dev.kobj,
|
||||
"device");
|
||||
if (result)
|
||||
dev_err(&device->dev, "Failed to create sysfs link "
|
||||
dev_err(&pdev->dev, "Failed to create sysfs link "
|
||||
"'device'\n");
|
||||
|
||||
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
|
||||
acpi_device_name(device), acpi_device_bid(device),
|
||||
!device->power.state ? "on" : "off");
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int acpi_fan_remove(struct acpi_device *device)
|
||||
static int acpi_fan_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct acpi_fan *fan = platform_get_drvdata(pdev);
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
cdev = acpi_driver_data(device);
|
||||
if (!cdev)
|
||||
return -EINVAL;
|
||||
|
||||
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
|
||||
sysfs_remove_link(&cdev->device.kobj, "device");
|
||||
thermal_cooling_device_unregister(cdev);
|
||||
sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
|
||||
sysfs_remove_link(&fan->cdev->device.kobj, "device");
|
||||
thermal_cooling_device_unregister(fan->cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -200,10 +387,11 @@ static int acpi_fan_remove(struct acpi_device *device)
|
|||
#ifdef CONFIG_PM_SLEEP
|
||||
static int acpi_fan_suspend(struct device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
struct acpi_fan *fan = dev_get_drvdata(dev);
|
||||
if (fan->acpi4)
|
||||
return 0;
|
||||
|
||||
acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0);
|
||||
acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
@ -211,16 +399,17 @@ static int acpi_fan_suspend(struct device *dev)
|
|||
static int acpi_fan_resume(struct device *dev)
|
||||
{
|
||||
int result;
|
||||
struct acpi_fan *fan = dev_get_drvdata(dev);
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
if (fan->acpi4)
|
||||
return 0;
|
||||
|
||||
result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
|
||||
result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
|
||||
if (result)
|
||||
printk(KERN_ERR PREFIX "Error updating fan power state\n");
|
||||
dev_err(dev, "Error updating fan power state\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
module_acpi_driver(acpi_fan_driver);
|
||||
module_platform_driver(acpi_fan_driver);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* ACPI support for int340x thermal drivers
|
||||
*
|
||||
* Copyright (C) 2014, Intel Corporation
|
||||
* Authors: Zhang Rui <rui.zhang@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define DO_ENUMERATION 0x01
|
||||
static const struct acpi_device_id int340x_thermal_device_ids[] = {
|
||||
{"INT3400", DO_ENUMERATION },
|
||||
{"INT3401"},
|
||||
{"INT3402"},
|
||||
{"INT3403"},
|
||||
{"INT3404"},
|
||||
{"INT3406"},
|
||||
{"INT3407"},
|
||||
{"INT3408"},
|
||||
{"INT3409"},
|
||||
{"INT340A"},
|
||||
{"INT340B"},
|
||||
{""},
|
||||
};
|
||||
|
||||
static int int340x_thermal_handler_attach(struct acpi_device *adev,
|
||||
const struct acpi_device_id *id)
|
||||
{
|
||||
#if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE)
|
||||
if (id->driver_data == DO_ENUMERATION)
|
||||
acpi_create_platform_device(adev);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct acpi_scan_handler int340x_thermal_handler = {
|
||||
.ids = int340x_thermal_device_ids,
|
||||
.attach = int340x_thermal_handler_attach,
|
||||
};
|
||||
|
||||
void __init acpi_int340x_thermal_init(void)
|
||||
{
|
||||
acpi_scan_add_handler(&int340x_thermal_handler);
|
||||
}
|
|
@ -31,6 +31,7 @@ void acpi_pci_link_init(void);
|
|||
void acpi_processor_init(void);
|
||||
void acpi_platform_init(void);
|
||||
void acpi_pnp_init(void);
|
||||
void acpi_int340x_thermal_init(void);
|
||||
int acpi_sysfs_init(void);
|
||||
void acpi_container_init(void);
|
||||
void acpi_memory_hotplug_init(void);
|
||||
|
@ -103,8 +104,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
|
|||
int acpi_power_on_resources(struct acpi_device *device, int state);
|
||||
int acpi_power_transition(struct acpi_device *device, int state);
|
||||
|
||||
int acpi_device_update_power(struct acpi_device *device, int *state_p);
|
||||
|
||||
int acpi_wakeup_device_init(void);
|
||||
|
||||
#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
|
||||
|
@ -167,13 +166,6 @@ static inline int suspend_nvs_save(void) { return 0; }
|
|||
static inline void suspend_nvs_restore(void) {}
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Platform bus support
|
||||
-------------------------------------------------------------------------- */
|
||||
struct platform_device;
|
||||
|
||||
struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Video
|
||||
-------------------------------------------------------------------------- */
|
||||
|
|
|
@ -2306,6 +2306,7 @@ int __init acpi_scan_init(void)
|
|||
acpi_container_init();
|
||||
acpi_memory_hotplug_init();
|
||||
acpi_pnp_init();
|
||||
acpi_int340x_thermal_init();
|
||||
|
||||
mutex_lock(&acpi_scan_lock);
|
||||
/*
|
||||
|
|
|
@ -528,7 +528,6 @@ static void acpi_thermal_check(void *data)
|
|||
}
|
||||
|
||||
/* sys I/F for generic thermal sysfs support */
|
||||
#define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100)
|
||||
|
||||
static int thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
unsigned long *temp)
|
||||
|
@ -543,7 +542,8 @@ static int thermal_get_temp(struct thermal_zone_device *thermal,
|
|||
if (result)
|
||||
return result;
|
||||
|
||||
*temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset);
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature,
|
||||
tz->kelvin_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -647,7 +647,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
|||
|
||||
if (tz->trips.critical.flags.valid) {
|
||||
if (!trip) {
|
||||
*temp = KELVIN_TO_MILLICELSIUS(
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
|
||||
tz->trips.critical.temperature,
|
||||
tz->kelvin_offset);
|
||||
return 0;
|
||||
|
@ -657,7 +657,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
|||
|
||||
if (tz->trips.hot.flags.valid) {
|
||||
if (!trip) {
|
||||
*temp = KELVIN_TO_MILLICELSIUS(
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
|
||||
tz->trips.hot.temperature,
|
||||
tz->kelvin_offset);
|
||||
return 0;
|
||||
|
@ -667,7 +667,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
|||
|
||||
if (tz->trips.passive.flags.valid) {
|
||||
if (!trip) {
|
||||
*temp = KELVIN_TO_MILLICELSIUS(
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
|
||||
tz->trips.passive.temperature,
|
||||
tz->kelvin_offset);
|
||||
return 0;
|
||||
|
@ -678,7 +678,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
|||
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
|
||||
tz->trips.active[i].flags.valid; i++) {
|
||||
if (!trip) {
|
||||
*temp = KELVIN_TO_MILLICELSIUS(
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
|
||||
tz->trips.active[i].temperature,
|
||||
tz->kelvin_offset);
|
||||
return 0;
|
||||
|
@ -694,7 +694,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
|
|||
struct acpi_thermal *tz = thermal->devdata;
|
||||
|
||||
if (tz->trips.critical.flags.valid) {
|
||||
*temperature = KELVIN_TO_MILLICELSIUS(
|
||||
*temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
|
||||
tz->trips.critical.temperature,
|
||||
tz->kelvin_offset);
|
||||
return 0;
|
||||
|
@ -714,8 +714,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal,
|
|||
|
||||
if (type == THERMAL_TRIP_ACTIVE) {
|
||||
unsigned long trip_temp;
|
||||
unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature,
|
||||
tz->kelvin_offset);
|
||||
unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
|
||||
tz->temperature, tz->kelvin_offset);
|
||||
if (thermal_get_trip_temp(thermal, trip, &trip_temp))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package,
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case ACPI_TYPE_LOCAL_REFERENCE:
|
||||
switch (format_string[i]) {
|
||||
case 'R':
|
||||
size_required += sizeof(void *);
|
||||
tail_offset += sizeof(void *);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING PREFIX "Invalid package element"
|
||||
" [%d] got reference,"
|
||||
" expecting [%c]\n",
|
||||
i, format_string[i]);
|
||||
return AE_BAD_DATA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACPI_TYPE_PACKAGE:
|
||||
default:
|
||||
|
@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package,
|
|||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ACPI_TYPE_LOCAL_REFERENCE:
|
||||
switch (format_string[i]) {
|
||||
case 'R':
|
||||
*(void **)head =
|
||||
(void *)element->reference.handle;
|
||||
head += sizeof(void *);
|
||||
break;
|
||||
default:
|
||||
/* Should never get here */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ACPI_TYPE_PACKAGE:
|
||||
/* TBD: handle nested packages... */
|
||||
default:
|
||||
|
|
|
@ -217,21 +217,6 @@ config X86_PKG_TEMP_THERMAL
|
|||
two trip points which can be set by user to get notifications via thermal
|
||||
notification methods.
|
||||
|
||||
config ACPI_INT3403_THERMAL
|
||||
tristate "ACPI INT3403 thermal driver"
|
||||
depends on X86 && ACPI
|
||||
help
|
||||
Newer laptops and tablets that use ACPI may have thermal sensors
|
||||
outside the core CPU/SOC for thermal safety reasons. These
|
||||
temperature sensors are also exposed for the OS to use via the so
|
||||
called INT3403 ACPI object. This driver will, on devices that have
|
||||
such sensors, expose the temperature information from these sensors
|
||||
to userspace via the normal thermal framework. This means that a wide
|
||||
range of applications and GUI widgets can show this information to
|
||||
the user or use this information for making decisions. For example,
|
||||
the Intel Thermal Daemon can use this information to allow the user
|
||||
to select his laptop to run without turning on the fans.
|
||||
|
||||
config INTEL_SOC_DTS_THERMAL
|
||||
tristate "Intel SoCs DTS thermal driver"
|
||||
depends on X86 && IOSF_MBI
|
||||
|
@ -244,6 +229,30 @@ config INTEL_SOC_DTS_THERMAL
|
|||
notification methods.The other trip is a critical trip point, which
|
||||
was set by the driver based on the TJ MAX temperature.
|
||||
|
||||
config INT340X_THERMAL
|
||||
tristate "ACPI INT340X thermal drivers"
|
||||
depends on X86 && ACPI
|
||||
select THERMAL_GOV_USER_SPACE
|
||||
select ACPI_THERMAL_REL
|
||||
select ACPI_FAN
|
||||
help
|
||||
Newer laptops and tablets that use ACPI may have thermal sensors and
|
||||
other devices with thermal control capabilities outside the core
|
||||
CPU/SOC, for thermal safety reasons.
|
||||
They are exposed for the OS to use via the INT3400 ACPI device object
|
||||
as the master, and INT3401~INT340B ACPI device objects as the slaves.
|
||||
Enable this to expose the temperature information and cooling ability
|
||||
from these objects to userspace via the normal thermal framework.
|
||||
This means that a wide range of applications and GUI widgets can show
|
||||
the information to the user or use this information for making
|
||||
decisions. For example, the Intel Thermal Daemon can use this
|
||||
information to allow the user to select his laptop to run without
|
||||
turning on the fans.
|
||||
|
||||
config ACPI_THERMAL_REL
|
||||
tristate
|
||||
depends on ACPI
|
||||
|
||||
menu "Texas Instruments thermal drivers"
|
||||
source "drivers/thermal/ti-soc-thermal/Kconfig"
|
||||
endmenu
|
||||
|
|
|
@ -32,5 +32,5 @@ obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
|
|||
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
|
||||
obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
|
||||
obj-$(CONFIG_ST_THERMAL) += st/
|
||||
|
|
|
@ -1,296 +0,0 @@
|
|||
/*
|
||||
* ACPI INT3403 thermal driver
|
||||
* Copyright (c) 2013, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define INT3403_TYPE_SENSOR 0x03
|
||||
#define INT3403_PERF_CHANGED_EVENT 0x80
|
||||
#define INT3403_THERMAL_EVENT 0x90
|
||||
|
||||
#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
|
||||
#define KELVIN_OFFSET 2732
|
||||
#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
|
||||
|
||||
#define ACPI_INT3403_CLASS "int3403"
|
||||
#define ACPI_INT3403_FILE_STATE "state"
|
||||
|
||||
struct int3403_sensor {
|
||||
struct thermal_zone_device *tzone;
|
||||
unsigned long *thresholds;
|
||||
unsigned long crit_temp;
|
||||
int crit_trip_id;
|
||||
unsigned long psv_temp;
|
||||
int psv_trip_id;
|
||||
};
|
||||
|
||||
static int sys_get_curr_temp(struct thermal_zone_device *tzone,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
unsigned long long hyst;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Thermal hysteresis represents a temperature difference.
|
||||
* Kelvin and Celsius have same degree size. So the
|
||||
* conversion here between tenths of degree Kelvin unit
|
||||
* and Milli-Celsius unit is just to multiply 100.
|
||||
*/
|
||||
*temp = hyst * 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
if (trip == obj->crit_trip_id)
|
||||
*temp = obj->crit_temp;
|
||||
else if (trip == obj->psv_trip_id)
|
||||
*temp = obj->psv_temp;
|
||||
else {
|
||||
/*
|
||||
* get_trip_temp is a mandatory callback but
|
||||
* PATx method doesn't return any value, so return
|
||||
* cached value, which was last set from user space.
|
||||
*/
|
||||
*temp = obj->thresholds[trip];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
struct acpi_device *device = thermal->devdata;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
/* Mandatory callback, may not mean much here */
|
||||
if (trip == obj->crit_trip_id)
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
else
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct acpi_device *device = tzone->devdata;
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
int ret = 0;
|
||||
struct int3403_sensor *obj = acpi_driver_data(device);
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
if (acpi_has_method(device->handle, name)) {
|
||||
status = acpi_execute_simple_method(device->handle, name,
|
||||
MILLI_CELSIUS_TO_DECI_KELVIN(temp,
|
||||
KELVIN_OFFSET));
|
||||
if (ACPI_FAILURE(status))
|
||||
ret = -EIO;
|
||||
else
|
||||
obj->thresholds[trip] = temp;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
.get_trip_hyst = sys_get_trip_hyst,
|
||||
};
|
||||
|
||||
static void acpi_thermal_notify(struct acpi_device *device, u32 event)
|
||||
{
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
obj = acpi_driver_data(device);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case INT3403_PERF_CHANGED_EVENT:
|
||||
break;
|
||||
case INT3403_THERMAL_EVENT:
|
||||
thermal_zone_device_update(obj->tzone);
|
||||
break;
|
||||
default:
|
||||
dev_err(&device->dev, "Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
|
||||
{
|
||||
unsigned long long crt;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
|
||||
{
|
||||
unsigned long long psv;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_int3403_add(struct acpi_device *device)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned long long ptyp;
|
||||
acpi_status status;
|
||||
struct int3403_sensor *obj;
|
||||
unsigned long long trip_cnt;
|
||||
int trip_mask = 0;
|
||||
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EINVAL;
|
||||
|
||||
if (ptyp != INT3403_TYPE_SENSOR)
|
||||
return -EINVAL;
|
||||
|
||||
obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
device->driver_data = obj;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "PATC", NULL,
|
||||
&trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
|
||||
if (trip_cnt) {
|
||||
/* We have to cache, thresholds can't be readback */
|
||||
obj->thresholds = devm_kzalloc(&device->dev,
|
||||
sizeof(*obj->thresholds) * trip_cnt,
|
||||
GFP_KERNEL);
|
||||
if (!obj->thresholds)
|
||||
return -ENOMEM;
|
||||
trip_mask = BIT(trip_cnt) - 1;
|
||||
}
|
||||
|
||||
obj->psv_trip_id = -1;
|
||||
if (!sys_get_trip_psv(device, &obj->psv_temp))
|
||||
obj->psv_trip_id = trip_cnt++;
|
||||
|
||||
obj->crit_trip_id = -1;
|
||||
if (!sys_get_trip_crt(device, &obj->crit_temp))
|
||||
obj->crit_trip_id = trip_cnt++;
|
||||
|
||||
obj->tzone = thermal_zone_device_register(acpi_device_bid(device),
|
||||
trip_cnt, trip_mask, device, &tzone_ops,
|
||||
NULL, 0, 0);
|
||||
if (IS_ERR(obj->tzone)) {
|
||||
result = PTR_ERR(obj->tzone);
|
||||
return result;
|
||||
}
|
||||
|
||||
strcpy(acpi_device_name(device), "INT3403");
|
||||
strcpy(acpi_device_class(device), ACPI_INT3403_CLASS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_int3403_remove(struct acpi_device *device)
|
||||
{
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
obj = acpi_driver_data(device);
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ACPI_MODULE_NAME("int3403");
|
||||
static const struct acpi_device_id int3403_device_ids[] = {
|
||||
{"INT3403", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
|
||||
|
||||
static struct acpi_driver acpi_int3403_driver = {
|
||||
.name = "INT3403",
|
||||
.class = ACPI_INT3403_CLASS,
|
||||
.ids = int3403_device_ids,
|
||||
.ops = {
|
||||
.add = acpi_int3403_add,
|
||||
.remove = acpi_int3403_remove,
|
||||
.notify = acpi_thermal_notify,
|
||||
},
|
||||
};
|
||||
|
||||
module_acpi_driver(acpi_int3403_driver);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
|
|
@ -0,0 +1,4 @@
|
|||
obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
|
||||
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
|
||||
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
|
|
@ -0,0 +1,400 @@
|
|||
/* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
|
||||
*
|
||||
* Copyright (c) 2014 Intel Corp
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Two functionalities included:
|
||||
* 1. Export _TRT, _ART, via misc device interface to the userspace.
|
||||
* 2. Provide parsing result to kernel drivers
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include "acpi_thermal_rel.h"
|
||||
|
||||
static acpi_handle acpi_thermal_rel_handle;
|
||||
static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
|
||||
static int acpi_thermal_rel_chrdev_count; /* #times opened */
|
||||
static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */
|
||||
|
||||
static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
spin_lock(&acpi_thermal_rel_chrdev_lock);
|
||||
if (acpi_thermal_rel_chrdev_exclu ||
|
||||
(acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
|
||||
spin_unlock(&acpi_thermal_rel_chrdev_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (file->f_flags & O_EXCL)
|
||||
acpi_thermal_rel_chrdev_exclu = 1;
|
||||
acpi_thermal_rel_chrdev_count++;
|
||||
|
||||
spin_unlock(&acpi_thermal_rel_chrdev_lock);
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
spin_lock(&acpi_thermal_rel_chrdev_lock);
|
||||
acpi_thermal_rel_chrdev_count--;
|
||||
acpi_thermal_rel_chrdev_exclu = 0;
|
||||
spin_unlock(&acpi_thermal_rel_chrdev_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
|
||||
*
|
||||
* @handle: ACPI handle of the device contains _TRT
|
||||
* @art_count: the number of valid entries resulted from parsing _TRT
|
||||
* @artp: pointer to pointer of array of art entries in parsing result
|
||||
* @create_dev: whether to create platform devices for target and source
|
||||
*
|
||||
*/
|
||||
int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
|
||||
bool create_dev)
|
||||
{
|
||||
acpi_status status;
|
||||
int result = 0;
|
||||
int i;
|
||||
int nr_bad_entries = 0;
|
||||
struct trt *trts;
|
||||
struct acpi_device *adev;
|
||||
union acpi_object *p;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_buffer element = { 0, NULL };
|
||||
struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
|
||||
|
||||
if (!acpi_has_method(handle, "_TRT"))
|
||||
return 0;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
p = buffer.pointer;
|
||||
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
|
||||
pr_err("Invalid _TRT data\n");
|
||||
result = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
*trt_count = p->package.count;
|
||||
trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL);
|
||||
if (!trts) {
|
||||
result = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i = 0; i < *trt_count; i++) {
|
||||
struct trt *trt = &trts[i - nr_bad_entries];
|
||||
|
||||
element.length = sizeof(struct trt);
|
||||
element.pointer = trt;
|
||||
|
||||
status = acpi_extract_package(&(p->package.elements[i]),
|
||||
&trt_format, &element);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
nr_bad_entries++;
|
||||
pr_warn("_TRT package %d is invalid, ignored\n", i);
|
||||
continue;
|
||||
}
|
||||
if (!create_dev)
|
||||
continue;
|
||||
|
||||
result = acpi_bus_get_device(trt->source, &adev);
|
||||
if (!result)
|
||||
acpi_create_platform_device(adev);
|
||||
else
|
||||
pr_warn("Failed to get source ACPI device\n");
|
||||
|
||||
result = acpi_bus_get_device(trt->target, &adev);
|
||||
if (!result)
|
||||
acpi_create_platform_device(adev);
|
||||
else
|
||||
pr_warn("Failed to get target ACPI device\n");
|
||||
}
|
||||
|
||||
*trtp = trts;
|
||||
/* don't count bad entries */
|
||||
*trt_count -= nr_bad_entries;
|
||||
end:
|
||||
kfree(buffer.pointer);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_parse_trt);
|
||||
|
||||
/**
|
||||
* acpi_parse_art - Parse Active Relationship Table _ART
|
||||
*
|
||||
* @handle: ACPI handle of the device contains _ART
|
||||
* @art_count: the number of valid entries resulted from parsing _ART
|
||||
* @artp: pointer to pointer of array of art entries in parsing result
|
||||
* @create_dev: whether to create platform devices for target and source
|
||||
*
|
||||
*/
|
||||
int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
|
||||
bool create_dev)
|
||||
{
|
||||
acpi_status status;
|
||||
int result = 0;
|
||||
int i;
|
||||
int nr_bad_entries = 0;
|
||||
struct art *arts;
|
||||
struct acpi_device *adev;
|
||||
union acpi_object *p;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_buffer element = { 0, NULL };
|
||||
struct acpi_buffer art_format = {
|
||||
sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
|
||||
|
||||
if (!acpi_has_method(handle, "_ART"))
|
||||
return 0;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
p = buffer.pointer;
|
||||
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
|
||||
pr_err("Invalid _ART data\n");
|
||||
result = -EFAULT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* ignore p->package.elements[0], as this is _ART Revision field */
|
||||
*art_count = p->package.count - 1;
|
||||
arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL);
|
||||
if (!arts) {
|
||||
result = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i = 0; i < *art_count; i++) {
|
||||
struct art *art = &arts[i - nr_bad_entries];
|
||||
|
||||
element.length = sizeof(struct art);
|
||||
element.pointer = art;
|
||||
|
||||
status = acpi_extract_package(&(p->package.elements[i + 1]),
|
||||
&art_format, &element);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_warn("_ART package %d is invalid, ignored", i);
|
||||
nr_bad_entries++;
|
||||
continue;
|
||||
}
|
||||
if (!create_dev)
|
||||
continue;
|
||||
|
||||
if (art->source) {
|
||||
result = acpi_bus_get_device(art->source, &adev);
|
||||
if (!result)
|
||||
acpi_create_platform_device(adev);
|
||||
else
|
||||
pr_warn("Failed to get source ACPI device\n");
|
||||
}
|
||||
if (art->target) {
|
||||
result = acpi_bus_get_device(art->target, &adev);
|
||||
if (!result)
|
||||
acpi_create_platform_device(adev);
|
||||
else
|
||||
pr_warn("Failed to get source ACPI device\n");
|
||||
}
|
||||
}
|
||||
|
||||
*artp = arts;
|
||||
/* don't count bad entries */
|
||||
*art_count -= nr_bad_entries;
|
||||
end:
|
||||
kfree(buffer.pointer);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_parse_art);
|
||||
|
||||
|
||||
/* get device name from acpi handle */
|
||||
static void get_single_name(acpi_handle handle, char *name)
|
||||
{
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
|
||||
|
||||
if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
|
||||
pr_warn("Failed get name from handle\n");
|
||||
else {
|
||||
memcpy(name, buffer.pointer, ACPI_NAME_SIZE);
|
||||
kfree(buffer.pointer);
|
||||
}
|
||||
}
|
||||
|
||||
static int fill_art(char __user *ubuf)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int count;
|
||||
int art_len;
|
||||
struct art *arts = NULL;
|
||||
union art_object *art_user;
|
||||
|
||||
ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
|
||||
if (ret)
|
||||
goto free_art;
|
||||
art_len = count * sizeof(union art_object);
|
||||
art_user = kzalloc(art_len, GFP_KERNEL);
|
||||
if (!art_user) {
|
||||
ret = -ENOMEM;
|
||||
goto free_art;
|
||||
}
|
||||
/* now fill in user art data */
|
||||
for (i = 0; i < count; i++) {
|
||||
/* userspace art needs device name instead of acpi reference */
|
||||
get_single_name(arts[i].source, art_user[i].source_device);
|
||||
get_single_name(arts[i].target, art_user[i].target_device);
|
||||
/* copy the rest int data in addition to source and target */
|
||||
memcpy(&art_user[i].weight, &arts[i].weight,
|
||||
sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
|
||||
}
|
||||
|
||||
if (copy_to_user(ubuf, art_user, art_len))
|
||||
ret = -EFAULT;
|
||||
kfree(art_user);
|
||||
free_art:
|
||||
kfree(arts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fill_trt(char __user *ubuf)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int count;
|
||||
int trt_len;
|
||||
struct trt *trts = NULL;
|
||||
union trt_object *trt_user;
|
||||
|
||||
ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
|
||||
if (ret)
|
||||
goto free_trt;
|
||||
trt_len = count * sizeof(union trt_object);
|
||||
trt_user = kzalloc(trt_len, GFP_KERNEL);
|
||||
if (!trt_user) {
|
||||
ret = -ENOMEM;
|
||||
goto free_trt;
|
||||
}
|
||||
/* now fill in user trt data */
|
||||
for (i = 0; i < count; i++) {
|
||||
/* userspace trt needs device name instead of acpi reference */
|
||||
get_single_name(trts[i].source, trt_user[i].source_device);
|
||||
get_single_name(trts[i].target, trt_user[i].target_device);
|
||||
trt_user[i].sample_period = trts[i].sample_period;
|
||||
trt_user[i].influence = trts[i].influence;
|
||||
}
|
||||
|
||||
if (copy_to_user(ubuf, trt_user, trt_len))
|
||||
ret = -EFAULT;
|
||||
kfree(trt_user);
|
||||
free_trt:
|
||||
kfree(trts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
|
||||
unsigned long __arg)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long length = 0;
|
||||
unsigned long count = 0;
|
||||
char __user *arg = (void __user *)__arg;
|
||||
struct trt *trts;
|
||||
struct art *arts;
|
||||
|
||||
switch (cmd) {
|
||||
case ACPI_THERMAL_GET_TRT_COUNT:
|
||||
ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
|
||||
&trts, false);
|
||||
kfree(trts);
|
||||
if (!ret)
|
||||
return put_user(count, (unsigned long __user *)__arg);
|
||||
return ret;
|
||||
case ACPI_THERMAL_GET_TRT_LEN:
|
||||
ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
|
||||
&trts, false);
|
||||
kfree(trts);
|
||||
length = count * sizeof(union trt_object);
|
||||
if (!ret)
|
||||
return put_user(length, (unsigned long __user *)__arg);
|
||||
return ret;
|
||||
case ACPI_THERMAL_GET_TRT:
|
||||
return fill_trt(arg);
|
||||
case ACPI_THERMAL_GET_ART_COUNT:
|
||||
ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
|
||||
&arts, false);
|
||||
kfree(arts);
|
||||
if (!ret)
|
||||
return put_user(count, (unsigned long __user *)__arg);
|
||||
return ret;
|
||||
case ACPI_THERMAL_GET_ART_LEN:
|
||||
ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
|
||||
&arts, false);
|
||||
kfree(arts);
|
||||
length = count * sizeof(union art_object);
|
||||
if (!ret)
|
||||
return put_user(length, (unsigned long __user *)__arg);
|
||||
return ret;
|
||||
|
||||
case ACPI_THERMAL_GET_ART:
|
||||
return fill_art(arg);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations acpi_thermal_rel_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = acpi_thermal_rel_open,
|
||||
.release = acpi_thermal_rel_release,
|
||||
.unlocked_ioctl = acpi_thermal_rel_ioctl,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice acpi_thermal_rel_misc_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
"acpi_thermal_rel",
|
||||
&acpi_thermal_rel_fops
|
||||
};
|
||||
|
||||
int acpi_thermal_rel_misc_device_add(acpi_handle handle)
|
||||
{
|
||||
acpi_thermal_rel_handle = handle;
|
||||
|
||||
return misc_register(&acpi_thermal_rel_misc_device);
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
|
||||
|
||||
int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
|
||||
{
|
||||
misc_deregister(&acpi_thermal_rel_misc_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
|
||||
|
||||
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
|
||||
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
|
||||
MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,84 @@
|
|||
#ifndef __ACPI_ACPI_THERMAL_H
|
||||
#define __ACPI_ACPI_THERMAL_H
|
||||
|
||||
#include <asm/ioctl.h>
|
||||
|
||||
#define ACPI_THERMAL_MAGIC 's'
|
||||
|
||||
#define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long)
|
||||
#define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long)
|
||||
#define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long)
|
||||
#define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long)
|
||||
|
||||
#define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
|
||||
#define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
|
||||
|
||||
struct art {
|
||||
acpi_handle source;
|
||||
acpi_handle target;
|
||||
u64 weight;
|
||||
u64 ac0_max;
|
||||
u64 ac1_max;
|
||||
u64 ac2_max;
|
||||
u64 ac3_max;
|
||||
u64 ac4_max;
|
||||
u64 ac5_max;
|
||||
u64 ac6_max;
|
||||
u64 ac7_max;
|
||||
u64 ac8_max;
|
||||
u64 ac9_max;
|
||||
} __packed;
|
||||
|
||||
struct trt {
|
||||
acpi_handle source;
|
||||
acpi_handle target;
|
||||
u64 influence;
|
||||
u64 sample_period;
|
||||
u64 reverved1;
|
||||
u64 reverved2;
|
||||
u64 reverved3;
|
||||
u64 reverved4;
|
||||
} __packed;
|
||||
|
||||
#define ACPI_NR_ART_ELEMENTS 13
|
||||
/* for usrspace */
|
||||
union art_object {
|
||||
struct {
|
||||
char source_device[8]; /* ACPI single name */
|
||||
char target_device[8]; /* ACPI single name */
|
||||
u64 weight;
|
||||
u64 ac0_max_level;
|
||||
u64 ac1_max_level;
|
||||
u64 ac2_max_level;
|
||||
u64 ac3_max_level;
|
||||
u64 ac4_max_level;
|
||||
u64 ac5_max_level;
|
||||
u64 ac6_max_level;
|
||||
u64 ac7_max_level;
|
||||
u64 ac8_max_level;
|
||||
u64 ac9_max_level;
|
||||
};
|
||||
u64 __data[ACPI_NR_ART_ELEMENTS];
|
||||
};
|
||||
|
||||
union trt_object {
|
||||
struct {
|
||||
char source_device[8]; /* ACPI single name */
|
||||
char target_device[8]; /* ACPI single name */
|
||||
u64 influence;
|
||||
u64 sample_period;
|
||||
u64 reserved[4];
|
||||
};
|
||||
u64 __data[8];
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int acpi_thermal_rel_misc_device_add(acpi_handle handle);
|
||||
int acpi_thermal_rel_misc_device_remove(acpi_handle handle);
|
||||
int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts,
|
||||
bool create_dev);
|
||||
int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts,
|
||||
bool create_dev);
|
||||
#endif
|
||||
|
||||
#endif /* __ACPI_ACPI_THERMAL_H */
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* INT3400 thermal driver
|
||||
*
|
||||
* Copyright (C) 2014, Intel Corporation
|
||||
* Authors: Zhang Rui <rui.zhang@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
#include "acpi_thermal_rel.h"
|
||||
|
||||
enum int3400_thermal_uuid {
|
||||
INT3400_THERMAL_PASSIVE_1,
|
||||
INT3400_THERMAL_PASSIVE_2,
|
||||
INT3400_THERMAL_ACTIVE,
|
||||
INT3400_THERMAL_CRITICAL,
|
||||
INT3400_THERMAL_COOLING_MODE,
|
||||
INT3400_THERMAL_MAXIMUM_UUID,
|
||||
};
|
||||
|
||||
static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
|
||||
"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
|
||||
"9E04115A-AE87-4D1C-9500-0F3E340BFE75",
|
||||
"3A95C389-E4B8-4629-A526-C52C88626BAE",
|
||||
"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
|
||||
"16CAF1B7-DD38-40ed-B1C1-1B8A1913D531",
|
||||
};
|
||||
|
||||
struct int3400_thermal_priv {
|
||||
struct acpi_device *adev;
|
||||
struct thermal_zone_device *thermal;
|
||||
int mode;
|
||||
int art_count;
|
||||
struct art *arts;
|
||||
int trt_count;
|
||||
struct trt *trts;
|
||||
u8 uuid_bitmap;
|
||||
int rel_misc_dev_res;
|
||||
};
|
||||
|
||||
static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
|
||||
{
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
|
||||
union acpi_object *obja, *objb;
|
||||
int i, j;
|
||||
int result = 0;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
obja = (union acpi_object *)buf.pointer;
|
||||
if (obja->type != ACPI_TYPE_PACKAGE) {
|
||||
result = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i = 0; i < obja->package.count; i++) {
|
||||
objb = &obja->package.elements[i];
|
||||
if (objb->type != ACPI_TYPE_BUFFER) {
|
||||
result = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* UUID must be 16 bytes */
|
||||
if (objb->buffer.length != 16) {
|
||||
result = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
|
||||
u8 uuid[16];
|
||||
|
||||
acpi_str_to_uuid(int3400_thermal_uuids[j], uuid);
|
||||
if (!strncmp(uuid, objb->buffer.pointer, 16)) {
|
||||
priv->uuid_bitmap |= (1 << j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
kfree(buf.pointer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3400_thermal_run_osc(acpi_handle handle,
|
||||
enum int3400_thermal_uuid uuid, bool enable)
|
||||
{
|
||||
u32 ret, buf[2];
|
||||
acpi_status status;
|
||||
int result = 0;
|
||||
struct acpi_osc_context context = {
|
||||
.uuid_str = int3400_thermal_uuids[uuid],
|
||||
.rev = 1,
|
||||
.cap.length = 8,
|
||||
};
|
||||
|
||||
buf[OSC_QUERY_DWORD] = 0;
|
||||
buf[OSC_SUPPORT_DWORD] = enable;
|
||||
|
||||
context.cap.pointer = buf;
|
||||
|
||||
status = acpi_run_osc(handle, &context);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
ret = *((u32 *)(context.ret.pointer + 4));
|
||||
if (ret != enable)
|
||||
result = -EPERM;
|
||||
} else
|
||||
result = -EPERM;
|
||||
|
||||
kfree(context.ret.pointer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
unsigned long *temp)
|
||||
{
|
||||
*temp = 20 * 1000; /* faked temp sensor with 20C */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode *mode)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = thermal->devdata;
|
||||
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
*mode = priv->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
|
||||
enum thermal_device_mode mode)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = thermal->devdata;
|
||||
bool enable;
|
||||
int result = 0;
|
||||
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED)
|
||||
enable = true;
|
||||
else if (mode == THERMAL_DEVICE_DISABLED)
|
||||
enable = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (enable != priv->mode) {
|
||||
priv->mode = enable;
|
||||
/* currently, only PASSIVE COOLING is supported */
|
||||
result = int3400_thermal_run_osc(priv->adev->handle,
|
||||
INT3400_THERMAL_PASSIVE_1, enable);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops int3400_thermal_ops = {
|
||||
.get_temp = int3400_thermal_get_temp,
|
||||
};
|
||||
|
||||
static struct thermal_zone_params int3400_thermal_params = {
|
||||
.governor_name = "user_space",
|
||||
.no_hwmon = true,
|
||||
};
|
||||
|
||||
static int int3400_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct int3400_thermal_priv *priv;
|
||||
int result;
|
||||
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->adev = adev;
|
||||
|
||||
result = int3400_thermal_get_uuids(priv);
|
||||
if (result)
|
||||
goto free_priv;
|
||||
|
||||
result = acpi_parse_art(priv->adev->handle, &priv->art_count,
|
||||
&priv->arts, true);
|
||||
if (result)
|
||||
goto free_priv;
|
||||
|
||||
|
||||
result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
|
||||
&priv->trts, true);
|
||||
if (result)
|
||||
goto free_art;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) {
|
||||
int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
|
||||
int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
|
||||
}
|
||||
priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
|
||||
priv, &int3400_thermal_ops,
|
||||
&int3400_thermal_params, 0, 0);
|
||||
if (IS_ERR(priv->thermal)) {
|
||||
result = PTR_ERR(priv->thermal);
|
||||
goto free_trt;
|
||||
}
|
||||
|
||||
priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
|
||||
priv->adev->handle);
|
||||
|
||||
return 0;
|
||||
free_trt:
|
||||
kfree(priv->trts);
|
||||
free_art:
|
||||
kfree(priv->arts);
|
||||
free_priv:
|
||||
kfree(priv);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3400_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (!priv->rel_misc_dev_res)
|
||||
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
|
||||
|
||||
thermal_zone_device_unregister(priv->thermal);
|
||||
kfree(priv->trts);
|
||||
kfree(priv->arts);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id int3400_thermal_match[] = {
|
||||
{"INT3400", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
|
||||
|
||||
static struct platform_driver int3400_thermal_driver = {
|
||||
.probe = int3400_thermal_probe,
|
||||
.remove = int3400_thermal_remove,
|
||||
.driver = {
|
||||
.name = "int3400 thermal",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = ACPI_PTR(int3400_thermal_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(int3400_thermal_driver);
|
||||
|
||||
MODULE_DESCRIPTION("INT3400 Thermal driver");
|
||||
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* INT3402 thermal driver for memory temperature reporting
|
||||
*
|
||||
* Copyright (C) 2014, Intel Corporation
|
||||
* Authors: Aaron Lu <aaron.lu@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#define ACPI_ACTIVE_COOLING_MAX_NR 10
|
||||
|
||||
struct active_trip {
|
||||
unsigned long temp;
|
||||
int id;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct int3402_thermal_data {
|
||||
unsigned long *aux_trips;
|
||||
int aux_trip_nr;
|
||||
unsigned long psv_temp;
|
||||
int psv_trip_id;
|
||||
unsigned long crt_temp;
|
||||
int crt_trip_id;
|
||||
unsigned long hot_temp;
|
||||
int hot_trip_id;
|
||||
struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR];
|
||||
acpi_handle *handle;
|
||||
};
|
||||
|
||||
static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct int3402_thermal_data *d = zone->devdata;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
/* _TMP returns the temperature in tenths of degrees Kelvin */
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct int3402_thermal_data *d = zone->devdata;
|
||||
int i;
|
||||
|
||||
if (trip < d->aux_trip_nr)
|
||||
*temp = d->aux_trips[trip];
|
||||
else if (trip == d->crt_trip_id)
|
||||
*temp = d->crt_temp;
|
||||
else if (trip == d->psv_trip_id)
|
||||
*temp = d->psv_temp;
|
||||
else if (trip == d->hot_trip_id)
|
||||
*temp = d->hot_temp;
|
||||
else {
|
||||
for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
|
||||
if (d->act_trips[i].valid &&
|
||||
d->act_trips[i].id == trip) {
|
||||
*temp = d->act_trips[i].temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ACPI_ACTIVE_COOLING_MAX_NR)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
struct int3402_thermal_data *d = zone->devdata;
|
||||
int i;
|
||||
|
||||
if (trip < d->aux_trip_nr)
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
else if (trip == d->crt_trip_id)
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
else if (trip == d->hot_trip_id)
|
||||
*type = THERMAL_TRIP_HOT;
|
||||
else if (trip == d->psv_trip_id)
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
else {
|
||||
for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
|
||||
if (d->act_trips[i].valid &&
|
||||
d->act_trips[i].id == trip) {
|
||||
*type = THERMAL_TRIP_ACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ACPI_ACTIVE_COOLING_MAX_NR)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct int3402_thermal_data *d = zone->devdata;
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
status = acpi_execute_simple_method(d->handle, name,
|
||||
MILLICELSIUS_TO_DECI_KELVIN(temp));
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
d->aux_trips[trip] = temp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops int3402_thermal_zone_ops = {
|
||||
.get_temp = int3402_thermal_get_zone_temp,
|
||||
.get_trip_temp = int3402_thermal_get_trip_temp,
|
||||
.get_trip_type = int3402_thermal_get_trip_type,
|
||||
.set_trip_temp = int3402_thermal_set_trip_temp,
|
||||
};
|
||||
|
||||
static struct thermal_zone_params int3402_thermal_params = {
|
||||
.governor_name = "user_space",
|
||||
.no_hwmon = true,
|
||||
};
|
||||
|
||||
static int int3402_thermal_get_temp(acpi_handle handle, char *name,
|
||||
unsigned long *temp)
|
||||
{
|
||||
unsigned long long r;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(handle, name, NULL, &r);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS(r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3402_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct int3402_thermal_data *d;
|
||||
struct thermal_zone_device *zone;
|
||||
acpi_status status;
|
||||
unsigned long long trip_cnt;
|
||||
int trip_mask = 0, i;
|
||||
|
||||
if (!acpi_has_method(adev->handle, "_TMP"))
|
||||
return -ENODEV;
|
||||
|
||||
d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
else {
|
||||
d->aux_trips = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL);
|
||||
if (!d->aux_trips)
|
||||
return -ENOMEM;
|
||||
trip_mask = trip_cnt - 1;
|
||||
d->handle = adev->handle;
|
||||
d->aux_trip_nr = trip_cnt;
|
||||
}
|
||||
|
||||
d->crt_trip_id = -1;
|
||||
if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp))
|
||||
d->crt_trip_id = trip_cnt++;
|
||||
d->hot_trip_id = -1;
|
||||
if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp))
|
||||
d->hot_trip_id = trip_cnt++;
|
||||
d->psv_trip_id = -1;
|
||||
if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp))
|
||||
d->psv_trip_id = trip_cnt++;
|
||||
for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
|
||||
char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
|
||||
if (int3402_thermal_get_temp(adev->handle, name,
|
||||
&d->act_trips[i].temp))
|
||||
break;
|
||||
d->act_trips[i].id = trip_cnt++;
|
||||
d->act_trips[i].valid = true;
|
||||
}
|
||||
|
||||
zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt,
|
||||
trip_mask, d,
|
||||
&int3402_thermal_zone_ops,
|
||||
&int3402_thermal_params,
|
||||
0, 0);
|
||||
if (IS_ERR(zone))
|
||||
return PTR_ERR(zone);
|
||||
platform_set_drvdata(pdev, zone);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3402_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct thermal_zone_device *zone = platform_get_drvdata(pdev);
|
||||
|
||||
thermal_zone_device_unregister(zone);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id int3402_thermal_match[] = {
|
||||
{"INT3402", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, int3402_thermal_match);
|
||||
|
||||
static struct platform_driver int3402_thermal_driver = {
|
||||
.probe = int3402_thermal_probe,
|
||||
.remove = int3402_thermal_remove,
|
||||
.driver = {
|
||||
.name = "int3402 thermal",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = int3402_thermal_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(int3402_thermal_driver);
|
||||
|
||||
MODULE_DESCRIPTION("INT3402 Thermal driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* ACPI INT3403 thermal driver
|
||||
* Copyright (c) 2013, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define INT3403_TYPE_SENSOR 0x03
|
||||
#define INT3403_TYPE_CHARGER 0x0B
|
||||
#define INT3403_TYPE_BATTERY 0x0C
|
||||
#define INT3403_PERF_CHANGED_EVENT 0x80
|
||||
#define INT3403_THERMAL_EVENT 0x90
|
||||
|
||||
#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
|
||||
#define KELVIN_OFFSET 2732
|
||||
#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
|
||||
|
||||
struct int3403_sensor {
|
||||
struct thermal_zone_device *tzone;
|
||||
unsigned long *thresholds;
|
||||
unsigned long crit_temp;
|
||||
int crit_trip_id;
|
||||
unsigned long psv_temp;
|
||||
int psv_trip_id;
|
||||
|
||||
};
|
||||
|
||||
struct int3403_performance_state {
|
||||
u64 performance;
|
||||
u64 power;
|
||||
u64 latency;
|
||||
u64 linear;
|
||||
u64 control;
|
||||
u64 raw_performace;
|
||||
char *raw_unit;
|
||||
int reserved;
|
||||
};
|
||||
|
||||
struct int3403_cdev {
|
||||
struct thermal_cooling_device *cdev;
|
||||
unsigned long max_state;
|
||||
};
|
||||
|
||||
struct int3403_priv {
|
||||
struct platform_device *pdev;
|
||||
struct acpi_device *adev;
|
||||
unsigned long long type;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
static int sys_get_curr_temp(struct thermal_zone_device *tzone,
|
||||
unsigned long *temp)
|
||||
{
|
||||
struct int3403_priv *priv = tzone->devdata;
|
||||
struct acpi_device *device = priv->adev;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct int3403_priv *priv = tzone->devdata;
|
||||
struct acpi_device *device = priv->adev;
|
||||
unsigned long long hyst;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_temp(struct thermal_zone_device *tzone,
|
||||
int trip, unsigned long *temp)
|
||||
{
|
||||
struct int3403_priv *priv = tzone->devdata;
|
||||
struct int3403_sensor *obj = priv->priv;
|
||||
|
||||
if (priv->type != INT3403_TYPE_SENSOR || !obj)
|
||||
return -EINVAL;
|
||||
|
||||
if (trip == obj->crit_trip_id)
|
||||
*temp = obj->crit_temp;
|
||||
else if (trip == obj->psv_trip_id)
|
||||
*temp = obj->psv_temp;
|
||||
else {
|
||||
/*
|
||||
* get_trip_temp is a mandatory callback but
|
||||
* PATx method doesn't return any value, so return
|
||||
* cached value, which was last set from user space
|
||||
*/
|
||||
*temp = obj->thresholds[trip];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_type(struct thermal_zone_device *thermal,
|
||||
int trip, enum thermal_trip_type *type)
|
||||
{
|
||||
struct int3403_priv *priv = thermal->devdata;
|
||||
struct int3403_sensor *obj = priv->priv;
|
||||
|
||||
/* Mandatory callback, may not mean much here */
|
||||
if (trip == obj->crit_trip_id)
|
||||
*type = THERMAL_TRIP_CRITICAL;
|
||||
else
|
||||
*type = THERMAL_TRIP_PASSIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct int3403_priv *priv = tzone->devdata;
|
||||
struct acpi_device *device = priv->adev;
|
||||
struct int3403_sensor *obj = priv->priv;
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
int ret = 0;
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
if (acpi_has_method(device->handle, name)) {
|
||||
status = acpi_execute_simple_method(device->handle, name,
|
||||
MILLI_CELSIUS_TO_DECI_KELVIN(temp,
|
||||
KELVIN_OFFSET));
|
||||
if (ACPI_FAILURE(status))
|
||||
ret = -EIO;
|
||||
else
|
||||
obj->thresholds[trip] = temp;
|
||||
} else {
|
||||
ret = -EIO;
|
||||
dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops tzone_ops = {
|
||||
.get_temp = sys_get_curr_temp,
|
||||
.get_trip_temp = sys_get_trip_temp,
|
||||
.get_trip_type = sys_get_trip_type,
|
||||
.set_trip_temp = sys_set_trip_temp,
|
||||
.get_trip_hyst = sys_get_trip_hyst,
|
||||
};
|
||||
|
||||
static struct thermal_zone_params int3403_thermal_params = {
|
||||
.governor_name = "user_space",
|
||||
.no_hwmon = true,
|
||||
};
|
||||
|
||||
static void int3403_notify(acpi_handle handle,
|
||||
u32 event, void *data)
|
||||
{
|
||||
struct int3403_priv *priv = data;
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
obj = priv->priv;
|
||||
if (priv->type != INT3403_TYPE_SENSOR || !obj)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case INT3403_PERF_CHANGED_EVENT:
|
||||
break;
|
||||
case INT3403_THERMAL_EVENT:
|
||||
thermal_zone_device_update(obj->tzone);
|
||||
break;
|
||||
default:
|
||||
dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
|
||||
{
|
||||
unsigned long long crt;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
|
||||
{
|
||||
unsigned long long psv;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3403_sensor_add(struct int3403_priv *priv)
|
||||
{
|
||||
int result = 0;
|
||||
acpi_status status;
|
||||
struct int3403_sensor *obj;
|
||||
unsigned long long trip_cnt;
|
||||
int trip_mask = 0;
|
||||
|
||||
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->priv = obj;
|
||||
|
||||
status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL,
|
||||
&trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
|
||||
if (trip_cnt) {
|
||||
/* We have to cache, thresholds can't be readback */
|
||||
obj->thresholds = devm_kzalloc(&priv->pdev->dev,
|
||||
sizeof(*obj->thresholds) * trip_cnt,
|
||||
GFP_KERNEL);
|
||||
if (!obj->thresholds) {
|
||||
result = -ENOMEM;
|
||||
goto err_free_obj;
|
||||
}
|
||||
trip_mask = BIT(trip_cnt) - 1;
|
||||
}
|
||||
|
||||
obj->psv_trip_id = -1;
|
||||
if (!sys_get_trip_psv(priv->adev, &obj->psv_temp))
|
||||
obj->psv_trip_id = trip_cnt++;
|
||||
|
||||
obj->crit_trip_id = -1;
|
||||
if (!sys_get_trip_crt(priv->adev, &obj->crit_temp))
|
||||
obj->crit_trip_id = trip_cnt++;
|
||||
|
||||
obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev),
|
||||
trip_cnt, trip_mask, priv, &tzone_ops,
|
||||
&int3403_thermal_params, 0, 0);
|
||||
if (IS_ERR(obj->tzone)) {
|
||||
result = PTR_ERR(obj->tzone);
|
||||
obj->tzone = NULL;
|
||||
goto err_free_obj;
|
||||
}
|
||||
|
||||
result = acpi_install_notify_handler(priv->adev->handle,
|
||||
ACPI_DEVICE_NOTIFY, int3403_notify,
|
||||
(void *)priv);
|
||||
if (result)
|
||||
goto err_free_obj;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_obj:
|
||||
if (obj->tzone)
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3403_sensor_remove(struct int3403_priv *priv)
|
||||
{
|
||||
struct int3403_sensor *obj = priv->priv;
|
||||
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* INT3403 Cooling devices */
|
||||
static int int3403_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct int3403_priv *priv = cdev->devdata;
|
||||
struct int3403_cdev *obj = priv->priv;
|
||||
|
||||
*state = obj->max_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct int3403_priv *priv = cdev->devdata;
|
||||
unsigned long long level;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
*state = level;
|
||||
return 0;
|
||||
} else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
|
||||
{
|
||||
struct int3403_priv *priv = cdev->devdata;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops int3403_cooling_ops = {
|
||||
.get_max_state = int3403_get_max_state,
|
||||
.get_cur_state = int3403_get_cur_state,
|
||||
.set_cur_state = int3403_set_cur_state,
|
||||
};
|
||||
|
||||
static int int3403_cdev_add(struct int3403_priv *priv)
|
||||
{
|
||||
int result = 0;
|
||||
acpi_status status;
|
||||
struct int3403_cdev *obj;
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *p;
|
||||
|
||||
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
p = buf.pointer;
|
||||
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
|
||||
printk(KERN_WARNING "Invalid PPSS data\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
obj->max_state = p->package.count - 1;
|
||||
obj->cdev =
|
||||
thermal_cooling_device_register(acpi_device_bid(priv->adev),
|
||||
priv, &int3403_cooling_ops);
|
||||
if (IS_ERR(obj->cdev))
|
||||
result = PTR_ERR(obj->cdev);
|
||||
|
||||
priv->priv = obj;
|
||||
|
||||
/* TODO: add ACPI notification support */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3403_cdev_remove(struct int3403_priv *priv)
|
||||
{
|
||||
struct int3403_cdev *obj = priv->priv;
|
||||
|
||||
thermal_cooling_device_unregister(obj->cdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3403_add(struct platform_device *pdev)
|
||||
{
|
||||
struct int3403_priv *priv;
|
||||
int result = 0;
|
||||
acpi_status status;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->pdev = pdev;
|
||||
priv->adev = ACPI_COMPANION(&(pdev->dev));
|
||||
if (!priv->adev) {
|
||||
result = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
|
||||
NULL, &priv->type);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
result = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
switch (priv->type) {
|
||||
case INT3403_TYPE_SENSOR:
|
||||
result = int3403_sensor_add(priv);
|
||||
break;
|
||||
case INT3403_TYPE_CHARGER:
|
||||
case INT3403_TYPE_BATTERY:
|
||||
result = int3403_cdev_add(priv);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
}
|
||||
|
||||
if (result)
|
||||
goto err;
|
||||
return result;
|
||||
|
||||
err:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3403_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct int3403_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
switch (priv->type) {
|
||||
case INT3403_TYPE_SENSOR:
|
||||
int3403_sensor_remove(priv);
|
||||
break;
|
||||
case INT3403_TYPE_CHARGER:
|
||||
case INT3403_TYPE_BATTERY:
|
||||
int3403_cdev_remove(priv);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id int3403_device_ids[] = {
|
||||
{"INT3403", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
|
||||
|
||||
static struct platform_driver int3403_driver = {
|
||||
.probe = int3403_add,
|
||||
.remove = int3403_remove,
|
||||
.driver = {
|
||||
.name = "int3403 thermal",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = int3403_device_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(int3403_driver);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
|
|
@ -435,6 +435,7 @@ int acpi_device_set_power(struct acpi_device *device, int state);
|
|||
int acpi_bus_init_power(struct acpi_device *device);
|
||||
int acpi_device_fix_up_power(struct acpi_device *device);
|
||||
int acpi_bus_update_power(acpi_handle handle, int *state_p);
|
||||
int acpi_device_update_power(struct acpi_device *device, int *state_p);
|
||||
bool acpi_bus_power_manageable(acpi_handle handle);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
|
|
@ -432,6 +432,7 @@ static inline bool acpi_driver_match_device(struct device *dev,
|
|||
int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
|
||||
int acpi_device_modalias(struct device *, char *, int);
|
||||
|
||||
struct platform_device *acpi_create_platform_device(struct acpi_device *);
|
||||
#define ACPI_PTR(_ptr) (_ptr)
|
||||
|
||||
#else /* !CONFIG_ACPI */
|
||||
|
|
|
@ -44,6 +44,10 @@
|
|||
#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
|
||||
((long)t-2732+5)/10 : ((long)t-2732-5)/10)
|
||||
#define CELSIUS_TO_KELVIN(t) ((t)*10+2732)
|
||||
#define DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, off) (((t) - (off)) * 100)
|
||||
#define DECI_KELVIN_TO_MILLICELSIUS(t) DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, 2732)
|
||||
#define MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, off) (((t) / 100) + (off))
|
||||
#define MILLICELSIUS_TO_DECI_KELVIN(t) MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, 2732)
|
||||
|
||||
/* Adding event notification support elements */
|
||||
#define THERMAL_GENL_FAMILY_NAME "thermal_event"
|
||||
|
|
Loading…
Reference in New Issue