2014-01-16 18:28:37

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 00/10] Thermal Framework Enhancements

This patch set is a v5 of the previous versions submitted here:
[v4]: http://lwn.net/Articles/568988/
[v3]: https://lkml.org/lkml/2013/2/5/228
[v2]: http://lwn.net/Articles/531720/
[v1]: https://lkml.org/lkml/2012/12/18/108
[RFC]:https://patchwork.kernel.org/patch/1758921/

This patch set is based on Rui's -next tree, on top
of commit 'c698a4492f01127ca90fc28cd5157f3d616fe4ff'
This is tested on a Core-i5 running ubuntu 12.04.

Changes since v4:
* Cleaned up the thermal_release function; Now all
kfree's are done in _unregister functions.
* Kconfig option to selectively enable and use this
new version of thermal_core.c
* According to a previous comment from Rui, moved all
new API's into a file named thermal_core_new.c
Changes since v3:
* Added a patch to conditionally do kfree(cdev) in
thermal_release function.
* Reworked all sysfs attributes to have one value per file
This includes sensor_trip_* and map_weight* attributes.
* Added 'lock' variable in thermal_zone structure to
protect its members.
* Added Documentation to all functions in thermal_core.c
* Changes all strcpy() to strlcpy()
* Used devm_kzalloc() in places where applicable
* Address some buffer overflow conditions and contentions
in tz->sensors[] and tz->cdevs[].
Changes since v2:
* Reworked the map sysfs attributes in patch [5/8]
* Dropped configuration for maximum sensors and
cooling devices, through Kconfig.
* Added __remove_trip_attr method
* Renamed __clean_map_entry to __remove_map_entry
for consistency in naming.
Changes Since v1:
* Removed kobject creation for thermal_trip and thermal_map
nodes as per Greg-KH's comments.
* Added ABI Documentation under 'testing'.
* Modified the GET_INDEX macro to be more linux-like, thanks
to Joe Perches.
* Added get_[sensor/cdev]_by_name APIs to thermal.h

This series contains 10 patches:
Patch 1/10: Remove code from thermal_release function.
Do kfree in _unregister functions.
Patch 2/10: Creates new sensor level APIs
Patch 3/10: Adds new cooling device APIs
Patch 4/10: Creates new zone level APIs. The existing tzd structure is
kept as such for clarity and compatibility purposes.
Patch 5/10: Creates functions to add/remove a cdev to/from a zone. The
existing tcd structure need not be modified.
Patch 6/10: Adds sensorX_trip_[active/passive/hot/critical] sysfs nodes,
under /sys/class/thermal/zoneY/. This exposes various trip
points for sensorX present in zoneY.
Patch 7/10: Adds mapY_* sysfs node. These attributes represent the
relationship between various sensors and cooling
devices in a thermal zone.
Patch 8/10: Creates Documentation for the new APIs. A new file is
created for clarity. Final goal is to merge with the existing
file or refactor the files, as whatever seems appropriate.
Patch 9/10: Add ABI documentation for sysfs interfaces introduced in this patch.
Patch 10/10: A dummy driver that can be used for testing. This is not for merge.

Thanks to Rui for helping me in resolving some
Kconfig issues, related to this series.

Durgadoss R (10):
Thermal: Do kfree in _unregister functions
Thermal: Create sensor level APIs
Thermal: Add cooling device APIs
Thermal: Create zone level APIs
Thermal: Add APIs to bind cdev to new zone structure
Thermal: Add trip point sysfs nodes for sensor
Thermal: Create Thermal map sysfs attributes for a zone
Thermal: Add Documentation to new APIs
Thermal: Add ABI Documentation for sysfs interfaces
Thermal: Dummy driver used for testing

Documentation/ABI/testing/sysfs-class-thermal | 137 +++
Documentation/thermal/sysfs-api2.txt | 240 ++++
drivers/thermal/Kconfig | 9 +
drivers/thermal/Makefile | 4 +
drivers/thermal/thermal_core.c | 19 +-
drivers/thermal/thermal_core.h | 1 +
drivers/thermal/thermal_core_new.c | 1585 +++++++++++++++++++++++++
drivers/thermal/thermal_test.c | 322 +++++
include/linux/thermal.h | 144 ++-
9 files changed, 2447 insertions(+), 14 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-thermal
create mode 100644 Documentation/thermal/sysfs-api2.txt
create mode 100644 drivers/thermal/thermal_core_new.c
create mode 100644 drivers/thermal/thermal_test.c

--
1.7.9.5


2014-01-16 18:28:48

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 03/10] Thermal: Add cooling device APIs

This patch adds the cooling device APIs to the
new thermal framework. The register/unregister
APIs stay the same. Below are some minimal
changes:
* Use DEVICE_ATTR_RO/RW macros
* Add 'struct idr idr' to struct thermal_cooling_device
* Tidy up the error handling paths in register API
* Use thermal_cdev_register as API name, to keep
THERMAL_V2 APIs along with the existing ones.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/thermal_core_new.c | 184 ++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 12 +++
2 files changed, 196 insertions(+)

diff --git a/drivers/thermal/thermal_core_new.c b/drivers/thermal/thermal_core_new.c
index b369a6f..ba38438 100644
--- a/drivers/thermal/thermal_core_new.c
+++ b/drivers/thermal/thermal_core_new.c
@@ -39,15 +39,21 @@ MODULE_DESCRIPTION("Generic thermal management sysfs support v2");
MODULE_LICENSE("GPL v2");

static DEFINE_IDR(thermal_sensor_idr);
+static DEFINE_IDR(thermal_cdev_idr);

static LIST_HEAD(thermal_sensor_list);
+static LIST_HEAD(thermal_cdev_list);

static DEFINE_MUTEX(thermal_idr_lock);
static DEFINE_MUTEX(sensor_list_lock);
+static DEFINE_MUTEX(cdev_list_lock);

#define to_thermal_sensor(_dev) \
container_of(_dev, struct thermal_sensor, device)

+#define to_cooling_device(_dev) \
+ container_of(_dev, struct thermal_cooling_device, device)
+
static int get_idr(struct idr *idr, int *id)
{
int ret;
@@ -168,10 +174,69 @@ threshold_store(struct device *dev, struct device_attribute *attr,
return ret ? ret : count;
}

+static ssize_t
+type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+
+ return sprintf(buf, "%s\n", cdev->type);
+}
+
+static ssize_t
+max_state_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ unsigned long state;
+ int ret;
+
+ ret = cdev->ops->get_max_state(cdev, &state);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%ld\n", state);
+}
+
+static ssize_t
+cur_state_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ unsigned long state;
+ int ret;
+
+ ret = cdev->ops->get_cur_state(cdev, &state);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%ld\n", state);
+}
+
+static ssize_t
+cur_state_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+ unsigned long state;
+ int ret;
+
+ ret = sscanf(buf, "%ld\n", &state);
+ if (!ret)
+ return -EINVAL;
+
+ if ((long)state < 0)
+ return -EINVAL;
+
+ ret = cdev->ops->set_cur_state(cdev, state);
+
+ return ret ? ret : count;
+}
+
/* Thermal sensor attributes */
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(temp);

+/* Thermal cooling device attributes */
+static DEVICE_ATTR_RO(type);
+static DEVICE_ATTR_RO(max_state);
+static DEVICE_ATTR_RW(cur_state);
+
/**
* thermal_create_sensor_sysfs - create sysfs nodes for sensorX
* @ts: the thermal sensor
@@ -365,3 +430,122 @@ void thermal_sensor_unregister(struct thermal_sensor *ts)
return;
}
EXPORT_SYMBOL_GPL(thermal_sensor_unregister);
+
+/**
+ * thermal_cdev_register() - register a new thermal cooling device
+ * @type: the thermal cooling device type.
+ * @devdata: device private data.
+ * @ops: standard thermal cooling devices callbacks.
+ *
+ * This interface function adds a new thermal cooling device to
+ * /sys/class/thermal/ folder as cooling_device[0-*].
+ *
+ * Return: a pointer to the created struct thermal_cooling_device or an
+ * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
+ */
+struct thermal_cooling_device *thermal_cdev_register(char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{
+ struct thermal_cooling_device *cdev;
+ int result;
+
+ if (!type || (type && strlen(type) >= THERMAL_NAME_LENGTH))
+ return ERR_PTR(-EINVAL);
+
+ if (!ops || !ops->get_max_state || !ops->get_cur_state ||
+ !ops->set_cur_state)
+ return ERR_PTR(-EINVAL);
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return ERR_PTR(-ENOMEM);
+
+ idr_init(&cdev->idr);
+ result = get_idr(&thermal_cdev_idr, &cdev->id);
+ if (result)
+ goto exit_free;
+
+ strlcpy(cdev->type, type ? : "", sizeof(cdev->type));
+ mutex_init(&cdev->lock);
+ cdev->ops = ops;
+ cdev->updated = true;
+ cdev->device.class = &thermal_class;
+ cdev->devdata = devdata;
+
+ dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
+ result = device_register(&cdev->device);
+ if (result)
+ goto exit_idr;
+
+ result = device_create_file(&cdev->device, &dev_attr_type);
+ if (result)
+ goto exit_unregister;
+
+ result = device_create_file(&cdev->device, &dev_attr_max_state);
+ if (result)
+ goto exit_type;
+
+ result = device_create_file(&cdev->device, &dev_attr_cur_state);
+ if (result) {
+ device_remove_file(&cdev->device, &dev_attr_max_state);
+ goto exit_type;
+ }
+
+ /* Add 'this' new cdev to the global cdev list */
+ mutex_lock(&cdev_list_lock);
+ list_add(&cdev->node, &thermal_cdev_list);
+ mutex_unlock(&cdev_list_lock);
+
+ return cdev;
+
+exit_type:
+ device_remove_file(&cdev->device, &dev_attr_type);
+exit_unregister:
+ device_unregister(&cdev->device);
+exit_idr:
+ release_idr(&thermal_cdev_idr, cdev->id);
+ idr_destroy(&cdev->idr);
+exit_free:
+ kfree(cdev);
+ return ERR_PTR(result);
+}
+EXPORT_SYMBOL_GPL(thermal_cdev_register);
+
+/**
+ * thermal_cdev_unregister - removes the registered thermal cooling device
+ * @cdev: the thermal cooling device to remove.
+ *
+ * thermal_cdev_unregister() must be called when the device is no
+ * longer needed.
+ */
+void thermal_cdev_unregister(struct thermal_cooling_device *cdev)
+{
+ struct thermal_cooling_device *pos, *next;
+ bool found = false;
+
+ if (!cdev)
+ return;
+
+ mutex_lock(&cdev_list_lock);
+ list_for_each_entry_safe(pos, next, &thermal_cdev_list, node) {
+ if (pos == cdev) {
+ list_del(&cdev->node);
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&cdev_list_lock);
+ if (!found)
+ return;
+
+ device_remove_file(&cdev->device, &dev_attr_type);
+ device_remove_file(&cdev->device, &dev_attr_max_state);
+ device_remove_file(&cdev->device, &dev_attr_cur_state);
+
+ release_idr(&thermal_cdev_idr, cdev->id);
+ idr_destroy(&cdev->idr);
+ device_unregister(&cdev->device);
+ kfree(cdev);
+ return;
+}
+EXPORT_SYMBOL_GPL(thermal_cdev_unregister);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index bb6b183..46d7dc3 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -158,6 +158,7 @@ struct thermal_cooling_device {
void *devdata;
const struct thermal_cooling_device_ops *ops;
bool updated; /* true if the cooling device does not need update */
+ struct idr idr;
struct mutex lock; /* protect thermal_instances list */
struct list_head thermal_instances;
struct list_head node;
@@ -334,6 +335,9 @@ static inline int thermal_generate_netlink_event(struct thermal_zone_device *tz,
struct thermal_sensor *thermal_sensor_register(const char *, int,
struct thermal_sensor_ops *, void *);
void thermal_sensor_unregister(struct thermal_sensor *);
+struct thermal_cooling_device *thermal_cdev_register(char *, void *,
+ const struct thermal_cooling_device_ops *);
+void thermal_cdev_unregister(struct thermal_cooling_device *);
#else
static inline struct thermal_sensor *thermal_sensor_register(const char *name,
int count, struct thermal_sensor_ops *ops, void *devdata)
@@ -341,5 +345,13 @@ static inline struct thermal_sensor *thermal_sensor_register(const char *name,
return ERR_PTR(-ENODEV);
}
static inline void thermal_sensor_unregister(struct thermal_sensor *ts) {}
+static inline struct thermal_cooling_device *thermal_cdev_register(
+ char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{
+ return ERR_PTR(-ENODEV);
+}
+static
+inline void thermal_cdev_unregister(struct thermal_cooling_device *cdev) {}
#endif
#endif /* __THERMAL_H__ */
--
1.7.9.5

2014-01-16 18:28:53

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 07/10] Thermal: Create Thermal map sysfs attributes for a zone

This patch creates a thermal map sysfs node under
/sys/class/thermal/zoneX/. The thermal map
shows the binding relationship between a sensor
and a cooling device within a particular zone.
This contains entries named mapY_trip_type,
mapY_sensor_name, mapY_cdev_name, mapY_trip_mask,
mapY_weightX.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/thermal_core_new.c | 275 ++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 25 ++++
2 files changed, 300 insertions(+)

diff --git a/drivers/thermal/thermal_core_new.c b/drivers/thermal/thermal_core_new.c
index e6a35f0..62f95c2 100644
--- a/drivers/thermal/thermal_core_new.c
+++ b/drivers/thermal/thermal_core_new.c
@@ -399,6 +399,131 @@ critical_trip_show(struct device *dev,
return sprintf(buf, "%d\n", val);
}

+static ssize_t
+map_ttype_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ ret = sscanf(attr->attr.name, "map%d_trip_type", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ if (indx < 0 || indx >= MAX_MAPS_PER_ZONE)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->map[indx])
+ goto exit;
+
+ ret = sprintf(buf, "%s\n",
+ tz->map[indx]->trip_type == THERMAL_TRIP_ACTIVE ?
+ "active" : "passive");
+exit:
+ mutex_unlock(&tz->lock);
+ return ret;
+}
+
+static ssize_t map_ts_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ ret = sscanf(attr->attr.name, "map%d_sensor_name", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ if (indx < 0 || indx >= MAX_MAPS_PER_ZONE)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->map[indx])
+ goto exit;
+
+ ret = sprintf(buf, "%s\n", tz->map[indx]->sensor_name);
+exit:
+ mutex_unlock(&tz->lock);
+ return ret;
+}
+
+static ssize_t map_cdev_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ ret = sscanf(attr->attr.name, "map%d_cdev_name", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ if (indx < 0 || indx >= MAX_MAPS_PER_ZONE)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->map[indx])
+ goto exit;
+
+ ret = sprintf(buf, "%s\n", tz->map[indx]->cdev_name);
+exit:
+ mutex_unlock(&tz->lock);
+ return ret;
+}
+
+static ssize_t map_trip_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ ret = sscanf(attr->attr.name, "map%d_trip_mask", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ if (indx < 0 || indx >= MAX_MAPS_PER_ZONE)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->map[indx])
+ goto exit;
+
+ ret = sprintf(buf, "0x%x\n", tz->map[indx]->trip_mask);
+exit:
+ mutex_unlock(&tz->lock);
+ return ret;
+}
+
+static ssize_t map_weights_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, j, ret;
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ ret = sscanf(attr->attr.name, "map%d_weight%d", &i, &j);
+ if (!ret)
+ return -EINVAL;
+
+ if (i < 0 || i >= MAX_MAPS_PER_ZONE)
+ return -EINVAL;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->map[i])
+ goto exit;
+
+ if (j < 0 || j >= tz->map[i]->num_weights)
+ goto exit;
+
+ ret = sprintf(buf, "%d\n", tz->map[i]->weights[j]);
+exit:
+ mutex_unlock(&tz->lock);
+ return ret;
+}
+
/* Thermal sensor attributes */
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(temp);
@@ -446,6 +571,24 @@ static void __remove_trip_attr(struct thermal_zone *tz, int indx)
tz->trip_attr[indx] = NULL;
}

+static void __remove_map_entry(struct thermal_zone *tz, int indx)
+{
+ int i;
+ struct thermal_map_attr *attr;
+
+ attr = tz->map_attr[indx];
+
+ for (i = 0; i < NUM_MAP_ATTRS; i++)
+ device_remove_file(&tz->device, &attr->attrs[i].attr);
+
+ for (i = 0; i < tz->map[indx]->num_weights; i++)
+ device_remove_file(&tz->device, &attr->weights_attr[i].attr);
+
+ kfree(tz->map_attr[indx]);
+ tz->map_attr[indx] = NULL;
+ tz->map[indx] = NULL;
+}
+
static void remove_sensor_from_zone(struct thermal_zone *tz,
struct thermal_sensor *ts)
{
@@ -470,6 +613,14 @@ static void remove_sensor_from_zone(struct thermal_zone *tz,
}

tz->sensor_indx--;
+
+ /* Remove all mappings associated with this sensor */
+ for (j = 0; j < MAX_MAPS_PER_ZONE; j++) {
+ if (tz->map[j] && !strnicmp(ts->name, tz->map[j]->sensor_name,
+ THERMAL_NAME_LENGTH)) {
+ __remove_map_entry(tz, j);
+ }
+ }
mutex_unlock(&tz->lock);
}

@@ -491,6 +642,14 @@ static void remove_cdev_from_zone(struct thermal_zone *tz,
tz->cdevs[j] = tz->cdevs[j + 1];

tz->cdev_indx--;
+
+ /* Remove all mappings associated with this sensor */
+ for (j = 0; j < MAX_MAPS_PER_ZONE; j++) {
+ if (tz->map[j] && !strnicmp(cdev->type, tz->map[j]->cdev_name,
+ THERMAL_NAME_LENGTH)) {
+ __remove_map_entry(tz, j);
+ }
+ }
mutex_unlock(&tz->lock);
}

@@ -652,6 +811,71 @@ exit:
return ret;
}

+static int create_map_weight_attrs(struct thermal_zone *tz, int num_wts,
+ int indx, struct thermal_map_attr *attr)
+{
+ int i, ret, size;
+ char name[THERMAL_NAME_LENGTH];
+
+ if (!attr || num_wts <= 0)
+ return 0;
+
+ size = sizeof(struct thermal_attr) * num_wts;
+ attr->weights_attr = kzalloc(size, GFP_KERNEL);
+ if (!attr->weights_attr)
+ return -ENOMEM;
+
+ for (i = 0; i < num_wts; i++) {
+ snprintf(name, THERMAL_NAME_LENGTH,
+ "map%d_weight%d", indx, i);
+ ret = create_single_trip_attr(tz, &attr->weights_attr[i],
+ name, map_weights_show);
+ if (ret)
+ goto exit;
+ }
+ return 0;
+
+exit:
+ while (--i >= 0)
+ device_remove_file(&tz->device, &attr->weights_attr[i].attr);
+ kfree(attr->weights_attr);
+ return ret;
+}
+
+static int create_map_attrs(struct thermal_zone *tz, int indx, int num_wts)
+{
+ int i, ret;
+ char name[THERMAL_NAME_LENGTH];
+ struct thermal_map_attr *attr = tz->map_attr[indx];
+
+ static const char *const attr_names[NUM_MAP_ATTRS] = {
+ "map%d_trip_type", "map%d_sensor_name",
+ "map%d_cdev_name", "map%d_trip_mask"};
+
+ static ssize_t (*const rd_ptr[NUM_MAP_ATTRS]) (struct device *dev,
+ struct device_attribute *devattr, char *buf) = {
+ map_ttype_show, map_ts_name_show,
+ map_cdev_name_show, map_trip_mask_show};
+
+ for (i = 0; i < NUM_MAP_ATTRS; i++) {
+ snprintf(name, THERMAL_NAME_LENGTH, attr_names[i], indx);
+ ret = create_single_trip_attr(tz, &attr->attrs[i],
+ name, rd_ptr[i]);
+ if (ret)
+ goto exit_free;
+ }
+
+ ret = create_map_weight_attrs(tz, num_wts, indx, attr);
+ if (ret)
+ goto exit_free;
+
+ return 0;
+exit_free:
+ while (--i >= 0)
+ device_remove_file(&tz->device, &attr->attrs[i].attr);
+ return ret;
+}
+
/**
* thermal_sensor_register - register a new thermal sensor
* @name: name of the thermal sensor
@@ -1308,3 +1532,54 @@ exit:
return ret;
}
EXPORT_SYMBOL_GPL(thermal_add_sensor_trip_info);
+
+/**
+ * thermal_add_map_entry - Add Thermal Map information for @tz
+ * @tz: Thermal zone reference
+ * @map: Thermal map reference
+ *
+ * Returns 0 on success, otherwise
+ * -EINVAL for invalid paramenters
+ * -EINVAL for array out of bounds
+ * -ENOMEM on kzalloc failures
+ */
+int thermal_add_map_entry(struct thermal_zone *tz, struct thermal_map *map)
+{
+ int ret, indx;
+
+ if (!tz || !map)
+ return -EINVAL;
+
+ mutex_lock(&zone_list_lock);
+
+ /* Find the first unused index which is NULL */
+ for (indx = 0; indx < MAX_MAPS_PER_ZONE; indx++) {
+ if (tz->map[indx] == NULL)
+ break;
+ }
+
+ if (indx >= MAX_MAPS_PER_ZONE) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ tz->map_attr[indx] = kzalloc(sizeof(struct thermal_map_attr),
+ GFP_KERNEL);
+ if (!tz->map_attr[indx]) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = create_map_attrs(tz, indx, map->num_weights);
+ if (ret) {
+ kfree(tz->map_attr[indx]);
+ tz->map_attr[indx] = NULL;
+ goto exit;
+ }
+
+ tz->map[indx] = map;
+exit:
+ mutex_unlock(&zone_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thermal_add_map_entry);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index fe7abc9..24231b7 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -62,6 +62,11 @@
#define MAX_SENSORS_PER_ZONE 5
#define MAX_CDEVS_PER_ZONE 5

+#define NUM_MAP_ATTRS 4
+
+/* If we map each sensor with every possible cdev for a zone */
+#define MAX_MAPS_PER_ZONE (MAX_SENSORS_PER_ZONE * MAX_CDEVS_PER_ZONE)
+
struct thermal_sensor;
struct thermal_zone_device;
struct thermal_cooling_device;
@@ -172,6 +177,21 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH];
};

+struct thermal_map {
+ enum thermal_trip_type trip_type;
+ char cdev_name[THERMAL_NAME_LENGTH];
+ char sensor_name[THERMAL_NAME_LENGTH];
+
+ int trip_mask;
+ int num_weights;
+ int *weights;
+};
+
+struct thermal_map_attr {
+ struct thermal_attr attrs[NUM_MAP_ATTRS];
+ struct thermal_attr *weights_attr;
+};
+
/*
* This structure defines the trip points for a sensor.
* The actual values for these trip points come from
@@ -266,6 +286,10 @@ struct thermal_zone {
/* Thermal sensors trip information */
struct thermal_trip_point *sensor_trip[MAX_SENSORS_PER_ZONE];
struct thermal_trip_attr *trip_attr[MAX_SENSORS_PER_ZONE];
+
+ /* Thermal map information */
+ struct thermal_map *map[MAX_MAPS_PER_ZONE];
+ struct thermal_map_attr *map_attr[MAX_MAPS_PER_ZONE];
};

/* Structure that holds thermal governor information */
@@ -387,6 +411,7 @@ int thermal_add_cdev_to_zone(struct thermal_zone *,
struct thermal_cooling_device *thermal_get_cdev_by_name(const char *);
int thermal_add_sensor_trip_info(struct thermal_zone *,
struct thermal_sensor *, struct thermal_trip_point *);
+int thermal_add_map_entry(struct thermal_zone *, struct thermal_map *);

#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
--
1.7.9.5

2014-01-16 18:28:58

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 09/10] Thermal: Add ABI Documentation for sysfs interfaces

This patch adds Documentation for ABI's introduced
for thermal subsystem (under /sys/class/thermal/).

Signed-off-by: Durgadoss R <[email protected]>
---
Documentation/ABI/testing/sysfs-class-thermal | 137 +++++++++++++++++++++++++
1 file changed, 137 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-thermal

diff --git a/Documentation/ABI/testing/sysfs-class-thermal b/Documentation/ABI/testing/sysfs-class-thermal
new file mode 100644
index 0000000..c646585
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-thermal
@@ -0,0 +1,137 @@
+What: /sys/class/thermal/sensorX/temp
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Exposes 'temperature' of a thermal sensor in mC
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/sensorX/name
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Name of the thermal sensor
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/sensorX/thresholdY
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Programmable threshold (in terms of mC). On reaching
+ this, the thermal governors may take action to control
+ temperature.
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/zone_name
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Name of the thermal zone.
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/sensorY
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Symlink to a sensor associated with this zone.
+Users: User space thermal governors or applications
+
+What: /sys/class/thermal/zoneX/cooling_deviceY
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Symlink to a cooling device associated with this zone.
+Users: User space thermal governors or applications
+
+What: /sys/class/thermal/zoneX/sensorY_trip_activeM
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Active Trip point temperature in mC for sensorY in
+ thermal zoneX.
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/sensorY_trip_passiveN
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Passive Trip point temperature in mC for sensorY in
+ thermal zoneX.
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/sensorY_trip_hot
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Hot trip point for 'sensorY' in 'zoneX' (in mC).
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/sensorY_trip_critical
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Critical trip point for 'sensorY' in 'zoneX' (in mC).
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/mapY_trip_type
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Mapping information between a sensor and a cooling device
+ in 'zoneX'. This interface provides the trip_type for a
+ particular map(Y).
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/mapY_sensor_name
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Mapping information between a sensor and a cooling device
+ in 'zoneX'. This interface provides the name of the sensor
+ used in this map(Y).
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/mapY_cdev_name
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Mapping information between a sensor and a cooling device
+ in 'zoneX'. This interface provides the name of the cooling
+ device used in this map(Y).
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/mapY_trip_mask
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Mapping information between a sensor and a cooling device
+ in 'zoneX'. This interface provides the trip point mask,
+ which defines whether or not to throttle a particular
+ cooling device. See Documentation/thermal/sysfs-api2.txt
+ for more information on this interface.
+Users: Kernel/User space thermal governors
+
+What: /sys/class/thermal/zoneX/mapY_weightN
+Date: January 2014
+KernelVersion: 3.13
+Contact: Linux PM Mailing list <[email protected]>
+Description:
+ Mapping information between a sensor and a cooling device
+ in 'zoneX'. This interface provides the weights that can
+ be used to throttle a cooling device, on thermal violations.
+ See Documentation/thermal/sysfs-api2.txt for more details on
+ this interface.
+Users: Kernel/User space thermal governors
--
1.7.9.5

2014-01-16 18:29:06

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 10/10] Thermal: Dummy driver used for testing

This patch has a dummy driver that can be used for
testing purposes. This patch is not for merge.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/Kconfig | 6 +
drivers/thermal/Makefile | 3 +
drivers/thermal/thermal_test.c | 322 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 331 insertions(+)
create mode 100644 drivers/thermal/thermal_test.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f6a8057..7bfa23e 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -225,4 +225,10 @@ depends on PLAT_SAMSUNG
source "drivers/thermal/samsung/Kconfig"
endmenu

+config THERMAL_TEST
+ tristate "test driver"
+ select THERMAL_V2
+ help
+ Enable this to test the thermal framework.
+
endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index d9ae9ac..9a7ed0e 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -32,3 +32,6 @@ obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
+
+# dummy driver for testing
+obj-$(CONFIG_THERMAL_TEST) += thermal_test.o
diff --git a/drivers/thermal/thermal_test.c b/drivers/thermal/thermal_test.c
new file mode 100644
index 0000000..6bdb22b
--- /dev/null
+++ b/drivers/thermal/thermal_test.c
@@ -0,0 +1,322 @@
+/*
+ * thermal_test.c - This driver can be used to test Thermal
+ * Framework changes. Not specific to any
+ * platform. Fills the log buffer generously ;)
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Author: Durgadoss R <[email protected]>
+ */
+
+#define pr_fmt(fmt) "thermal_test: " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/thermal.h>
+
+#define MAX_THERMAL_ZONES 2
+#define MAX_THERMAL_SENSORS 2
+#define MAX_COOLING_DEVS 4
+#define NUM_THRESHOLDS 3
+
+static struct ts_data {
+ int curr_temp;
+ int flag;
+} ts_data;
+
+int active_trips[10] = {100, 90, 80, 70, 60, 50, 40, 30, 20, 10};
+int passive_trips[5] = {100, 90, 60, 50, 40};
+int wts[5] = {50, 50, 50, 50, 40};
+
+static struct platform_device *pdev;
+static unsigned long cur_cdev_state = 2;
+static struct thermal_sensor *ts, *ts1;
+static struct thermal_zone *tz;
+static struct thermal_cooling_device *cdev;
+
+static long thermal_thresholds[NUM_THRESHOLDS] = {30000, 40000, 50000};
+
+static struct thermal_trip_point trip = {
+ .hot = 90,
+ .crit = 100,
+ .num_passive_trips = 5,
+ .passive_trips = passive_trips,
+ .num_active_trips = 10,
+ .active_trips = active_trips,
+};
+
+static struct thermal_trip_point trip1 = {
+ .hot = 95,
+ .crit = 125,
+ .num_passive_trips = 0,
+ .passive_trips = passive_trips,
+ .num_active_trips = 6,
+ .active_trips = active_trips,
+};
+
+static struct thermal_map map = {
+ .trip_type = THERMAL_TRIP_PASSIVE,
+ .sensor_name = "ts",
+ .cdev_name = "cdev",
+ .num_weights = 5,
+ .trip_mask = 0x0F,
+ .weights = wts,
+};
+
+static int read_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = cur_cdev_state;
+ return 0;
+}
+
+static int write_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ cur_cdev_state = state;
+ return 0;
+}
+
+static int read_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = 5;
+ return 0;
+}
+
+static int read_curr_temp(struct thermal_sensor *ts, long *temp)
+{
+ *temp = ts_data.curr_temp;
+ return 0;
+}
+
+static ssize_t
+flag_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%d\n", ts_data.flag);
+}
+
+static ssize_t
+flag_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long flag;
+
+ if (kstrtol(buf, 10, &flag))
+ return -EINVAL;
+
+ ts_data.flag = flag;
+
+ if (flag == 0) {
+ thermal_sensor_unregister(ts);
+ ts = NULL;
+ pr_err("thermal_sensor_unregister (ts) done\n");
+ } else if (flag == 1) {
+ thermal_sensor_unregister(ts1);
+ ts1 = NULL;
+ pr_err("thermal_sensor_unregister (ts1) done\n");
+ } else if (flag == 2) {
+ thermal_cdev_unregister(cdev);
+ cdev = NULL;
+ pr_err("cdev unregister (cdev) done\n");
+ } else if (flag == 3) {
+ if (tz)
+ thermal_remove_thermal_zone(tz);
+ tz = NULL;
+ pr_err("removed thermal zone\n");
+ }
+
+ return count;
+}
+
+static ssize_t
+temp_show(struct device *dev, struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%d\n", ts_data.curr_temp);
+}
+
+static int read_threshold(struct thermal_sensor *ts, int indx, long *val)
+{
+ if (indx < 0 || indx >= NUM_THRESHOLDS)
+ return -EINVAL;
+
+ *val = thermal_thresholds[indx];
+ return 0;
+}
+
+static int write_threshold(struct thermal_sensor *ts, int indx, long val)
+{
+ if (indx < 0 || indx >= NUM_THRESHOLDS)
+ return -EINVAL;
+
+ thermal_thresholds[indx] = val;
+ return 0;
+}
+
+static ssize_t
+temp_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long temp;
+
+ if (kstrtol(buf, 10, &temp))
+ return -EINVAL;
+
+ ts_data.curr_temp = temp;
+ return count;
+}
+
+static struct thermal_sensor_ops ts_ops = {
+ .get_temp = read_curr_temp,
+ .get_threshold = read_threshold,
+ .set_threshold = write_threshold,
+};
+
+static struct thermal_sensor_ops ts1_ops = {
+ .get_temp = read_curr_temp,
+ .get_threshold = read_threshold,
+ .set_threshold = write_threshold,
+};
+
+static struct thermal_cooling_device_ops cdev_ops = {
+ .get_cur_state = read_cur_state,
+ .set_cur_state = write_cur_state,
+ .get_max_state = read_max_state,
+};
+
+static DEVICE_ATTR(test_temp, S_IRUGO | S_IWUSR, temp_show, temp_store);
+static DEVICE_ATTR(sensor_enable, S_IRUGO | S_IWUSR, flag_show, flag_store);
+
+static int thermal_test_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ts_data.curr_temp = 30000;
+ ts_data.flag = 1;
+
+ ts = thermal_sensor_register("ts", NUM_THRESHOLDS, &ts_ops, &ts_data);
+ if (!ts) {
+ pr_err("thermal_sensor_register failed:\n");
+ return -EINVAL;
+ }
+
+ ts1 = thermal_sensor_register("ts1", NUM_THRESHOLDS, &ts1_ops, NULL);
+
+ cdev = thermal_cdev_register("cdev", NULL, &cdev_ops);
+ if (!cdev) {
+ pr_err("cdev_register failed:\n");
+ return -EINVAL;
+ }
+
+ device_create_file(&pdev->dev, &dev_attr_test_temp);
+ device_create_file(&pdev->dev, &dev_attr_sensor_enable);
+
+ /* Create a zone */
+ tz = thermal_create_thermal_zone("myZone", NULL);
+ if (!tz) {
+ pr_err("create_thermal_zone failed:\n");
+ return -EINVAL;
+ }
+
+ pr_err("Zone created successfully..\n");
+
+ ret = thermal_add_sensor_to_zone(tz, ts);
+ if (ret) {
+ pr_err("add_sensor_to_zone failed:%d\n", ret);
+ return ret;
+ }
+
+ ret = thermal_add_sensor_to_zone(tz, ts1);
+ pr_err("add_sensor (ts1) ret_val: %d\n", ret);
+
+ ret = thermal_add_cdev_to_zone(tz, cdev);
+ pr_err("add_cdev_to_zone (cdev) ret_val: %d\n", ret);
+
+ ret = thermal_add_sensor_trip_info(tz, ts, &trip);
+ ret = thermal_add_sensor_trip_info(tz, ts1, &trip1);
+ pr_err("add_sensor_trip_info (ts) ret_val: %d\n", ret);
+
+ ret = thermal_add_map_entry(tz, &map);
+ pr_err("add_map_entry (ts) ret_val: %d\n", ret);
+
+ return 0;
+}
+
+static int thermal_test_remove(struct platform_device *pdev)
+{
+ device_remove_file(&pdev->dev, &dev_attr_test_temp);
+ device_remove_file(&pdev->dev, &dev_attr_sensor_enable);
+
+ return 0;
+}
+
+/*********************************************************************
+ * Driver initialization and finalization
+ *********************************************************************/
+
+#define DRIVER_NAME "thermal_test"
+
+static const struct platform_device_id therm_id_table[] = {
+ { DRIVER_NAME, 1 },
+};
+
+static struct platform_driver thermal_test_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = thermal_test_probe,
+ .remove = thermal_test_remove,
+ .id_table = therm_id_table,
+};
+
+static int __init thermal_test_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&thermal_test_driver);
+ if (ret) {
+ pr_err("platform driver register failed:%d\n", ret);
+ return ret;
+ }
+
+ pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ ret = PTR_ERR(pdev);
+ pr_err("platform device register failed:%d\n", ret);
+ platform_driver_unregister(&thermal_test_driver);
+ }
+
+ return ret;
+}
+
+static void __exit thermal_test_exit(void)
+{
+ pr_err("in thermal_test_exit\n");
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&thermal_test_driver);
+}
+
+module_init(thermal_test_init);
+module_exit(thermal_test_exit);
+
+MODULE_AUTHOR("Durgadoss R <[email protected]>");
+MODULE_DESCRIPTION("A dummy driver to test Thermal Framework");
+MODULE_LICENSE("GPL");
--
1.7.9.5

2014-01-16 18:29:52

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 06/10] Thermal: Add trip point sysfs nodes for sensor

This patch adds a trip point related sysfs nodes
for each sensor under a zone in /sys/class/thermal/zoneX/.
The nodes will be named, sensorX_trip_activeY,
sensorX_trip_passiveY, sensorX_trip_hot, sensorX_trip_critical
for active, passive, hot and critical trip points
respectively for sensorX.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/thermal_core_new.c | 358 +++++++++++++++++++++++++++++++++++-
include/linux/thermal.h | 39 +++-
2 files changed, 395 insertions(+), 2 deletions(-)

diff --git a/drivers/thermal/thermal_core_new.c b/drivers/thermal/thermal_core_new.c
index 3134b1a..e6a35f0 100644
--- a/drivers/thermal/thermal_core_new.c
+++ b/drivers/thermal/thermal_core_new.c
@@ -111,6 +111,25 @@ static void release_idr(struct idr *idr, int id)
mutex_unlock(&thermal_idr_lock);
}

+static int get_sensor_indx_by_kobj(struct thermal_zone *tz, const char *name)
+{
+ int i, indx = -EINVAL;
+
+ /* Protect against tz->sensors[i] being unregistered */
+ mutex_lock(&sensor_list_lock);
+
+ for (i = 0; i < tz->sensor_indx; i++) {
+ if (!strnicmp(name, kobject_name(&tz->sensors[i]->device.kobj),
+ THERMAL_NAME_LENGTH)) {
+ indx = i;
+ break;
+ }
+ }
+
+ mutex_unlock(&sensor_list_lock);
+ return indx;
+}
+
static ssize_t
name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -271,6 +290,115 @@ zone_name_show(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%s\n", tz->name);
}

+static ssize_t
+active_trip_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int i, j, val;
+ char kobj_name[THERMAL_NAME_LENGTH];
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ val = sscanf(attr->attr.name, "sensor%d_trip_active%d", &i, &j);
+ if (!val)
+ return -EINVAL;
+
+ snprintf(kobj_name, THERMAL_NAME_LENGTH, "sensor%d", i);
+
+ mutex_lock(&tz->lock);
+
+ i = get_sensor_indx_by_kobj(tz, kobj_name);
+ if (i < 0) {
+ mutex_unlock(&tz->lock);
+ return i;
+ }
+
+ val = tz->sensor_trip[i]->active_trips[j];
+ mutex_unlock(&tz->lock);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+passive_trip_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int i, j, val;
+ char kobj_name[THERMAL_NAME_LENGTH];
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ val = sscanf(attr->attr.name, "sensor%d_trip_passive%d", &i, &j);
+ if (!val)
+ return -EINVAL;
+
+ snprintf(kobj_name, THERMAL_NAME_LENGTH, "sensor%d", i);
+
+ mutex_lock(&tz->lock);
+
+ i = get_sensor_indx_by_kobj(tz, kobj_name);
+ if (i < 0) {
+ mutex_unlock(&tz->lock);
+ return i;
+ }
+
+ val = tz->sensor_trip[i]->passive_trips[j];
+ mutex_unlock(&tz->lock);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+hot_trip_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int indx, val;
+ char kobj_name[THERMAL_NAME_LENGTH];
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ val = sscanf(attr->attr.name, "sensor%d_trip_hot", &indx);
+ if (!val)
+ return -EINVAL;
+
+ snprintf(kobj_name, THERMAL_NAME_LENGTH, "sensor%d", indx);
+
+ mutex_lock(&tz->lock);
+
+ indx = get_sensor_indx_by_kobj(tz, kobj_name);
+ if (indx < 0) {
+ mutex_unlock(&tz->lock);
+ return indx;
+ }
+
+ val = tz->sensor_trip[indx]->hot;
+ mutex_unlock(&tz->lock);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t
+critical_trip_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int indx, val;
+ char kobj_name[THERMAL_NAME_LENGTH];
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ val = sscanf(attr->attr.name, "sensor%d_trip_critical", &indx);
+ if (!val)
+ return -EINVAL;
+
+ snprintf(kobj_name, THERMAL_NAME_LENGTH, "sensor%d", indx);
+
+ mutex_lock(&tz->lock);
+
+ indx = get_sensor_indx_by_kobj(tz, kobj_name);
+ if (indx < 0) {
+ mutex_unlock(&tz->lock);
+ return indx;
+ }
+
+ val = tz->sensor_trip[indx]->crit;
+ mutex_unlock(&tz->lock);
+
+ return sprintf(buf, "%d\n", val);
+}
+
/* Thermal sensor attributes */
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(temp);
@@ -283,6 +411,41 @@ static DEVICE_ATTR_RW(cur_state);
/* Thermal zone attributes */
static DEVICE_ATTR_RO(zone_name);

+static void __remove_trip_attr(struct thermal_zone *tz, int indx)
+{
+ int i;
+ struct thermal_trip_attr *attr = tz->trip_attr[indx];
+ struct thermal_trip_point *trip = tz->sensor_trip[indx];
+
+ if (!attr || !trip)
+ return;
+
+ if (trip->crit != THERMAL_TRIPS_NONE)
+ device_remove_file(&tz->device, &attr->crit_attr.attr);
+
+ if (trip->hot != THERMAL_TRIPS_NONE)
+ device_remove_file(&tz->device, &attr->hot_attr.attr);
+
+ if (trip->num_passive_trips > 0) {
+ for (i = 0; i < trip->num_passive_trips; i++) {
+ device_remove_file(&tz->device,
+ &attr->passive_attrs[i].attr);
+ }
+ kfree(attr->passive_attrs);
+ }
+
+ if (trip->num_active_trips > 0) {
+ for (i = 0; i < trip->num_active_trips; i++) {
+ device_remove_file(&tz->device,
+ &attr->active_attrs[i].attr);
+ }
+ kfree(attr->active_attrs);
+ }
+
+ kfree(tz->trip_attr[indx]);
+ tz->trip_attr[indx] = NULL;
+}
+
static void remove_sensor_from_zone(struct thermal_zone *tz,
struct thermal_sensor *ts)
{
@@ -296,9 +459,15 @@ static void remove_sensor_from_zone(struct thermal_zone *tz,

mutex_lock(&tz->lock);

+ /* Remove trip point attributes associated with this sensor */
+ __remove_trip_attr(tz, indx);
+
/* Shift the entries in the tz->sensors array */
- for (j = indx; j < MAX_SENSORS_PER_ZONE - 1; j++)
+ for (j = indx; j < MAX_SENSORS_PER_ZONE - 1; j++) {
tz->sensors[j] = tz->sensors[j + 1];
+ tz->sensor_trip[j] = tz->sensor_trip[j + 1];
+ tz->trip_attr[j] = tz->trip_attr[j + 1];
+ }

tz->sensor_indx--;
mutex_unlock(&tz->lock);
@@ -407,6 +576,82 @@ exit_name:
return ret;
}

+static int create_single_trip_attr(struct thermal_zone *tz,
+ struct thermal_attr *attr,
+ const char *attr_name,
+ ssize_t (*rd_ptr)(struct device *dev,
+ struct device_attribute *devattr, char *buf))
+{
+ snprintf(attr->name, THERMAL_NAME_LENGTH, attr_name);
+ sysfs_attr_init(&attr->attr.attr);
+ attr->attr.attr.name = attr->name;
+ attr->attr.attr.mode = S_IRUGO;
+ attr->attr.show = rd_ptr;
+ return device_create_file(&tz->device, &attr->attr);
+}
+
+static int create_passive_trip_attrs(struct thermal_zone *tz, int num_trips,
+ int indx, struct thermal_trip_attr *attrs)
+{
+ char name[THERMAL_NAME_LENGTH];
+ int size, i, ret;
+
+ if (num_trips <= 0)
+ return 0;
+
+ size = sizeof(struct thermal_attr) * num_trips;
+ attrs->passive_attrs = kzalloc(size, GFP_KERNEL);
+ if (!attrs->passive_attrs)
+ return -ENOMEM;
+
+ for (i = 0; i < num_trips; i++) {
+ snprintf(name, THERMAL_NAME_LENGTH,
+ "sensor%d_trip_passive%d", indx, i);
+ ret = create_single_trip_attr(tz, &attrs->passive_attrs[i],
+ name, passive_trip_show);
+ if (ret)
+ goto exit;
+ }
+ return 0;
+
+exit:
+ while (--i >= 0)
+ device_remove_file(&tz->device, &attrs->passive_attrs[i].attr);
+ kfree(attrs->passive_attrs);
+ return ret;
+}
+
+static int create_active_trip_attrs(struct thermal_zone *tz, int num_trips,
+ int indx, struct thermal_trip_attr *attrs)
+{
+ char name[THERMAL_NAME_LENGTH];
+ int size, i, ret;
+
+ if (num_trips <= 0)
+ return 0;
+
+ size = sizeof(struct thermal_attr) * num_trips;
+ attrs->active_attrs = kzalloc(size, GFP_KERNEL);
+ if (!attrs->active_attrs)
+ return -ENOMEM;
+
+ for (i = 0; i < num_trips; i++) {
+ snprintf(name, THERMAL_NAME_LENGTH,
+ "sensor%d_trip_active%d", indx, i);
+ ret = create_single_trip_attr(tz, &attrs->active_attrs[i],
+ name, active_trip_show);
+ if (ret)
+ goto exit;
+ }
+ return 0;
+
+exit:
+ while (--i >= 0)
+ device_remove_file(&tz->device, &attrs->active_attrs[i].attr);
+ kfree(attrs->active_attrs);
+ return ret;
+}
+
/**
* thermal_sensor_register - register a new thermal sensor
* @name: name of the thermal sensor
@@ -952,3 +1197,114 @@ exit_zone:
return ret;
}
EXPORT_SYMBOL_GPL(thermal_add_cdev_to_zone);
+
+/**
+ * thermal_add_sensor_trip_info - Add trip point information for @ts in @tz
+ * @tz: Thermal zone reference
+ * @ts: Thermal sensor reference
+ * @trip: Trip point structure reference
+ *
+ * Returns 0 on success, otherwise
+ * -EINVAL for invalid paramenters
+ * -EINVAL if @ts is not part of 'this' thermal zone @tz
+ * -ENOMEM on kzalloc failures
+ */
+int thermal_add_sensor_trip_info(struct thermal_zone *tz,
+ struct thermal_sensor *ts, struct thermal_trip_point *trip)
+{
+ char name[THERMAL_NAME_LENGTH];
+ int i, indx, kobj_indx, ret;
+ struct thermal_trip_attr *attrs;
+
+ if (!tz || !ts || !trip)
+ return -EINVAL;
+
+ ret = sscanf(kobject_name(&ts->device.kobj), "sensor%d", &kobj_indx);
+ if (!ret)
+ return -EINVAL;
+
+ mutex_lock(&zone_list_lock);
+
+ indx = GET_INDEX(tz, ts, sensor);
+ if (indx < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Protect against 'ts' being unregistered */
+ mutex_lock(&sensor_list_lock);
+
+ /* Protect tz->trip_attr[] and tz->sensor_trip[] */
+ mutex_lock(&tz->lock);
+
+ tz->trip_attr[indx] = kzalloc(sizeof(struct thermal_trip_attr),
+ GFP_KERNEL);
+ if (!tz->trip_attr[indx]) {
+ ret = -ENOMEM;
+ goto exit_lock;
+ }
+
+ attrs = tz->trip_attr[indx];
+
+ /* Create Critical trip point attribute */
+ if (trip->crit != THERMAL_TRIPS_NONE) {
+ snprintf(name, THERMAL_NAME_LENGTH,
+ "sensor%d_trip_critical", kobj_indx);
+ ret = create_single_trip_attr(tz, &attrs->crit_attr,
+ name, critical_trip_show);
+ if (ret)
+ goto exit_trip;
+ }
+
+ /* Create Hot trip point attribute */
+ if (trip->hot != THERMAL_TRIPS_NONE) {
+ snprintf(name, THERMAL_NAME_LENGTH,
+ "sensor%d_trip_hot", kobj_indx);
+ ret = create_single_trip_attr(tz, &attrs->hot_attr,
+ name, hot_trip_show);
+ if (ret)
+ goto exit_crit_trip;
+ }
+
+ /* Create Passive trip point attributes */
+ ret = create_passive_trip_attrs(tz, trip->num_passive_trips,
+ kobj_indx, attrs);
+ if (ret)
+ goto exit_hot_trip;
+
+ /* Create Active trip point attributes */
+ ret = create_active_trip_attrs(tz, trip->num_active_trips,
+ kobj_indx, attrs);
+ if (ret)
+ goto exit_passive_trips;
+
+ tz->sensor_trip[indx] = trip;
+
+ mutex_unlock(&tz->lock);
+ mutex_unlock(&sensor_list_lock);
+ mutex_unlock(&zone_list_lock);
+
+ return 0;
+
+exit_passive_trips:
+ i = trip->num_passive_trips;
+ while (--i >= 0)
+ device_remove_file(&tz->device, &attrs->passive_attrs[i].attr);
+ kfree(attrs->passive_attrs);
+exit_hot_trip:
+ if (trip->hot != THERMAL_TRIPS_NONE)
+ device_remove_file(&tz->device, &attrs->hot_attr.attr);
+exit_crit_trip:
+ if (trip->crit != THERMAL_TRIPS_NONE)
+ device_remove_file(&tz->device, &attrs->crit_attr.attr);
+exit_trip:
+ kfree(tz->trip_attr[indx]);
+ tz->trip_attr[indx] = NULL;
+exit_lock:
+ mutex_unlock(&tz->lock);
+ mutex_unlock(&sensor_list_lock);
+exit:
+ mutex_unlock(&zone_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thermal_add_sensor_trip_info);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 9b080f2..fe7abc9 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -32,7 +32,7 @@

#define THERMAL_TRIPS_NONE -1
#define THERMAL_MAX_TRIPS 12
-#define THERMAL_NAME_LENGTH 20
+#define THERMAL_NAME_LENGTH 25

/* invalid cooling state */
#define THERMAL_CSTATE_INVALID -1UL
@@ -172,6 +172,37 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH];
};

+/*
+ * This structure defines the trip points for a sensor.
+ * The actual values for these trip points come from
+ * platform characterization. The thermal governors
+ * (either kernel or user space) may take appropriate
+ * actions when the sensors reach these trip points.
+ * See Documentation/thermal/sysfs-api2.txt for more details.
+ *
+ * As of now, For a particular sensor, we support:
+ * a) 1 hot trip point
+ * b) 1 critical trip point
+ * c) 'n' passive trip points
+ * d) 'm' active trip points
+ */
+struct thermal_trip_point {
+ int hot;
+ int crit;
+ int num_passive_trips;
+ int *passive_trips;
+ int num_active_trips;
+ int *active_trips;
+ int active_trip_mask;
+};
+
+struct thermal_trip_attr {
+ struct thermal_attr hot_attr;
+ struct thermal_attr crit_attr;
+ struct thermal_attr *active_attrs;
+ struct thermal_attr *passive_attrs;
+};
+
struct thermal_sensor {
char name[THERMAL_NAME_LENGTH];
int id;
@@ -231,6 +262,10 @@ struct thermal_zone {
/* cdev level information */
int cdev_indx; /* index into 'cdevs' array */
struct thermal_cooling_device *cdevs[MAX_CDEVS_PER_ZONE];
+
+ /* Thermal sensors trip information */
+ struct thermal_trip_point *sensor_trip[MAX_SENSORS_PER_ZONE];
+ struct thermal_trip_attr *trip_attr[MAX_SENSORS_PER_ZONE];
};

/* Structure that holds thermal governor information */
@@ -350,6 +385,8 @@ struct thermal_sensor *thermal_get_sensor_by_name(const char *);
int thermal_add_cdev_to_zone(struct thermal_zone *,
struct thermal_cooling_device *);
struct thermal_cooling_device *thermal_get_cdev_by_name(const char *);
+int thermal_add_sensor_trip_info(struct thermal_zone *,
+ struct thermal_sensor *, struct thermal_trip_point *);

#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
--
1.7.9.5

2014-01-16 18:29:50

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 08/10] Thermal: Add Documentation to new APIs

This patch adds Documentation for the new APIs
introduced in this patch set. The documentation
also has a model sysfs structure for reference.

Signed-off-by: Durgadoss R <[email protected]>
---
Documentation/thermal/sysfs-api2.txt | 240 ++++++++++++++++++++++++++++++++++
1 file changed, 240 insertions(+)
create mode 100644 Documentation/thermal/sysfs-api2.txt

diff --git a/Documentation/thermal/sysfs-api2.txt b/Documentation/thermal/sysfs-api2.txt
new file mode 100644
index 0000000..dd03297
--- /dev/null
+++ b/Documentation/thermal/sysfs-api2.txt
@@ -0,0 +1,240 @@
+Thermal Framework
+-----------------
+
+Written by Durgadoss R <[email protected]>
+Copyright (c) 2014 Intel Corporation
+
+Created on: 15 January 2014
+
+0. Introduction
+---------------
+The Linux thermal framework provides a set of interfaces for thermal
+sensors and thermal cooling devices (fan, processor...) to register
+with the thermal management solution and to be a part of it.
+
+This document focuses on how to enable new thermal sensors and cooling
+devices to participate in thermal management. This solution is intended
+to be 'light-weight' and platform/architecture independent. Any thermal
+sensor/cooling device should be able to use the infrastructure easily.
+
+The goal of thermal framework is to expose the thermal sensor/zone and
+cooling device attributes in a consistent way. This will help the
+thermal governors to make use of the information to manage platform
+thermals efficiently.
+
+The thermal sensor source file can be generic (can be any sensor driver,
+in any subsystem). This driver will use the sensor APIs and register with
+thermal framework to participate in platform Thermal management. This
+does not (and should not) know about which zone it belongs to, or any
+other information about platform thermals. A sensor driver is a standalone
+piece of code, which can optionally register with thermal framework.
+
+However, for any platform, there should be a platformX_thermal.c file,
+which will know about the platform thermal characteristics (e.g how many
+sensors, zones, cooling devices, etc.. And how they are related to each other
+i.e the mapping information). Only in this file, the zone level APIs should
+be used, in which case the file will have all information required to attach
+various sensors to a particular zone.
+
+This way, we can have one platform level thermal file, which can support
+multiple platforms (may be)using the same set of sensors (but)binded in
+a different way. This file can get the platform thermal information
+through Firmware, ACPI tables, device tree etc.
+
+Unfortunately, today we don't have many drivers that can be clearly
+differentiated as 'sensor_file.c' and 'platform_thermal_file.c'.
+But very soon we will need/have. We see a lot of chip drivers,
+starting to use thermal framework; we should keep it really
+light-weight for them to do so but at the same time provide all
+the necessary features to participate in platform thermal management.
+
+An Example: drivers/hwmon/emc1403.c - a generic thermal chip driver
+In one platform this sensor can belong to 'ZoneA' and in another the
+same can belong to 'ZoneB'. But, emc1403.c does not really care about
+where does it belong. It just reports temperature.
+
+1. Terminology
+--------------
+This section describes the terminology used in the rest of this
+document as well as the thermal framework code.
+
+thermal_sensor: Hardware that can report temperature of a particular
+ spot in the platform, where it is placed. The temperature
+ reported by the sensor is the 'real' temperature reported
+ by the hardware.
+thermal_zone: A virtual area on the device, that gets heated up. It may
+ have one or more thermal sensors attached to it.
+cooling_device: Any component that can help in reducing the temperature of
+ a 'hot spot' either by reducing its performance (passive
+ cooling) or by other means(Active cooling E.g. Fan)
+
+trip_points: Various temperature levels for each sensor. As of now, we
+ have four levels namely active, passive, hot and critical.
+ Hot and critical trip point support only one value whereas
+ active and passive can have any number of values. These
+ temperature values can come from platform data, and are
+ exposed through sysfs in a consistent manner. Stand-alone
+ thermal sensor drivers are not expected to know these values.
+ These values are RO.
+thresholds: These are programmable temperature limits, on reaching which
+ the thermal sensor generates an interrupt. The framework is
+ notified about this interrupt to take appropriate action.
+ There can be as many number of thresholds as that of the
+ hardware supports. These values are RW.
+
+thermal_map: This provides the mapping (aka binding) information between
+ various sensors and cooling devices in a particular zone.
+ Typically, this also comes from platform data; Stand-alone
+ sensor drivers or cooling device drivers are not expected
+ to know these mapping information.
+
+2. Thermal framework APIs
+-------------------------
+2.1: For Thermal Sensors
+2.1.1 thermal_sensor_register:
+ This function creates a new sensor directory under /sys/class/thermal/
+ as sensor[0-*]. This API is expected to be called by thermal sensor
+ drivers. These drivers may or may not be in thermal subsystem. This
+ function returns a thermal_sensor structure on success and appropriate
+ error on failure.
+
+ name: Name of the sensor
+ count: Number of programmable thresholds associated with this sensor
+ devdata: Device private data
+ ops: Thermal sensor callbacks
+ .get_temp: obtain the current temperature of the sensor
+ .get_trend: obtain the trend of the sensor
+ .get_threshold: get a particular threshold temperature
+ .set_threshold: set a particular threshold temperature
+ .get_hyst: get hysteresis value associated with a threshold
+ .set_hyst: set hysteresis value associated with a threshold
+
+2.1.2 thermal_sensor_unregister:
+ This function deletes the sensor directory under /sys/class/thermal/
+ for the given sensor. Thermal sensor drivers may call this API
+ during the driver's 'exit' routine.
+
+ ts: Thermal sensor that has to be unregistered
+
+2.2: For Cooling Devices
+2.2.1 thermal_cdev_register:
+ This function adds a new thermal cooling device (fan/processor/...)
+ to /sys/class/thermal/ folder as cooling_device[0-*]. This function
+ is expected to be called by cooling device drivers that may be
+ present in other subsystems also.
+
+ name: the cooling device name
+ devdata: device private data
+ ops: thermal cooling devices callbacks
+ .get_max_state: get the Maximum throttle state of the cooling device
+ .get_cur_state: get the Current throttle state of the cooling device
+ .set_cur_state: set the Current throttle state of the cooling device
+
+2.2.2 thermal_cdev_unregister:
+ This function deletes the given cdev entry form /sys/class/thermal;
+ and also cleans all the symlinks referred from various zones.
+
+ cdev: Cooling device to be unregistered
+
+2.3: For Thermal Zones
+2.3.1 thermal_create_thermal_zone:
+ This function adds a new 'zone' under /sys/class/thermal/
+ directory as zone[0-*]. This zone has at least one thermal
+ sensor and at most MAX_SENSORS_PER_ZONE number of sensors
+ attached to it. Similarly, this zone has at least one cdev
+ and at most MAX_CDEVS_PER_ZONE number of cdevs attached to it.
+ As of now, MAX_*_PER_ZONE values are hard-coded to 5. We can
+ make them configurable, through Kconfig option(during 'menuconfig').
+
+ name: Name of the thermal zone
+ devdata: Device private data
+
+2.3.2 thermal_add_sensor_to_zone
+ This function adds a 'sensorX' entry under /sys/class/thermal/
+ zoneY/ directory. This 'sensorX' is a symlink to the actual
+ sensor entry under /sys/class/thermal/. Correspondingly, the
+ method remove_sensor_from_zone deletes the symlink.
+
+ tz: thermal zone structure
+ ts: thermal sensor structure
+
+2.3.3 thermal_add_cdev_to_zone
+ This function adds a 'cdevX' entry under /sys/class/thermal/
+ zoneY/ directory. This 'cdevX' is a symlink to the actual
+ cdev entry under /sys/class/thermal/. Correspondingly, the
+ method remove_cdev_from_zone deletes the symlink.
+
+ tz: thermal zone structure
+ cdev: thermal cooling device structure
+
+2.4 For Thermal Trip
+2.4.1 thermal_add_sensor_trip_info
+ This function adds trip point information for the given sensor,
+ (under a given zone) under /sys/class/thermal/zoneX/.
+ This API creates sysfs attributes namely:
+ sensorX_trip_activeY, sensorX_trip_passiveY, sensorX_trip_hot,
+ sensorX_trip_critical. Each of these hold one trip point temperature
+ (in mC) values, as provided from platform data. As of now, we
+ support many Active and Passive trip points but only one hot
+ one critical trip point.
+
+ tz: thermal zone structure
+ ts: thermal sensor to which the trip points are attached
+ trip: trip point structure. Usually obtained from platform data
+
+2.5 For Thermal Map
+2.5.1 thermal_add_map_entry
+ This function adds a 'map[0-*]' sysfs attribute under
+ /sys/class/thermal/zoneX/mapY_*. Each map attribute helps
+ to describe the binding relationship between a sensor and
+ a cdev in the given zone. The map structure is typically
+ obtained as platform data. For example, through ACPI tables,
+ SFI tables, Device tree etc. The trip mask is a hex value;
+ if 'n' th bit (from LSB) is set, then for trip point 'n' this
+ cdev is throttled with the given weight[n].
+
+ tz: thermal zone to which a 'map' is being added
+ map: thermal_map structure
+
+3. Sysfs attributes structure
+-----------------------------
+Thermal sysfs attributes will be represented under /sys/class/thermal.
+
+3.1: For Thermal Sensors
+ /sys/class/thermal/sensor[0-*]:
+ |---type: Name of the thermal sensor
+ |---temp_input: Current temperature in mC
+ |---threshold[0-*]: Threshold temperature in mC
+ |---threshold[0-*]_hyst:Optional hysteresis value in mC
+
+3.2: For Thermal Cooling Devices
+ /sys/class/thermal/cooling_device[0-*]:
+ |---type: Type of the cooling device
+ |---max_state: Maximum throttle state of the cdev
+ |---cur_state: Current throttle state of the cdev
+
+3.3: For Thermal Zones
+ /sys/class/thermal/zone[0-*]:
+ |---zone_name: Name of the thermal zone
+ |---sensorX: Symlink to ../sensorX
+ |---cdevY: Symlink to ../cdevY
+ |---sensorX_trip_activeM: Active trip point for sensorX
+ |---sensorX_trip_passiveN: Passive trip point for sensorX
+ |---sensorX_trip_hot: Hot trip point for sensorX
+ |---sensorX_trip_critical: Critical trip point for sensorX
+ |---mapX_sensor_name: Sensor Name
+ |---mapX_cdev_name: Cooling device Name
+ |---mapX_trip_type: Trip point type
+ |---mapX_trip_mask: Trip point mask
+ |---mapX_weightY: Weights (used by governors)
+
+3.5: For Thermal Map
+ Each attribute represents the mapping/binding information between
+ a sensor and a cdev, together with a trip type.
+ /sys/class/thermal/zoneX/:
+ |---mapY_trip_type: active/passive
+ |---mapY_sensor_name: cpu
+ |---mapY_cdev_name: proc
+ |---mapY_trip_mask: 0x03
+ |---mapY_weight0: 50
+ |---mapY_weight1: 30
--
1.7.9.5

2014-01-16 18:30:30

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 05/10] Thermal: Add APIs to bind cdev to new zone structure

This patch creates new APIs to add/remove a
cdev to/from a zone. This patch does not change
the existing cooling device implementation.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/thermal_core_new.c | 135 ++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 8 +++
2 files changed, 143 insertions(+)

diff --git a/drivers/thermal/thermal_core_new.c b/drivers/thermal/thermal_core_new.c
index 4f84cb5..3134b1a 100644
--- a/drivers/thermal/thermal_core_new.c
+++ b/drivers/thermal/thermal_core_new.c
@@ -66,6 +66,9 @@ static DEFINE_MUTEX(zone_list_lock);
#define for_each_thermal_zone(pos) \
list_for_each_entry(pos, &thermal_zone_list, node)

+#define for_each_cdev(pos) \
+ list_for_each_entry(pos, &thermal_cdev_list, node)
+
#define GET_INDEX(tz, ptr, type) \
({ \
int i, ret = -EINVAL; \
@@ -301,6 +304,27 @@ static void remove_sensor_from_zone(struct thermal_zone *tz,
mutex_unlock(&tz->lock);
}

+static void remove_cdev_from_zone(struct thermal_zone *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int j, indx;
+
+ indx = GET_INDEX(tz, cdev, cdev);
+ if (indx < 0)
+ return;
+
+ sysfs_remove_link(&tz->device.kobj, kobject_name(&cdev->device.kobj));
+
+ mutex_lock(&tz->lock);
+
+ /* Shift the entries in the tz->cdevs array */
+ for (j = indx; j < MAX_CDEVS_PER_ZONE - 1; j++)
+ tz->cdevs[j] = tz->cdevs[j + 1];
+
+ tz->cdev_indx--;
+ mutex_unlock(&tz->lock);
+}
+
/**
* thermal_create_sensor_sysfs - create sysfs nodes for sensorX
* @ts: the thermal sensor
@@ -593,6 +617,7 @@ EXPORT_SYMBOL_GPL(thermal_cdev_register);
void thermal_cdev_unregister(struct thermal_cooling_device *cdev)
{
struct thermal_cooling_device *pos, *next;
+ struct thermal_zone *tz;
bool found = false;

if (!cdev)
@@ -610,6 +635,13 @@ void thermal_cdev_unregister(struct thermal_cooling_device *cdev)
if (!found)
return;

+ mutex_lock(&zone_list_lock);
+
+ for_each_thermal_zone(tz)
+ remove_cdev_from_zone(tz, cdev);
+
+ mutex_unlock(&zone_list_lock);
+
device_remove_file(&cdev->device, &dev_attr_type);
device_remove_file(&cdev->device, &dev_attr_max_state);
device_remove_file(&cdev->device, &dev_attr_cur_state);
@@ -688,6 +720,7 @@ void thermal_remove_thermal_zone(struct thermal_zone *tz)
{
struct thermal_zone *pos, *next;
struct thermal_sensor *ts;
+ struct thermal_cooling_device *cdev;
bool found = false;

if (!tz)
@@ -709,6 +742,9 @@ void thermal_remove_thermal_zone(struct thermal_zone *tz)
for_each_thermal_sensor(ts)
remove_sensor_from_zone(tz, ts);

+ for_each_cdev(cdev)
+ remove_cdev_from_zone(tz, cdev);
+
device_remove_file(&tz->device, &dev_attr_zone_name);

mutex_destroy(&tz->lock);
@@ -817,3 +853,102 @@ exit_zone:
return ret;
}
EXPORT_SYMBOL_GPL(thermal_add_sensor_to_zone);
+
+/**
+ * thermal_get_cdev_by_name() - search for a cdev and returns its reference
+ * @name: cooling device name to fetch the reference
+ *
+ * On success returns a reference to an unique cdev with
+ * the name matching that of @name, an ERR_PTR otherwise:
+ * -EINVAL for invalid paramenters
+ * -ENODEV for cdev not found
+ * -EEXIST for multiple matches
+ */
+struct thermal_cooling_device *thermal_get_cdev_by_name(const char *name)
+{
+ int found = 0;
+ struct thermal_cooling_device *pos;
+ struct thermal_cooling_device *cdev = ERR_PTR(-EINVAL);
+
+ if (!name)
+ return cdev;
+
+ mutex_lock(&cdev_list_lock);
+ for_each_cdev(pos) {
+ if (!strnicmp(pos->type, name, THERMAL_NAME_LENGTH)) {
+ found++;
+ cdev = pos;
+ }
+ }
+ mutex_unlock(&cdev_list_lock);
+
+ /*
+ * Nothing has been found; return an error code for it.
+ * Return success only when an unique cdev is found.
+ */
+ if (found == 0)
+ cdev = ERR_PTR(-ENODEV);
+ else if (found > 1)
+ cdev = ERR_PTR(-EEXIST);
+
+ return cdev;
+}
+EXPORT_SYMBOL_GPL(thermal_get_cdev_by_name);
+
+/**
+ * thermal_add_cdev_to_zone - Add @cdev to thermal zone @tz
+ * @tz: Thermal zone reference
+ * @cdev: Cooling device reference
+ *
+ * Returns 0 on success, otherwise
+ * -EINVAL for invalid paramenters
+ * -EINVAL when trying to add more cdevs than MAX_CDEVS_PER_ZONE
+ * -EEXIST when trying add existing cdev again
+ */
+int thermal_add_cdev_to_zone(struct thermal_zone *tz,
+ struct thermal_cooling_device *cdev)
+{
+ int ret;
+
+ if (!tz || !cdev)
+ return -EINVAL;
+
+ mutex_lock(&zone_list_lock);
+
+ /* Ensure we are not adding the same cdev again!! */
+ ret = GET_INDEX(tz, cdev, cdev);
+ if (ret >= 0) {
+ ret = -EEXIST;
+ goto exit_zone;
+ }
+
+ /* Protect against 'cdev' being unregistered */
+ mutex_lock(&cdev_list_lock);
+
+ ret = sysfs_create_link(&tz->device.kobj, &cdev->device.kobj,
+ kobject_name(&cdev->device.kobj));
+ if (ret)
+ goto exit_cdev;
+
+ mutex_lock(&tz->lock);
+
+ if (tz->cdev_indx >= MAX_CDEVS_PER_ZONE - 1) {
+ dev_err(&tz->device, "Only %d cdevs allowed per zone\n",
+ MAX_CDEVS_PER_ZONE);
+ sysfs_remove_link(&tz->device.kobj,
+ kobject_name(&cdev->device.kobj));
+ ret = -EINVAL;
+ goto exit_indx_check;
+ }
+
+ tz->cdevs[tz->cdev_indx++] = cdev;
+
+exit_indx_check:
+ mutex_unlock(&tz->lock);
+exit_cdev:
+ mutex_unlock(&cdev_list_lock);
+exit_zone:
+ mutex_unlock(&zone_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thermal_add_cdev_to_zone);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 2afc483..9b080f2 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -60,6 +60,7 @@
#endif

#define MAX_SENSORS_PER_ZONE 5
+#define MAX_CDEVS_PER_ZONE 5

struct thermal_sensor;
struct thermal_zone_device;
@@ -226,6 +227,10 @@ struct thermal_zone {
/* Sensor level information */
int sensor_indx; /* index into 'sensors' array */
struct thermal_sensor *sensors[MAX_SENSORS_PER_ZONE];
+
+ /* cdev level information */
+ int cdev_indx; /* index into 'cdevs' array */
+ struct thermal_cooling_device *cdevs[MAX_CDEVS_PER_ZONE];
};

/* Structure that holds thermal governor information */
@@ -342,6 +347,9 @@ struct thermal_zone *thermal_create_thermal_zone(const char *, void *);
void thermal_remove_thermal_zone(struct thermal_zone *);
int thermal_add_sensor_to_zone(struct thermal_zone *, struct thermal_sensor *);
struct thermal_sensor *thermal_get_sensor_by_name(const char *);
+int thermal_add_cdev_to_zone(struct thermal_zone *,
+ struct thermal_cooling_device *);
+struct thermal_cooling_device *thermal_get_cdev_by_name(const char *);

#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
--
1.7.9.5

2014-01-16 18:28:44

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 02/10] Thermal: Create sensor level APIs

This patch creates sensor level APIs, in the
generic thermal framework, in a new file named
thermal_core_new.c. The thermal_class variable
is moved as extern to thermal_core.h to
facilitate co-existence of both the APIs.

A Thermal sensor is a piece of hardware that can report
temperature of the spot in which it is placed. A thermal
sensor driver reads the temperature from this sensor
and reports it out. This kind of driver can be in
any subsystem. If the sensor needs to participate
in platform thermal management, the corresponding
driver can use the APIs introduced in this patch, to
register(or unregister) with the thermal framework.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/Kconfig | 3 +
drivers/thermal/Makefile | 1 +
drivers/thermal/thermal_core.c | 2 +-
drivers/thermal/thermal_core.h | 1 +
drivers/thermal/thermal_core_new.c | 367 ++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 37 ++++
6 files changed, 410 insertions(+), 1 deletion(-)
create mode 100644 drivers/thermal/thermal_core_new.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 35c0664..f6a8057 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -15,6 +15,9 @@ menuconfig THERMAL

if THERMAL

+config THERMAL_V2
+ bool
+
config THERMAL_HWMON
bool
prompt "Expose thermal sensors as hwmon device"
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 54e4ec9..d9ae9ac 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -4,6 +4,7 @@

obj-$(CONFIG_THERMAL) += thermal_sys.o
thermal_sys-y += thermal_core.o
+thermal_sys-$(CONFIG_THERMAL_V2) += thermal_core_new.o

# interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 165afc6..a17702f 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1045,7 +1045,7 @@ static void thermal_release(struct device *dev)
/* No-op since kfree(dev) is done in _unregister functions */
}

-static struct class thermal_class = {
+struct class thermal_class = {
.name = "thermal",
.dev_release = thermal_release,
};
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h
index 3db339f..adf817c 100644
--- a/drivers/thermal/thermal_core.h
+++ b/drivers/thermal/thermal_core.h
@@ -27,6 +27,7 @@
#include <linux/device.h>
#include <linux/thermal.h>

+extern struct class thermal_class;
/* Initial state of a cooling device during binding */
#define THERMAL_NO_TARGET -1UL

diff --git a/drivers/thermal/thermal_core_new.c b/drivers/thermal/thermal_core_new.c
new file mode 100644
index 0000000..b369a6f
--- /dev/null
+++ b/drivers/thermal/thermal_core_new.c
@@ -0,0 +1,367 @@
+/*
+ * thermal_core_new.c - Generic Thermal Management Sysfs support.
+ * Derived from the previous thermal_core.c
+ * This adds multiple sensor per zone support along with various
+ * options to provide platform data for Thermal management.
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/thermal.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+
+#include "thermal_core.h"
+
+MODULE_AUTHOR("Durgadoss R");
+MODULE_DESCRIPTION("Generic thermal management sysfs support v2");
+MODULE_LICENSE("GPL v2");
+
+static DEFINE_IDR(thermal_sensor_idr);
+
+static LIST_HEAD(thermal_sensor_list);
+
+static DEFINE_MUTEX(thermal_idr_lock);
+static DEFINE_MUTEX(sensor_list_lock);
+
+#define to_thermal_sensor(_dev) \
+ container_of(_dev, struct thermal_sensor, device)
+
+static int get_idr(struct idr *idr, int *id)
+{
+ int ret;
+
+ mutex_lock(&thermal_idr_lock);
+ ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
+ mutex_unlock(&thermal_idr_lock);
+
+ if (unlikely(ret < 0))
+ return ret;
+
+ *id = ret;
+ return 0;
+}
+
+static void release_idr(struct idr *idr, int id)
+{
+ mutex_lock(&thermal_idr_lock);
+ idr_remove(idr, id);
+ mutex_unlock(&thermal_idr_lock);
+}
+
+static ssize_t
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ return sprintf(buf, "%s\n", ts->name);
+}
+
+static ssize_t
+temp_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ ret = ts->ops->get_temp(ts, &val);
+
+ return ret ? ret : sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t
+hyst_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ ret = sscanf(attr->attr.name, "threshold%d_hyst", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ ret = ts->ops->get_hyst(ts, indx, &val);
+
+ return ret ? ret : sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t
+hyst_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ if (!ts->ops->set_hyst)
+ return -EPERM;
+
+ ret = sscanf(attr->attr.name, "threshold%d_hyst", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ ret = ts->ops->set_hyst(ts, indx, val);
+
+ return ret ? ret : count;
+}
+
+static ssize_t
+threshold_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ ret = sscanf(attr->attr.name, "threshold%d", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ ret = ts->ops->get_threshold(ts, indx, &val);
+
+ return ret ? ret : sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t
+threshold_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int indx, ret;
+ long val;
+ struct thermal_sensor *ts = to_thermal_sensor(dev);
+
+ if (!ts->ops->set_threshold)
+ return -EPERM;
+
+ ret = sscanf(attr->attr.name, "threshold%d", &indx);
+ if (!ret)
+ return -EINVAL;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ ret = ts->ops->set_threshold(ts, indx, val);
+
+ return ret ? ret : count;
+}
+
+/* Thermal sensor attributes */
+static DEVICE_ATTR_RO(name);
+static DEVICE_ATTR_RO(temp);
+
+/**
+ * thermal_create_sensor_sysfs - create sysfs nodes for sensorX
+ * @ts: the thermal sensor
+ * @count: Number of thresholds supported by sensor hardware
+ *
+ * 'Thresholds' are temperatures programmed into the sensor hardware,
+ * on crossing which the sensor may generate an interrupt.
+ */
+static int thermal_create_sensor_sysfs(struct thermal_sensor *ts, int count)
+{
+ int i, ret;
+ int size = sizeof(struct thermal_attr) * count;
+
+ ret = device_create_file(&ts->device, &dev_attr_name);
+ if (ret)
+ return ret;
+
+ ret = device_create_file(&ts->device, &dev_attr_temp);
+ if (ret)
+ goto exit_name;
+
+ /*
+ * If the sensor does not support any thresholds
+ * or, we cannot read the thresholds then do not
+ * create these sysfs nodes. This is not an error.
+ */
+ if (count < 1 || !ts->ops->get_threshold)
+ return 0;
+
+ ts->thresh_attrs = devm_kzalloc(&ts->device, size, GFP_KERNEL);
+ if (!ts->thresh_attrs) {
+ ret = -ENOMEM;
+ goto exit_temp;
+ }
+
+ if (ts->ops->get_hyst) {
+ ts->hyst_attrs = devm_kzalloc(&ts->device, size, GFP_KERNEL);
+ if (!ts->hyst_attrs) {
+ ret = -ENOMEM;
+ goto exit_temp;
+ }
+ }
+
+ ts->thresholds = count;
+
+ /* Create threshold attributes */
+ for (i = 0; i < count; i++) {
+ snprintf(ts->thresh_attrs[i].name, THERMAL_NAME_LENGTH,
+ "threshold%d", i);
+
+ sysfs_attr_init(&ts->thresh_attrs[i].attr.attr);
+ ts->thresh_attrs[i].attr.attr.name = ts->thresh_attrs[i].name;
+ ts->thresh_attrs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
+ ts->thresh_attrs[i].attr.show = threshold_show;
+ ts->thresh_attrs[i].attr.store = threshold_store;
+
+ device_create_file(&ts->device, &ts->thresh_attrs[i].attr);
+
+ /* Create threshold_hyst attributes */
+ if (!ts->ops->get_hyst)
+ continue;
+
+ snprintf(ts->hyst_attrs[i].name, THERMAL_NAME_LENGTH,
+ "threshold%d_hyst", i);
+
+ sysfs_attr_init(&ts->hyst_attrs[i].attr.attr);
+ ts->hyst_attrs[i].attr.attr.name = ts->hyst_attrs[i].name;
+ ts->hyst_attrs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
+ ts->hyst_attrs[i].attr.show = hyst_show;
+ ts->hyst_attrs[i].attr.store = hyst_store;
+
+ device_create_file(&ts->device, &ts->hyst_attrs[i].attr);
+ }
+ return 0;
+
+exit_temp:
+ device_remove_file(&ts->device, &dev_attr_temp);
+exit_name:
+ device_remove_file(&ts->device, &dev_attr_name);
+ return ret;
+}
+
+/**
+ * thermal_sensor_register - register a new thermal sensor
+ * @name: name of the thermal sensor
+ * @count: Number of thresholds supported by hardware
+ * @ops: standard thermal sensor callbacks
+ * @devdata: private device data
+ *
+ * On Success returns a thermal sensor reference, otherwise:
+ * -EINVAL for invalid parameters,
+ * -ENOMEM for insufficient memory cases,
+ */
+struct thermal_sensor *thermal_sensor_register(const char *name, int count,
+ struct thermal_sensor_ops *ops,
+ void *devdata)
+{
+ struct thermal_sensor *ts;
+ int ret;
+
+ if (!name || (name && strlen(name) >= THERMAL_NAME_LENGTH))
+ return ERR_PTR(-EINVAL);
+
+ if (!ops || !ops->get_temp)
+ return ERR_PTR(-EINVAL);
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return ERR_PTR(-ENOMEM);
+
+ idr_init(&ts->idr);
+ ret = get_idr(&thermal_sensor_idr, &ts->id);
+ if (ret)
+ goto exit_free;
+
+ strlcpy(ts->name, name, sizeof(ts->name));
+ ts->ops = ops;
+ ts->devdata = devdata;
+ ts->device.class = &thermal_class;
+
+ dev_set_name(&ts->device, "sensor%d", ts->id);
+ ret = device_register(&ts->device);
+ if (ret)
+ goto exit_idr;
+
+ ret = thermal_create_sensor_sysfs(ts, count);
+ if (ret)
+ goto exit_unregister;
+
+ /* Add this sensor to the global list of sensors */
+ mutex_lock(&sensor_list_lock);
+ list_add_tail(&ts->node, &thermal_sensor_list);
+ mutex_unlock(&sensor_list_lock);
+
+ return ts;
+
+exit_unregister:
+ device_unregister(&ts->device);
+exit_idr:
+ release_idr(&thermal_sensor_idr, ts->id);
+ idr_destroy(&ts->idr);
+exit_free:
+ kfree(ts);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(thermal_sensor_register);
+
+/**
+ * remove_thermal_zone - removes the sysfs nodes for given sensor
+ * @ts: Thermal sensor to be removed.
+ */
+void thermal_sensor_unregister(struct thermal_sensor *ts)
+{
+ int i;
+ struct thermal_sensor *pos, *next;
+ bool found = false;
+
+ if (!ts)
+ return;
+
+ mutex_lock(&sensor_list_lock);
+ list_for_each_entry_safe(pos, next, &thermal_sensor_list, node) {
+ if (pos == ts) {
+ list_del(&ts->node);
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&sensor_list_lock);
+
+ if (!found)
+ return;
+
+ for (i = 0; i < ts->thresholds; i++) {
+ device_remove_file(&ts->device, &ts->thresh_attrs[i].attr);
+ if (ts->ops->get_hyst) {
+ device_remove_file(&ts->device,
+ &ts->hyst_attrs[i].attr);
+ }
+ }
+
+ device_remove_file(&ts->device, &dev_attr_name);
+ device_remove_file(&ts->device, &dev_attr_temp);
+
+ release_idr(&thermal_sensor_idr, ts->id);
+ idr_destroy(&ts->idr);
+
+ device_unregister(&ts->device);
+
+ kfree(ts);
+ return;
+}
+EXPORT_SYMBOL_GPL(thermal_sensor_unregister);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index f7e11c7..bb6b183 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -59,6 +59,7 @@
#define DEFAULT_THERMAL_GOVERNOR "user_space"
#endif

+struct thermal_sensor;
struct thermal_zone_device;
struct thermal_cooling_device;

@@ -140,6 +141,15 @@ struct thermal_cooling_device_ops {
int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
};

+struct thermal_sensor_ops {
+ int (*get_temp) (struct thermal_sensor *, long *);
+ int (*get_trend) (struct thermal_sensor *, int, enum thermal_trend *);
+ int (*set_threshold) (struct thermal_sensor *, int, long);
+ int (*get_threshold) (struct thermal_sensor *, int, long *);
+ int (*set_hyst) (struct thermal_sensor *, int, long);
+ int (*get_hyst) (struct thermal_sensor *, int, long *);
+};
+
struct thermal_cooling_device {
int id;
char type[THERMAL_NAME_LENGTH];
@@ -158,6 +168,21 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH];
};

+struct thermal_sensor {
+ char name[THERMAL_NAME_LENGTH];
+ int id;
+ int temp;
+ int prev_temp;
+ int thresholds;
+ void *devdata;
+ struct idr idr;
+ struct device device;
+ struct list_head node;
+ struct thermal_sensor_ops *ops;
+ struct thermal_attr *thresh_attrs;
+ struct thermal_attr *hyst_attrs;
+};
+
struct thermal_zone_device {
int id;
char type[THERMAL_NAME_LENGTH];
@@ -305,4 +330,16 @@ static inline int thermal_generate_netlink_event(struct thermal_zone_device *tz,
}
#endif

+#ifdef CONFIG_THERMAL_V2
+struct thermal_sensor *thermal_sensor_register(const char *, int,
+ struct thermal_sensor_ops *, void *);
+void thermal_sensor_unregister(struct thermal_sensor *);
+#else
+static inline struct thermal_sensor *thermal_sensor_register(const char *name,
+ int count, struct thermal_sensor_ops *ops, void *devdata)
+{
+ return ERR_PTR(-ENODEV);
+}
+static inline void thermal_sensor_unregister(struct thermal_sensor *ts) {}
+#endif
#endif /* __THERMAL_H__ */
--
1.7.9.5

2014-01-16 18:31:05

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 04/10] Thermal: Create zone level APIs

This patch adds a new thermal_zone structure to
thermal.h. Also, adds zone level APIs to the thermal
framework.

A thermal zone is a hot spot on the platform, which
can have one or more sensors and cooling devices attached
to it. These sensors can be mapped to a set of cooling
devices, which when throttled, can help to bring down
the temperature of the hot spot.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/thermal_core_new.c | 268 ++++++++++++++++++++++++++++++++++++
include/linux/thermal.h | 23 ++++
2 files changed, 291 insertions(+)

diff --git a/drivers/thermal/thermal_core_new.c b/drivers/thermal/thermal_core_new.c
index ba38438..4f84cb5 100644
--- a/drivers/thermal/thermal_core_new.c
+++ b/drivers/thermal/thermal_core_new.c
@@ -40,13 +40,16 @@ MODULE_LICENSE("GPL v2");

static DEFINE_IDR(thermal_sensor_idr);
static DEFINE_IDR(thermal_cdev_idr);
+static DEFINE_IDR(thermal_zone_idr);

static LIST_HEAD(thermal_sensor_list);
static LIST_HEAD(thermal_cdev_list);
+static LIST_HEAD(thermal_zone_list);

static DEFINE_MUTEX(thermal_idr_lock);
static DEFINE_MUTEX(sensor_list_lock);
static DEFINE_MUTEX(cdev_list_lock);
+static DEFINE_MUTEX(zone_list_lock);

#define to_thermal_sensor(_dev) \
container_of(_dev, struct thermal_sensor, device)
@@ -54,6 +57,35 @@ static DEFINE_MUTEX(cdev_list_lock);
#define to_cooling_device(_dev) \
container_of(_dev, struct thermal_cooling_device, device)

+#define to_thermal_zone(_dev) \
+ container_of(_dev, struct thermal_zone, device)
+
+#define for_each_thermal_sensor(pos) \
+ list_for_each_entry(pos, &thermal_sensor_list, node)
+
+#define for_each_thermal_zone(pos) \
+ list_for_each_entry(pos, &thermal_zone_list, node)
+
+#define GET_INDEX(tz, ptr, type) \
+({ \
+ int i, ret = -EINVAL; \
+ do { \
+ if (!tz || !ptr) \
+ break; \
+ mutex_lock(&tz->lock); \
+ mutex_lock(&type##_list_lock); \
+ for (i = 0; i < tz->type##_indx; i++) { \
+ if (tz->type##s[i] == ptr) { \
+ ret = i; \
+ break; \
+ } \
+ } \
+ mutex_unlock(&type##_list_lock); \
+ mutex_unlock(&tz->lock); \
+ } while (0); \
+ ret; \
+})
+
static int get_idr(struct idr *idr, int *id)
{
int ret;
@@ -228,6 +260,14 @@ cur_state_store(struct device *dev, struct device_attribute *attr,
return ret ? ret : count;
}

+static ssize_t
+zone_name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct thermal_zone *tz = to_thermal_zone(dev);
+
+ return sprintf(buf, "%s\n", tz->name);
+}
+
/* Thermal sensor attributes */
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(temp);
@@ -237,6 +277,30 @@ static DEVICE_ATTR_RO(type);
static DEVICE_ATTR_RO(max_state);
static DEVICE_ATTR_RW(cur_state);

+/* Thermal zone attributes */
+static DEVICE_ATTR_RO(zone_name);
+
+static void remove_sensor_from_zone(struct thermal_zone *tz,
+ struct thermal_sensor *ts)
+{
+ int j, indx;
+
+ indx = GET_INDEX(tz, ts, sensor);
+ if (indx < 0)
+ return;
+
+ sysfs_remove_link(&tz->device.kobj, kobject_name(&ts->device.kobj));
+
+ mutex_lock(&tz->lock);
+
+ /* Shift the entries in the tz->sensors array */
+ for (j = indx; j < MAX_SENSORS_PER_ZONE - 1; j++)
+ tz->sensors[j] = tz->sensors[j + 1];
+
+ tz->sensor_indx--;
+ mutex_unlock(&tz->lock);
+}
+
/**
* thermal_create_sensor_sysfs - create sysfs nodes for sensorX
* @ts: the thermal sensor
@@ -391,6 +455,7 @@ EXPORT_SYMBOL_GPL(thermal_sensor_register);
void thermal_sensor_unregister(struct thermal_sensor *ts)
{
int i;
+ struct thermal_zone *tz;
struct thermal_sensor *pos, *next;
bool found = false;

@@ -410,6 +475,13 @@ void thermal_sensor_unregister(struct thermal_sensor *ts)
if (!found)
return;

+ mutex_lock(&zone_list_lock);
+
+ for_each_thermal_zone(tz)
+ remove_sensor_from_zone(tz, ts);
+
+ mutex_unlock(&zone_list_lock);
+
for (i = 0; i < ts->thresholds; i++) {
device_remove_file(&ts->device, &ts->thresh_attrs[i].attr);
if (ts->ops->get_hyst) {
@@ -549,3 +621,199 @@ void thermal_cdev_unregister(struct thermal_cooling_device *cdev)
return;
}
EXPORT_SYMBOL_GPL(thermal_cdev_unregister);
+
+/**
+ * thermal_create_thermal_zone - create sysfs nodes for thermal zone
+ * @name: Name of the thermla zone
+ * @devdata: Data supplied by the caller
+ *
+ * On Success returns a thermal zone reference, otherwise:
+ * -EINVAL for invalid parameters,
+ * -ENOMEM for insufficient memory cases,
+ */
+struct thermal_zone *thermal_create_thermal_zone(const char *name,
+ void *devdata)
+{
+ struct thermal_zone *tz;
+ int ret;
+
+ if (!name || (name && strlen(name) >= THERMAL_NAME_LENGTH))
+ return ERR_PTR(-EINVAL);
+
+ tz = kzalloc(sizeof(*tz), GFP_KERNEL);
+ if (!tz)
+ return ERR_PTR(-ENOMEM);
+
+ idr_init(&tz->idr);
+ ret = get_idr(&thermal_zone_idr, &tz->id);
+ if (ret)
+ goto exit_free;
+
+ mutex_init(&tz->lock);
+ strlcpy(tz->name, name, sizeof(tz->name));
+ tz->devdata = devdata;
+ tz->device.class = &thermal_class;
+
+ dev_set_name(&tz->device, "zone%d", tz->id);
+ ret = device_register(&tz->device);
+ if (ret)
+ goto exit_idr;
+
+ ret = device_create_file(&tz->device, &dev_attr_zone_name);
+ if (ret)
+ goto exit_unregister;
+
+ /* Add this zone to the global list of thermal zones */
+ mutex_lock(&zone_list_lock);
+ list_add_tail(&tz->node, &thermal_zone_list);
+ mutex_unlock(&zone_list_lock);
+ return tz;
+
+exit_unregister:
+ device_unregister(&tz->device);
+exit_idr:
+ mutex_destroy(&tz->lock);
+ release_idr(&thermal_zone_idr, tz->id);
+exit_free:
+ kfree(tz);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(thermal_create_thermal_zone);
+
+/**
+ * thermal_remove_thermal_zone - removes the sysfs nodes for given tz
+ * @tz: Thermal zone to be removed.
+ */
+void thermal_remove_thermal_zone(struct thermal_zone *tz)
+{
+ struct thermal_zone *pos, *next;
+ struct thermal_sensor *ts;
+ bool found = false;
+
+ if (!tz)
+ return;
+
+ mutex_lock(&zone_list_lock);
+
+ list_for_each_entry_safe(pos, next, &thermal_zone_list, node) {
+ if (pos == tz) {
+ list_del(&tz->node);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto exit;
+
+ for_each_thermal_sensor(ts)
+ remove_sensor_from_zone(tz, ts);
+
+ device_remove_file(&tz->device, &dev_attr_zone_name);
+
+ mutex_destroy(&tz->lock);
+ release_idr(&thermal_zone_idr, tz->id);
+ idr_destroy(&tz->idr);
+
+ device_unregister(&tz->device);
+ kfree(tz);
+exit:
+ mutex_unlock(&zone_list_lock);
+ return;
+}
+EXPORT_SYMBOL_GPL(thermal_remove_thermal_zone);
+
+/**
+ * thermal_get_sensor_by_name() - search for a sensor and return its reference
+ * @name: thermal sensor name to fetch the reference
+ *
+ * On success returns a reference to a unique sensor with
+ * name matching that of @name, an ERR_PTR otherwise:
+ * -EINVAL for invalid paramenters
+ * -ENODEV for sensor not found
+ * -EEXIST for multiple matches
+ */
+struct thermal_sensor *thermal_get_sensor_by_name(const char *name)
+{
+ int found = 0;
+ struct thermal_sensor *pos;
+ struct thermal_sensor *ts = ERR_PTR(-EINVAL);
+
+ if (!name)
+ return ts;
+
+ mutex_lock(&sensor_list_lock);
+ for_each_thermal_sensor(pos) {
+ if (!strnicmp(pos->name, name, THERMAL_NAME_LENGTH)) {
+ found++;
+ ts = pos;
+ }
+ }
+ mutex_unlock(&sensor_list_lock);
+
+ if (found == 0)
+ ts = ERR_PTR(-ENODEV);
+ else if (found > 1)
+ ts = ERR_PTR(-EEXIST);
+
+ return ts;
+}
+EXPORT_SYMBOL_GPL(thermal_get_sensor_by_name);
+
+/**
+ * thermal_add_sensor_to_zone - Add @ts to thermal zone @tz
+ * @tz: Thermal zone reference
+ * @ts: Thermal sensor reference
+ *
+ * Returns 0 on success, otherwise
+ * -EINVAL for invalid paramenters
+ * -EINVAL when trying to add more zones than MAX_SENSORS_PER_ZONE
+ * -EEXIST when trying add existing thermal sensor again
+ */
+int thermal_add_sensor_to_zone(struct thermal_zone *tz,
+ struct thermal_sensor *ts)
+{
+ int ret;
+
+ if (!tz || !ts)
+ return -EINVAL;
+
+ mutex_lock(&zone_list_lock);
+
+ /* Ensure we are not adding the same sensor again!! */
+ ret = GET_INDEX(tz, ts, sensor);
+ if (ret >= 0) {
+ ret = -EEXIST;
+ goto exit_zone;
+ }
+
+ /* Protect against 'ts' being unregistered */
+ mutex_lock(&sensor_list_lock);
+
+ ret = sysfs_create_link(&tz->device.kobj, &ts->device.kobj,
+ kobject_name(&ts->device.kobj));
+ if (ret)
+ goto exit_sensor;
+
+ mutex_lock(&tz->lock);
+
+ if (tz->sensor_indx >= MAX_SENSORS_PER_ZONE - 1) {
+ dev_err(&tz->device, "Only %d sensors allowed per zone\n",
+ MAX_SENSORS_PER_ZONE);
+ sysfs_remove_link(&tz->device.kobj,
+ kobject_name(&ts->device.kobj));
+ ret = -EINVAL;
+ goto exit_indx_check;
+ }
+
+ tz->sensors[tz->sensor_indx++] = ts;
+
+exit_indx_check:
+ mutex_unlock(&tz->lock);
+exit_sensor:
+ mutex_unlock(&sensor_list_lock);
+exit_zone:
+ mutex_unlock(&zone_list_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(thermal_add_sensor_to_zone);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 46d7dc3..2afc483 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -59,6 +59,8 @@
#define DEFAULT_THERMAL_GOVERNOR "user_space"
#endif

+#define MAX_SENSORS_PER_ZONE 5
+
struct thermal_sensor;
struct thermal_zone_device;
struct thermal_cooling_device;
@@ -210,6 +212,22 @@ struct thermal_zone_device {
struct delayed_work poll_queue;
};

+struct thermal_zone {
+ char name[THERMAL_NAME_LENGTH];
+ int id;
+ void *devdata;
+ struct thermal_zone *ops;
+ struct thermal_governor *governor;
+ struct idr idr;
+ struct mutex lock; /* protect elements of this structure */
+ struct device device;
+ struct list_head node;
+
+ /* Sensor level information */
+ int sensor_indx; /* index into 'sensors' array */
+ struct thermal_sensor *sensors[MAX_SENSORS_PER_ZONE];
+};
+
/* Structure that holds thermal governor information */
struct thermal_governor {
char name[THERMAL_NAME_LENGTH];
@@ -320,6 +338,11 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);

+struct thermal_zone *thermal_create_thermal_zone(const char *, void *);
+void thermal_remove_thermal_zone(struct thermal_zone *);
+int thermal_add_sensor_to_zone(struct thermal_zone *, struct thermal_sensor *);
+struct thermal_sensor *thermal_get_sensor_by_name(const char *);
+
#ifdef CONFIG_NET
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event);
--
1.7.9.5

2014-01-16 18:31:24

by R, Durgadoss

[permalink] [raw]
Subject: [PATCHv5 01/10] Thermal: Do kfree in _unregister functions

Currently the thermal_release function does
kfree for all devices when they call device_unregister.
This makes code scattering i.e whenever we add new
devices (to thermal class) we need to add its
corresponding kfree in thermal_release function.
And the if-else is also growing lengthy.

Instead, do all kfree() of devices in their own
_unregister functions. This makes the code look
clean and easy to maintain.

Signed-off-by: Durgadoss R <[email protected]>
---
drivers/thermal/thermal_core.c | 17 +++++------------
1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 338a88b..165afc6 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1042,18 +1042,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);

static void thermal_release(struct device *dev)
{
- struct thermal_zone_device *tz;
- struct thermal_cooling_device *cdev;
-
- if (!strncmp(dev_name(dev), "thermal_zone",
- sizeof("thermal_zone") - 1)) {
- tz = to_thermal_zone(dev);
- kfree(tz);
- } else if(!strncmp(dev_name(dev), "cooling_device",
- sizeof("cooling_device") - 1)){
- cdev = to_cooling_device(dev);
- kfree(cdev);
- }
+ /* No-op since kfree(dev) is done in _unregister functions */
}

static struct class thermal_class = {
@@ -1146,6 +1135,7 @@ __thermal_cooling_device_register(struct device_node *np,
unregister:
release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
device_unregister(&cdev->device);
+ kfree(cdev);
return ERR_PTR(result);
}

@@ -1250,6 +1240,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)

release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
device_unregister(&cdev->device);
+ kfree(cdev);
return;
}
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
@@ -1563,6 +1554,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
unregister:
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
device_unregister(&tz->device);
+ kfree(tz);
return ERR_PTR(result);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_register);
@@ -1630,6 +1622,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
idr_destroy(&tz->idr);
mutex_destroy(&tz->lock);
device_unregister(&tz->device);
+ kfree(tz);
return;
}
EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
--
1.7.9.5

2014-01-21 19:46:36

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCHv5 01/10] Thermal: Do kfree in _unregister functions

Hi Durgadoss,

On Fri, Jan 17, 2014 at 05:26:18AM +0530, Durgadoss R wrote:
> Currently the thermal_release function does
> kfree for all devices when they call device_unregister.
> This makes code scattering i.e whenever we add new
> devices (to thermal class) we need to add its
> corresponding kfree in thermal_release function.
> And the if-else is also growing lengthy.
>
> Instead, do all kfree() of devices in their own
> _unregister functions. This makes the code look
> clean and easy to maintain.
>
> Signed-off-by: Durgadoss R <[email protected]>
> ---
> drivers/thermal/thermal_core.c | 17 +++++------------
> 1 file changed, 5 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index 338a88b..165afc6 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -1042,18 +1042,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);
>
> static void thermal_release(struct device *dev)
> {
> - struct thermal_zone_device *tz;
> - struct thermal_cooling_device *cdev;
> -
> - if (!strncmp(dev_name(dev), "thermal_zone",
> - sizeof("thermal_zone") - 1)) {
> - tz = to_thermal_zone(dev);
> - kfree(tz);
> - } else if(!strncmp(dev_name(dev), "cooling_device",
> - sizeof("cooling_device") - 1)){
> - cdev = to_cooling_device(dev);
> - kfree(cdev);
> - }
> + /* No-op since kfree(dev) is done in _unregister functions */
> }
>
> static struct class thermal_class = {
> @@ -1146,6 +1135,7 @@ __thermal_cooling_device_register(struct device_node *np,
> unregister:
> release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
> device_unregister(&cdev->device);
> + kfree(cdev);

This is broken, you can not assume that you are holding the last
reference. Please observe help text of DEBUG_KOBJECT_RELEASE config
option, activate said option and try unregistering thermal device.

Thanks.

--
Dmitry