Thermal: Introduce simple arbitrator for setting device cooling state
[deliverable/linux.git] / drivers / thermal / thermal_sys.c
index 70045c12d7df09cbbee27e4a42b51437e151e8b8..36ae2f43ee2e8944c67e9623e1a5c4b3b428d26b 100644 (file)
@@ -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);
This page took 0.029734 seconds and 5 git commands to generate.