mutex_unlock(lock);
}
- cancel_delayed_work(&(tz->poll_queue));
-
- if (!delay)
- return;
-
+ int get_tz_trend(struct thermal_zone_device *tz, int trip)
+ {
+ enum thermal_trend trend;
+
+ if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) {
+ if (tz->temperature > tz->last_temperature)
+ trend = THERMAL_TREND_RAISING;
+ else if (tz->temperature < tz->last_temperature)
+ trend = THERMAL_TREND_DROPPING;
+ else
+ trend = THERMAL_TREND_STABLE;
+ }
+
+ return trend;
+ }
+ EXPORT_SYMBOL(get_tz_trend);
+
+ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev, int trip)
+ {
+ struct thermal_instance *pos = NULL;
+ struct thermal_instance *target_instance = NULL;
+
+ mutex_lock(&tz->lock);
+ mutex_lock(&cdev->lock);
+
+ list_for_each_entry(pos, &tz->thermal_instances, tz_node) {
+ if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
+ target_instance = pos;
+ break;
+ }
+ }
+
+ mutex_unlock(&cdev->lock);
+ mutex_unlock(&tz->lock);
+
+ return target_instance;
+ }
+ EXPORT_SYMBOL(get_thermal_instance);
+
+ static void print_bind_err_msg(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev, int ret)
+ {
+ dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n",
+ tz->type, cdev->type, ret);
+ }
+
+ static void __bind(struct thermal_zone_device *tz, int mask,
+ struct thermal_cooling_device *cdev)
+ {
+ int i, ret;
+
+ for (i = 0; i < tz->trips; i++) {
+ if (mask & (1 << i)) {
+ ret = thermal_zone_bind_cooling_device(tz, i, cdev,
+ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT);
+ if (ret)
+ print_bind_err_msg(tz, cdev, ret);
+ }
+ }
+ }
+
+ static void __unbind(struct thermal_zone_device *tz, int mask,
+ struct thermal_cooling_device *cdev)
+ {
+ int i;
+
+ for (i = 0; i < tz->trips; i++)
+ if (mask & (1 << i))
+ thermal_zone_unbind_cooling_device(tz, i, cdev);
+ }
+
+ static void bind_cdev(struct thermal_cooling_device *cdev)
+ {
+ int i, ret;
+ const struct thermal_zone_params *tzp;
+ struct thermal_zone_device *pos = NULL;
+
+ mutex_lock(&thermal_list_lock);
+
+ list_for_each_entry(pos, &thermal_tz_list, node) {
+ if (!pos->tzp && !pos->ops->bind)
+ continue;
+
+ if (!pos->tzp && pos->ops->bind) {
+ ret = pos->ops->bind(pos, cdev);
+ if (ret)
+ print_bind_err_msg(pos, cdev, ret);
+ }
+
+ tzp = pos->tzp;
+ if (!tzp || !tzp->tbp)
+ continue;
+
+ for (i = 0; i < tzp->num_tbps; i++) {
+ if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
+ continue;
+ if (tzp->tbp[i].match(pos, cdev))
+ continue;
+ tzp->tbp[i].cdev = cdev;
+ __bind(pos, tzp->tbp[i].trip_mask, cdev);
+ }
+ }
+
+ mutex_unlock(&thermal_list_lock);
+ }
+
+ static void bind_tz(struct thermal_zone_device *tz)
+ {
+ int i, ret;
+ struct thermal_cooling_device *pos = NULL;
+ const struct thermal_zone_params *tzp = tz->tzp;
+
+ if (!tzp && !tz->ops->bind)
+ return;
+
+ mutex_lock(&thermal_list_lock);
+
+ /* If there is no platform data, try to use ops->bind */
+ if (!tzp && tz->ops->bind) {
+ list_for_each_entry(pos, &thermal_cdev_list, node) {
+ ret = tz->ops->bind(tz, pos);
+ if (ret)
+ print_bind_err_msg(tz, pos, ret);
+ }
+ goto exit;
+ }
+
+ if (!tzp || !tzp->tbp)
+ goto exit;
+
+ list_for_each_entry(pos, &thermal_cdev_list, node) {
+ for (i = 0; i < tzp->num_tbps; i++) {
+ if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
+ continue;
+ if (tzp->tbp[i].match(tz, pos))
+ continue;
+ tzp->tbp[i].cdev = pos;
+ __bind(tz, tzp->tbp[i].trip_mask, pos);
+ }
+ }
+ exit:
+ mutex_unlock(&thermal_list_lock);
+ }
+
+ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
+ int delay)
+ {
- queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
- round_jiffies(msecs_to_jiffies(delay)));
+ if (delay > 1000)
- queue_delayed_work(system_freezable_wq, &(tz->poll_queue),
- msecs_to_jiffies(delay));
++ mod_delayed_work(system_freezable_wq, &tz->poll_queue,
++ round_jiffies(msecs_to_jiffies(delay)));
++ else if (delay)
++ mod_delayed_work(system_freezable_wq, &tz->poll_queue,
++ msecs_to_jiffies(delay));
+ else
++ cancel_delayed_work(&tz->poll_queue);
+ }
+
+ static void monitor_thermal_zone(struct thermal_zone_device *tz)
+ {
+ mutex_lock(&tz->lock);
+
+ 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);
+ else
+ thermal_zone_device_set_polling(tz, 0);
+
+ mutex_unlock(&tz->lock);
+ }
+
+ static void handle_non_critical_trips(struct thermal_zone_device *tz,
+ int trip, enum thermal_trip_type trip_type)
+ {
+ tz->governor->throttle(tz, trip);
+ }
+
+ static void handle_critical_trips(struct thermal_zone_device *tz,
+ int trip, enum thermal_trip_type trip_type)
+ {
+ long trip_temp;
+
+ tz->ops->get_trip_temp(tz, trip, &trip_temp);
+
+ /* If we have not crossed the trip_temp, we do not care. */
+ if (tz->temperature < trip_temp)
+ return;
+
+ if (tz->ops->notify)
+ tz->ops->notify(tz, trip, trip_type);
+
+ if (trip_type == THERMAL_TRIP_CRITICAL) {
+ pr_emerg("Critical temperature reached(%d C),shutting down\n",
+ tz->temperature / 1000);
+ orderly_poweroff(true);
+ }
+ }
+
+ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
+ {
+ enum thermal_trip_type type;
+
+ tz->ops->get_trip_type(tz, trip, &type);
+
+ if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
+ handle_critical_trips(tz, trip, type);
+ else
+ handle_non_critical_trips(tz, trip, type);
+ /*
+ * Alright, we handled this trip successfully.
+ * So, start monitoring again.
+ */
+ monitor_thermal_zone(tz);
+ }
+
+ static void update_temperature(struct thermal_zone_device *tz)
+ {
+ long temp;
+ int ret;
+
+ mutex_lock(&tz->lock);
+
+ ret = tz->ops->get_temp(tz, &temp);
+ if (ret) {
+ pr_warn("failed to read out thermal zone %d\n", tz->id);
+ goto exit;
+ }
+
+ tz->last_temperature = tz->temperature;
+ tz->temperature = temp;
+
+ exit:
+ mutex_unlock(&tz->lock);
+ }
+
+ void thermal_zone_device_update(struct thermal_zone_device *tz)
+ {
+ int count;
+
+ update_temperature(tz);
+
+ for (count = 0; count < tz->trips; count++)
+ handle_thermal_trip(tz, count);
+ }
+ 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);
+ }
+
/* sys I/F for thermal zone */
#define to_thermal_zone(_dev) \