summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPraveen Chidambaram <pchidamb@codeaurora.org>2013-07-02 13:04:58 -0600
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:08:17 -0700
commiteb00c793d7c3a772583650dac023cf37e742f4bf (patch)
tree8a35d9aea87ce018154b340078f9e5bc72ab8c07
parent0637fa244fff60c961668a29c67c0e0310d2a95e (diff)
thermal: Add sensor API to allow any driver to set thresholds
Sensor API allow drivers to set min or max thresholds and get notified when the corresponding sensor crosses these thresholds. The sensor API, uses the THERMAL_TRIP_CONFIGURABLE_HI and THERMAL_TRIP_CONFIGURABLE_LOW, to set the threshold. The existing sysfs interfaces will not be affected by the newly added API. Change-Id: I85d2ae132fc3b7b6d157faf0a7390e31fdc7e6da Signed-off-by: Praveen Chidambaram <pchidamb@codeaurora.org> Conflicts: include/linux/thermal.h
-rw-r--r--drivers/thermal/thermal_core.c277
-rw-r--r--include/linux/thermal.h41
2 files changed, 316 insertions, 2 deletions
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index f392eb8fccd5..00dd5b7cbf7d 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -4,6 +4,7 @@
* Copyright (C) 2008 Intel Corp
* Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
* Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
@@ -203,6 +204,273 @@ exit:
return;
}
+static LIST_HEAD(sensor_info_list);
+static DEFINE_MUTEX(sensor_list_lock);
+
+static struct sensor_info *get_sensor(uint32_t sensor_id)
+{
+ struct sensor_info *pos, *var;
+
+ list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
+ if (pos->sensor_id == sensor_id)
+ break;
+ }
+
+ return pos;
+}
+
+int sensor_get_id(char *name)
+{
+ struct sensor_info *pos, *var;
+
+ list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
+ if (!strcmp(pos->tz->type, name))
+ return pos->sensor_id;
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(sensor_get_id);
+
+static void __update_sensor_thresholds(struct sensor_info *sensor)
+{
+ int min = INT_MIN;
+ int max = INT_MAX;
+ struct sensor_threshold *pos, *var;
+ enum thermal_trip_type type;
+ int i;
+ unsigned long curr_temp;
+
+ for (i = 0; ((sensor->max_idx == -1) || (sensor->min_idx == -1)) &&
+ (sensor->tz->ops->get_trip_type) && (i < sensor->tz->trips);
+ i++) {
+ sensor->tz->ops->get_trip_type(sensor->tz, i, &type);
+ if (type == THERMAL_TRIP_CONFIGURABLE_HI)
+ sensor->max_idx = i;
+ if (type == THERMAL_TRIP_CONFIGURABLE_LOW)
+ sensor->min_idx = i;
+ }
+
+ get_cpu();
+ sensor->tz->ops->get_temp(sensor->tz, &curr_temp);
+ list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
+ if ((pos->trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
+ (pos->temp < (int)curr_temp))
+ if (pos->temp > min)
+ min = pos->temp;
+ if ((pos->trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
+ (pos->temp > (int)curr_temp))
+ if (pos->temp < max)
+ max = pos->temp;
+ }
+ put_cpu();
+
+ if (sensor->tz->ops->set_trip_temp) {
+ if (max != INT_MAX) {
+ sensor->tz->ops->set_trip_temp(sensor->tz,
+ sensor->max_idx, max);
+ sensor->threshold_max = max;
+ }
+ if (min != INT_MIN) {
+ sensor->tz->ops->set_trip_temp(sensor->tz,
+ sensor->min_idx, min);
+ sensor->threshold_min = min;
+ }
+ }
+
+ pr_debug("sensor %d, min: %d, max %d\n", sensor->sensor_id, min, max);
+}
+
+static void sensor_update_work(struct work_struct *work)
+{
+ struct sensor_info *sensor = container_of(work, struct sensor_info,
+ work);
+ mutex_lock(&sensor->lock);
+ __update_sensor_thresholds(sensor);
+ mutex_unlock(&sensor->lock);
+}
+
+/* May be called in an interrupt context.
+ * Do NOT call sensor_set_trip from this function
+ */
+int thermal_sensor_trip(struct thermal_zone_device *tz,
+ enum thermal_trip_type trip, unsigned long temp)
+{
+ struct sensor_threshold *pos, *var;
+ int ret = -ENODEV;
+
+ if (trip != THERMAL_TRIP_CONFIGURABLE_HI &&
+ trip != THERMAL_TRIP_CONFIGURABLE_LOW)
+ return 0;
+
+ if (list_empty(&tz->sensor.threshold_list))
+ return 0;
+
+ list_for_each_entry_safe(pos, var, &tz->sensor.threshold_list, list) {
+ if (pos->trip != trip)
+ continue;
+ if (((trip == THERMAL_TRIP_CONFIGURABLE_LOW) &&
+ (pos->temp <= tz->sensor.threshold_min) &&
+ (pos->temp >= (int) temp)) ||
+ ((trip == THERMAL_TRIP_CONFIGURABLE_HI) &&
+ (pos->temp >= tz->sensor.threshold_max) &&
+ (pos->temp <= (int)temp))) {
+ pos->notify(trip, temp, pos->data);
+ }
+ }
+
+ schedule_work(&tz->sensor.work);
+
+ return ret;
+}
+EXPORT_SYMBOL(thermal_sensor_trip);
+
+int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
+{
+ struct sensor_threshold *pos, *var;
+ struct sensor_info *sensor = get_sensor(sensor_id);
+
+ if (!sensor)
+ return -ENODEV;
+
+ if (!threshold || !threshold->notify)
+ return -EFAULT;
+
+ mutex_lock(&sensor->lock);
+ list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
+ if (pos == threshold)
+ break;
+ }
+
+ if (pos != threshold) {
+ INIT_LIST_HEAD(&threshold->list);
+ list_add(&threshold->list, &sensor->threshold_list);
+ }
+
+ __update_sensor_thresholds(sensor);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+
+}
+EXPORT_SYMBOL(sensor_set_trip);
+
+int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold)
+{
+ struct sensor_threshold *pos, *var;
+ struct sensor_info *sensor = get_sensor(sensor_id);
+
+ if (!sensor)
+ return -ENODEV;
+
+ mutex_lock(&sensor->lock);
+ list_for_each_entry_safe(pos, var, &sensor->threshold_list, list) {
+ if (pos == threshold) {
+ list_del(&pos->list);
+ break;
+ }
+ }
+
+ __update_sensor_thresholds(sensor);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(sensor_cancel_trip);
+
+static int sensor_get_trip_temp(struct thermal_zone_device *tz,
+ int type, unsigned long *temp)
+{
+ struct sensor_info *sensor = get_sensor(tz->id);
+
+ if (!sensor)
+ return -EFAULT;
+
+ switch (type) {
+ case THERMAL_TRIP_CONFIGURABLE_HI:
+ *temp = tz->sensor.threshold_max;
+ break;
+ case THERMAL_TRIP_CONFIGURABLE_LOW:
+ *temp = tz->sensor.threshold_min;
+ break;
+ default:
+ tz->ops->get_trip_temp(tz, type, temp);
+ break;
+ }
+
+ return 0;
+}
+
+static int tz_notify_trip(enum thermal_trip_type type, int temp, void *data)
+{
+ struct thermal_zone_device *tz = (struct thermal_zone_device *)data;
+
+ pr_debug("sensor %d tripped: type %d temp %d\n",
+ tz->sensor.sensor_id, type, temp);
+
+ return 0;
+}
+
+int sensor_set_trip_temp(struct thermal_zone_device *tz,
+ int trip, long temp)
+{
+ int ret = 0;
+ enum thermal_trip_type type;
+
+ if (!tz->ops->get_trip_type)
+ return -EPERM;
+
+ tz->ops->get_trip_type(tz, trip, &type);
+ switch (type) {
+ case THERMAL_TRIP_CONFIGURABLE_HI:
+ tz->tz_threshold[0].temp = temp;
+ tz->tz_threshold[0].trip = THERMAL_TRIP_CONFIGURABLE_HI;
+ tz->tz_threshold[0].notify = tz_notify_trip;
+ tz->tz_threshold[0].data = tz;
+ ret = sensor_set_trip(tz->sensor.sensor_id,
+ &tz->tz_threshold[0]);
+ break;
+ case THERMAL_TRIP_CONFIGURABLE_LOW:
+ tz->tz_threshold[1].temp = temp;
+ tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
+ tz->tz_threshold[1].notify = tz_notify_trip;
+ tz->tz_threshold[1].data = tz;
+ ret = sensor_set_trip(tz->sensor.sensor_id,
+ &tz->tz_threshold[1]);
+ break;
+ default:
+ ret = tz->ops->set_trip_temp(tz, trip, temp);
+ break;
+ }
+
+ return ret;
+}
+
+int sensor_init(struct thermal_zone_device *tz)
+{
+ struct sensor_info *sensor = &tz->sensor;
+
+ sensor->sensor_id = tz->id;
+ sensor->tz = tz;
+ sensor->threshold_min = 0;
+ sensor->threshold_max = INT_MAX;
+ sensor->max_idx = -1;
+ sensor->min_idx = -1;
+ mutex_init(&sensor->lock);
+ INIT_LIST_HEAD(&sensor->sensor_list);
+ INIT_LIST_HEAD(&sensor->threshold_list);
+ INIT_LIST_HEAD(&tz->tz_threshold[0].list);
+ INIT_LIST_HEAD(&tz->tz_threshold[1].list);
+ tz->tz_threshold[0].notify = NULL;
+ tz->tz_threshold[0].data = NULL;
+ tz->tz_threshold[1].notify = NULL;
+ tz->tz_threshold[1].data = NULL;
+ list_add(&sensor->sensor_list, &sensor_info_list);
+ INIT_WORK(&sensor->work, sensor_update_work);
+
+ return 0;
+}
+
static int get_idr(struct idr *idr, struct mutex *lock, int *id)
{
int ret;
@@ -718,7 +986,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
if (kstrtoul(buf, 10, &temperature))
return -EINVAL;
- ret = tz->ops->set_trip_temp(tz, trip, temperature);
+ ret = sensor_set_trip_temp(tz, trip, temperature);
return ret ? ret : count;
}
@@ -737,7 +1005,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip))
return -EINVAL;
- ret = tz->ops->get_trip_temp(tz, trip, &temperature);
+ ret = sensor_get_trip_temp(tz, trip, &temperature);
if (ret)
return ret;
@@ -1937,6 +2205,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
mutex_lock(&thermal_list_lock);
list_add_tail(&tz->node, &thermal_tz_list);
+ sensor_init(tz);
mutex_unlock(&thermal_list_lock);
/* Bind cooling devices for this zone */
@@ -2015,6 +2284,10 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
thermal_set_governor(tz, NULL);
thermal_remove_hwmon_sysfs(tz);
+ flush_work(&tz->sensor.work);
+ mutex_lock(&thermal_list_lock);
+ list_del(&tz->sensor.sensor_list);
+ mutex_unlock(&thermal_list_lock);
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
idr_destroy(&tz->idr);
mutex_destroy(&tz->lock);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index f11670b48878..03eec380f7f8 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -153,6 +153,27 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH];
};
+struct sensor_threshold {
+ int temp;
+ enum thermal_trip_type trip;
+ int (*notify)(enum thermal_trip_type type, int temp, void *data);
+ void *data;
+ struct list_head list;
+};
+
+struct sensor_info {
+ uint32_t sensor_id;
+ struct thermal_zone_device *tz;
+ int threshold_min;
+ int threshold_max;
+ int max_idx;
+ int min_idx;
+ struct list_head sensor_list;
+ struct list_head threshold_list;
+ struct mutex lock;
+ struct work_struct work;
+};
+
/**
* struct thermal_zone_device - structure for a thermal zone
* @id: unique id number for each thermal zone
@@ -213,6 +234,8 @@ struct thermal_zone_device {
struct mutex lock;
struct list_head node;
struct delayed_work poll_queue;
+ struct sensor_threshold tz_threshold[2];
+ struct sensor_info sensor;
};
/**
@@ -423,6 +446,12 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
struct thermal_cooling_device *, int);
void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
+int sensor_get_id(char *name);
+int sensor_set_trip(uint32_t sensor_id, struct sensor_threshold *threshold);
+int sensor_cancel_trip(uint32_t sensor_id, struct sensor_threshold *threshold);
+int thermal_sensor_trip(struct thermal_zone_device *tz,
+ enum thermal_trip_type trip, unsigned long temp);
+
#else
static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev)
{ return false; }
@@ -485,6 +514,18 @@ static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
static inline void thermal_notify_framework(struct thermal_zone_device *tz,
int trip)
{ }
+static inline int sensor_get_id(char *name){ return -ENODEV;}
+static inline int sensor_set_trip(uint32_t sensor_id,
+ struct sensor_threshold *threshold)
+{ return ENODEV;}
+static inline int sensor_cancel_trip(uint32_t sensor_id,
+ struct sensor_threshold *threshold)
+{ return -ENODEV;}
+
+static inline int thermal_sensor_trip(struct thermal_zone_device *tz,
+ enum thermal_trip_type trip, unsigned long temp)
+{ return -ENODEV;}
+
#endif /* CONFIG_THERMAL */
#if defined(CONFIG_NET) && IS_ENABLED(CONFIG_THERMAL)