2008-10-01 12:04:37

by Matthew Garrett

[permalink] [raw]
Subject: [PATCH] thermal: Make it simpler to use the thermal layer inside the kernel

The thermal layer passes temperatures and trip point types around as
strings. This is fine for sysfs, but makes it hard to use them for other
purposes in-kernel. Move the string conversion to the sysfs-specific
code.

Signed-off-by: Matthew Garrett <[email protected]>

---

This addresses Pavel's concerns about the use of longs, and also changes
the trip point type to an enum in preparation for a followup patch.

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 2655bc1..5487a98 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -69,27 +69,30 @@ 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 int
+ *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 int
+ *state)
{
struct acpi_device *device = cdev->devdata;
- int state;
int result;

if (!device)
return -EINVAL;

- result = acpi_bus_get_power(device->handle, &state);
+ result = acpi_bus_get_power(device->handle, state);
if (result)
return result;

- return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
- (state == ACPI_STATE_D0 ? "1" : "unknown"));
+ *state = (*state == ACPI_STATE_D3 ? 0 :
+ (*state == ACPI_STATE_D0 ? 1 : -1));
+ return 0;
}

static int
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index ef34b18..4bc094c 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -374,7 +374,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 int
+ *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_processor *pr = acpi_driver_data(device);
@@ -382,24 +383,24 @@ 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 int
+ *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
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 9127036..754967f 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -934,7 +934,8 @@ static void acpi_thermal_check(void *data)
/* 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,
+ int *temp)
{
struct acpi_thermal *tz = thermal->devdata;
int result;
@@ -946,7 +947,8 @@ 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";
@@ -993,7 +995,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_t *type)
{
struct acpi_thermal *tz = thermal->devdata;
int i;
@@ -1002,27 +1004,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--;
}

@@ -1030,7 +1040,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, int *temp)
{
struct acpi_thermal *tz = thermal->devdata;
int i;
@@ -1039,31 +1049,40 @@ 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--;
}

@@ -1071,7 +1090,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
}

static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
- unsigned long *temperature) {
+ int *temperature) {
struct acpi_thermal *tz = thermal->devdata;

if (tz->trips.critical.flags.valid) {
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index e8a51a1..bac2901 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -358,26 +358,30 @@ 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
+ int *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
+ int *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_video_device *video = acpi_driver_data(device);
unsigned 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;
}
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index fe07462..8ca2f59 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -104,11 +104,18 @@ static ssize_t
temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
+ int 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, "%d\n", temperature);
}

static ssize_t
@@ -145,6 +152,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int trip;
+ enum thermal_trip_t trip_type;
+ int ret;

if (!tz->ops->get_trip_type)
return -EPERM;
@@ -152,7 +161,28 @@ 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);
+
+ ret = tz->ops->get_trip_type(tz, trip, &trip_type);
+ if (ret)
+ return ret;
+
+ switch (trip_type) {
+ case THERMAL_TRIP_CRITICAL:
+ return sprintf(buf, "critical\n");
+ break;
+ case THERMAL_TRIP_HOT:
+ return sprintf(buf, "hot\n");
+ break;
+ case THERMAL_TRIP_PASSIVE:
+ return sprintf(buf, "passive\n");
+ break;
+ case THERMAL_TRIP_ACTIVE:
+ return sprintf(buf, "active\n");
+ break;
+ default:
+ return sprintf(buf, "unknown\n");
+ break;
+ }
}

static ssize_t
@@ -160,7 +190,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;
+ int temperature;

if (!tz->ops->get_trip_temp)
return -EPERM;
@@ -168,7 +199,12 @@ 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, "%d\n", temperature);
}

static DEVICE_ATTR(type, 0444, type_show, NULL);
@@ -236,8 +272,12 @@ thermal_cooling_device_max_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ int state, ret;

- return cdev->ops->get_max_state(cdev, buf);
+ ret = cdev->ops->get_max_state(cdev, &state);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", state);
}

static ssize_t
@@ -245,8 +285,12 @@ thermal_cooling_device_cur_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ int state, ret;

- return cdev->ops->get_cur_state(cdev, buf);
+ ret = cdev->ops->get_cur_state(cdev, &state);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", state);
}

static ssize_t
@@ -312,13 +356,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 +381,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
struct thermal_zone_device *tz
= container_of(hwmon_attr, struct thermal_zone_device,
temp_crit);
+ long temperature;
+ int ret;
+
+ ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ if (ret)
+ return ret;

- return tz->ops->get_trip_temp(tz, 0, buf);
+ return sprintf(buf, "%ld\n", temperature);
}


diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 917707e..9e3475a 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -28,6 +28,10 @@
#include <linux/idr.h>
#include <linux/device.h>

+enum thermal_trip_t { THERMAL_TRIP_CRITICAL, THERMAL_TRIP_HOT,
+ THERMAL_TRIP_PASSIVE, THERMAL_TRIP_ACTIVE
+};
+
struct thermal_zone_device;
struct thermal_cooling_device;

@@ -36,17 +40,19 @@ struct thermal_zone_device_ops {
struct thermal_cooling_device *);
int (*unbind) (struct thermal_zone_device *,
struct thermal_cooling_device *);
- int (*get_temp) (struct thermal_zone_device *, char *);
+ int (*get_temp) (struct thermal_zone_device *, int *);
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_crit_temp) (struct thermal_zone_device *, unsigned long *);
+ int (*get_trip_type) (struct thermal_zone_device *, int,
+ enum thermal_trip_t *);
+ int (*get_trip_temp) (struct thermal_zone_device *, int,
+ int *);
+ int (*get_crit_temp) (struct thermal_zone_device *, int *);
};

struct thermal_cooling_device_ops {
- int (*get_max_state) (struct thermal_cooling_device *, char *);
- int (*get_cur_state) (struct thermal_cooling_device *, char *);
+ int (*get_max_state) (struct thermal_cooling_device *, unsigned int *);
+ int (*get_cur_state) (struct thermal_cooling_device *, unsigned int *);
int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
};


--
Matthew Garrett | [email protected]


2008-10-01 12:13:59

by Matthew Garrett

[permalink] [raw]
Subject: [RFC] thermal: Move trip point handling code from ACPI to generic code

Having trip point handling code in a single central location makes more
sense than implementing it per thermal driver. This is a slightly rough
prototype that should be pretty much equivalent in functionality to the
existing code (at least, my laptop hasn't melted yet). Only thing I can
think of off-hand that's missing are the updates for crossing trip
points (another callback into the specific code?) and hooking up the
polling configuration that's currently in /proc. Any comments?

Motivation for this is wanting to start hooking DRM drivers into the
thermal layer as we get better information on handing their PM
functionality. I suspect lack of thermal control in GPUs is one of the
things that's causing us to hit thermal limits on some laptops.

diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 754967f..c106a02 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -549,145 +549,6 @@ 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,
- tz->device->dev.bus_id,
- 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,
- tz->device->dev.bus_id,
- 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;
@@ -762,13 +623,6 @@ static void acpi_thermal_active(struct acpi_thermal *tz)

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;
@@ -812,123 +666,9 @@ static void acpi_thermal_active_off(void *data)

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 */
@@ -1213,8 +953,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;

@@ -1736,10 +1489,6 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
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;
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 8ca2f59..7eff12f 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -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");
@@ -161,7 +162,6 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
return -EINVAL;

-
ret = tz->ops->get_trip_type(tz, trip, &trip_type);
if (ret)
return ret;
@@ -778,12 +778,166 @@ void thermal_cooling_device_unregister(struct

EXPORT_SYMBOL(thermal_cooling_device_unregister);

+static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
+ int delay)
+{
+ cancel_delayed_work_sync(&(tz->poll_queue));
+
+ if (!delay)
+ return;
+
+ if (delay > 1000)
+ schedule_delayed_work(&(tz->poll_queue),
+ round_jiffies(delay * HZ));
+ else
+ schedule_delayed_work(&(tz->poll_queue), delay * HZ);
+}
+
+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;
+ int state, max_state;
+
+ if (tz->passive == false)
+ return;
+
+ /*
+ * 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;
+ }
+}
+
+void thermal_zone_device_update(struct thermal_zone_device *tz)
+{
+ int temp, trip_temp;
+ int count;
+ enum thermal_trip_t trip_type;
+ struct thermal_cooling_device_instance *instance;
+ struct thermal_cooling_device *cdev;
+
+ 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)
+ /* FIXME: send notification */
+ orderly_poweroff(true);
+ break;
+ case THERMAL_TRIP_HOT:
+ if (temp > trip_temp)
+ /* FIXME: send notification */
+ printk(KERN_WARNING "Hot trip point\n");
+ 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:
+ thermal_zone_device_passive(tz, temp, trip_temp, count);
+ break;
+ }
+ }
+ 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);
+}
+
+EXPORT_SYMBOL(thermal_zone_device_update);
+
+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_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.
@@ -792,7 +946,10 @@ 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;
@@ -826,6 +983,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;
+
sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
result = device_register(&tz->device);
if (result) {
@@ -871,6 +1033,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_set_polling(tz, tz->polling_delay);
+
if (!result)
return tz;

@@ -910,6 +1076,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);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 9e3475a..38ac33d 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -94,11 +94,18 @@ 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;
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;
@@ -110,13 +117,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

--
Matthew Garrett | [email protected]

2008-10-01 12:41:40

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH] thermal: Make it simpler to use the thermal layer inside the kernel

Hi!

> The thermal layer passes temperatures and trip point types around as
> strings. This is fine for sysfs, but makes it hard to use them for other
> purposes in-kernel. Move the string conversion to the sysfs-specific
> code.

Makes sense.

> Signed-off-by: Matthew Garrett <[email protected]>

Acked-by: Pavel Machek <[email protected]>

Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2008-10-01 18:41:31

by Matthew Garrett

[permalink] [raw]
Subject: [PATCH v2] thermal: Make it simpler to use the thermal layer inside the kernel

The thermal layer passes temperatures and trip point types around as
strings. This is fine for sysfs, but makes it hard to use them for other
purposes in-kernel. Move the string conversion to the sysfs-specific
code.

Signed-off-by: Matthew Garrett <[email protected]>

---

This version also moves the mode setting (user/kernel) away from using
strings, which gets them out of the majority of the API.

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 2655bc1..5487a98 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -69,27 +69,30 @@ 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 int
+ *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 int
+ *state)
{
struct acpi_device *device = cdev->devdata;
- int state;
int result;

if (!device)
return -EINVAL;

- result = acpi_bus_get_power(device->handle, &state);
+ result = acpi_bus_get_power(device->handle, state);
if (result)
return result;

- return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
- (state == ACPI_STATE_D0 ? "1" : "unknown"));
+ *state = (*state == ACPI_STATE_D3 ? 0 :
+ (*state == ACPI_STATE_D0 ? 1 : -1));
+ return 0;
}

static int
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
index ef34b18..4bc094c 100644
--- a/drivers/acpi/processor_thermal.c
+++ b/drivers/acpi/processor_thermal.c
@@ -374,7 +374,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 int
+ *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_processor *pr = acpi_driver_data(device);
@@ -382,24 +383,24 @@ 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 int
+ *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
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 9127036..2e4e2a1 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -934,7 +934,8 @@ static void acpi_thermal_check(void *data)
/* 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,
+ int *temp)
{
struct acpi_thermal *tz = thermal->devdata;
int result;
@@ -946,25 +947,25 @@ 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_mode_t *thermal_mode)
{
struct acpi_thermal *tz = thermal->devdata;

if (!tz)
return -EINVAL;

- return sprintf(buf, "%s\n", tz->tz_enabled ?
- enabled : disabled);
+ *thermal_mode = tz->tz_enabled ? THERMAL_MODE_KERNEL
+ : THERMAL_MODE_USER;
+ return 0;
}

static int thermal_set_mode(struct thermal_zone_device *thermal,
- const char *buf)
+ enum thermal_mode_t thermal_mode)
{
struct acpi_thermal *tz = thermal->devdata;
int enable;
@@ -975,12 +976,10 @@ 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 (thermal_mode == THERMAL_MODE_KERNEL)
enable = 1;
- else if (!strncmp(buf, disabled, sizeof disabled - 1))
- enable = 0;
else
- return -EINVAL;
+ enable = 0;

if (enable != tz->tz_enabled) {
tz->tz_enabled = enable;
@@ -993,7 +992,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_t *type)
{
struct acpi_thermal *tz = thermal->devdata;
int i;
@@ -1002,27 +1001,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--;
}

@@ -1030,7 +1037,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, int *temp)
{
struct acpi_thermal *tz = thermal->devdata;
int i;
@@ -1039,31 +1046,40 @@ 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--;
}

@@ -1071,7 +1087,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
}

static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
- unsigned long *temperature) {
+ int *temperature) {
struct acpi_thermal *tz = thermal->devdata;

if (tz->trips.critical.flags.valid) {
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index e8a51a1..bac2901 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -358,26 +358,30 @@ 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
+ int *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
+ int *state)
{
struct acpi_device *device = cdev->devdata;
struct acpi_video_device *video = acpi_driver_data(device);
unsigned 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;
}
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index fe07462..c1f7e65 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -104,22 +104,41 @@ static ssize_t
temp_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
+ int 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, "%d\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_mode_t mode;
+ int ret;

if (!tz->ops->get_mode)
return -EPERM;

- return tz->ops->get_mode(tz, buf);
+ ret = tz->ops->get_mode(tz, &mode);
+
+ if (ret)
+ return ret;
+
+ if (mode == THERMAL_MODE_KERNEL)
+ return sprintf(buf, "%s\n", "kernel");
+ else if (mode == THERMAL_MODE_USER)
+ return sprintf(buf, "%s\n", "user");
+ else
+ return sprintf(buf, "%s\n", "unknown");
}

static ssize_t
@@ -132,7 +151,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, "kernel", count))
+ result = tz->ops->set_mode(tz, THERMAL_MODE_KERNEL);
+ else if (!strncmp(buf, "user", count))
+ result = tz->ops->set_mode(tz, THERMAL_MODE_USER);
+ else
+ return -EINVAL;
+
if (result)
return result;

@@ -145,6 +170,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
{
struct thermal_zone_device *tz = to_thermal_zone(dev);
int trip;
+ enum thermal_trip_t trip_type;
+ int ret;

if (!tz->ops->get_trip_type)
return -EPERM;
@@ -152,7 +179,28 @@ 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);
+
+ ret = tz->ops->get_trip_type(tz, trip, &trip_type);
+ if (ret)
+ return ret;
+
+ switch (trip_type) {
+ case THERMAL_TRIP_CRITICAL:
+ return sprintf(buf, "critical\n");
+ break;
+ case THERMAL_TRIP_HOT:
+ return sprintf(buf, "hot\n");
+ break;
+ case THERMAL_TRIP_PASSIVE:
+ return sprintf(buf, "passive\n");
+ break;
+ case THERMAL_TRIP_ACTIVE:
+ return sprintf(buf, "active\n");
+ break;
+ default:
+ return sprintf(buf, "unknown\n");
+ break;
+ }
}

static ssize_t
@@ -160,7 +208,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;
+ int temperature;

if (!tz->ops->get_trip_temp)
return -EPERM;
@@ -168,7 +217,12 @@ 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, "%d\n", temperature);
}

static DEVICE_ATTR(type, 0444, type_show, NULL);
@@ -236,8 +290,12 @@ thermal_cooling_device_max_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ int state, ret;

- return cdev->ops->get_max_state(cdev, buf);
+ ret = cdev->ops->get_max_state(cdev, &state);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", state);
}

static ssize_t
@@ -245,8 +303,12 @@ thermal_cooling_device_cur_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ int state, ret;

- return cdev->ops->get_cur_state(cdev, buf);
+ ret = cdev->ops->get_cur_state(cdev, &state);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%d\n", state);
}

static ssize_t
@@ -312,13 +374,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 +399,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
struct thermal_zone_device *tz
= container_of(hwmon_attr, struct thermal_zone_device,
temp_crit);
+ long temperature;
+ int ret;
+
+ ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ if (ret)
+ return ret;

- return tz->ops->get_trip_temp(tz, 0, buf);
+ return sprintf(buf, "%ld\n", temperature);
}


diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 917707e..05e56d8 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -28,6 +28,12 @@
#include <linux/idr.h>
#include <linux/device.h>

+enum thermal_trip_t { THERMAL_TRIP_CRITICAL, THERMAL_TRIP_HOT,
+ THERMAL_TRIP_PASSIVE, THERMAL_TRIP_ACTIVE
+};
+
+enum thermal_mode_t { THERMAL_MODE_KERNEL, THERMAL_MODE_USER };
+
struct thermal_zone_device;
struct thermal_cooling_device;

@@ -36,17 +42,19 @@ struct thermal_zone_device_ops {
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_crit_temp) (struct thermal_zone_device *, unsigned long *);
+ int (*get_temp) (struct thermal_zone_device *, int *);
+ int (*get_mode) (struct thermal_zone_device *, enum thermal_mode_t *);
+ int (*set_mode) (struct thermal_zone_device *, enum thermal_mode_t);
+ int (*get_trip_type) (struct thermal_zone_device *, int,
+ enum thermal_trip_t *);
+ int (*get_trip_temp) (struct thermal_zone_device *, int,
+ int *);
+ int (*get_crit_temp) (struct thermal_zone_device *, int *);
};

struct thermal_cooling_device_ops {
- int (*get_max_state) (struct thermal_cooling_device *, char *);
- int (*get_cur_state) (struct thermal_cooling_device *, char *);
+ int (*get_max_state) (struct thermal_cooling_device *, unsigned int *);
+ int (*get_cur_state) (struct thermal_cooling_device *, unsigned int *);
int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
};

--
Matthew Garrett | [email protected]

2008-10-01 18:43:17

by Matthew Garrett

[permalink] [raw]
Subject: Re: [RFC] thermal: Move trip point handling code from ACPI to generic code

And here's a slightly updated version. Couple of bugfixes, added support
for the missing features. Would be nice if someone else could give this
a go.

diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 2e4e2a1..1302851 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -37,7 +37,6 @@
#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>
@@ -191,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;
@@ -292,6 +290,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));
@@ -549,386 +552,11 @@ 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,
- tz->device->dev.bus_id,
- 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,
- tz->device->dev.bus_id,
- 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 */
@@ -1098,6 +726,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_t 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,
+ tz->device->dev.bus_id, 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,
@@ -1190,6 +841,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)
@@ -1210,8 +862,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;

@@ -1443,13 +1108,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;
@@ -1679,12 +1344,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);
@@ -1713,36 +1372,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);
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index c1f7e65..a97c541 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -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");
@@ -179,7 +180,6 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
return -EINVAL;

-
ret = tz->ops->get_trip_type(tz, trip, &trip_type);
if (ret)
return ret;
@@ -527,6 +527,99 @@ 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_sync(&(tz->poll_queue));
+
+ if (!delay)
+ return;
+
+ if (delay > 1000)
+ schedule_delayed_work(&(tz->poll_queue),
+ round_jiffies(delay * HZ));
+ else
+ schedule_delayed_work(&(tz->poll_queue), delay * HZ);
+}
+
+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;
+ int state, max_state;
+
+ if (tz->passive == false)
+ return;
+
+ /*
+ * 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
@@ -797,20 +890,98 @@ 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 temp, trip_temp, count, ret = 0;
+ enum thermal_trip_t 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 (%d 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:
+ thermal_zone_device_passive(tz, temp, trip_temp, count);
+ break;
+ }
+ }
+ 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;
@@ -844,6 +1015,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;
+
sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
result = device_register(&tz->device);
if (result) {
@@ -889,6 +1065,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;

@@ -928,6 +1108,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);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 05e56d8..35baa11 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -50,6 +50,7 @@ struct thermal_zone_device_ops {
int (*get_trip_temp) (struct thermal_zone_device *, int,
int *);
int (*get_crit_temp) (struct thermal_zone_device *, int *);
+ int (*notify) (struct thermal_zone_device *, int, enum thermal_trip_t);
};

struct thermal_cooling_device_ops {
@@ -96,11 +97,18 @@ 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;
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;
@@ -112,13 +120,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


--
Matthew Garrett | [email protected]

2008-10-01 20:32:26

by Pavel Machek

[permalink] [raw]
Subject: Re: [PATCH v2] thermal: Make it simpler to use the thermal layer inside the kernel

On Wed 2008-10-01 19:41:11, Matthew Garrett wrote:
> The thermal layer passes temperatures and trip point types around as
> strings. This is fine for sysfs, but makes it hard to use them for other
> purposes in-kernel. Move the string conversion to the sysfs-specific
> code.
>
> Signed-off-by: Matthew Garrett <[email protected]>

Still acked. Can you mail it to Len Brown for inclusion, Cc: akpm in
case len is sleeping? It is quite obvious anyway -- passing strings is
bad.


> ---
>
> This version also moves the mode setting (user/kernel) away from using
> strings, which gets them out of the majority of the API.
>
> diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
> index 2655bc1..5487a98 100644
> --- a/drivers/acpi/fan.c
> +++ b/drivers/acpi/fan.c
> @@ -69,27 +69,30 @@ 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 int
> + *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 int
> + *state)
> {
> struct acpi_device *device = cdev->devdata;
> - int state;
> int result;
>
> if (!device)
> return -EINVAL;
>
> - result = acpi_bus_get_power(device->handle, &state);
> + result = acpi_bus_get_power(device->handle, state);
> if (result)
> return result;
>
> - return sprintf(buf, "%s\n", state == ACPI_STATE_D3 ? "0" :
> - (state == ACPI_STATE_D0 ? "1" : "unknown"));
> + *state = (*state == ACPI_STATE_D3 ? 0 :
> + (*state == ACPI_STATE_D0 ? 1 : -1));
> + return 0;
> }
>
> static int
> diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
> index ef34b18..4bc094c 100644
> --- a/drivers/acpi/processor_thermal.c
> +++ b/drivers/acpi/processor_thermal.c
> @@ -374,7 +374,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 int
> + *state)
> {
> struct acpi_device *device = cdev->devdata;
> struct acpi_processor *pr = acpi_driver_data(device);
> @@ -382,24 +383,24 @@ 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 int
> + *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
> diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
> index 9127036..2e4e2a1 100644
> --- a/drivers/acpi/thermal.c
> +++ b/drivers/acpi/thermal.c
> @@ -934,7 +934,8 @@ static void acpi_thermal_check(void *data)
> /* 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,
> + int *temp)
> {
> struct acpi_thermal *tz = thermal->devdata;
> int result;
> @@ -946,25 +947,25 @@ 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_mode_t *thermal_mode)
> {
> struct acpi_thermal *tz = thermal->devdata;
>
> if (!tz)
> return -EINVAL;
>
> - return sprintf(buf, "%s\n", tz->tz_enabled ?
> - enabled : disabled);
> + *thermal_mode = tz->tz_enabled ? THERMAL_MODE_KERNEL
> + : THERMAL_MODE_USER;
> + return 0;
> }
>
> static int thermal_set_mode(struct thermal_zone_device *thermal,
> - const char *buf)
> + enum thermal_mode_t thermal_mode)
> {
> struct acpi_thermal *tz = thermal->devdata;
> int enable;
> @@ -975,12 +976,10 @@ 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 (thermal_mode == THERMAL_MODE_KERNEL)
> enable = 1;
> - else if (!strncmp(buf, disabled, sizeof disabled - 1))
> - enable = 0;
> else
> - return -EINVAL;
> + enable = 0;
>
> if (enable != tz->tz_enabled) {
> tz->tz_enabled = enable;
> @@ -993,7 +992,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_t *type)
> {
> struct acpi_thermal *tz = thermal->devdata;
> int i;
> @@ -1002,27 +1001,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--;
> }
>
> @@ -1030,7 +1037,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, int *temp)
> {
> struct acpi_thermal *tz = thermal->devdata;
> int i;
> @@ -1039,31 +1046,40 @@ 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--;
> }
>
> @@ -1071,7 +1087,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
> }
>
> static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
> - unsigned long *temperature) {
> + int *temperature) {
> struct acpi_thermal *tz = thermal->devdata;
>
> if (tz->trips.critical.flags.valid) {
> diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
> index e8a51a1..bac2901 100644
> --- a/drivers/acpi/video.c
> +++ b/drivers/acpi/video.c
> @@ -358,26 +358,30 @@ 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
> + int *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
> + int *state)
> {
> struct acpi_device *device = cdev->devdata;
> struct acpi_video_device *video = acpi_driver_data(device);
> unsigned 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;
> }
> diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
> index fe07462..c1f7e65 100644
> --- a/drivers/thermal/thermal_sys.c
> +++ b/drivers/thermal/thermal_sys.c
> @@ -104,22 +104,41 @@ static ssize_t
> temp_show(struct device *dev, struct device_attribute *attr, char *buf)
> {
> struct thermal_zone_device *tz = to_thermal_zone(dev);
> + int 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, "%d\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_mode_t mode;
> + int ret;
>
> if (!tz->ops->get_mode)
> return -EPERM;
>
> - return tz->ops->get_mode(tz, buf);
> + ret = tz->ops->get_mode(tz, &mode);
> +
> + if (ret)
> + return ret;
> +
> + if (mode == THERMAL_MODE_KERNEL)
> + return sprintf(buf, "%s\n", "kernel");
> + else if (mode == THERMAL_MODE_USER)
> + return sprintf(buf, "%s\n", "user");
> + else
> + return sprintf(buf, "%s\n", "unknown");
> }
>
> static ssize_t
> @@ -132,7 +151,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, "kernel", count))
> + result = tz->ops->set_mode(tz, THERMAL_MODE_KERNEL);
> + else if (!strncmp(buf, "user", count))
> + result = tz->ops->set_mode(tz, THERMAL_MODE_USER);
> + else
> + return -EINVAL;
> +
> if (result)
> return result;
>
> @@ -145,6 +170,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
> {
> struct thermal_zone_device *tz = to_thermal_zone(dev);
> int trip;
> + enum thermal_trip_t trip_type;
> + int ret;
>
> if (!tz->ops->get_trip_type)
> return -EPERM;
> @@ -152,7 +179,28 @@ 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);
> +
> + ret = tz->ops->get_trip_type(tz, trip, &trip_type);
> + if (ret)
> + return ret;
> +
> + switch (trip_type) {
> + case THERMAL_TRIP_CRITICAL:
> + return sprintf(buf, "critical\n");
> + break;
> + case THERMAL_TRIP_HOT:
> + return sprintf(buf, "hot\n");
> + break;
> + case THERMAL_TRIP_PASSIVE:
> + return sprintf(buf, "passive\n");
> + break;
> + case THERMAL_TRIP_ACTIVE:
> + return sprintf(buf, "active\n");
> + break;
> + default:
> + return sprintf(buf, "unknown\n");
> + break;
> + }
> }
>
> static ssize_t
> @@ -160,7 +208,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;
> + int temperature;
>
> if (!tz->ops->get_trip_temp)
> return -EPERM;
> @@ -168,7 +217,12 @@ 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, "%d\n", temperature);
> }
>
> static DEVICE_ATTR(type, 0444, type_show, NULL);
> @@ -236,8 +290,12 @@ thermal_cooling_device_max_state_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> struct thermal_cooling_device *cdev = to_cooling_device(dev);
> + int state, ret;
>
> - return cdev->ops->get_max_state(cdev, buf);
> + ret = cdev->ops->get_max_state(cdev, &state);
> + if (ret)
> + return ret;
> + return sprintf(buf, "%d\n", state);
> }
>
> static ssize_t
> @@ -245,8 +303,12 @@ thermal_cooling_device_cur_state_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> struct thermal_cooling_device *cdev = to_cooling_device(dev);
> + int state, ret;
>
> - return cdev->ops->get_cur_state(cdev, buf);
> + ret = cdev->ops->get_cur_state(cdev, &state);
> + if (ret)
> + return ret;
> + return sprintf(buf, "%d\n", state);
> }
>
> static ssize_t
> @@ -312,13 +374,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 +399,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
> struct thermal_zone_device *tz
> = container_of(hwmon_attr, struct thermal_zone_device,
> temp_crit);
> + long temperature;
> + int ret;
> +
> + ret = tz->ops->get_trip_temp(tz, 0, &temperature);
> + if (ret)
> + return ret;
>
> - return tz->ops->get_trip_temp(tz, 0, buf);
> + return sprintf(buf, "%ld\n", temperature);
> }
>
>
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 917707e..05e56d8 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -28,6 +28,12 @@
> #include <linux/idr.h>
> #include <linux/device.h>
>
> +enum thermal_trip_t { THERMAL_TRIP_CRITICAL, THERMAL_TRIP_HOT,
> + THERMAL_TRIP_PASSIVE, THERMAL_TRIP_ACTIVE
> +};
> +
> +enum thermal_mode_t { THERMAL_MODE_KERNEL, THERMAL_MODE_USER };
> +
> struct thermal_zone_device;
> struct thermal_cooling_device;
>
> @@ -36,17 +42,19 @@ struct thermal_zone_device_ops {
> 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_crit_temp) (struct thermal_zone_device *, unsigned long *);
> + int (*get_temp) (struct thermal_zone_device *, int *);
> + int (*get_mode) (struct thermal_zone_device *, enum thermal_mode_t *);
> + int (*set_mode) (struct thermal_zone_device *, enum thermal_mode_t);
> + int (*get_trip_type) (struct thermal_zone_device *, int,
> + enum thermal_trip_t *);
> + int (*get_trip_temp) (struct thermal_zone_device *, int,
> + int *);
> + int (*get_crit_temp) (struct thermal_zone_device *, int *);
> };
>
> struct thermal_cooling_device_ops {
> - int (*get_max_state) (struct thermal_cooling_device *, char *);
> - int (*get_cur_state) (struct thermal_cooling_device *, char *);
> + int (*get_max_state) (struct thermal_cooling_device *, unsigned int *);
> + int (*get_cur_state) (struct thermal_cooling_device *, unsigned int *);
> int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
> };
>

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2008-10-02 08:36:59

by Pavel Machek

[permalink] [raw]
Subject: Re: [RFC] thermal: Move trip point handling code from ACPI to generic code

Hi!

> And here's a slightly updated version. Couple of bugfixes, added support
> for the missing features. Would be nice if someone else could give this
> a go.

On a short look it looks okay to me. It seems to depend on the
previous (or some other?) patch, so it did not apply to me....

But if you have only moved code, that should not break anything,
right? ;-).
Pavel


> diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
> index 2e4e2a1..1302851 100644
> --- a/drivers/acpi/thermal.c
> +++ b/drivers/acpi/thermal.c
> @@ -37,7 +37,6 @@
> #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>
> @@ -191,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;
> @@ -292,6 +290,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));
> @@ -549,386 +552,11 @@ 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,
> - tz->device->dev.bus_id,
> - 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,
> - tz->device->dev.bus_id,
> - 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 */
> @@ -1098,6 +726,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_t 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,
> + tz->device->dev.bus_id, 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,
> @@ -1190,6 +841,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)
> @@ -1210,8 +862,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;
>
> @@ -1443,13 +1108,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;
> @@ -1679,12 +1344,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);
> @@ -1713,36 +1372,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);
> diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
> index c1f7e65..a97c541 100644
> --- a/drivers/thermal/thermal_sys.c
> +++ b/drivers/thermal/thermal_sys.c
> @@ -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");
> @@ -179,7 +180,6 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
> if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip))
> return -EINVAL;
>
> -
> ret = tz->ops->get_trip_type(tz, trip, &trip_type);
> if (ret)
> return ret;
> @@ -527,6 +527,99 @@ 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_sync(&(tz->poll_queue));
> +
> + if (!delay)
> + return;
> +
> + if (delay > 1000)
> + schedule_delayed_work(&(tz->poll_queue),
> + round_jiffies(delay * HZ));
> + else
> + schedule_delayed_work(&(tz->poll_queue), delay * HZ);
> +}
> +
> +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;
> + int state, max_state;
> +
> + if (tz->passive == false)
> + return;
> +
> + /*
> + * 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
> @@ -797,20 +890,98 @@ 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 temp, trip_temp, count, ret = 0;
> + enum thermal_trip_t 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 (%d 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:
> + thermal_zone_device_passive(tz, temp, trip_temp, count);
> + break;
> + }
> + }
> + 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;
> @@ -844,6 +1015,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;
> +
> sprintf(tz->device.bus_id, "thermal_zone%d", tz->id);
> result = device_register(&tz->device);
> if (result) {
> @@ -889,6 +1065,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;
>
> @@ -928,6 +1108,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);
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 05e56d8..35baa11 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -50,6 +50,7 @@ struct thermal_zone_device_ops {
> int (*get_trip_temp) (struct thermal_zone_device *, int,
> int *);
> int (*get_crit_temp) (struct thermal_zone_device *, int *);
> + int (*notify) (struct thermal_zone_device *, int, enum thermal_trip_t);
> };
>
> struct thermal_cooling_device_ops {
> @@ -96,11 +97,18 @@ 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;
> 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;
> @@ -112,13 +120,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
>
>

--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

2008-10-03 17:08:51

by Thomas Renninger

[permalink] [raw]
Subject: Re: [PATCH v2] thermal: Make it simpler to use the thermal layer inside the kernel

On Wednesday 01 October 2008 08:41:11 pm Matthew Garrett wrote:
> The thermal layer passes temperatures and trip point types around as
> strings. This is fine for sysfs, but makes it hard to use them for other
> purposes in-kernel. Move the string conversion to the sysfs-specific
> code.
I went through your very first version and I also like the string to
int conversion very much.
I wanted to move the polling from acpi/thermal.c to the new thermal
layer to have the thermal polling in the architecture independent part
and started on top of your first version, but I got stuck and run out of time,
otherwise I'd also sent this.

> struct thermal_cooling_device_ops {
> - int (*get_max_state) (struct thermal_cooling_device *, char *);
> - int (*get_cur_state) (struct thermal_cooling_device *, char *);
> + int (*get_max_state) (struct thermal_cooling_device *, unsigned int *);
> + int (*get_cur_state) (struct thermal_cooling_device *, unsigned int *);
> int (*set_cur_state) (struct thermal_cooling_device *, unsigned int);
> };
Looks like you forgot the menlow driver again which also makes use of this:

grep get_max_state drivers/misc/intel_menlow.c
.get_max_state = memory_get_max_bandwidth,

It would be great to see the conversion in .28.

Thomas

2008-10-03 17:10:35

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH v2] thermal: Make it simpler to use the thermal layer inside the kernel

On Fri, Oct 03, 2008 at 07:08:36PM +0200, Thomas Renninger wrote:

> grep get_max_state drivers/misc/intel_menlow.c
> .get_max_state = memory_get_max_bandwidth,

Oops, yeah. I'll fix that one up as well.
--
Matthew Garrett | [email protected]