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 */
|
/* 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 */
|
/* 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;
|
struct acpi_device *device = cdev->devdata;
|
||||||
int state;
|
|
||||||
int result;
|
int result;
|
||||||
|
int acpi_state;
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
result = acpi_bus_get_power(device->handle, &state);
|
result = acpi_bus_get_power(device->handle, &acpi_state);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
|
*state = (acpi_state == ACPI_STATE_D3 ? 0 :
|
||||||
(state == ACPI_STATE_D0 ? "1" : "unknown"));
|
(acpi_state == ACPI_STATE_D0 ? 1 : -1));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
struct acpi_device *device = cdev->devdata;
|
||||||
int result;
|
int result;
|
||||||
|
|
|
@ -373,7 +373,8 @@ static int acpi_processor_max_state(struct acpi_processor *pr)
|
||||||
return max_state;
|
return max_state;
|
||||||
}
|
}
|
||||||
static int
|
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_device *device = cdev->devdata;
|
||||||
struct acpi_processor *pr = acpi_driver_data(device);
|
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)
|
if (!device || !pr)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", acpi_processor_max_state(pr));
|
*state = acpi_processor_max_state(pr);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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_device *device = cdev->devdata;
|
||||||
struct acpi_processor *pr = acpi_driver_data(device);
|
struct acpi_processor *pr = acpi_driver_data(device);
|
||||||
int cur_state;
|
|
||||||
|
|
||||||
if (!device || !pr)
|
if (!device || !pr)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
cur_state = cpufreq_get_cur_state(pr->id);
|
*cur_state = cpufreq_get_cur_state(pr->id);
|
||||||
if (pr->flags.throttling)
|
if (pr->flags.throttling)
|
||||||
cur_state += pr->throttling.state;
|
*cur_state += pr->throttling.state;
|
||||||
|
return 0;
|
||||||
return sprintf(buf, "%d\n", cur_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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_device *device = cdev->devdata;
|
||||||
struct acpi_processor *pr = acpi_driver_data(device);
|
struct acpi_processor *pr = acpi_driver_data(device);
|
||||||
|
|
|
@ -37,11 +37,11 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
#include <linux/timer.h>
|
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kmod.h>
|
#include <linux/kmod.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
|
#include <linux/device.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
#include <acpi/acpi_bus.h>
|
#include <acpi/acpi_bus.h>
|
||||||
|
@ -190,7 +190,6 @@ struct acpi_thermal {
|
||||||
struct acpi_thermal_state state;
|
struct acpi_thermal_state state;
|
||||||
struct acpi_thermal_trips trips;
|
struct acpi_thermal_trips trips;
|
||||||
struct acpi_handle_list devices;
|
struct acpi_handle_list devices;
|
||||||
struct timer_list timer;
|
|
||||||
struct thermal_zone_device *thermal_zone;
|
struct thermal_zone_device *thermal_zone;
|
||||||
int tz_enabled;
|
int tz_enabled;
|
||||||
struct mutex lock;
|
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->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,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
"Polling frequency set to %lu seconds\n",
|
"Polling frequency set to %lu seconds\n",
|
||||||
tz->polling_frequency/10));
|
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);
|
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)
|
static void acpi_thermal_check(void *data)
|
||||||
{
|
{
|
||||||
int result = 0;
|
|
||||||
struct acpi_thermal *tz = data;
|
struct acpi_thermal *tz = data;
|
||||||
unsigned long sleep_time = 0;
|
|
||||||
unsigned long timeout_jiffies = 0;
|
|
||||||
int i = 0;
|
|
||||||
struct acpi_thermal_state state;
|
|
||||||
|
|
||||||
|
thermal_zone_device_update(tz->thermal_zone);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sys I/F for generic thermal sysfs support */
|
/* sys I/F for generic thermal sysfs support */
|
||||||
#define KELVIN_TO_MILLICELSIUS(t) (t * 100 - 273200)
|
#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;
|
struct acpi_thermal *tz = thermal->devdata;
|
||||||
int result;
|
int result;
|
||||||
|
@ -966,25 +596,28 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf)
|
||||||
if (result)
|
if (result)
|
||||||
return 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 enabled[] = "kernel";
|
||||||
static const char disabled[] = "user";
|
static const char disabled[] = "user";
|
||||||
static int thermal_get_mode(struct thermal_zone_device *thermal,
|
static int thermal_get_mode(struct thermal_zone_device *thermal,
|
||||||
char *buf)
|
enum thermal_device_mode *mode)
|
||||||
{
|
{
|
||||||
struct acpi_thermal *tz = thermal->devdata;
|
struct acpi_thermal *tz = thermal->devdata;
|
||||||
|
|
||||||
if (!tz)
|
if (!tz)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", tz->tz_enabled ?
|
*mode = tz->tz_enabled ? THERMAL_DEVICE_ENABLED :
|
||||||
enabled : disabled);
|
THERMAL_DEVICE_DISABLED;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int thermal_set_mode(struct thermal_zone_device *thermal,
|
static int thermal_set_mode(struct thermal_zone_device *thermal,
|
||||||
const char *buf)
|
enum thermal_device_mode mode)
|
||||||
{
|
{
|
||||||
struct acpi_thermal *tz = thermal->devdata;
|
struct acpi_thermal *tz = thermal->devdata;
|
||||||
int enable;
|
int enable;
|
||||||
|
@ -995,9 +628,9 @@ static int thermal_set_mode(struct thermal_zone_device *thermal,
|
||||||
/*
|
/*
|
||||||
* enable/disable thermal management from ACPI thermal driver
|
* enable/disable thermal management from ACPI thermal driver
|
||||||
*/
|
*/
|
||||||
if (!strncmp(buf, enabled, sizeof enabled - 1))
|
if (mode == THERMAL_DEVICE_ENABLED)
|
||||||
enable = 1;
|
enable = 1;
|
||||||
else if (!strncmp(buf, disabled, sizeof disabled - 1))
|
else if (mode == THERMAL_DEVICE_DISABLED)
|
||||||
enable = 0;
|
enable = 0;
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
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,
|
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;
|
struct acpi_thermal *tz = thermal->devdata;
|
||||||
int i;
|
int i;
|
||||||
|
@ -1022,27 +655,35 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (tz->trips.critical.flags.valid) {
|
if (tz->trips.critical.flags.valid) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "critical\n");
|
*type = THERMAL_TRIP_CRITICAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tz->trips.hot.flags.valid) {
|
if (tz->trips.hot.flags.valid) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "hot\n");
|
*type = THERMAL_TRIP_HOT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tz->trips.passive.flags.valid) {
|
if (tz->trips.passive.flags.valid) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "passive\n");
|
*type = THERMAL_TRIP_PASSIVE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
|
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
|
||||||
tz->trips.active[i].flags.valid; i++) {
|
tz->trips.active[i].flags.valid; i++) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "active%d\n", i);
|
*type = THERMAL_TRIP_ACTIVE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
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,
|
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;
|
struct acpi_thermal *tz = thermal->devdata;
|
||||||
int i;
|
int i;
|
||||||
|
@ -1059,31 +700,39 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (tz->trips.critical.flags.valid) {
|
if (tz->trips.critical.flags.valid) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
|
*temp = KELVIN_TO_MILLICELSIUS(
|
||||||
tz->trips.critical.temperature));
|
tz->trips.critical.temperature);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tz->trips.hot.flags.valid) {
|
if (tz->trips.hot.flags.valid) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
|
*temp = KELVIN_TO_MILLICELSIUS(
|
||||||
tz->trips.hot.temperature));
|
tz->trips.hot.temperature);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tz->trips.passive.flags.valid) {
|
if (tz->trips.passive.flags.valid) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
|
*temp = KELVIN_TO_MILLICELSIUS(
|
||||||
tz->trips.passive.temperature));
|
tz->trips.passive.temperature);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
|
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
|
||||||
tz->trips.active[i].flags.valid; i++) {
|
tz->trips.active[i].flags.valid; i++) {
|
||||||
if (!trip)
|
if (!trip) {
|
||||||
return sprintf(buf, "%ld\n", KELVIN_TO_MILLICELSIUS(
|
*temp = KELVIN_TO_MILLICELSIUS(
|
||||||
tz->trips.active[i].temperature));
|
tz->trips.active[i].temperature);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
trip--;
|
trip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1102,6 +751,29 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
|
||||||
return -EINVAL;
|
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,
|
typedef int (*cb)(struct thermal_zone_device *, int,
|
||||||
struct thermal_cooling_device *);
|
struct thermal_cooling_device *);
|
||||||
static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal,
|
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_type = thermal_get_trip_type,
|
||||||
.get_trip_temp = thermal_get_trip_temp,
|
.get_trip_temp = thermal_get_trip_temp,
|
||||||
.get_crit_temp = thermal_get_crit_temp,
|
.get_crit_temp = thermal_get_crit_temp,
|
||||||
|
.notify = thermal_notify,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
|
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 &&
|
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
|
||||||
tz->trips.active[i].flags.valid; i++, trips++);
|
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))
|
if (IS_ERR(tz->thermal_zone))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
@ -1447,13 +1133,13 @@ static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset)
|
||||||
if (!tz)
|
if (!tz)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
if (!tz->polling_frequency) {
|
if (!tz->thermal_zone->polling_delay) {
|
||||||
seq_puts(seq, "<polling disabled>\n");
|
seq_puts(seq, "<polling disabled>\n");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
seq_printf(seq, "polling frequency: %lu seconds\n",
|
seq_printf(seq, "polling frequency: %d seconds\n",
|
||||||
(tz->polling_frequency / 10));
|
(tz->thermal_zone->polling_delay / 1000));
|
||||||
|
|
||||||
end:
|
end:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1683,12 +1369,6 @@ static int acpi_thermal_add(struct acpi_device *device)
|
||||||
if (result)
|
if (result)
|
||||||
goto unregister_thermal_zone;
|
goto unregister_thermal_zone;
|
||||||
|
|
||||||
init_timer(&tz->timer);
|
|
||||||
|
|
||||||
acpi_thermal_active_off(tz);
|
|
||||||
|
|
||||||
acpi_thermal_check(tz);
|
|
||||||
|
|
||||||
status = acpi_install_notify_handler(device->handle,
|
status = acpi_install_notify_handler(device->handle,
|
||||||
ACPI_DEVICE_NOTIFY,
|
ACPI_DEVICE_NOTIFY,
|
||||||
acpi_thermal_notify, tz);
|
acpi_thermal_notify, tz);
|
||||||
|
@ -1717,36 +1397,15 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
|
||||||
acpi_status status = AE_OK;
|
acpi_status status = AE_OK;
|
||||||
struct acpi_thermal *tz = NULL;
|
struct acpi_thermal *tz = NULL;
|
||||||
|
|
||||||
|
|
||||||
if (!device || !acpi_driver_data(device))
|
if (!device || !acpi_driver_data(device))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
tz = acpi_driver_data(device);
|
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,
|
status = acpi_remove_notify_handler(device->handle,
|
||||||
ACPI_DEVICE_NOTIFY,
|
ACPI_DEVICE_NOTIFY,
|
||||||
acpi_thermal_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_remove_fs(device);
|
||||||
acpi_thermal_unregister_thermal_zone(tz);
|
acpi_thermal_unregister_thermal_zone(tz);
|
||||||
mutex_destroy(&tz->lock);
|
mutex_destroy(&tz->lock);
|
||||||
|
|
|
@ -358,32 +358,36 @@ static struct output_properties acpi_output_properties = {
|
||||||
|
|
||||||
|
|
||||||
/* thermal cooling device callbacks */
|
/* 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_device *device = cdev->devdata;
|
||||||
struct acpi_video_device *video = acpi_driver_data(device);
|
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_device *device = cdev->devdata;
|
||||||
struct acpi_video_device *video = acpi_driver_data(device);
|
struct acpi_video_device *video = acpi_driver_data(device);
|
||||||
unsigned long long level;
|
unsigned long long level;
|
||||||
int state;
|
int offset;
|
||||||
|
|
||||||
acpi_video_device_lcd_get_level_current(video, &level);
|
acpi_video_device_lcd_get_level_current(video, &level);
|
||||||
for (state = 2; state < video->brightness->count; state++)
|
for (offset = 2; offset < video->brightness->count; offset++)
|
||||||
if (level == video->brightness->levels[state])
|
if (level == video->brightness->levels[offset]) {
|
||||||
return sprintf(buf, "%d\n",
|
*state = video->brightness->count - offset - 1;
|
||||||
video->brightness->count - state - 1);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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_device *device = cdev->devdata;
|
||||||
struct acpi_video_device *video = acpi_driver_data(device);
|
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
|
* In that case max_cstate would be n-1
|
||||||
* GTHS returning '0' would mean that no bandwidth control states are supported
|
* GTHS returning '0' would mean that no bandwidth control states are supported
|
||||||
*/
|
*/
|
||||||
static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
|
static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
|
||||||
unsigned long *max_state)
|
unsigned long *max_state)
|
||||||
{
|
{
|
||||||
struct acpi_device *device = cdev->devdata;
|
struct acpi_device *device = cdev->devdata;
|
||||||
acpi_handle handle = device->handle;
|
acpi_handle handle = device->handle;
|
||||||
|
@ -83,22 +83,12 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
|
||||||
return 0;
|
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,
|
static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
|
||||||
char *buf)
|
unsigned long *value)
|
||||||
{
|
{
|
||||||
struct acpi_device *device = cdev->devdata;
|
struct acpi_device *device = cdev->devdata;
|
||||||
acpi_handle handle = device->handle;
|
acpi_handle handle = device->handle;
|
||||||
unsigned long long value;
|
unsigned long long result;
|
||||||
struct acpi_object_list arg_list;
|
struct acpi_object_list arg_list;
|
||||||
union acpi_object arg;
|
union acpi_object arg;
|
||||||
acpi_status status = AE_OK;
|
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.type = ACPI_TYPE_INTEGER;
|
||||||
arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
|
arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
|
||||||
status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
|
status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
|
||||||
&arg_list, &value);
|
&arg_list, &result);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
return sprintf(buf, "%llu\n", value);
|
*value = result;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
|
static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
|
||||||
unsigned int state)
|
unsigned long state)
|
||||||
{
|
{
|
||||||
struct acpi_device *device = cdev->devdata;
|
struct acpi_device *device = cdev->devdata;
|
||||||
acpi_handle handle = device->handle;
|
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 long temp;
|
||||||
unsigned long max_state;
|
unsigned long max_state;
|
||||||
|
|
||||||
if (memory_get_int_max_bandwidth(cdev, &max_state))
|
if (memory_get_max_bandwidth(cdev, &max_state))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (state > max_state)
|
if (state > max_state)
|
||||||
|
@ -142,7 +133,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
|
||||||
&temp);
|
&temp);
|
||||||
|
|
||||||
printk(KERN_INFO
|
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))
|
if (ACPI_FAILURE(status))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/thermal.h>
|
#include <linux/thermal.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
|
||||||
MODULE_AUTHOR("Zhang Rui");
|
MODULE_AUTHOR("Zhang Rui");
|
||||||
MODULE_DESCRIPTION("Generic thermal management sysfs support");
|
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)
|
temp_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||||
|
long temperature;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!tz->ops->get_temp)
|
if (!tz->ops->get_temp)
|
||||||
return -EPERM;
|
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
|
static ssize_t
|
||||||
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
mode_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||||
|
enum thermal_device_mode mode;
|
||||||
|
int result;
|
||||||
|
|
||||||
if (!tz->ops->get_mode)
|
if (!tz->ops->get_mode)
|
||||||
return -EPERM;
|
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
|
static ssize_t
|
||||||
|
@ -132,7 +147,13 @@ mode_store(struct device *dev, struct device_attribute *attr,
|
||||||
if (!tz->ops->set_mode)
|
if (!tz->ops->set_mode)
|
||||||
return -EPERM;
|
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)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
@ -144,7 +165,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
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)
|
if (!tz->ops->get_trip_type)
|
||||||
return -EPERM;
|
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))
|
if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
|
||||||
return -EINVAL;
|
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
|
static ssize_t
|
||||||
|
@ -160,7 +197,8 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
struct thermal_zone_device *tz = to_thermal_zone(dev);
|
||||||
int trip;
|
int trip, ret;
|
||||||
|
long temperature;
|
||||||
|
|
||||||
if (!tz->ops->get_trip_temp)
|
if (!tz->ops->get_trip_temp)
|
||||||
return -EPERM;
|
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))
|
if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
|
||||||
return -EINVAL;
|
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(type, 0444, type_show, NULL);
|
||||||
static DEVICE_ATTR(temp, 0444, temp_show, NULL);
|
static DEVICE_ATTR(temp, 0444, temp_show, NULL);
|
||||||
static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
|
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[] = {
|
static struct device_attribute trip_point_attrs[] = {
|
||||||
__ATTR(trip_point_0_type, 0444, trip_point_type_show, NULL),
|
__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 device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
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
|
static ssize_t
|
||||||
|
@ -245,8 +353,13 @@ thermal_cooling_device_cur_state_show(struct device *dev,
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
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
|
static ssize_t
|
||||||
|
@ -255,10 +368,10 @@ thermal_cooling_device_cur_state_store(struct device *dev,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||||
int state;
|
unsigned long state;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (!sscanf(buf, "%d\n", &state))
|
if (!sscanf(buf, "%ld\n", &state))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (state < 0)
|
if (state < 0)
|
||||||
|
@ -312,13 +425,20 @@ static DEVICE_ATTR(name, 0444, name_show, NULL);
|
||||||
static ssize_t
|
static ssize_t
|
||||||
temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
|
temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
|
long temperature;
|
||||||
|
int ret;
|
||||||
struct thermal_hwmon_attr *hwmon_attr
|
struct thermal_hwmon_attr *hwmon_attr
|
||||||
= container_of(attr, struct thermal_hwmon_attr, attr);
|
= container_of(attr, struct thermal_hwmon_attr, attr);
|
||||||
struct thermal_zone_device *tz
|
struct thermal_zone_device *tz
|
||||||
= container_of(hwmon_attr, struct thermal_zone_device,
|
= container_of(hwmon_attr, struct thermal_zone_device,
|
||||||
temp_input);
|
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
|
static ssize_t
|
||||||
|
@ -330,8 +450,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
|
||||||
struct thermal_zone_device *tz
|
struct thermal_zone_device *tz
|
||||||
= container_of(hwmon_attr, struct thermal_zone_device,
|
= container_of(hwmon_attr, struct thermal_zone_device,
|
||||||
temp_crit);
|
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
|
#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
|
* 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);
|
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
|
* thermal_zone_device_register - register a new thermal zone device
|
||||||
* @type: the thermal zone device type
|
* @type: the thermal zone device type
|
||||||
* @trips: the number of trip points the thermal zone support
|
* @trips: the number of trip points the thermal zone support
|
||||||
* @devdata: private device data
|
* @devdata: private device data
|
||||||
* @ops: standard thermal zone device callbacks
|
* @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
|
* 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,
|
struct thermal_zone_device *thermal_zone_device_register(char *type,
|
||||||
int trips,
|
int trips,
|
||||||
void *devdata, struct
|
void *devdata, struct
|
||||||
thermal_zone_device_ops
|
thermal_zone_device_ops
|
||||||
*ops)
|
*ops, int tc1, int
|
||||||
|
tc2,
|
||||||
|
int passive_delay,
|
||||||
|
int polling_delay)
|
||||||
{
|
{
|
||||||
struct thermal_zone_device *tz;
|
struct thermal_zone_device *tz;
|
||||||
struct thermal_cooling_device *pos;
|
struct thermal_cooling_device *pos;
|
||||||
|
enum thermal_trip_type trip_type;
|
||||||
int result;
|
int result;
|
||||||
int count;
|
int count;
|
||||||
|
int passive = 0;
|
||||||
|
|
||||||
if (strlen(type) >= THERMAL_NAME_LENGTH)
|
if (strlen(type) >= THERMAL_NAME_LENGTH)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
@ -769,6 +1074,11 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
|
||||||
tz->device.class = &thermal_class;
|
tz->device.class = &thermal_class;
|
||||||
tz->devdata = devdata;
|
tz->devdata = devdata;
|
||||||
tz->trips = trips;
|
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);
|
dev_set_name(&tz->device, "thermal_zone%d", tz->id);
|
||||||
result = device_register(&tz->device);
|
result = device_register(&tz->device);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -798,8 +1108,18 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
|
||||||
TRIP_POINT_ATTR_ADD(&tz->device, count, result);
|
TRIP_POINT_ATTR_ADD(&tz->device, count, result);
|
||||||
if (result)
|
if (result)
|
||||||
goto unregister;
|
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);
|
result = thermal_add_hwmon_sysfs(tz);
|
||||||
if (result)
|
if (result)
|
||||||
goto unregister;
|
goto unregister;
|
||||||
|
@ -814,6 +1134,10 @@ struct thermal_zone_device *thermal_zone_device_register(char *type,
|
||||||
}
|
}
|
||||||
mutex_unlock(&thermal_list_lock);
|
mutex_unlock(&thermal_list_lock);
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
|
||||||
|
|
||||||
|
thermal_zone_device_update(tz);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return tz;
|
return tz;
|
||||||
|
|
||||||
|
@ -853,6 +1177,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
||||||
tz->ops->unbind(tz, cdev);
|
tz->ops->unbind(tz, cdev);
|
||||||
mutex_unlock(&thermal_list_lock);
|
mutex_unlock(&thermal_list_lock);
|
||||||
|
|
||||||
|
thermal_zone_device_set_polling(tz, 0);
|
||||||
|
|
||||||
if (tz->type[0])
|
if (tz->type[0])
|
||||||
device_remove_file(&tz->device, &dev_attr_type);
|
device_remove_file(&tz->device, &dev_attr_type);
|
||||||
device_remove_file(&tz->device, &dev_attr_temp);
|
device_remove_file(&tz->device, &dev_attr_temp);
|
||||||
|
|
|
@ -27,27 +27,46 @@
|
||||||
|
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
struct thermal_zone_device;
|
struct thermal_zone_device;
|
||||||
struct thermal_cooling_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 {
|
struct thermal_zone_device_ops {
|
||||||
int (*bind) (struct thermal_zone_device *,
|
int (*bind) (struct thermal_zone_device *,
|
||||||
struct thermal_cooling_device *);
|
struct thermal_cooling_device *);
|
||||||
int (*unbind) (struct thermal_zone_device *,
|
int (*unbind) (struct thermal_zone_device *,
|
||||||
struct thermal_cooling_device *);
|
struct thermal_cooling_device *);
|
||||||
int (*get_temp) (struct thermal_zone_device *, char *);
|
int (*get_temp) (struct thermal_zone_device *, unsigned long *);
|
||||||
int (*get_mode) (struct thermal_zone_device *, char *);
|
int (*get_mode) (struct thermal_zone_device *,
|
||||||
int (*set_mode) (struct thermal_zone_device *, const char *);
|
enum thermal_device_mode *);
|
||||||
int (*get_trip_type) (struct thermal_zone_device *, int, char *);
|
int (*set_mode) (struct thermal_zone_device *,
|
||||||
int (*get_trip_temp) (struct thermal_zone_device *, int, char *);
|
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 (*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 {
|
struct thermal_cooling_device_ops {
|
||||||
int (*get_max_state) (struct thermal_cooling_device *, char *);
|
int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
|
||||||
int (*get_cur_state) (struct thermal_cooling_device *, char *);
|
int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
|
||||||
int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
|
int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define THERMAL_TRIPS_NONE -1
|
#define THERMAL_TRIPS_NONE -1
|
||||||
|
@ -88,11 +107,19 @@ struct thermal_zone_device {
|
||||||
struct device device;
|
struct device device;
|
||||||
void *devdata;
|
void *devdata;
|
||||||
int trips;
|
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 thermal_zone_device_ops *ops;
|
||||||
struct list_head cooling_devices;
|
struct list_head cooling_devices;
|
||||||
struct idr idr;
|
struct idr idr;
|
||||||
struct mutex lock; /* protect cooling devices list */
|
struct mutex lock; /* protect cooling devices list */
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
struct delayed_work poll_queue;
|
||||||
#if defined(CONFIG_THERMAL_HWMON)
|
#if defined(CONFIG_THERMAL_HWMON)
|
||||||
struct list_head hwmon_node;
|
struct list_head hwmon_node;
|
||||||
struct thermal_hwmon_device *hwmon;
|
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 *thermal_zone_device_register(char *, int, void *,
|
||||||
struct
|
struct
|
||||||
thermal_zone_device_ops
|
thermal_zone_device_ops
|
||||||
*);
|
*, int tc1, int tc2,
|
||||||
|
int passive_freq,
|
||||||
|
int polling_freq);
|
||||||
void thermal_zone_device_unregister(struct thermal_zone_device *);
|
void thermal_zone_device_unregister(struct thermal_zone_device *);
|
||||||
|
|
||||||
int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
|
int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
|
||||||
struct thermal_cooling_device *);
|
struct thermal_cooling_device *);
|
||||||
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
|
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
|
||||||
struct thermal_cooling_device *);
|
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 *thermal_cooling_device_register(char *, void *,
|
||||||
struct
|
struct
|
||||||
thermal_cooling_device_ops
|
thermal_cooling_device_ops
|
||||||
|
|
Loading…
Add table
Reference in a new issue