Merge branch 'thermal' into release
This commit is contained in:
commit
4f3bff70a6
7 changed files with 525 additions and 509 deletions
|
@ -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;
|
||||
|
||||
return tz->ops->get_trip_temp(tz, 0, buf);
|
||||
ret = tz->ops->get_trip_temp(tz, 0, &temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue