#include <linux/of.h>
#include <net/netlink.h>
#include <net/genetlink.h>
+ +#include <linux/suspend.h>
#define CREATE_TRACE_POINTS
#include <trace/events/thermal.h>
static DEFINE_MUTEX(thermal_list_lock);
static DEFINE_MUTEX(thermal_governor_lock);
+ +static atomic_t in_suspend;
+ +
static struct thermal_governor *def_governor;
static struct thermal_governor *__find_governor(const char *name)
mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
- - dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
- - tz->last_temperature, tz->temperature);
+ + if (tz->last_temperature == THERMAL_TEMP_INVALID)
+ + dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n",
+ + tz->temperature);
+ + else
+ + dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
+ + tz->last_temperature, tz->temperature);
+ +}
+ +
+ +static void thermal_zone_device_reset(struct thermal_zone_device *tz)
+ +{
+ + struct thermal_instance *pos;
+ +
+ + tz->temperature = THERMAL_TEMP_INVALID;
+ + tz->passive = 0;
+ + list_for_each_entry(pos, &tz->thermal_instances, tz_node)
+ + pos->initialized = false;
}
void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count;
+ + if (atomic_read(&in_suspend))
+ + return;
+ +
if (!tz->ops->get_temp)
return;
return -EINVAL;
ret = tz->ops->set_trip_temp(tz, trip, temperature);
++ if (ret)
++ return ret;
-- return ret ? ret : count;
++ thermal_zone_device_update(tz);
++
++ return count;
}
static ssize_t
if (!result) {
list_add_tail(&dev->tz_node, &tz->thermal_instances);
list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
+ + atomic_set(&tz->need_update, 1);
}
mutex_unlock(&cdev->lock);
mutex_unlock(&tz->lock);
const struct thermal_cooling_device_ops *ops)
{
struct thermal_cooling_device *cdev;
+ + struct thermal_zone_device *pos = NULL;
int result;
if (type && strlen(type) >= THERMAL_NAME_LENGTH)
/* Update binding information for 'this' new cdev */
bind_cdev(cdev);
+ + mutex_lock(&thermal_list_lock);
+ + list_for_each_entry(pos, &thermal_tz_list, node)
+ + if (atomic_cmpxchg(&pos->need_update, 1, 0))
+ + thermal_zone_device_update(pos);
+ + mutex_unlock(&thermal_list_lock);
+ +
return cdev;
}
tz->trips = trips;
tz->passive_delay = passive_delay;
tz->polling_delay = polling_delay;
+ + /* A new thermal zone needs to be updated anyway. */
+ + atomic_set(&tz->need_update, 1);
dev_set_name(&tz->device, "thermal_zone%d", tz->id);
result = device_register(&tz->device);
INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
- - thermal_zone_device_update(tz);
+ + thermal_zone_device_reset(tz);
+ + /* Update the new thermal zone and mark it as already updated. */
+ + if (atomic_cmpxchg(&tz->need_update, 1, 0))
+ + thermal_zone_device_update(tz);
return tz;
thermal_gov_power_allocator_unregister();
}
+ +static int thermal_pm_notify(struct notifier_block *nb,
+ + unsigned long mode, void *_unused)
+ +{
+ + struct thermal_zone_device *tz;
+ +
+ + switch (mode) {
+ + case PM_HIBERNATION_PREPARE:
+ + case PM_RESTORE_PREPARE:
+ + case PM_SUSPEND_PREPARE:
+ + atomic_set(&in_suspend, 1);
+ + break;
+ + case PM_POST_HIBERNATION:
+ + case PM_POST_RESTORE:
+ + case PM_POST_SUSPEND:
+ + atomic_set(&in_suspend, 0);
+ + list_for_each_entry(tz, &thermal_tz_list, node) {
+ + thermal_zone_device_reset(tz);
+ + thermal_zone_device_update(tz);
+ + }
+ + break;
+ + default:
+ + break;
+ + }
+ + return 0;
+ +}
+ +
+ +static struct notifier_block thermal_pm_nb = {
+ + .notifier_call = thermal_pm_notify,
+ +};
+ +
static int __init thermal_init(void)
{
int result;
if (result)
goto exit_netlink;
+ + result = register_pm_notifier(&thermal_pm_nb);
+ + if (result)
+ + pr_warn("Thermal: Can not register suspend notifier, return %d\n",
+ + result);
+ +
return 0;
exit_netlink:
static void __exit thermal_exit(void)
{
+ + unregister_pm_notifier(&thermal_pm_nb);
of_thermal_destroy_zones();
genetlink_exit();
class_unregister(&thermal_class);