#include <linux/seq_file.h>
#include <linux/reboot.h>
#include <asm/uaccess.h>
--
++ #include <linux/thermal.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#define ACPI_THERMAL_MAX_ACTIVE 10
#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65
-- #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+273)*10)
--
#define _COMPONENT ACPI_THERMAL_COMPONENT
ACPI_MODULE_NAME("thermal");
struct acpi_thermal_trips trips;
struct acpi_handle_list devices;
struct timer_list timer;
++ struct thermal_zone_device *thermal_zone;
++ int tz_enabled;
struct mutex lock;
};
return 0;
}
-- static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
-- {
-- acpi_status status = AE_OK;
-- int i = 0;
++ #define ACPI_TRIPS_CRITICAL 0x01
++ #define ACPI_TRIPS_HOT 0x02
++ #define ACPI_TRIPS_PASSIVE 0x04
++ #define ACPI_TRIPS_ACTIVE 0x08
++ #define ACPI_TRIPS_DEVICES 0x10
++ #define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE)
++ #define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES
-- if (!tz)
-- return -EINVAL;
++ #define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \
++ ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \
++ ACPI_TRIPS_DEVICES)
-- /* Critical Shutdown (required) */
--
-- status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL,
-- &tz->trips.critical.temperature);
-- if (ACPI_FAILURE(status)) {
-- tz->trips.critical.flags.valid = 0;
-- ACPI_EXCEPTION((AE_INFO, status, "No critical threshold"));
-- return -ENODEV;
-- } else {
-- tz->trips.critical.flags.valid = 1;
-- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-- "Found critical threshold [%lu]\n",
-- tz->trips.critical.temperature));
-- }
++ /*
++ * This exception is thrown out in two cases:
++ * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid
++ * when re-evaluating the AML code.
++ * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change.
++ * We need to re-bind the cooling devices of a thermal zone when this occurs.
++ */
++ #define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \
++ do { \
++ if (flags != ACPI_TRIPS_INIT) \
++ ACPI_EXCEPTION((AE_INFO, AE_ERROR, \
++ "ACPI thermal trip point %s changed\n" \
++ "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \
++ } while (0)
++
++ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
++ {
++ acpi_status status = AE_OK;
++ struct acpi_handle_list devices;
++ int valid = 0;
++ int i;
-- if (tz->trips.critical.flags.valid == 1) {
-- if (crt == -1) {
++ /* Critical Shutdown (required) */
++ if (flag & ACPI_TRIPS_CRITICAL) {
++ status = acpi_evaluate_integer(tz->device->handle,
++ "_CRT", NULL, &tz->trips.critical.temperature);
++ if (ACPI_FAILURE(status)) {
tz->trips.critical.flags.valid = 0;
-- } else if (crt > 0) {
-- unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
--
-- /*
-- * Allow override to lower critical threshold
-- */
-- if (crt_k < tz->trips.critical.temperature)
-- tz->trips.critical.temperature = crt_k;
++ ACPI_EXCEPTION((AE_INFO, status,
++ "No critical threshold"));
++ return -ENODEV;
++ } else {
++ tz->trips.critical.flags.valid = 1;
++ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
++ "Found critical threshold [%lu]\n",
++ tz->trips.critical.temperature));
++ }
++ if (tz->trips.critical.flags.valid == 1) {
++ if (crt == -1) {
++ tz->trips.critical.flags.valid = 0;
++ } else if (crt > 0) {
++ unsigned long crt_k = CELSIUS_TO_KELVIN(crt);
++ /*
++ * Allow override to lower critical threshold
++ */
++ if (crt_k < tz->trips.critical.temperature)
++ tz->trips.critical.temperature = crt_k;
++ }
}
}
/* Critical Sleep (optional) */
--
-- status =
-- acpi_evaluate_integer(tz->device->handle, "_HOT", NULL,
-- &tz->trips.hot.temperature);
-- if (ACPI_FAILURE(status)) {
-- tz->trips.hot.flags.valid = 0;
-- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n"));
-- } else {
-- tz->trips.hot.flags.valid = 1;
-- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n",
-- tz->trips.hot.temperature));
-- }
--
-- /* Passive: Processors (optional) */
--
-- if (psv == -1) {
-- status = AE_SUPPORT;
-- } else if (psv > 0) {
-- tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
-- status = AE_OK;
-- } else {
++ if (flag & ACPI_TRIPS_HOT) {
status = acpi_evaluate_integer(tz->device->handle,
-- "_PSV", NULL, &tz->trips.passive.temperature);
++ "_HOT", NULL, &tz->trips.hot.temperature);
++ if (ACPI_FAILURE(status)) {
++ tz->trips.hot.flags.valid = 0;
++ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
++ "No hot threshold\n"));
++ } else {
++ tz->trips.hot.flags.valid = 1;
++ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
++ "Found hot threshold [%lu]\n",
++ tz->trips.critical.temperature));
++ }
}
-- if (ACPI_FAILURE(status)) {
-- tz->trips.passive.flags.valid = 0;
-- ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n"));
-- } else {
-- tz->trips.passive.flags.valid = 1;
--
-- status =
-- acpi_evaluate_integer(tz->device->handle, "_TC1", NULL,
-- &tz->trips.passive.tc1);
-- if (ACPI_FAILURE(status))
-- tz->trips.passive.flags.valid = 0;
--
-- status =
-- acpi_evaluate_integer(tz->device->handle, "_TC2", NULL,
-- &tz->trips.passive.tc2);
-- if (ACPI_FAILURE(status))
-- tz->trips.passive.flags.valid = 0;
++ /* Passive (optional) */
++ if (flag & ACPI_TRIPS_PASSIVE) {
++ valid = tz->trips.passive.flags.valid;
++ if (psv == -1) {
++ status = AE_SUPPORT;
++ } else if (psv > 0) {
++ tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv);
++ status = AE_OK;
++ } else {
++ status = acpi_evaluate_integer(tz->device->handle,
++ "_PSV", NULL, &tz->trips.passive.temperature);
++ }
-- status =
-- acpi_evaluate_integer(tz->device->handle, "_TSP", NULL,
-- &tz->trips.passive.tsp);
if (ACPI_FAILURE(status))
tz->trips.passive.flags.valid = 0;
--
-- status =
-- acpi_evaluate_reference(tz->device->handle, "_PSL", NULL,
-- &tz->trips.passive.devices);
++ else {
++ tz->trips.passive.flags.valid = 1;
++ if (flag == ACPI_TRIPS_INIT) {
++ status = acpi_evaluate_integer(
++ tz->device->handle, "_TC1",
++ NULL, &tz->trips.passive.tc1);
++ if (ACPI_FAILURE(status))
++ tz->trips.passive.flags.valid = 0;
++ status = acpi_evaluate_integer(
++ tz->device->handle, "_TC2",
++ NULL, &tz->trips.passive.tc2);
++ if (ACPI_FAILURE(status))
++ tz->trips.passive.flags.valid = 0;
++ status = acpi_evaluate_integer(
++ tz->device->handle, "_TSP",
++ NULL, &tz->trips.passive.tsp);
++ if (ACPI_FAILURE(status))
++ tz->trips.passive.flags.valid = 0;
++ }
++ }
++ }
++ if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) {
++ memset(&devices, 0, sizeof(struct acpi_handle_list));
++ status = acpi_evaluate_reference(tz->device->handle, "_PSL",
++ NULL, &devices);
if (ACPI_FAILURE(status))
tz->trips.passive.flags.valid = 0;
--
-- if (!tz->trips.passive.flags.valid)
-- printk(KERN_WARNING PREFIX "Invalid passive threshold\n");
else
-- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-- "Found passive threshold [%lu]\n",
-- tz->trips.passive.temperature));
-- }
++ tz->trips.passive.flags.valid = 1;
-- /* Active: Fans, etc. (optional) */
++ if (memcmp(&tz->trips.passive.devices, &devices,
++ sizeof(struct acpi_handle_list))) {
++ memcpy(&tz->trips.passive.devices, &devices,
++ sizeof(struct acpi_handle_list));
++ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
++ }
++ }
++ if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) {
++ if (valid != tz->trips.passive.flags.valid)
++ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
++ }
++ /* Active (optional) */
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
--
char name[5] = { '_', 'A', 'C', ('0' + i), '\0' };
++ valid = tz->trips.active[i].flags.valid;
if (act == -1)
-- break; /* disable all active trip points */
--
-- status = acpi_evaluate_integer(tz->device->handle,
-- name, NULL, &tz->trips.active[i].temperature);
--
-- if (ACPI_FAILURE(status)) {
-- if (i == 0) /* no active trip points */
-- break;
-- if (act <= 0) /* no override requested */
++ break; /* disable all active trip points */
++
++ if (flag & ACPI_TRIPS_ACTIVE) {
++ status = acpi_evaluate_integer(tz->device->handle,
++ name, NULL, &tz->trips.active[i].temperature);
++ if (ACPI_FAILURE(status)) {
++ tz->trips.active[i].flags.valid = 0;
++ if (i == 0)
++ break;
++ if (act <= 0)
++ break;
++ if (i == 1)
++ tz->trips.active[0].temperature =
++ CELSIUS_TO_KELVIN(act);
++ else
++ /*
++ * Don't allow override higher than
++ * the next higher trip point
++ */
++ tz->trips.active[i - 1].temperature =
++ (tz->trips.active[i - 2].temperature <
++ CELSIUS_TO_KELVIN(act) ?
++ tz->trips.active[i - 2].temperature :
++ CELSIUS_TO_KELVIN(act));
break;
-- if (i == 1) { /* 1 trip point */
-- tz->trips.active[0].temperature =
-- CELSIUS_TO_KELVIN(act);
-- } else { /* multiple trips */
-- /*
-- * Don't allow override higher than
-- * the next higher trip point
-- */
-- tz->trips.active[i - 1].temperature =
-- (tz->trips.active[i - 2].temperature <
-- CELSIUS_TO_KELVIN(act) ?
-- tz->trips.active[i - 2].temperature :
-- CELSIUS_TO_KELVIN(act));
-- }
-- break;
++ } else
++ tz->trips.active[i].flags.valid = 1;
}
name[2] = 'L';
-- status =
-- acpi_evaluate_reference(tz->device->handle, name, NULL,
-- &tz->trips.active[i].devices);
-- if (ACPI_SUCCESS(status)) {
-- tz->trips.active[i].flags.valid = 1;
-- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-- "Found active threshold [%d]:[%lu]\n",
-- i, tz->trips.active[i].temperature));
-- } else
-- ACPI_EXCEPTION((AE_INFO, status,
-- "Invalid active threshold [%d]", i));
++ if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) {
++ memset(&devices, 0, sizeof(struct acpi_handle_list));
++ status = acpi_evaluate_reference(tz->device->handle,
++ name, NULL, &devices);
++ if (ACPI_FAILURE(status))
++ tz->trips.active[i].flags.valid = 0;
++ else
++ tz->trips.active[i].flags.valid = 1;
++
++ if (memcmp(&tz->trips.active[i].devices, &devices,
++ sizeof(struct acpi_handle_list))) {
++ memcpy(&tz->trips.active[i].devices, &devices,
++ sizeof(struct acpi_handle_list));
++ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
++ }
++ }
++ if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES))
++ if (valid != tz->trips.active[i].flags.valid)
++ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state");
++
++ if (!tz->trips.active[i].flags.valid)
++ break;
++ }
++
++ if (flag & ACPI_TRIPS_DEVICES) {
++ memset(&devices, 0, sizeof(struct acpi_handle_list));
++ status = acpi_evaluate_reference(tz->device->handle, "_TZD",
++ NULL, &devices);
++ if (memcmp(&tz->devices, &devices,
++ sizeof(struct acpi_handle_list))) {
++ memcpy(&tz->devices, &devices,
++ sizeof(struct acpi_handle_list));
++ ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device");
++ }
}
return 0;
}
-- static int acpi_thermal_get_devices(struct acpi_thermal *tz)
++ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
{
-- acpi_status status = AE_OK;
--
--
-- if (!tz)
-- return -EINVAL;
--
-- status =
-- acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices);
-- if (ACPI_FAILURE(status))
-- return -ENODEV;
--
-- return 0;
++ return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT);
}
static int acpi_thermal_critical(struct acpi_thermal *tz)
{
- if (!tz || !tz->trips.critical.flags.valid || nocrt)
+ if (!tz || !tz->trips.critical.flags.valid)
return -EINVAL;
if (tz->temperature >= tz->trips.critical.temperature) {
} else if (tz->trips.critical.flags.enabled)
tz->trips.critical.flags.enabled = 0;
- printk(KERN_EMERG
- "Critical temperature reached (%ld C), shutting down.\n",
- KELVIN_TO_CELSIUS(tz->temperature));
acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL,
tz->trips.critical.flags.enabled);
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
ACPI_THERMAL_NOTIFY_CRITICAL,
tz->trips.critical.flags.enabled);
- orderly_poweroff(true);
+ /* take no action if nocrt is set */
+ if(!nocrt) {
+ printk(KERN_EMERG
+ "Critical temperature reached (%ld C), shutting down.\n",
+ KELVIN_TO_CELSIUS(tz->temperature));
+ orderly_poweroff(true);
+ }
return 0;
}
static int acpi_thermal_hot(struct acpi_thermal *tz)
{
- if (!tz || !tz->trips.hot.flags.valid || nocrt)
+ if (!tz || !tz->trips.hot.flags.valid)
return -EINVAL;
if (tz->temperature >= tz->trips.hot.temperature) {
ACPI_THERMAL_NOTIFY_HOT,
tz->trips.hot.flags.enabled);
- /* TBD: Call user-mode "sleep(S4)" function */
+ /* TBD: Call user-mode "sleep(S4)" function if nocrt is cleared */
return 0;
}
if (result)
goto unlock;
++ if (!tz->tz_enabled)
++ goto unlock;
++
memset(&tz->state, 0, sizeof(tz->state));
/*
mutex_unlock(&tz->lock);
}
++ /* sys I/F for generic thermal sysfs support */
++ static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
++ {
++ struct acpi_thermal *tz = thermal->devdata;
++
++ if (!tz)
++ return -EINVAL;
++
++ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature));
++ }
++
++ static const char enabled[] = "kernel";
++ static const char disabled[] = "user";
++ static int thermal_get_mode(struct thermal_zone_device *thermal,
++ char *buf)
++ {
++ struct acpi_thermal *tz = thermal->devdata;
++
++ if (!tz)
++ return -EINVAL;
++
++ return sprintf(buf, "%s\n", tz->tz_enabled ?
++ enabled : disabled);
++ }
++
++ static int thermal_set_mode(struct thermal_zone_device *thermal,
++ const char *buf)
++ {
++ struct acpi_thermal *tz = thermal->devdata;
++ int enable;
++
++ if (!tz)
++ return -EINVAL;
++
++ /*
++ * enable/disable thermal management from ACPI thermal driver
++ */
++ if (!strncmp(buf, enabled, sizeof enabled - 1))
++ enable = 1;
++ else if (!strncmp(buf, disabled, sizeof disabled - 1))
++ enable = 0;
++ else
++ return -EINVAL;
++
++ if (enable != tz->tz_enabled) {
++ tz->tz_enabled = enable;
++ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
++ "%s ACPI thermal control\n",
++ tz->tz_enabled ? enabled : disabled));
++ acpi_thermal_check(tz);
++ }
++ return 0;
++ }
++
++ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
++ int trip, char *buf)
++ {
++ struct acpi_thermal *tz = thermal->devdata;
++ int i;
++
++ if (!tz || trip < 0)
++ return -EINVAL;
++
++ if (tz->trips.critical.flags.valid) {
++ if (!trip)
++ return sprintf(buf, "critical\n");
++ trip--;
++ }
++
++ if (tz->trips.hot.flags.valid) {
++ if (!trip)
++ return sprintf(buf, "hot\n");
++ trip--;
++ }
++
++ if (tz->trips.passive.flags.valid) {
++ if (!trip)
++ return sprintf(buf, "passive\n");
++ trip--;
++ }
++
++ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
++ tz->trips.active[i].flags.valid; i++) {
++ if (!trip)
++ return sprintf(buf, "active%d\n", i);
++ trip--;
++ }
++
++ return -EINVAL;
++ }
++
++ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
++ int trip, char *buf)
++ {
++ struct acpi_thermal *tz = thermal->devdata;
++ int i;
++
++ if (!tz || trip < 0)
++ return -EINVAL;
++
++ if (tz->trips.critical.flags.valid) {
++ if (!trip)
++ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
++ tz->trips.critical.temperature));
++ trip--;
++ }
++
++ if (tz->trips.hot.flags.valid) {
++ if (!trip)
++ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
++ tz->trips.hot.temperature));
++ trip--;
++ }
++
++ if (tz->trips.passive.flags.valid) {
++ if (!trip)
++ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
++ tz->trips.passive.temperature));
++ trip--;
++ }
++
++ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
++ tz->trips.active[i].flags.valid; i++) {
++ if (!trip)
++ return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(
++ tz->trips.active[i].temperature));
++ trip--;
++ }
++
++ return -EINVAL;
++ }
++
++ typedef int (*cb)(struct thermal_zone_device *, int,
++ struct thermal_cooling_device *);
++ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
++ struct thermal_cooling_device *cdev,
++ cb action)
++ {
++ struct acpi_device *device = cdev->devdata;
++ struct acpi_thermal *tz = thermal->devdata;
++ struct acpi_device *dev;
++ acpi_status status;
++ acpi_handle handle;
++ int i;
++ int j;
++ int trip = -1;
++ int result = 0;
++
++ if (tz->trips.critical.flags.valid)
++ trip++;
++
++ if (tz->trips.hot.flags.valid)
++ trip++;
++
++ if (tz->trips.passive.flags.valid) {
++ trip++;
++ for (i = 0; i < tz->trips.passive.devices.count;
++ i++) {
++ handle = tz->trips.passive.devices.handles[i];
++ status = acpi_bus_get_device(handle, &dev);
++ if (ACPI_SUCCESS(status) && (dev == device)) {
++ result = action(thermal, trip, cdev);
++ if (result)
++ goto failed;
++ }
++ }
++ }
++
++ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
++ if (!tz->trips.active[i].flags.valid)
++ break;
++ trip++;
++ for (j = 0;
++ j < tz->trips.active[i].devices.count;
++ j++) {
++ handle = tz->trips.active[i].devices.handles[j];
++ status = acpi_bus_get_device(handle, &dev);
++ if (ACPI_SUCCESS(status) && (dev == device)) {
++ result = action(thermal, trip, cdev);
++ if (result)
++ goto failed;
++ }
++ }
++ }
++
++ for (i = 0; i < tz->devices.count; i++) {
++ handle = tz->devices.handles[i];
++ status = acpi_bus_get_device(handle, &dev);
++ if (ACPI_SUCCESS(status) && (dev == device)) {
++ result = action(thermal, -1, cdev);
++ if (result)
++ goto failed;
++ }
++ }
++
++ failed:
++ return result;
++ }
++
++ static int
++ acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal,
++ struct thermal_cooling_device *cdev)
++ {
++ return acpi_thermal_cooling_device_cb(thermal, cdev,
++ thermal_zone_bind_cooling_device);
++ }
++
++ static int
++ acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
++ struct thermal_cooling_device *cdev)
++ {
++ return acpi_thermal_cooling_device_cb(thermal, cdev,
++ thermal_zone_unbind_cooling_device);
++ }
++
++ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
++ .bind = acpi_thermal_bind_cooling_device,
++ .unbind = acpi_thermal_unbind_cooling_device,
++ .get_temp = thermal_get_temp,
++ .get_mode = thermal_get_mode,
++ .set_mode = thermal_set_mode,
++ .get_trip_type = thermal_get_trip_type,
++ .get_trip_temp = thermal_get_trip_temp,
++ };
++
++ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
++ {
++ int trips = 0;
++ int result;
++ acpi_status status;
++ int i;
++
++ if (tz->trips.critical.flags.valid)
++ trips++;
++
++ if (tz->trips.hot.flags.valid)
++ trips++;
++
++ if (tz->trips.passive.flags.valid)
++ trips++;
++
++ for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
++ tz->trips.active[i].flags.valid; i++, trips++);
++ tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone",
++ trips, tz, &acpi_thermal_zone_ops);
++ if (!tz->thermal_zone)
++ return -ENODEV;
++
++ result = sysfs_create_link(&tz->device->dev.kobj,
++ &tz->thermal_zone->device.kobj, "thermal_zone");
++ if (result)
++ return result;
++
++ result = sysfs_create_link(&tz->thermal_zone->device.kobj,
++ &tz->device->dev.kobj, "device");
++ if (result)
++ return result;
++
++ status = acpi_attach_data(tz->device->handle,
++ acpi_bus_private_data_handler,
++ tz->thermal_zone);
++ if (ACPI_FAILURE(status)) {
++ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
++ "Error attaching device data\n"));
++ return -ENODEV;
++ }
++
++ tz->tz_enabled = 1;
++
++ printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",
++ tz->device->dev.bus_id, tz->thermal_zone->id);
++ return 0;
++ }
++
++ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
++ {
++ sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone");
++ sysfs_remove_link(&tz->thermal_zone->device.kobj, "device");
++ thermal_zone_device_unregister(tz->thermal_zone);
++ tz->thermal_zone = NULL;
++ acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler);
++ }
++
++
/* --------------------------------------------------------------------------
FS Interface (/proc)
-------------------------------------------------------------------------- */
acpi_thermal_check(tz);
break;
case ACPI_THERMAL_NOTIFY_THRESHOLDS:
-- acpi_thermal_get_trip_points(tz);
++ acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
acpi_thermal_check(tz);
acpi_bus_generate_proc_event(device, event, 0);
acpi_bus_generate_netlink_event(device->pnp.device_class,
device->dev.bus_id, event, 0);
break;
case ACPI_THERMAL_NOTIFY_DEVICES:
-- if (tz->flags.devices)
-- acpi_thermal_get_devices(tz);
++ acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
++ acpi_thermal_check(tz);
acpi_bus_generate_proc_event(device, event, 0);
acpi_bus_generate_netlink_event(device->pnp.device_class,
device->dev.bus_id, event, 0);
else
acpi_thermal_get_polling_frequency(tz);
-- /* Get devices in this thermal zone [_TZD] (optional) */
-- result = acpi_thermal_get_devices(tz);
-- if (!result)
-- tz->flags.devices = 1;
--
return 0;
}
strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS);
acpi_driver_data(device) = tz;
mutex_init(&tz->lock);
++
++
result = acpi_thermal_get_info(tz);
if (result)
-- goto end;
++ goto free_memory;
++
++ result = acpi_thermal_register_thermal_zone(tz);
++ if (result)
++ goto free_memory;
result = acpi_thermal_add_fs(device);
if (result)
-- goto end;
++ goto unregister_thermal_zone;
init_timer(&tz->timer);
acpi_thermal_notify, tz);
if (ACPI_FAILURE(status)) {
result = -ENODEV;
-- goto end;
++ goto remove_fs;
}
printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n",
acpi_device_name(device), acpi_device_bid(device),
KELVIN_TO_CELSIUS(tz->temperature));
++ goto end;
-- end:
-- if (result) {
-- acpi_thermal_remove_fs(device);
-- kfree(tz);
-- }
--
++ remove_fs:
++ acpi_thermal_remove_fs(device);
++ unregister_thermal_zone:
++ thermal_zone_device_unregister(tz->thermal_zone);
++ free_memory:
++ kfree(tz);
++ end:
return result;
}
}
acpi_thermal_remove_fs(device);
++ acpi_thermal_unregister_thermal_zone(tz);
mutex_destroy(&tz->lock);
kfree(tz);
return 0;
#include <linux/seq_file.h>
#include <linux/input.h>
#include <linux/backlight.h>
++ #include <linux/thermal.h>
#include <linux/video_output.h>
#include <asm/uaccess.h>
struct acpi_device *dev;
struct acpi_video_device_brightness *brightness;
struct backlight_device *backlight;
++ struct thermal_cooling_device *cdev;
struct output_device *output_dev;
};
static int acpi_video_get_brightness(struct backlight_device *bd)
{
unsigned long cur_level;
+ int i;
struct acpi_video_device *vd =
(struct acpi_video_device *)bl_get_data(bd);
acpi_video_device_lcd_get_level_current(vd, &cur_level);
- return (int) cur_level;
+ for (i = 2; i < vd->brightness->count; i++) {
+ if (vd->brightness->levels[i] == cur_level)
+ /* The first two entries are special - see page 575
+ of the ACPI spec 3.0 */
+ return i-2;
+ }
+ return 0;
}
static int acpi_video_set_brightness(struct backlight_device *bd)
{
- int request_level = bd->props.brightness;
+ int request_level = bd->props.brightness+2;
struct acpi_video_device *vd =
(struct acpi_video_device *)bl_get_data(bd);
- acpi_video_device_lcd_set_level(vd, request_level);
+ acpi_video_device_lcd_set_level(vd,
+ vd->brightness->levels[request_level]);
return 0;
}
.set_state = acpi_video_output_set,
.get_status = acpi_video_output_get,
};
++
++
++ /* thermal cooling device callbacks */
++ static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
++ {
++ struct acpi_device *device = cdev->devdata;
++ struct acpi_video_device *video = acpi_driver_data(device);
++
++ return sprintf(buf, "%d\n", video->brightness->count - 3);
++ }
++
++ static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
++ {
++ struct acpi_device *device = cdev->devdata;
++ struct acpi_video_device *video = acpi_driver_data(device);
++ unsigned long level;
++ int state;
++
++ acpi_video_device_lcd_get_level_current(video, &level);
++ for (state = 2; state < video->brightness->count; state++)
++ if (level == video->brightness->levels[state])
++ return sprintf(buf, "%d\n",
++ video->brightness->count - state - 1);
++
++ return -EINVAL;
++ }
++
++ static int
++ video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
++ {
++ struct acpi_device *device = cdev->devdata;
++ struct acpi_video_device *video = acpi_driver_data(device);
++ int level;
++
++ if ( state >= video->brightness->count - 2)
++ return -EINVAL;
++
++ state = video->brightness->count - state;
++ level = video->brightness->levels[state -1];
++ return acpi_video_device_lcd_set_level(video, level);
++ }
++
++ static struct thermal_cooling_device_ops video_cooling_ops = {
++ .get_max_state = video_get_max_state,
++ .get_cur_state = video_get_cur_state,
++ .set_cur_state = video_set_cur_state,
++ };
++
/* --------------------------------------------------------------------------
Video Management
-------------------------------------------------------------------------- */
kfree(obj);
if (device->cap._BCL && device->cap._BCM && device->cap._BQC && max_level > 0){
- unsigned long tmp;
++ int result;
static int count = 0;
char *name;
name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
return;
sprintf(name, "acpi_video%d", count++);
- acpi_video_device_lcd_get_level_current(device, &tmp);
device->backlight = backlight_device_register(name,
NULL, device, &acpi_backlight_ops);
- device->backlight->props.max_brightness = max_level;
- device->backlight->props.brightness = (int)tmp;
+ device->backlight->props.max_brightness = device->brightness->count-3;
+ device->backlight->props.brightness = acpi_video_get_brightness(device->backlight);
backlight_update_status(device->backlight);
--
kfree(name);
++
++ device->cdev = thermal_cooling_device_register("LCD",
++ device->dev, &video_cooling_ops);
++ if (device->cdev) {
++ printk(KERN_INFO PREFIX
++ "%s is registered as cooling_device%d\n",
++ device->dev->dev.bus_id, device->cdev->id);
++ result = sysfs_create_link(&device->dev->dev.kobj,
++ &device->cdev->device.kobj,
++ "thermal_cooling");
++ if (result)
++ printk(KERN_ERR PREFIX "Create sysfs link\n");
++ result = sysfs_create_link(&device->cdev->device.kobj,
++ &device->dev->dev.kobj,
++ "device");
++ if (result)
++ printk(KERN_ERR PREFIX "Create sysfs link\n");
++ }
}
if (device->cap._DCS && device->cap._DSS){
static int count = 0;
static int acpi_video_bus_add_fs(struct acpi_device *device)
{
+ long device_id;
+ int status;
struct proc_dir_entry *entry = NULL;
struct acpi_video_bus *video;
+ struct device *dev;
+
+ status =
+ acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
+
+ if (!ACPI_SUCCESS(status))
+ return -ENODEV;
+
+ /* We need to attempt to determine whether the _ADR refers to a
+ PCI device or not. There's no terribly good way to do this,
+ so the best we can hope for is to assume that there'll never
+ be a video device in the host bridge */
+ if (device_id >= 0x10000) {
+ /* It looks like a PCI device. Does it exist? */
+ dev = acpi_get_physical_device(device->handle);
+ } else {
+ /* It doesn't look like a PCI device. Does its parent
+ exist? */
+ acpi_handle phandle;
+ if (acpi_get_parent(device->handle, &phandle))
+ return -ENODEV;
+ dev = acpi_get_physical_device(phandle);
+ }
+ if (!dev)
+ return -ENODEV;
+ put_device(dev);
+
video = acpi_driver_data(device);
ACPI_DEVICE_NOTIFY,
acpi_video_device_notify);
backlight_device_unregister(device->backlight);
++ if (device->cdev) {
++ sysfs_remove_link(&device->dev->dev.kobj,
++ "thermal_cooling");
++ sysfs_remove_link(&device->cdev->device.kobj,
++ "device");
++ thermal_cooling_device_unregister(device->cdev);
++ device->cdev = NULL;
++ }
video_output_unregister(device->output_dev);
return 0;