[PATCH 0/2] Enable powernv based platform sensors

STALE4584d

4 messages, 2 authors, 2014-03-28 · open the first message on its own page

[PATCH 0/2] Enable powernv based platform sensors

From: Neelesh Gupta <hidden>
Date: 2014-03-07 05:35:01

This patchset contains the enablement code to expose platform sensor data
on powernv platform. First patch fetches the sensor data from the firmware
and second patch being an hwmon driver, enables the sysfs interfaces.

---

Neelesh Gupta (1):
      powerpc/powernv: Enable fetching of platform sensor data

Shivaprasad G Bhat (1):
      powerpc/powernv: hwmon driver for power values, fan rpm and temperature


 arch/powerpc/include/asm/opal.h                |    4 
 arch/powerpc/platforms/powernv/Makefile        |    2 
 arch/powerpc/platforms/powernv/opal-sensor.c   |   64 +++
 arch/powerpc/platforms/powernv/opal-wrappers.S |    1 
 drivers/hwmon/Kconfig                          |    8 
 drivers/hwmon/Makefile                         |    1 
 drivers/hwmon/ibmpowernv.c                     |  529 ++++++++++++++++++++++++
 7 files changed, 608 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/platforms/powernv/opal-sensor.c
 create mode 100644 drivers/hwmon/ibmpowernv.c

-- 
Signature

[PATCH 1/2] powerpc/powernv: Enable fetching of platform sensor data

From: Neelesh Gupta <hidden>
Date: 2014-03-07 05:35:14

This patch enables fetching of various platform sensor data through
OPAL and expects a sensor handle from the driver to pass to OPAL.

Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/include/asm/opal.h                |    4 ++
 arch/powerpc/platforms/powernv/Makefile        |    2 -
 arch/powerpc/platforms/powernv/opal-sensor.c   |   64 ++++++++++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S |    1 
 4 files changed, 70 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/platforms/powernv/opal-sensor.c
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 60b3edc..a1cc4dd 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -159,6 +159,7 @@ extern int opal_enter_rtas(struct rtas_args *args,
 #define OPAL_GET_MSG				85
 #define OPAL_CHECK_ASYNC_COMPLETION		86
 #define OPAL_SYNC_HOST_REBOOT			87
+#define OPAL_SENSOR_READ			88
 #define OPAL_GET_PARAM				89
 #define OPAL_SET_PARAM				90
 
@@ -847,6 +848,8 @@ int64_t opal_get_param(uint64_t token, uint32_t param_id, uint64_t buffer,
 		size_t length);
 int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer,
 		size_t length);
+int64_t opal_sensor_read(uint32_t sensor_hndl, int token,
+		uint32_t *sensor_data);
 
 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data);
@@ -875,6 +878,7 @@ extern int opal_async_get_token_interruptible(void);
 extern int __opal_async_release_token(int token);
 extern int opal_async_release_token(int token);
 extern int opal_async_wait_response(uint64_t token, struct opal_msg *msg);
+extern int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data);
 
 extern void hvc_opal_init_early(void);
 
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index e6166d3..00ae938 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,6 +1,6 @@
 obj-y			+= setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o
 obj-y			+= opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o opal-sysparam.o
-obj-y			+= rng.o
+obj-y			+= rng.o opal-sensor.o
 
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-sensor.c b/arch/powerpc/platforms/powernv/opal-sensor.c
new file mode 100644
index 0000000..663cc9c
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-sensor.c
@@ -0,0 +1,64 @@
+/*
+ * PowerNV sensor code
+ *
+ * Copyright (C) 2013 IBM
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <asm/opal.h>
+
+static DEFINE_MUTEX(opal_sensor_mutex);
+
+/*
+ * This will return sensor information to driver based on the requested sensor
+ * handle. A handle is an opaque id for the powernv, read by the driver from the
+ * device tree..
+ */
+int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data)
+{
+	int ret, token;
+	struct opal_msg msg;
+
+	token = opal_async_get_token_interruptible();
+	if (token < 0) {
+		pr_err("%s: Couldn't get the token, returning\n", __func__);
+		ret = token;
+		goto out;
+	}
+
+	mutex_lock(&opal_sensor_mutex);
+	ret = opal_sensor_read(sensor_hndl, token, sensor_data);
+	if (ret != OPAL_ASYNC_COMPLETION)
+		goto out_token;
+
+	ret = opal_async_wait_response(token, &msg);
+	if (ret) {
+		pr_err("%s: Failed to wait for the async response, %d\n",
+				__func__, ret);
+		goto out_token;
+	}
+
+	ret = msg.params[1];
+
+out_token:
+	mutex_unlock(&opal_sensor_mutex);
+	opal_async_release_token(token);
+out:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(opal_get_sensor_data);
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 0afdeca..4279e30 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -129,5 +129,6 @@ OPAL_CALL(opal_update_flash,			OPAL_FLASH_UPDATE);
 OPAL_CALL(opal_get_msg,				OPAL_GET_MSG);
 OPAL_CALL(opal_check_completion,		OPAL_CHECK_ASYNC_COMPLETION);
 OPAL_CALL(opal_sync_host_reboot,		OPAL_SYNC_HOST_REBOOT);
+OPAL_CALL(opal_sensor_read,			OPAL_SENSOR_READ);
 OPAL_CALL(opal_get_param,			OPAL_GET_PARAM);
 OPAL_CALL(opal_set_param,			OPAL_SET_PARAM);

[PATCH 2/2] powerpc/powernv: hwmon driver for power values, fan rpm and temperature

From: Neelesh Gupta <hidden>
Date: 2014-03-07 05:35:27

From: Shivaprasad G Bhat <redacted>

This patch adds basic kernel enablement for reading power values, fan
speed rpm and temperature values on powernv platforms which will
be exported to user space through sysfs interface.

Signed-off-by: Shivaprasad G Bhat <redacted>
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 drivers/hwmon/Kconfig      |    8 +
 drivers/hwmon/Makefile     |    1 
 drivers/hwmon/ibmpowernv.c |  529 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 538 insertions(+)
 create mode 100644 drivers/hwmon/ibmpowernv.c
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5ce43d8..ad4cdcb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -557,6 +557,14 @@ config SENSORS_IBMPEX
 	  This driver can also be built as a module.  If so, the module
 	  will be called ibmpex.
 
+config SENSORS_IBMPOWERNV
+	tristate "IBM PowerNv Platform temperature/power/fan sensor"
+	depends on PPC_POWERNV
+	default y
+	help
+	  If you say yes here you get support for the temperature/fan/power
+	  sensors on your platform.
+
 config SENSORS_IIO_HWMON
 	tristate "Hwmon driver that uses channels specified via iio maps"
 	depends on IIO
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index ec7cde0..807e172 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_SENSORS_ULTRA45)	+= ultra45_env.o
 obj-$(CONFIG_SENSORS_I5K_AMB)	+= i5k_amb.o
 obj-$(CONFIG_SENSORS_IBMAEM)	+= ibmaem.o
 obj-$(CONFIG_SENSORS_IBMPEX)	+= ibmpex.o
+obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
 obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
 obj-$(CONFIG_SENSORS_INA209)	+= ina209.o
 obj-$(CONFIG_SENSORS_INA2XX)	+= ina2xx.o
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
new file mode 100644
index 0000000..b7b1297
--- /dev/null
+++ b/drivers/hwmon/ibmpowernv.c
@@ -0,0 +1,529 @@
+/*
+ * hwmon driver for temperature/power/fan on IBM PowerNV platform
+ * Copyright (C) 2013 IBM
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <asm/opal.h>
+#include <linux/err.h>
+
+MODULE_DESCRIPTION("IBM PowerNV Platform power/temp/fan sensor hwmon module");
+MODULE_LICENSE("GPL");
+
+#define MAX_ATTR_LENGTH		32
+
+/* Device tree sensor name prefixes. The device tree has the names in the
+ * format "cooling-fan#2-faulted" where the "cooling-fan" is the sensor type,
+ * 2 is the sensor count, and "faulted" is the sensor data attribute type.
+ */
+#define DT_FAULT_ATTR_SUFFIX		"faulted"
+#define DT_DATA_ATTR_SUFFIX		"data"
+#define DT_THRESHOLD_ATTR_SUFFIX	"thrs"
+
+enum sensors {
+	FAN,
+	TEMPERATURE,
+	POWERSUPPLY,
+	POWER,
+	MAX_SENSOR_TYPE,
+};
+
+enum attributes {
+	INPUT,
+	MINIMUM,
+	MAXIMUM,
+	FAULT,
+	MAX_ATTR_TYPES
+};
+
+static struct sensor_name {
+	char *name;
+	char *compaible;
+} sensor_names[] = {
+		{"fan-sensor", "ibm,opal-sensor-cooling-fan"},
+		{"amb-temp-sensor", "ibm,opal-sensor-amb-temp"},
+		{"power-sensor", "ibm,opal-sensor-power-supply"},
+		{"power", "ibm,opal-sensor-power"}
+};
+
+static const char * const attribute_type_table[] = {
+	"input",
+	"min",
+	"max",
+	"fault",
+	NULL
+};
+
+struct pdev_entry {
+	struct list_head list;
+	struct platform_device *pdev;
+	enum sensors type;
+};
+
+static LIST_HEAD(pdev_list);
+
+/* The sensors are categorised on type.
+ *
+ * The sensors of same type are categorised under a common platform device.
+ * So, The pdev is shared by all sensors of same type.
+ * Ex : temp1_input, temp1_max, temp2_input,temp2_max all share same platform
+ * device.
+ *
+ * "sensor_data" is the Platform device specific data.
+ * There is one hwmon_device instance for all the sensors of same type.
+ * This also holds the list of all sensors with same type but different
+ * attribute and index.
+ */
+struct sensor_specific_data {
+	u32 sensor_id; /* The hex value as in the device tree */
+	u32 sensor_index; /* The sensor instance index */
+	struct sensor_device_attribute sd_attr;
+	enum attributes attr_type;
+	char attr_name[64];
+};
+
+struct sensor_data {
+	struct device *hwmon_dev;
+	struct list_head sensor_list;
+	struct device_attribute name_attr;
+};
+
+struct  sensor_entry {
+	struct list_head list;
+	struct sensor_specific_data *sensor_data;
+};
+
+static struct platform_device *powernv_sensor_get_pdev(enum sensors type)
+{
+	struct pdev_entry *p;
+	list_for_each_entry(p, &pdev_list, list)
+		if (p->type == type)
+			return p->pdev;
+
+	return NULL;
+}
+
+static struct sensor_specific_data *powernv_sensor_get_sensor_data(
+					struct sensor_data *pdata,
+					int index, enum attributes attr_type)
+{
+	struct sensor_entry *p;
+	list_for_each_entry(p, &pdata->sensor_list, list)
+		if ((p->sensor_data->sensor_index == index) &&
+		    (attr_type == p->sensor_data->attr_type))
+			return p->sensor_data;
+
+	return NULL;
+}
+
+static ssize_t show_name(struct device *dev,
+				struct device_attribute *devattr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	return sprintf(buf, "%s\n", pdev->name);
+}
+
+/* Note: Data from the sensors for each sensor type needs to be converted to
+ * the dimension appropriate.
+ */
+static ssize_t show_sensor(struct device *dev,
+				struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(devattr);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct sensor_data *pdata = platform_get_drvdata(pdev);
+	struct sensor_specific_data *tdata = NULL;
+	enum sensors sensor_type = pdev->id;
+	u32 x = -1;
+	int ret;
+
+	if (sd_attr && sd_attr->dev_attr.attr.name) {
+		char *pos = strchr(sd_attr->dev_attr.attr.name, '_');
+		int i;
+
+		for (i = 0; i < MAX_ATTR_TYPES; i++) {
+			if (strcmp(pos+1, attribute_type_table[i]) == 0) {
+				tdata = powernv_sensor_get_sensor_data(pdata,
+						sd_attr->index, i);
+				break;
+			}
+		}
+	}
+
+	if (tdata) {
+		ret = opal_get_sensor_data(tdata->sensor_id, &x);
+		if (ret)
+			x = -1;
+	}
+
+	if (sensor_type == TEMPERATURE && x > 0) {
+		/* Temperature comes in Degrees and convert it to
+		 * milli-degrees.
+		 */
+		x = x*1000;
+	} else if (sensor_type == POWER && x > 0) {
+		/* Power value comes in watts, convert to micro-watts */
+		x = x * 1000000;
+	}
+
+	return sprintf(buf, "%d\n", x);
+}
+
+static u32 get_sensor_index_from_name(const char *name)
+{
+	char *hash_position = strchr(name, '#');
+	u32 index = 0, copy_length;
+	char newbuf[8];
+
+	if (hash_position) {
+		copy_length = strchr(hash_position, '-') - hash_position - 1;
+		if (copy_length < sizeof(newbuf)) {
+			strncpy(newbuf, hash_position + 1, copy_length);
+			sscanf(newbuf, "%d", &index);
+		}
+	}
+
+	return index;
+}
+
+static inline void get_sensor_suffix_from_name(const char *name, char *suffix)
+{
+	char *dash_position = strrchr(name, '-');
+	if (dash_position)
+		strncpy(suffix, dash_position+1, MAX_ATTR_LENGTH);
+	else
+		strcpy(suffix,"");
+}
+
+static int get_sensor_attr_properties(const char *sensor_name,
+		enum sensors sensor_type, enum attributes *attr_type,
+		u32 *sensor_index)
+{
+	char suffix[MAX_ATTR_LENGTH];
+
+	*attr_type = MAX_ATTR_TYPES;
+	*sensor_index = get_sensor_index_from_name(sensor_name);
+	if (*sensor_index == 0)
+		return -EINVAL;
+
+	get_sensor_suffix_from_name(sensor_name, suffix);
+	if (strcmp(suffix, "") == 0)
+		return -EINVAL;
+
+	if (strcmp(suffix, DT_FAULT_ATTR_SUFFIX) == 0)
+		*attr_type = FAULT;
+	else if (strcmp(suffix, DT_DATA_ATTR_SUFFIX) == 0)
+		*attr_type = INPUT;
+	else if ((sensor_type == TEMPERATURE) &&
+			(strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0))
+		*attr_type = MAXIMUM;
+	else if ((sensor_type == FAN) &&
+			(strcmp(suffix, DT_THRESHOLD_ATTR_SUFFIX) == 0))
+		*attr_type = MINIMUM;
+	else
+		return -ENOENT;
+
+	if (((sensor_type == FAN) && ((*attr_type == INPUT) ||
+				    (*attr_type == MINIMUM)))
+	    || ((sensor_type == TEMPERATURE) && ((*attr_type == INPUT) ||
+						 (*attr_type == MAXIMUM)))
+	    || ((sensor_type == POWER) && ((*attr_type == INPUT))))
+		return 0;
+
+	return -ENOENT;
+}
+
+static int create_sensor_attr(struct sensor_specific_data *tdata,
+		struct device *dev, enum sensors sensor_type,
+		enum attributes attr_type)
+{
+	int err = 0;
+	char temp_file_prefix[50];
+	static const char *const file_name_format = "%s%d_%s";
+
+	tdata->attr_type = attr_type;
+
+	if (sensor_type == FAN)
+		strcpy(temp_file_prefix, "fan");
+	else if (sensor_type == TEMPERATURE)
+		strcpy(temp_file_prefix, "temp");
+	else if (sensor_type == POWERSUPPLY)
+		strcpy(temp_file_prefix, "powersupply");
+	else if (sensor_type == POWER)
+		strcpy(temp_file_prefix, "power");
+
+	snprintf(tdata->attr_name, sizeof(tdata->attr_name), file_name_format,
+		 temp_file_prefix, tdata->sensor_index,
+		 attribute_type_table[tdata->attr_type]);
+
+	sysfs_attr_init(&tdata->sd_attr.dev_attr.attr);
+	tdata->sd_attr.dev_attr.attr.name = tdata->attr_name;
+	tdata->sd_attr.dev_attr.attr.mode = S_IRUGO;
+	tdata->sd_attr.dev_attr.show = show_sensor;
+
+	tdata->sd_attr.index = tdata->sensor_index;
+	err = device_create_file(dev, &tdata->sd_attr.dev_attr);
+
+	return err;
+}
+
+static int create_name_attr(struct sensor_data *pdata,
+				struct device *dev)
+{
+	sysfs_attr_init(&pdata->name_attr.attr);
+	pdata->name_attr.attr.name = "name";
+	pdata->name_attr.attr.mode = S_IRUGO;
+	pdata->name_attr.show = show_name;
+	return device_create_file(dev, &pdata->name_attr);
+}
+
+static int create_platform_device(enum sensors sensor_type,
+					struct platform_device **pdev)
+{
+	struct pdev_entry *pdev_entry = NULL;
+	int err;
+
+	*pdev = platform_device_alloc(sensor_names[sensor_type].name,
+			sensor_type);
+	if (!*pdev) {
+		pr_err("Device allocation failed\n");
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
+	if (!pdev_entry) {
+		pr_err("Device allocation failed\n");
+		err = -ENOMEM;
+		goto exit_device_put;
+	}
+
+	err = platform_device_add(*pdev);
+	if (err) {
+		pr_err("Device addition failed (%d)\n", err);
+		goto exit_device_free;
+	}
+
+	pdev_entry->pdev = *pdev;
+	pdev_entry->type = (*pdev)->id;
+
+	list_add_tail(&pdev_entry->list, &pdev_list);
+
+	return 0;
+exit_device_free:
+	kfree(pdev_entry);
+exit_device_put:
+	platform_device_put(*pdev);
+exit:
+	return err;
+}
+
+static int create_sensor_data(struct platform_device *pdev)
+{
+	struct sensor_data *pdata = NULL;
+	int err = 0;
+
+	pdata = kzalloc(sizeof(struct sensor_data), GFP_KERNEL);
+	if (!pdata) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	err = create_name_attr(pdata, &pdev->dev);
+	if (err)
+		goto exit_free;
+
+	pdata->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(pdata->hwmon_dev)) {
+		err = PTR_ERR(pdata->hwmon_dev);
+		dev_err(&pdev->dev, "Class registration failed (%d)\n",
+			err);
+		goto exit_name;
+	}
+
+	INIT_LIST_HEAD(&pdata->sensor_list);
+	platform_set_drvdata(pdev, pdata);
+
+	return 0;
+
+exit_name:
+	device_remove_file(&pdev->dev, &pdata->name_attr);
+exit_free:
+	kfree(pdata);
+exit:
+	return err;
+}
+
+static void delete_sensor_attr(struct sensor_data *pdata)
+{
+	struct sensor_entry *s, *l;
+
+	list_for_each_entry_safe(s, l, &pdata->sensor_list, list) {
+		struct sensor_specific_data *tdata = s->sensor_data;
+			kfree(tdata);
+			list_del(&s->list);
+			kfree(s);
+		}
+}
+
+static int powernv_sensor_init(u32 sensor_id, const struct device_node *np,
+		enum sensors sensor_type, enum attributes attr_type,
+		u32 sensor_index)
+{
+	struct platform_device *pdev = powernv_sensor_get_pdev(sensor_type);
+	struct sensor_specific_data *tdata;
+	struct sensor_entry *sensor_entry;
+	struct sensor_data *pdata;
+	int err = 0;
+
+	if (!pdev) {
+		err = create_platform_device(sensor_type, &pdev);
+		if (err)
+			goto exit;
+
+		err = create_sensor_data(pdev);
+		if (err)
+			goto exit;
+	}
+
+	pdata = platform_get_drvdata(pdev);
+	if (!pdata) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	tdata = kzalloc(sizeof(struct sensor_specific_data), GFP_KERNEL);
+	if (!tdata) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	tdata->sensor_id = sensor_id;
+	tdata->sensor_index = sensor_index;
+
+	err = create_sensor_attr(tdata, &pdev->dev, sensor_type, attr_type);
+	if (err)
+		goto exit_free;
+
+	sensor_entry = kzalloc(sizeof(struct sensor_entry), GFP_KERNEL);
+	if (!sensor_entry) {
+		err = -ENOMEM;
+		goto exit_attr;
+	}
+
+	sensor_entry->sensor_data = tdata;
+
+	list_add_tail(&sensor_entry->list, &pdata->sensor_list);
+
+	return 0;
+exit_attr:
+	device_remove_file(&pdev->dev, &tdata->sd_attr.dev_attr);
+exit_free:
+	kfree(tdata);
+exit:
+	return err;
+}
+
+static void delete_unregister_sensors(void)
+{
+	struct pdev_entry *p, *n;
+
+	list_for_each_entry_safe(p, n, &pdev_list, list) {
+		struct sensor_data *pdata = platform_get_drvdata(p->pdev);
+			if (pdata) {
+				delete_sensor_attr(pdata);
+
+				hwmon_device_unregister(pdata->hwmon_dev);
+				kfree(pdata);
+			}
+		platform_device_unregister(p->pdev);
+		list_del(&p->list);
+		kfree(p);
+	}
+}
+
+static int __init powernv_hwmon_init(void)
+{
+	struct device_node *opal, *np = NULL;
+	enum attributes attr_type;
+	enum sensors type;
+	const u32 *sensor_id;
+	u32 sensor_index;
+	int err;
+
+	opal = of_find_node_by_path("/ibm,opal/sensors");
+	if (!opal) {
+		pr_err("%s: Opal 'sensors' node not found\n", __func__);
+		return -ENXIO;
+	}
+
+	for_each_child_of_node(opal, np) {
+		if (np->name == NULL)
+			continue;
+
+		for (type = 0; type < MAX_SENSOR_TYPE; type++)
+			if (of_device_is_compatible(np,
+					sensor_names[type].compaible))
+				break;
+
+		if (type == MAX_SENSOR_TYPE)
+			continue;
+
+		if (get_sensor_attr_properties(np->name, type, &attr_type,
+				&sensor_index))
+			continue;
+
+		sensor_id = of_get_property(np, "sensor-id", NULL);
+		if (!sensor_id) {
+			pr_info("%s: %s doesn't have sensor-id\n", __func__,
+					np->name);
+			continue;
+		}
+
+		err = powernv_sensor_init(*sensor_id, np, type, attr_type,
+				sensor_index);
+		if (err) {
+			of_node_put(opal);
+			goto exit;
+		}
+	}
+	of_node_put(opal);
+
+	return 0;
+exit:
+	delete_unregister_sensors();
+	return err;
+
+}
+
+static void powernv_hwmon_exit(void)
+{
+	delete_unregister_sensors();
+}
+
+module_init(powernv_hwmon_init);
+module_exit(powernv_hwmon_exit);

Re: [PATCH 1/2] powerpc/powernv: Enable fetching of platform sensor data

From: Paul Gortmaker <hidden>
Date: 2014-03-28 15:27:23

On Fri, Mar 7, 2014 at 12:33 AM, Neelesh Gupta
[off-list ref] wrote:
This patch enables fetching of various platform sensor data through
OPAL and expects a sensor handle from the driver to pass to OPAL.
Looks like a missing header, causing build failures on celleb_defconfig

cc1: warnings being treated as errors
In file included from arch/powerpc/platforms/powernv/opal-sensor.c:23:0:
/home/paul/git/linux-head/arch/powerpc/include/asm/opal.h:893:42:
error: 'struct notifier_block' declared inside parameter list
/home/paul/git/linux-head/arch/powerpc/include/asm/opal.h:893:42:
error: its scope is only this definition or declaration, which is
probably not what you want
/home/paul/git/linux-head/arch/powerpc/include/asm/opal.h:895:14:
error: 'struct notifier_block' declared inside parameter list
make[2]: *** [arch/powerpc/platforms/powernv/opal-sensor.o] Error 1
make[1]: *** [arch/powerpc/platforms/powernv] Error 2
make[1]: *** Waiting for unfinished jobs....
make: *** [arch/powerpc/platforms] Error 2
make: *** Waiting for unfinished jobs....
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[4029cd66545f0a45258eda5313b7559bfeaaaae4] powerpc/powernv: Enable
reading and updating of system parameters
running ./x
#
# configuration written to .config
#
7224adbbb80329d1a3ec5aa98213b50365fcd246 is the first bad commit
commit 7224adbbb80329d1a3ec5aa98213b50365fcd246
Author: Neelesh Gupta [off-list ref]
Date:   Fri Mar 7 11:03:27 2014 +0530

    powerpc/powernv: Enable fetching of platform sensor data

    This patch enables fetching of various platform sensor data through
    OPAL and expects a sensor handle from the driver to pass to OPAL.

    Signed-off-by: Neelesh Gupta [off-list ref]
    Signed-off-by: Benjamin Herrenschmidt [off-list ref]

:040000 040000 ee7c8dec121a0da53be09da0e1449d8b885ef8a1
b8c4906de326638be77387c78173f7148369d6ff M    arch
bisect run success

http://kisskb.ellerman.id.au/kisskb/buildresult/10844143/

Paul.
---
quoted hunk
Signed-off-by: Neelesh Gupta <redacted>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
 arch/powerpc/include/asm/opal.h                |    4 ++
 arch/powerpc/platforms/powernv/Makefile        |    2 -
 arch/powerpc/platforms/powernv/opal-sensor.c   |   64 ++++++++++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S |    1
 4 files changed, 70 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/platforms/powernv/opal-sensor.c
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 60b3edc..a1cc4dd 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -159,6 +159,7 @@ extern int opal_enter_rtas(struct rtas_args *args,
 #define OPAL_GET_MSG                           85
 #define OPAL_CHECK_ASYNC_COMPLETION            86
 #define OPAL_SYNC_HOST_REBOOT                  87
+#define OPAL_SENSOR_READ                       88
 #define OPAL_GET_PARAM                         89
 #define OPAL_SET_PARAM                         90
@@ -847,6 +848,8 @@ int64_t opal_get_param(uint64_t token, uint32_t param_id, uint64_t buffer,
                size_t length);
 int64_t opal_set_param(uint64_t token, uint32_t param_id, uint64_t buffer,
                size_t length);
+int64_t opal_sensor_read(uint32_t sensor_hndl, int token,
+               uint32_t *sensor_data);

 /* Internal functions */
 extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data);
@@ -875,6 +878,7 @@ extern int opal_async_get_token_interruptible(void);
 extern int __opal_async_release_token(int token);
 extern int opal_async_release_token(int token);
 extern int opal_async_wait_response(uint64_t token, struct opal_msg *msg);
+extern int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data);

 extern void hvc_opal_init_early(void);
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index e6166d3..00ae938 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,6 +1,6 @@
 obj-y                  += setup.o opal-takeover.o opal-wrappers.o opal.o opal-async.o
 obj-y                  += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o opal-sysparam.o
-obj-y                  += rng.o
+obj-y                  += rng.o opal-sensor.o

 obj-$(CONFIG_SMP)      += smp.o
 obj-$(CONFIG_PCI)      += pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-sensor.c b/arch/powerpc/platforms/powernv/opal-sensor.c
new file mode 100644
index 0000000..663cc9c
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-sensor.c
@@ -0,0 +1,64 @@
+/*
+ * PowerNV sensor code
+ *
+ * Copyright (C) 2013 IBM
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <asm/opal.h>
+
+static DEFINE_MUTEX(opal_sensor_mutex);
+
+/*
+ * This will return sensor information to driver based on the requested sensor
+ * handle. A handle is an opaque id for the powernv, read by the driver from the
+ * device tree..
+ */
+int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data)
+{
+       int ret, token;
+       struct opal_msg msg;
+
+       token = opal_async_get_token_interruptible();
+       if (token < 0) {
+               pr_err("%s: Couldn't get the token, returning\n", __func__);
+               ret = token;
+               goto out;
+       }
+
+       mutex_lock(&opal_sensor_mutex);
+       ret = opal_sensor_read(sensor_hndl, token, sensor_data);
+       if (ret != OPAL_ASYNC_COMPLETION)
+               goto out_token;
+
+       ret = opal_async_wait_response(token, &msg);
+       if (ret) {
+               pr_err("%s: Failed to wait for the async response, %d\n",
+                               __func__, ret);
+               goto out_token;
+       }
+
+       ret = msg.params[1];
+
+out_token:
+       mutex_unlock(&opal_sensor_mutex);
+       opal_async_release_token(token);
+out:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(opal_get_sensor_data);
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 0afdeca..4279e30 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -129,5 +129,6 @@ OPAL_CALL(opal_update_flash,                        OPAL_FLASH_UPDATE);
 OPAL_CALL(opal_get_msg,                                OPAL_GET_MSG);
 OPAL_CALL(opal_check_completion,               OPAL_CHECK_ASYNC_COMPLETION);
 OPAL_CALL(opal_sync_host_reboot,               OPAL_SYNC_HOST_REBOOT);
+OPAL_CALL(opal_sensor_read,                    OPAL_SENSOR_READ);
 OPAL_CALL(opal_get_param,                      OPAL_GET_PARAM);
 OPAL_CALL(opal_set_param,                      OPAL_SET_PARAM);
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help