|
@@ -41,6 +41,7 @@ MODULE_AUTHOR("Zhang Rui");
|
|
|
MODULE_DESCRIPTION("Generic thermal management sysfs support");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
+#define THERMAL_NO_TARGET -1UL
|
|
|
/*
|
|
|
* This structure is used to describe the behavior of
|
|
|
* a certain cooling device on a certain trip point
|
|
@@ -54,6 +55,7 @@ struct thermal_instance {
|
|
|
int trip;
|
|
|
unsigned long upper; /* Highest cooling state for this trip point */
|
|
|
unsigned long lower; /* Lowest cooling state for this trip point */
|
|
|
+ unsigned long target; /* expected cooling state */
|
|
|
char attr_name[THERMAL_NAME_LENGTH];
|
|
|
struct device_attribute attr;
|
|
|
struct list_head tz_node; /* node in tz->thermal_instances */
|
|
@@ -853,6 +855,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
|
|
|
dev->trip = trip;
|
|
|
dev->upper = upper;
|
|
|
dev->lower = lower;
|
|
|
+ dev->target = THERMAL_NO_TARGET;
|
|
|
|
|
|
result = get_idr(&tz->idr, &tz->lock, &dev->id);
|
|
|
if (result)
|
|
@@ -990,6 +993,7 @@ thermal_cooling_device_register(char *type, void *devdata,
|
|
|
strcpy(cdev->type, type);
|
|
|
INIT_LIST_HEAD(&cdev->thermal_instances);
|
|
|
cdev->ops = ops;
|
|
|
+ cdev->updated = true;
|
|
|
cdev->device.class = &thermal_class;
|
|
|
cdev->devdata = devdata;
|
|
|
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
|
@@ -1081,6 +1085,34 @@ void thermal_cooling_device_unregister(struct
|
|
|
}
|
|
|
EXPORT_SYMBOL(thermal_cooling_device_unregister);
|
|
|
|
|
|
+static void thermal_cdev_do_update(struct thermal_cooling_device *cdev)
|
|
|
+{
|
|
|
+ struct thermal_instance *instance;
|
|
|
+ unsigned long target = 0;
|
|
|
+
|
|
|
+ /* cooling device is updated*/
|
|
|
+ if (cdev->updated)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Make sure cdev enters the deepest cooling state */
|
|
|
+ list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
|
|
|
+ if (instance->target == THERMAL_NO_TARGET)
|
|
|
+ continue;
|
|
|
+ if (instance->target > target)
|
|
|
+ target = instance->target;
|
|
|
+ }
|
|
|
+ cdev->ops->set_cur_state(cdev, target);
|
|
|
+ cdev->updated = true;
|
|
|
+}
|
|
|
+
|
|
|
+static void thermal_zone_do_update(struct thermal_zone_device *tz)
|
|
|
+{
|
|
|
+ struct thermal_instance *instance;
|
|
|
+
|
|
|
+ list_for_each_entry(instance, &tz->thermal_instances, tz_node)
|
|
|
+ thermal_cdev_do_update(instance->cdev);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Cooling algorithm for active trip points
|
|
|
*
|
|
@@ -1138,19 +1170,24 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz,
|
|
|
cur_state = cur_state > instance->lower ?
|
|
|
(cur_state - 1) : instance->lower;
|
|
|
}
|
|
|
- cdev->ops->set_cur_state(cdev, cur_state);
|
|
|
+ instance->target = cur_state;
|
|
|
+ cdev->updated = false; /* cooling device needs update */
|
|
|
}
|
|
|
} else { /* below trip */
|
|
|
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
|
|
if (instance->trip != trip)
|
|
|
continue;
|
|
|
|
|
|
+ /* Do not use the inactive thermal instance */
|
|
|
+ if (instance->target == THERMAL_NO_TARGET)
|
|
|
+ continue;
|
|
|
cdev = instance->cdev;
|
|
|
cdev->ops->get_cur_state(cdev, &cur_state);
|
|
|
|
|
|
cur_state = cur_state > instance->lower ?
|
|
|
- (cur_state - 1) : instance->lower;
|
|
|
- cdev->ops->set_cur_state(cdev, cur_state);
|
|
|
+ (cur_state - 1) : THERMAL_NO_TARGET;
|
|
|
+ instance->target = cur_state;
|
|
|
+ cdev->updated = false; /* cooling device needs update */
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1211,6 +1248,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ thermal_zone_do_update(tz);
|
|
|
if (tz->forced_passive)
|
|
|
thermal_zone_device_passive(tz, temp, tz->forced_passive,
|
|
|
THERMAL_TRIPS_NONE);
|