From: Durgadoss R <durgadoss.r@intel.com>
To: rui.zhang@intel.com, eduardo.valentin@ti.com, linux-pm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, hongbo.zhang@freescale.com,
wni@nvidia.com, Durgadoss R <durgadoss.r@intel.com>
Subject: [PATCHv5 02/10] Thermal: Create sensor level APIs
Date: Fri, 17 Jan 2014 05:26:19 +0530 [thread overview]
Message-ID: <1389916587-2541-3-git-send-email-durgadoss.r@intel.com> (raw)
In-Reply-To: <1389916587-2541-1-git-send-email-durgadoss.r@intel.com>
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 <durgadoss.r@intel.com>
---
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
next prev parent reply other threads:[~2014-01-16 23:56 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-01-16 23:56 [PATCHv5 00/10] Thermal Framework Enhancements Durgadoss R
2014-01-16 23:56 ` [PATCHv5 01/10] Thermal: Do kfree in _unregister functions Durgadoss R
2014-01-21 19:46 ` Dmitry Torokhov
2014-03-03 13:44 ` Zhang Rui
2014-01-16 23:56 ` Durgadoss R [this message]
2014-01-16 23:56 ` [PATCHv5 03/10] Thermal: Add cooling device APIs Durgadoss R
2014-01-17 7:57 ` R, Durgadoss
2014-01-16 23:56 ` [PATCHv5 04/10] Thermal: Create zone level APIs Durgadoss R
2014-01-16 23:56 ` [PATCHv5 05/10] Thermal: Add APIs to bind cdev to new zone structure Durgadoss R
2014-01-16 23:56 ` [PATCHv5 06/10] Thermal: Add trip point sysfs nodes for sensor Durgadoss R
2014-01-16 23:56 ` [PATCHv5 07/10] Thermal: Create Thermal map sysfs attributes for a zone Durgadoss R
2014-01-16 23:56 ` [PATCHv5 08/10] Thermal: Add Documentation to new APIs Durgadoss R
2014-01-16 23:56 ` [PATCHv5 09/10] Thermal: Add ABI Documentation for sysfs interfaces Durgadoss R
2014-01-16 23:56 ` [PATCHv5 10/10] Thermal: Dummy driver used for testing Durgadoss R
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1389916587-2541-3-git-send-email-durgadoss.r@intel.com \
--to=durgadoss.r@intel.com \
--cc=eduardo.valentin@ti.com \
--cc=hongbo.zhang@freescale.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=rui.zhang@intel.com \
--cc=wni@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).