Commit 4f3bff70 authored by Len Brown's avatar Len Brown

Merge branch 'thermal' into release

parents 2ddb9f17 03a971a2
......@@ -68,31 +68,35 @@ static struct acpi_driver acpi_fan_driver = {
};
/* thermal cooling device callbacks */
static int fan_get_max_state(struct thermal_cooling_device *cdev, char *buf)
static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
/* ACPI fan device only support two states: ON/OFF */
return sprintf(buf, "1\n");
*state = 1;
return 0;
}
static int fan_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
struct acpi_device *device = cdev->devdata;
int state;
int result;
int acpi_state;
if (!device)
return -EINVAL;
result = acpi_bus_get_power(device->handle, &state);
result = acpi_bus_get_power(device->handle, &acpi_state);
if (result)
return result;
return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
(state == ACPI_STATE_D0 ? "1" : "unknown"));
*state = (acpi_state == ACPI_STATE_D3 ? 0 :
(acpi_state == ACPI_STATE_D0 ? 1 : -1));
return 0;
}
static int
fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
struct acpi_device *device = cdev->devdata;
int result;
......
......@@ -373,7 +373,8 @@ static int acpi_processor_max_state(struct acpi_processor *pr)
return max_state;
}
static int
processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)
processor_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_processor *pr = acpi_driver_data(device);
......@@ -381,28 +382,29 @@ processor_get_max_state(struct thermal_cooling_device *cdev, char *buf)
if (!device || !pr)
return -EINVAL;
return sprintf(buf, "%d\n", acpi_processor_max_state(pr));
*state = acpi_processor_max_state(pr);
return 0;
}
static int
processor_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
processor_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *cur_state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_processor *pr = acpi_driver_data(device);
int cur_state;
if (!device || !pr)
return -EINVAL;
cur_state = cpufreq_get_cur_state(pr->id);
*cur_state = cpufreq_get_cur_state(pr->id);
if (pr->flags.throttling)
cur_state += pr->throttling.state;
return sprintf(buf, "%d\n", cur_state);
*cur_state += pr->throttling.state;
return 0;
}
static int
processor_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
processor_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_processor *pr = acpi_driver_data(device);
......
......@@ -37,11 +37,11 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/kmod.h>
#include <linux/seq_file.h>
#include <linux/reboot.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/thermal.h>
#include <acpi/acpi_bus.h>
......@@ -190,7 +190,6 @@ struct acpi_thermal {
struct acpi_thermal_state state;
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;
......@@ -290,6 +289,11 @@ static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds)
tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */
tz->thermal_zone->polling_delay = seconds * 1000;
if (tz->tz_enabled)
thermal_zone_device_update(tz->thermal_zone);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Polling frequency set to %lu seconds\n",
tz->polling_frequency/10));
......@@ -569,392 +573,18 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
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)
return -EINVAL;
if (tz->temperature >= tz->trips.critical.temperature) {
printk(KERN_WARNING PREFIX "Critical trip point\n");
tz->trips.critical.flags.enabled = 1;
} else if (tz->trips.critical.flags.enabled)
tz->trips.critical.flags.enabled = 0;
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,
dev_name(&tz->device->dev),
ACPI_THERMAL_NOTIFY_CRITICAL,
tz->trips.critical.flags.enabled);
/* 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)
return -EINVAL;
if (tz->temperature >= tz->trips.hot.temperature) {
printk(KERN_WARNING PREFIX "Hot trip point\n");
tz->trips.hot.flags.enabled = 1;
} else if (tz->trips.hot.flags.enabled)
tz->trips.hot.flags.enabled = 0;
acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_HOT,
tz->trips.hot.flags.enabled);
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
dev_name(&tz->device->dev),
ACPI_THERMAL_NOTIFY_HOT,
tz->trips.hot.flags.enabled);
/* TBD: Call user-mode "sleep(S4)" function if nocrt is cleared */
return 0;
}
static void acpi_thermal_passive(struct acpi_thermal *tz)
{
int result = 1;
struct acpi_thermal_passive *passive = NULL;
int trend = 0;
int i = 0;
if (!tz || !tz->trips.passive.flags.valid)
return;
passive = &(tz->trips.passive);
/*
* Above Trip?
* -----------
* Calculate the thermal trend (using the passive cooling equation)
* and modify the performance limit for all passive cooling devices
* accordingly. Note that we assume symmetry.
*/
if (tz->temperature >= passive->temperature) {
trend =
(passive->tc1 * (tz->temperature - tz->last_temperature)) +
(passive->tc2 * (tz->temperature - passive->temperature));
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"trend[%d]=(tc1[%lu]*(tmp[%lu]-last[%lu]))+(tc2[%lu]*(tmp[%lu]-psv[%lu]))\n",
trend, passive->tc1, tz->temperature,
tz->last_temperature, passive->tc2,
tz->temperature, passive->temperature));
passive->flags.enabled = 1;
/* Heating up? */
if (trend > 0)
for (i = 0; i < passive->devices.count; i++)
acpi_processor_set_thermal_limit(passive->
devices.
handles[i],
ACPI_PROCESSOR_LIMIT_INCREMENT);
/* Cooling off? */
else if (trend < 0) {
for (i = 0; i < passive->devices.count; i++)
/*
* assume that we are on highest
* freq/lowest thrott and can leave
* passive mode, even in error case
*/
if (!acpi_processor_set_thermal_limit
(passive->devices.handles[i],
ACPI_PROCESSOR_LIMIT_DECREMENT))
result = 0;
/*
* Leave cooling mode, even if the temp might
* higher than trip point This is because some
* machines might have long thermal polling
* frequencies (tsp) defined. We will fall back
* into passive mode in next cycle (probably quicker)
*/
if (result) {
passive->flags.enabled = 0;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Disabling passive cooling, still above threshold,"
" but we are cooling down\n"));
}
}
return;
}
/*
* Below Trip?
* -----------
* Implement passive cooling hysteresis to slowly increase performance
* and avoid thrashing around the passive trip point. Note that we
* assume symmetry.
*/
if (!passive->flags.enabled)
return;
for (i = 0; i < passive->devices.count; i++)
if (!acpi_processor_set_thermal_limit
(passive->devices.handles[i],
ACPI_PROCESSOR_LIMIT_DECREMENT))
result = 0;
if (result) {
passive->flags.enabled = 0;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Disabling passive cooling (zone is cool)\n"));
}
}
static void acpi_thermal_active(struct acpi_thermal *tz)
{
int result = 0;
struct acpi_thermal_active *active = NULL;
int i = 0;
int j = 0;
unsigned long maxtemp = 0;
if (!tz)
return;
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
active = &(tz->trips.active[i]);
if (!active || !active->flags.valid)
break;
if (tz->temperature >= active->temperature) {
/*
* Above Threshold?
* ----------------
* If not already enabled, turn ON all cooling devices
* associated with this active threshold.
*/
if (active->temperature > maxtemp)
tz->state.active_index = i;
maxtemp = active->temperature;
if (active->flags.enabled)
continue;
for (j = 0; j < active->devices.count; j++) {
result =
acpi_bus_set_power(active->devices.
handles[j],
ACPI_STATE_D0);
if (result) {
printk(KERN_WARNING PREFIX
"Unable to turn cooling device [%p] 'on'\n",
active->devices.
handles[j]);
continue;
}
active->flags.enabled = 1;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Cooling device [%p] now 'on'\n",
active->devices.handles[j]));
}
continue;
}
if (!active->flags.enabled)
continue;
/*
* Below Threshold?
* ----------------
* Turn OFF all cooling devices associated with this
* threshold.
*/
for (j = 0; j < active->devices.count; j++) {
result = acpi_bus_set_power(active->devices.handles[j],
ACPI_STATE_D3);
if (result) {
printk(KERN_WARNING PREFIX
"Unable to turn cooling device [%p] 'off'\n",
active->devices.handles[j]);
continue;
}
active->flags.enabled = 0;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Cooling device [%p] now 'off'\n",
active->devices.handles[j]));
}
}
}
static void acpi_thermal_check(void *context);
static void acpi_thermal_run(unsigned long data)
{
struct acpi_thermal *tz = (struct acpi_thermal *)data;
if (!tz->zombie)
acpi_os_execute(OSL_GPE_HANDLER, acpi_thermal_check, (void *)data);
}
static void acpi_thermal_active_off(void *data)
{
int result = 0;
struct acpi_thermal *tz = data;
int i = 0;
int j = 0;
struct acpi_thermal_active *active = NULL;
if (!tz) {
printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
return;
}
result = acpi_thermal_get_temperature(tz);
if (result)
return;
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
active = &(tz->trips.active[i]);
if (!active || !active->flags.valid)
break;
if (tz->temperature >= active->temperature) {
/*
* If the thermal temperature is greater than the
* active threshod, unnecessary to turn off the
* the active cooling device.
*/
continue;
}
/*
* Below Threshold?
* ----------------
* Turn OFF all cooling devices associated with this
* threshold.
*/
for (j = 0; j < active->devices.count; j++)
result = acpi_bus_set_power(active->devices.handles[j],
ACPI_STATE_D3);
}
}
static void acpi_thermal_check(void *data)
{
int result = 0;
struct acpi_thermal *tz = data;
unsigned long sleep_time = 0;
unsigned long timeout_jiffies = 0;
int i = 0;
struct acpi_thermal_state state;
if (!tz) {
printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
return;
}
/* Check if someone else is already running */
if (!mutex_trylock(&tz->lock))
return;
state = tz->state;
result = acpi_thermal_get_temperature(tz);
if (result)
goto unlock;
if (!tz->tz_enabled)
goto unlock;
memset(&tz->state, 0, sizeof(tz->state));
/*
* Check Trip Points
* -----------------
* Compare the current temperature to the trip point values to see
* if we've entered one of the thermal policy states. Note that
* this function determines when a state is entered, but the
* individual policy decides when it is exited (e.g. hysteresis).
*/
if (tz->trips.critical.flags.valid)
state.critical |=
(tz->temperature >= tz->trips.critical.temperature);
if (tz->trips.hot.flags.valid)
state.hot |= (tz->temperature >= tz->trips.hot.temperature);
if (tz->trips.passive.flags.valid)
state.passive |=
(tz->temperature >= tz->trips.passive.temperature);
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
if (tz->trips.active[i].flags.valid)
state.active |=
(tz->temperature >=
tz->trips.active[i].temperature);
/*
* Invoke Policy
* -------------
* Separated from the above check to allow individual policy to
* determine when to exit a given state.
*/
if (state.critical)
acpi_thermal_critical(tz);
if (state.hot)
acpi_thermal_hot(tz);
if (state.passive)
acpi_thermal_passive(tz);
if (state.active)
acpi_thermal_active(tz);
/*
* Calculate State
* ---------------
* Again, separated from the above two to allow independent policy
* decisions.
*/
tz->state.critical = tz->trips.critical.flags.enabled;
tz->state.hot = tz->trips.hot.flags.enabled;
tz->state.passive = tz->trips.passive.flags.enabled;
tz->state.active = 0;
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++)
tz->state.active |= tz->trips.active[i].flags.enabled;
/*
* Calculate Sleep Time
* --------------------
* If we're in the passive state, use _TSP's value. Otherwise
* use the default polling frequency (e.g. _TZP). If no polling
* frequency is specified then we'll wait forever (at least until
* a thermal event occurs). Note that _TSP and _TZD values are
* given in 1/10th seconds (we must covert to milliseconds).
*/
if (tz->state.passive) {
sleep_time = tz->trips.passive.tsp * 100;
timeout_jiffies = jiffies + (HZ * sleep_time) / 1000;
} else if (tz->polling_frequency > 0) {
sleep_time = tz->polling_frequency * 100;
timeout_jiffies = round_jiffies(jiffies + (HZ * sleep_time) / 1000);
}
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n",
tz->name, tz->temperature, sleep_time));
/*
* Schedule Next Poll
* ------------------
*/
if (!sleep_time) {
if (timer_pending(&(tz->timer)))
del_timer(&(tz->timer));
} else {
if (timer_pending(&(tz->timer)))
mod_timer(&(tz->timer), timeout_jiffies);
else {
tz->timer.data = (unsigned long)tz;
tz->timer.function = acpi_thermal_run;
tz->timer.expires = timeout_jiffies;
add_timer(&(tz->timer));
}
}
unlock:
mutex_unlock(&tz->lock);
thermal_zone_device_update(tz->thermal_zone);
}
/* sys I/F for generic thermal sysfs support */
#define KELVIN_TO_MILLICELSIUS(t) (t * 100 - 273200)
static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
static int thermal_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct acpi_thermal *tz = thermal->devdata;
int result;
......@@ -966,25 +596,28 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
if (result)
return result;
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(tz->temperature));
*temp = KELVIN_TO_MILLICELSIUS(tz->temperature);
return 0;
}
static const char enabled[] = "kernel";
static const char disabled[] = "user";
static int thermal_get_mode(struct thermal_zone_device *thermal,
char *buf)
enum thermal_device_mode *mode)
{
struct acpi_thermal *tz = thermal->devdata;
if (!tz)
return -EINVAL;
return sprintf(buf, "%s\n", tz->tz_enabled ?
enabled : disabled);
*mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED :
THERMAL_DEVICE_DISABLED;
return 0;
}
static int thermal_set_mode(struct thermal_zone_device *thermal,
const char *buf)
enum thermal_device_mode mode)
{
struct acpi_thermal *tz = thermal->devdata;
int enable;
......@@ -995,9 +628,9 @@ static int thermal_set_mode(struct thermal_zone_device *thermal,
/*
* enable/disable thermal management from ACPI thermal driver
*/
if (!strncmp(buf, enabled, sizeof enabled - 1))
if (mode == THERMAL_DEVICE_ENABLED)
enable = 1;
else if (!strncmp(buf, disabled, sizeof disabled - 1))
else if (mode == THERMAL_DEVICE_DISABLED)
enable = 0;
else
return -EINVAL;
......@@ -1013,7 +646,7 @@ static int thermal_set_mode(struct thermal_zone_device *thermal,
}
static int thermal_get_trip_type(struct thermal_zone_device *thermal,
int trip, char *buf)
int trip, enum thermal_trip_type *type)
{
struct acpi_thermal *tz = thermal->devdata;
int i;
......@@ -1022,27 +655,35 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
return -EINVAL;
if (tz->trips.critical.flags.valid) {
if (!trip)
return sprintf(buf, "critical\n");
if (!trip) {
*type = THERMAL_TRIP_CRITICAL;
return 0;
}
trip--;
}
if (tz->trips.hot.flags.valid) {
if (!trip)
return sprintf(buf, "hot\n");
if (!trip) {
*type = THERMAL_TRIP_HOT;
return 0;
}
trip--;
}
if (tz->trips.passive.flags.valid) {
if (!trip)
return sprintf(buf, "passive\n");
if (!trip) {
*type = THERMAL_TRIP_PASSIVE;
return 0;
}
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);
if (!trip) {
*type = THERMAL_TRIP_ACTIVE;
return 0;
}
trip--;
}
......@@ -1050,7 +691,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
}
static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
int trip, char *buf)
int trip, unsigned long *temp)
{
struct acpi_thermal *tz = thermal->devdata;
int i;
......@@ -1059,31 +700,39 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
return -EINVAL;
if (tz->trips.critical.flags.valid) {
if (!trip)
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
tz->trips.critical.temperature));
if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS(
tz->trips.critical.temperature);
return 0;
}
trip--;
}
if (tz->trips.hot.flags.valid) {
if (!trip)
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
tz->trips.hot.temperature));
if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS(
tz->trips.hot.temperature);
return 0;
}
trip--;
}
if (tz->trips.passive.flags.valid) {
if (!trip)
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
tz->trips.passive.temperature));
if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS(
tz->trips.passive.temperature);
return 0;
}
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_MILLICELSIUS(
tz->trips.active[i].temperature));
if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS(
tz->trips.active[i].temperature);
return 0;
}
trip--;
}
......@@ -1102,6 +751,29 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
return -EINVAL;
}
static int thermal_notify(struct thermal_zone_device *thermal, int trip,
enum thermal_trip_type trip_type)
{
u8 type = 0;
struct acpi_thermal *tz = thermal->devdata;
if (trip_type == THERMAL_TRIP_CRITICAL)
type = ACPI_THERMAL_NOTIFY_CRITICAL;
else if (trip_type == THERMAL_TRIP_HOT)
type = ACPI_THERMAL_NOTIFY_HOT;
else
return 0;
acpi_bus_generate_proc_event(tz->device, type, 1);
acpi_bus_generate_netlink_event(tz->device->pnp.device_class,
dev_name(&tz->device->dev), type, 1);
if (trip_type == THERMAL_TRIP_CRITICAL && nocrt)
return 1;
return 0;
}
typedef int (*cb)(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
......@@ -1194,6 +866,7 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
.get_trip_type = thermal_get_trip_type,
.get_trip_temp = thermal_get_trip_temp,
.get_crit_temp = thermal_get_crit_temp,
.notify = thermal_notify,
};
static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
......@@ -1214,8 +887,21 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
tz->trips.active[i].flags.valid; i++, trips++);
tz->thermal_zone = thermal_zone_device_register("acpitz",
trips, tz, &acpi_thermal_zone_ops);
if (tz->trips.passive.flags.valid)
tz->thermal_zone =
thermal_zone_device_register("acpitz", trips, tz,
&acpi_thermal_zone_ops,
tz->trips.passive.tc1,
tz->trips.passive.tc2,
tz->trips.passive.tsp*100,
tz->polling_frequency*100);
else
tz->thermal_zone =
thermal_zone_device_register("acpitz", trips, tz,
&acpi_thermal_zone_ops,
0, 0, 0,
tz->polling_frequency);
if (IS_ERR(tz->thermal_zone))
return -ENODEV;
......@@ -1447,13 +1133,13 @@ static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
if (!tz)
goto end;
if (!tz->polling_frequency) {
if (!tz->thermal_zone->polling_delay) {
seq_puts(seq, "<polling disabled>\n");
goto end;
}
seq_printf(seq, "polling frequency: %lu seconds\n",
(tz->polling_frequency / 10));
seq_printf(seq, "polling frequency: %d seconds\n",
(tz->thermal_zone->polling_delay / 1000));
end:
return 0;
......@@ -1683,12 +1369,6 @@ static int acpi_thermal_add(struct acpi_device *device)
if (result)
goto unregister_thermal_zone;
init_timer(&tz->timer);
acpi_thermal_active_off(tz);
acpi_thermal_check(tz);
status = acpi_install_notify_handler(device->handle,
ACPI_DEVICE_NOTIFY,
acpi_thermal_notify, tz);
......@@ -1717,36 +1397,15 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
acpi_status status = AE_OK;
struct acpi_thermal *tz = NULL;
if (!device || !acpi_driver_data(device))
return -EINVAL;
tz = acpi_driver_data(device);
/* avoid timer adding new defer task */
tz->zombie = 1;
/* wait for running timer (on other CPUs) finish */
del_timer_sync(&(tz->timer));
/* synchronize deferred task */
acpi_os_wait_events_complete(NULL);
/* deferred task may reinsert timer */
del_timer_sync(&(tz->timer));
status = acpi_remove_notify_handler(device->handle,
ACPI_DEVICE_NOTIFY,
acpi_thermal_notify);
/* Terminate policy */
if (tz->trips.passive.flags.valid && tz->trips.passive.flags.enabled) {
tz->trips.passive.flags.enabled = 0;
acpi_thermal_passive(tz);
}
if (tz->trips.active[0].flags.valid
&& tz->trips.active[0].flags.enabled) {
tz->trips.active[0].flags.enabled = 0;
acpi_thermal_active(tz);
}
acpi_thermal_remove_fs(device);
acpi_thermal_unregister_thermal_zone(tz);
mutex_destroy(&tz->lock);
......
......@@ -358,32 +358,36 @@ static struct output_properties acpi_output_properties = {
/* thermal cooling device callbacks */
static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned
long *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_video_device *video = acpi_driver_data(device);
return sprintf(buf, "%d\n", video->brightness->count - 3);
*state = video->brightness->count - 3;
return 0;
}
static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
long *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_video_device *video = acpi_driver_data(device);
unsigned long long level;
int state;
int offset;
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);
for (offset = 2; offset < video->brightness->count; offset++)
if (level == video->brightness->levels[offset]) {
*state = video->brightness->count - offset - 1;
return 0;
}
return -EINVAL;
}
static int
video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
video_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_video_device *video = acpi_driver_data(device);
......
......@@ -57,8 +57,8 @@ MODULE_LICENSE("GPL");
* In that case max_cstate would be n-1
* GTHS returning '0' would mean that no bandwidth control states are supported
*/
static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
unsigned long *max_state)
static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
unsigned long *max_state)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
......@@ -83,22 +83,12 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
return 0;
}
static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
char *buf)
{
unsigned long value;
if (memory_get_int_max_bandwidth(cdev, &value))
return -EINVAL;
return sprintf(buf, "%ld\n", value);
}
static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
char *buf)
unsigned long *value)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
unsigned long long value;
unsigned long long result;
struct acpi_object_list arg_list;
union acpi_object arg;
acpi_status status = AE_OK;
......@@ -108,15 +98,16 @@ static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
&arg_list, &value);
&arg_list, &result);
if (ACPI_FAILURE(status))
return -EFAULT;
return sprintf(buf, "%llu\n", value);
*value = result;
return 0;
}
static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
unsigned int state)
unsigned long state)
{
struct acpi_device *device = cdev->devdata;
acpi_handle handle = device->handle;
......@@ -126,7 +117,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
unsigned long long temp;
unsigned long max_state;
if (memory_get_int_max_bandwidth(cdev, &max_state))
if (memory_get_max_bandwidth(cdev, &max_state))
return -EFAULT;
if (state > max_state)
......@@ -142,7 +133,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
&temp);
printk(KERN_INFO
"Bandwidth value was %d: status is %d\n", state, status);
"Bandwidth value was %ld: status is %d\n", state, status);
if (ACPI_FAILURE(status))
return -EFAULT;
......
......@@ -30,6 +30,7 @@
#include <linux/idr.h>
#include <linux/thermal.h>
#include <linux/spinlock.h>
#include <linux/reboot.h>
MODULE_AUTHOR("Zhang Rui");
MODULE_DESCRIPTION("Generic thermal management sysfs support");
......@@ -104,22 +105,36 @@ static ssize_t
temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
long temperature;
int ret;
if (!tz->ops->get_temp)
return -EPERM;
return tz->ops->get_temp(tz, buf);
ret = tz->ops->get_temp(tz, &temperature);
if (ret)
return ret;
return sprintf(buf, "%ld\n", temperature);
}
static ssize_t
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
enum thermal_device_mode mode;
int result;
if (!tz->ops->get_mode)
return -EPERM;
return tz->ops->get_mode(tz, buf);
result = tz->ops->get_mode(tz, &mode);
if (result)
return result;
return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled"
: "disabled");
}
static ssize_t
......@@ -132,7 +147,13 @@ mode_store(struct device *dev, struct device_attribute *attr,
if (!tz->ops->set_mode)
return -EPERM;
result = tz->ops->set_mode(tz, buf);
if (!strncmp(buf, "enabled", sizeof("enabled")))
result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
else if (!strncmp(buf, "disabled", sizeof("disabled")))
result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
else
result = -EINVAL;
if (result)
return result;
......@@ -144,7 +165,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int trip;
enum thermal_trip_type type;
int trip, result;
if (!tz->ops->get_trip_type)
return -EPERM;
......@@ -152,7 +174,22 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
return -EINVAL;
return tz->ops->get_trip_type(tz, trip, buf);
result = tz->ops->get_trip_type(tz, trip, &type);
if (result)
return result;
switch (type) {
case THERMAL_TRIP_CRITICAL:
return sprintf(buf, "critical");
case THERMAL_TRIP_HOT:
return sprintf(buf, "hot");
case THERMAL_TRIP_PASSIVE:
return sprintf(buf, "passive");
case THERMAL_TRIP_ACTIVE:
return sprintf(buf, "active");
default:
return sprintf(buf, "unknown");
}
}
static ssize_t
......@@ -160,7 +197,8 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int trip;
int trip, ret;
long temperature;
if (!tz->ops->get_trip_temp)
return -EPERM;
......@@ -168,12 +206,77 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
return -EINVAL;
return tz->ops->get_trip_temp(tz, trip, buf);
ret = tz->ops->get_trip_temp(tz, trip, &temperature);
if (ret)
return ret;
return sprintf(buf, "%ld\n", temperature);
}
static ssize_t
passive_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
struct thermal_cooling_device *cdev = NULL;
int state;
if (!sscanf(buf, "%d\n", &state))
return -EINVAL;
if (state && !tz->forced_passive) {
mutex_lock(&thermal_list_lock);
list_for_each_entry(cdev, &thermal_cdev_list, node) {
if (!strncmp("Processor", cdev->type,
sizeof("Processor")))
thermal_zone_bind_cooling_device(tz,
THERMAL_TRIPS_NONE,
cdev);
}
mutex_unlock(&thermal_list_lock);
} else if (!state && tz->forced_passive) {
mutex_lock(&thermal_list_lock);
list_for_each_entry(cdev, &thermal_cdev_list, node) {
if (!strncmp("Processor", cdev->type,
sizeof("Processor")))
thermal_zone_unbind_cooling_device(tz,
THERMAL_TRIPS_NONE,
cdev);
}
mutex_unlock(&thermal_list_lock);
}
tz->tc1 = 1;
tz->tc2 = 1;
if (!tz->passive_delay)
tz->passive_delay = 1000;
if (!tz->polling_delay)
tz->polling_delay = 10000;
tz->forced_passive = state;
thermal_zone_device_update(tz);
return count;
}
static ssize_t
passive_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
return sprintf(buf, "%d\n", tz->forced_passive);
}
static DEVICE_ATTR(type, 0444, type_show, NULL);
static DEVICE_ATTR(temp, 0444, temp_show, NULL);
static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, \
passive_store);
static struct device_attribute trip_point_attrs[] = {
__ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
......@@ -236,8 +339,13 @@ thermal_cooling_device_max_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
unsigned long state;
int ret;
return cdev->ops->get_max_state(cdev, buf);
ret = cdev->ops->get_max_state(cdev, &state);
if (ret)
return ret;
return sprintf(buf, "%ld\n", state);
}
static ssize_t
......@@ -245,8 +353,13 @@ thermal_cooling_device_cur_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
unsigned long state;
int ret;
return cdev->ops->get_cur_state(cdev, buf);
ret = cdev->ops->get_cur_state(cdev, &state);
if (ret)
return ret;
return sprintf(buf, "%ld\n", state);
}
static ssize_t
......@@ -255,10 +368,10 @@ thermal_cooling_device_cur_state_store(struct device *dev,
const char *buf, size_t count)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
int state;
unsigned long state;
int result;
if (!sscanf(buf, "%d\n", &state))
if (!sscanf(buf, "%ld\n", &state))
return -EINVAL;
if (state < 0)
......@@ -312,13 +425,20 @@ static DEVICE_ATTR(name, 0444, name_show, NULL);
static ssize_t
temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
{
long temperature;
int ret;
struct thermal_hwmon_attr *hwmon_attr
= container_of(attr, struct thermal_hwmon_attr, attr);
struct thermal_zone_device *tz
= container_of(hwmon_attr, struct thermal_zone_device,
temp_input);
return tz->ops->get_temp(tz, buf);
ret = tz->ops->get_temp(tz, &temperature);
if (ret)
return ret;
return sprintf(buf, "%ld\n", temperature);
}
static ssize_t
......@@ -330,8 +450,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
struct thermal_zone_device *tz
= container_of(hwmon_attr, struct thermal_zone_device,
temp_crit);
long temperature;
int ret;
ret = tz->ops->get_trip_temp(tz, 0, &temperature);
if (ret)
return ret;
return tz->ops->get_trip_temp(tz, 0, buf);
return sprintf(buf, "%ld\n", temperature);
}
......@@ -452,6 +578,97 @@ thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
}
#endif
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
int delay)
{
cancel_delayed_work(&(tz->poll_queue));
if (!delay)
return;
if (delay > 1000)
schedule_delayed_work(&(tz->poll_queue),
round_jiffies(msecs_to_jiffies(delay)));
else
schedule_delayed_work(&(tz->poll_queue),
msecs_to_jiffies(delay));
}
static void thermal_zone_device_passive(struct thermal_zone_device *tz,
int temp, int trip_temp, int trip)
{
int trend = 0;
struct thermal_cooling_device_instance *instance;
struct thermal_cooling_device *cdev;
long state, max_state;
/*
* Above Trip?
* -----------
* Calculate the thermal trend (using the passive cooling equation)
* and modify the performance limit for all passive cooling devices
* accordingly. Note that we assume symmetry.
*/
if (temp >= trip_temp) {
tz->passive = true;
trend = (tz->tc1 * (temp - tz->last_temperature)) +
(tz->tc2 * (temp - trip_temp));
/* Heating up? */
if (trend > 0) {
list_for_each_entry(instance, &tz->cooling_devices,
node) {
if (instance->trip != trip)
continue;
cdev = instance->cdev;
cdev->ops->get_cur_state(cdev, &state);
cdev->ops->get_max_state(cdev, &max_state);
if (state++ < max_state)
cdev->ops->set_cur_state(cdev, state);
}
} else if (trend < 0) { /* Cooling off? */
list_for_each_entry(instance, &tz->cooling_devices,
node) {
if (instance->trip != trip)
continue;
cdev = instance->cdev;
cdev->ops->get_cur_state(cdev, &state);
cdev->ops->get_max_state(cdev, &max_state);
if (state > 0)
cdev->ops->set_cur_state(cdev, --state);
}
}
return;
}
/*
* Below Trip?
* -----------
* Implement passive cooling hysteresis to slowly increase performance
* and avoid thrashing around the passive trip point. Note that we
* assume symmetry.
*/
list_for_each_entry(instance, &tz->cooling_devices, node) {
if (instance->trip != trip)
continue;
cdev = instance->cdev;
cdev->ops->get_cur_state(cdev, &state);
cdev->ops->get_max_state(cdev, &max_state);
if (state > 0)
cdev->ops->set_cur_state(cdev, --state);
if (state == 0)
tz->passive = false;
}
}
static void thermal_zone_device_check(struct work_struct *work)
{
struct thermal_zone_device *tz = container_of(work, struct
thermal_zone_device,
poll_queue.work);
thermal_zone_device_update(tz);
}
/**
* thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone
......@@ -721,26 +938,114 @@ void thermal_cooling_device_unregister(struct
EXPORT_SYMBOL(thermal_cooling_device_unregister);
/**
* thermal_zone_device_update - force an update of a thermal zone's state
* @ttz: the thermal zone to update
*/
void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count, ret = 0;
long temp, trip_temp;
enum thermal_trip_type trip_type;
struct thermal_cooling_device_instance *instance;
struct thermal_cooling_device *cdev;
mutex_lock(&tz->lock);
tz->ops->get_temp(tz, &temp);
for (count = 0; count < tz->trips; count++) {
tz->ops->get_trip_type(tz, count, &trip_type);
tz->ops->get_trip_temp(tz, count, &trip_temp);
switch (trip_type) {
case THERMAL_TRIP_CRITICAL:
if (temp > trip_temp) {
if (tz->ops->notify)
ret = tz->ops->notify(tz, count,
trip_type);
if (!ret) {
printk(KERN_EMERG
"Critical temperature reached (%ld C), shutting down.\n",
temp/1000);
orderly_poweroff(true);
}
}
break;
case THERMAL_TRIP_HOT:
if (temp > trip_temp)
if (tz->ops->notify)
tz->ops->notify(tz, count, trip_type);
break;
case THERMAL_TRIP_ACTIVE:
list_for_each_entry(instance, &tz->cooling_devices,
node) {
if (instance->trip != count)
continue;
cdev = instance->cdev;
if (temp > trip_temp)
cdev->ops->set_cur_state(cdev, 1);
else
cdev->ops->set_cur_state(cdev, 0);
}
break;
case THERMAL_TRIP_PASSIVE:
if (temp > trip_temp || tz->passive)
thermal_zone_device_passive(tz, temp,
trip_temp, count);
break;
}
}
if (tz->forced_passive)
thermal_zone_device_passive(tz, temp, tz->forced_passive,
THERMAL_TRIPS_NONE);
tz->last_temperature = temp;
if (tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay);
else if (tz->polling_delay)
thermal_zone_device_set_polling(tz, tz->polling_delay);
mutex_unlock(&tz->lock);
}
EXPORT_SYMBOL(thermal_zone_device_update);
/**
* thermal_zone_device_register - register a new thermal zone device
* @type: the thermal zone device type
* @trips: the number of trip points the thermal zone support
* @devdata: private device data
* @ops: standard thermal zone device callbacks
* @tc1: thermal coefficient 1 for passive calculations
* @tc2: thermal coefficient 2 for passive calculations
* @passive_delay: number of milliseconds to wait between polls when
* performing passive cooling
* @polling_delay: number of milliseconds to wait between polls when checking
* whether trip points have been crossed (0 for interrupt
* driven systems)
*
* thermal_zone_device_unregister() must be called when the device is no
* longer needed.
* longer needed. The passive cooling formula uses tc1 and tc2 as described in
* section 11.1.5.1 of the ACPI specification 3.0.
*/
struct thermal_zone_device *thermal_zone_device_register(char *type,
int trips,
void *devdata, struct
thermal_zone_device_ops
*ops)
*ops, int tc1, int
tc2,
int passive_delay,
int polling_delay)
{
struct thermal_zone_device *tz;
struct thermal_cooling_device *pos;
enum thermal_trip_type trip_type;
int result;
int count;
int passive = 0;
if (strlen(type) >= THERMAL_NAME_LENGTH)
return ERR_PTR(-EINVAL);
......@@ -769,6 +1074,11 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
tz->device.class = &thermal_class;
tz->devdata = devdata;
tz->trips = trips;
tz->tc1 = tc1;
tz->tc2 = tc2;
tz->passive_delay = passive_delay;
tz->polling_delay = polling_delay;
dev_set_name(&tz->device, "thermal_zone%d", tz->id);
result = device_register(&tz->device);
if (result) {
......@@ -798,8 +1108,18 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
TRIP_POINT_ATTR_ADD(&tz->device, count, result);
if (result)
goto unregister;
tz->ops->get_trip_type(tz, count, &trip_type);
if (trip_type == THERMAL_TRIP_PASSIVE)
passive = 1;
}
if (!passive)
result = device_create_file(&tz->device,
&dev_attr_passive);
if (result)
goto unregister;
result = thermal_add_hwmon_sysfs(tz);
if (result)
goto unregister;
......@@ -814,6 +1134,10 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
}
mutex_unlock(&thermal_list_lock);
INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
thermal_zone_device_update(tz);
if (!result)
return tz;
......@@ -853,6 +1177,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
tz->ops->unbind(tz, cdev);
mutex_unlock(&thermal_list_lock);
thermal_zone_device_set_polling(tz, 0);
if (tz->type[0])
device_remove_file(&tz->device, &dev_attr_type);
device_remove_file(&tz->device, &dev_attr_temp);
......
......@@ -27,27 +27,46 @@
#include <linux/idr.h>
#include <linux/device.h>
#include <linux/workqueue.h>
struct thermal_zone_device;
struct thermal_cooling_device;
enum thermal_device_mode {
THERMAL_DEVICE_DISABLED = 0,
THERMAL_DEVICE_ENABLED,
};
enum thermal_trip_type {
THERMAL_TRIP_ACTIVE = 0,
THERMAL_TRIP_PASSIVE,
THERMAL_TRIP_HOT,
THERMAL_TRIP_CRITICAL,
};
struct thermal_zone_device_ops {
int (*bind) (struct thermal_zone_device *,
struct thermal_cooling_device *);
int (*unbind) (struct thermal_zone_device *,
struct thermal_cooling_device *);
int (*get_temp) (struct thermal_zone_device *, char *);
int (*get_mode) (struct thermal_zone_device *, char *);
int (*set_mode) (struct thermal_zone_device *, const char *);
int (*get_trip_type) (struct thermal_zone_device *, int, char *);
int (*get_trip_temp) (struct thermal_zone_device *, int, char *);
int (*get_temp) (struct thermal_zone_device *, unsigned long *);
int (*get_mode) (struct thermal_zone_device *,
enum thermal_device_mode *);
int (*set_mode) (struct thermal_zone_device *,
enum thermal_device_mode);
int (*get_trip_type) (struct thermal_zone_device *, int,
enum thermal_trip_type *);
int (*get_trip_temp) (struct thermal_zone_device *, int,
unsigned long *);
int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *);
int (*notify) (struct thermal_zone_device *, int,
enum thermal_trip_type);
};
struct thermal_cooling_device_ops {
int (*get_max_state) (struct thermal_cooling_device *, char *);
int (*get_cur_state) (struct thermal_cooling_device *, char *);
int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
};
#define THERMAL_TRIPS_NONE -1
......@@ -88,11 +107,19 @@ struct thermal_zone_device {
struct device device;
void *devdata;
int trips;
int tc1;
int tc2;
int passive_delay;
int polling_delay;
int last_temperature;
bool passive;
unsigned int forced_passive;
struct thermal_zone_device_ops *ops;
struct list_head cooling_devices;
struct idr idr;
struct mutex lock; /* protect cooling devices list */
struct list_head node;
struct delayed_work poll_queue;
#if defined(CONFIG_THERMAL_HWMON)
struct list_head hwmon_node;
struct thermal_hwmon_device *hwmon;
......@@ -104,13 +131,16 @@ struct thermal_zone_device {
struct thermal_zone_device *thermal_zone_device_register(char *, int, void *,
struct
thermal_zone_device_ops
*);
*, int tc1, int tc2,
int passive_freq,
int polling_freq);
void thermal_zone_device_unregister(struct thermal_zone_device *);
int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
struct thermal_cooling_device *);
void thermal_zone_device_update(struct thermal_zone_device *);
struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
struct
thermal_cooling_device_ops
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment