[RFC PATCH 3/3] hwmon: (ibmpowernv) add DTS support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch reworks the ibmpowernv driver to support the new device tree
for sensors exposed by OPAL. It also adds new sensors : the core and
memory buffers DTS.

Hopefully, the proposed framework is good enough to easily add sensors
for other resources such as volts, planar temperatures, etc.

Signed-off-by: Cédric Le Goater <clg@xxxxxxxxxx>
---
 drivers/hwmon/ibmpowernv.c |  336 ++++++++++++++++++++++++--------------------
 1 file changed, 180 insertions(+), 156 deletions(-)

diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index febe8175d36c..9a6ee33f8219 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -32,247 +32,276 @@
 #include <linux/err.h>
 
 #define MAX_ATTR_LEN	32
-
-/* Sensor suffix name from DT */
-#define DT_FAULT_ATTR_SUFFIX		"faulted"
-#define DT_DATA_ATTR_SUFFIX		"data"
-#define DT_THRESHOLD_ATTR_SUFFIX	"thrs"
+#define MAX_LABEL_LEN	64
+#define MAX_ATTRS	3	/* sensor-data, sensor-status, label */
 
 /*
  * Enumerates all the types of sensors in the POWERNV platform and does index
- * into 'struct sensor_group'
+ * into 'struct sensor_type'
  */
 enum sensors {
 	FAN,
-	AMBIENT_TEMP,
+	TEMP,
 	POWER_SUPPLY,
 	POWER_INPUT,
 	MAX_SENSOR_TYPE,
 };
 
-static struct sensor_group {
+static struct sensor_type {
+	enum sensors type;
 	const char *name;
-	const char *compatible;
-	struct attribute_group group;
-	u32 attr_count;
-} sensor_groups[] = {
-	{"fan", "ibm,opal-sensor-cooling-fan"},
-	{"temp", "ibm,opal-sensor-amb-temp"},
-	{"in", "ibm,opal-sensor-power-supply"},
-	{"power", "ibm,opal-sensor-power"}
+	u32 count;
+} sensor_types[] = {
+	{ FAN,			"fan"	},
+	{ TEMP,			"temp"	},
+	{ POWER_SUPPLY,		"power"	},
+	{ POWER_INPUT,		"in"	},
+	{ MAX_SENSOR_TYPE,	NULL	}
 };
 
 struct sensor_data {
-	u32 id; /* An opaque id of the firmware for each sensor */
+	u32 data;
+	u32 status;
+	char label[MAX_LABEL_LEN];
 	enum sensors type;
-	char name[MAX_ATTR_LEN];
-	struct device_attribute dev_attr;
+	char attr_name[MAX_ATTRS][MAX_ATTR_LEN];
+	struct sensor_device_attribute sd_attrs[MAX_ATTRS];
 };
 
 struct platform_data {
-	const struct attribute_group *attr_groups[MAX_SENSOR_TYPE + 1];
+	struct attribute_group attr_group;
+	const struct attribute_group *groups[2];
 	u32 sensors_count; /* Total count of sensors from each group */
+	struct attribute **attrs;
+	struct sensor_data **sensors;
 };
 
 static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
 			   char *buf)
 {
-	struct sensor_data *sdata = container_of(devattr, struct sensor_data,
-						 dev_attr);
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct platform_data *pdata = dev_get_drvdata(dev);
+	struct sensor_data *sdata = pdata->sensors[attr->index];
 	ssize_t ret;
 	u32 x;
 
-	ret = opal_get_sensor_data(sdata->id, &x);
+	ret = opal_get_sensor_data(sdata->data, &x);
 	if (ret)
 		return ret;
 
 	/* Convert temperature to milli-degrees */
-	if (sdata->type == AMBIENT_TEMP)
+	if (sdata->type == TEMP)
 		x *= 1000;
 	/* Convert power to micro-watts */
-	else if (sdata->type == POWER_INPUT)
+	else if (sdata->type == POWER_SUPPLY)
 		x *= 1000000;
 
 	return sprintf(buf, "%u\n", x);
 }
 
-static int get_sensor_index_attr(const char *name, u32 *index,
-					char *attr)
+static ssize_t show_alarm(struct device *dev, struct device_attribute *devattr,
+			   char *buf)
 {
-	char *hash_pos = strchr(name, '#');
-	char buf[8] = { 0 };
-	char *dash_pos;
-	u32 copy_len;
-	int err;
-
-	if (!hash_pos)
-		return -EINVAL;
-
-	dash_pos = strchr(hash_pos, '-');
-	if (!dash_pos)
-		return -EINVAL;
-
-	copy_len = dash_pos - hash_pos - 1;
-	if (copy_len >= sizeof(buf))
-		return -EINVAL;
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct platform_data *pdata = dev_get_drvdata(dev);
+	struct sensor_data *sdata = pdata->sensors[attr->index];
+	ssize_t ret;
+	u32 x;
 
-	strncpy(buf, hash_pos + 1, copy_len);
+	ret = opal_get_sensor_data(sdata->status, &x);
+	if (ret)
+		return ret;
 
-	err = kstrtou32(buf, 10, index);
-	if (err)
-		return err;
+	/*
+	 * Depending on the sensor type, the status bits can have
+	 * different meanings. Let's not be too subtil for the moment,
+	 * testing against 0x6 should raise an alarm.
+	 */
+	return sprintf(buf, "%u\n", x & 0x6);
+}
 
-	strncpy(attr, dash_pos + 1, MAX_ATTR_LEN);
+static ssize_t show_label(struct device *dev, struct device_attribute *devattr,
+			   char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct platform_data *pdata = dev_get_drvdata(dev);
+	struct sensor_data *sdata = pdata->sensors[attr->index];
 
-	return 0;
+	return sprintf(buf, "%s\n", sdata->label);
 }
 
-/*
- * This function translates the DT node name into the 'hwmon' attribute name.
- * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc.
- * which need to be mapped as fan2_input, temp1_max respectively before
- * populating them inside hwmon device class.
- */
-static int create_hwmon_attr_name(struct device *dev, enum sensors type,
-					 const char *node_name,
-					 char *hwmon_attr_name)
+static struct sensor_type *__init get_sensor_type(struct platform_device *pdev,
+	struct device_node *np)
 {
-	char attr_suffix[MAX_ATTR_LEN];
-	char *attr_name;
-	u32 index;
-	int err;
+	const char *type;
+	int i;
 
-	err = get_sensor_index_attr(node_name, &index, attr_suffix);
-	if (err) {
-		dev_err(dev, "Sensor device node name '%s' is invalid\n",
-			node_name);
-		return err;
-	}
+	if (np->name == NULL)
+		return NULL;
+
+	if (!of_device_is_compatible(np, "ibm,opal-sensor"))
+		return NULL;
 
-	if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) {
-		attr_name = "fault";
-	} else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) {
-		attr_name = "input";
-	} else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) {
-		if (type == AMBIENT_TEMP)
-			attr_name = "max";
-		else if (type == FAN)
-			attr_name = "min";
-		else
-			return -ENOENT;
-	} else {
-		return -ENOENT;
+	if (of_property_read_string(np, "sensor-type", &type)) {
+		dev_info(&pdev->dev,
+			 "'sensor-type' missing in the node '%s'\n",
+			 np->name);
+		return NULL;
 	}
 
-	snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s",
-		 sensor_groups[type].name, index, attr_name);
-	return 0;
+	for (i = 0 ; i < MAX_SENSOR_TYPE; i++)
+		if (!strcmp(type, sensor_types[i].name))
+			return &sensor_types[i];
+
+	return NULL;
 }
 
-static int populate_attr_groups(struct platform_device *pdev)
+static void __init make_sensor_label(struct device_node *np,
+		    struct sensor_data *sdata, const char *label)
 {
-	struct platform_data *pdata = platform_get_drvdata(pdev);
-	const struct attribute_group **pgroups = pdata->attr_groups;
-	struct device_node *opal, *np;
-	enum sensors type;
+	u32 id;
+	size_t n;
 
-	opal = of_find_node_by_path("/ibm,opal/sensors");
-	for_each_child_of_node(opal, np) {
-		if (np->name == NULL)
-			continue;
+	n = snprintf(sdata->label, sizeof(sdata->label), "%s", label);
 
-		for (type = 0; type < MAX_SENSOR_TYPE; type++)
-			if (of_device_is_compatible(np,
-					sensor_groups[type].compatible)) {
-				sensor_groups[type].attr_count++;
-				break;
-			}
-	}
+	/*
+	 * Core temp pretty pretty
+	 */
+	if (!of_property_read_u32(np, "ibm,pir", &id)) {
+		int i;
 
-	of_node_put(opal);
+		for_each_possible_cpu(i)
+			if (paca[i].hw_cpu_id == id)
+				break;
 
-	for (type = 0; type < MAX_SENSOR_TYPE; type++) {
-		sensor_groups[type].group.attrs = devm_kzalloc(&pdev->dev,
-					sizeof(struct attribute *) *
-					(sensor_groups[type].attr_count + 1),
-					GFP_KERNEL);
-		if (!sensor_groups[type].group.attrs)
-			return -ENOMEM;
-
-		pgroups[type] = &sensor_groups[type].group;
-		pdata->sensors_count += sensor_groups[type].attr_count;
-		sensor_groups[type].attr_count = 0;
+		n += snprintf(sdata->label + n, sizeof(sdata->label) - n,
+			      " %d-%d", i, i+7);
 	}
 
-	return 0;
+	/*
+	 * Membuffer pretty print
+	 */
+	if (!of_property_read_u32(np, "ibm,chip-id", &id))
+		n += snprintf(sdata->label + n, sizeof(sdata->label) - n,
+			      " %d", id & 0xffff);
 }
 
-/*
- * Iterate through the device tree for each child of 'sensors' node, create
- * a sysfs attribute file, the file is named by translating the DT node name
- * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max
- * etc..
- */
-static int create_device_attrs(struct platform_device *pdev)
+static int __init populate_attr_groups(struct platform_device *pdev)
 {
 	struct platform_data *pdata = platform_get_drvdata(pdev);
-	const struct attribute_group **pgroups = pdata->attr_groups;
 	struct device_node *opal, *np;
-	struct sensor_data *sdata;
-	u32 sensor_id;
-	enum sensors type;
-	u32 count = 0;
 	int err = 0;
+	int i = 0;
 
 	opal = of_find_node_by_path("/ibm,opal/sensors");
-	sdata = devm_kzalloc(&pdev->dev, pdata->sensors_count * sizeof(*sdata),
-			     GFP_KERNEL);
-	if (!sdata) {
+	if (!opal) {
+		dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n");
+		return -ENODEV;
+	}
+
+	for_each_child_of_node(opal, np) {
+		if (of_device_is_compatible(np, "ibm,opal-sensor"))
+			pdata->sensors_count++;
+	}
+
+	pdata->attrs = devm_kzalloc(&pdev->dev,
+	    sizeof(*pdata->attrs) * pdata->sensors_count * MAX_ATTRS + 1,
+	    GFP_KERNEL);
+	if (!pdata->attrs) {
+		err = -ENOMEM;
+		goto exit_put_node;
+	}
+	pdata->sensors = devm_kzalloc(&pdev->dev,
+	    sizeof(*pdata->sensors) * pdata->sensors_count * MAX_ATTRS + 1,
+	    GFP_KERNEL);
+	if (!pdata->sensors) {
 		err = -ENOMEM;
 		goto exit_put_node;
 	}
 
-	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_groups[type].compatible))
-				break;
+	for_each_child_of_node(opal, np) {
+		struct sensor_data *sdata;
+		struct sensor_type *stype;
+		u32 sensor_data;
+		const char *label;
 
-		if (type == MAX_SENSOR_TYPE)
+		stype = get_sensor_type(pdev, np);
+		if (!stype)
 			continue;
 
-		if (of_property_read_u32(np, "sensor-id", &sensor_id)) {
+		if (of_property_read_u32(np, "sensor-data", &sensor_data)) {
 			dev_info(&pdev->dev,
-				 "'sensor-id' missing in the node '%s'\n",
+				 "'sensor-data' missing in the node '%s'\n",
 				 np->name);
 			continue;
 		}
 
-		sdata[count].id = sensor_id;
-		sdata[count].type = type;
-		err = create_hwmon_attr_name(&pdev->dev, type, np->name,
-					     sdata[count].name);
-		if (err)
+		sdata = devm_kzalloc(&pdev->dev, sizeof(*sdata), GFP_KERNEL);
+		if (sdata == NULL) {
+			err = -ENOMEM;
 			goto exit_put_node;
+		}
+
+		stype->count++;
+		sdata->data = sensor_data;
+		sdata->type = stype->type;
+
+		snprintf(sdata->attr_name[0], MAX_ATTR_LEN, "%s%d_input",
+			 stype->name, stype->count);
+
+		sysfs_attr_init(&sdata->sd_attrs[0].dev_attr.attr);
+		sdata->sd_attrs[0].dev_attr.attr.name = sdata->attr_name[0];
+		sdata->sd_attrs[0].dev_attr.attr.mode = S_IRUGO;
+		sdata->sd_attrs[0].dev_attr.show = show_sensor;
+		sdata->sd_attrs[0].index = i;
+
+		pdata->attrs[i] = &sdata->sd_attrs[0].dev_attr.attr;
+		pdata->sensors[i++] = sdata;
+
+		if (!of_property_read_u32(np, "sensor-status",
+					  &sdata->status)) {
+			snprintf(sdata->attr_name[1], MAX_ATTR_LEN,
+				 "%s%d_alarm", stype->name, stype->count);
+
+			sysfs_attr_init(&sdata->sd_attrs[1].dev_attr.attr);
+			sdata->sd_attrs[1].dev_attr.attr.name =
+				sdata->attr_name[1];
+			sdata->sd_attrs[1].dev_attr.attr.mode = S_IRUGO;
+			sdata->sd_attrs[1].dev_attr.show = show_alarm;
+			sdata->sd_attrs[1].index = i;
+
+			pdata->attrs[i] = &sdata->sd_attrs[1].dev_attr.attr;
+			pdata->sensors[i++] = sdata;
+		}
+
+		if (!of_property_read_string(np, "label", &label)) {
+			snprintf(sdata->attr_name[2], MAX_ATTR_LEN,
+				 "%s%d_label", stype->name, stype->count);
 
-		sysfs_attr_init(&sdata[count].dev_attr.attr);
-		sdata[count].dev_attr.attr.name = sdata[count].name;
-		sdata[count].dev_attr.attr.mode = S_IRUGO;
-		sdata[count].dev_attr.show = show_sensor;
+			make_sensor_label(np, sdata, label);
 
-		pgroups[type]->attrs[sensor_groups[type].attr_count++] =
-				&sdata[count++].dev_attr.attr;
+			sysfs_attr_init(&sdata->sd_attrs[2].dev_attr.attr);
+			sdata->sd_attrs[2].dev_attr.attr.name =
+				sdata->attr_name[2];
+			sdata->sd_attrs[2].dev_attr.attr.mode = S_IRUGO;
+			sdata->sd_attrs[2].dev_attr.show = show_label;
+			sdata->sd_attrs[2].index = i;
+
+			pdata->attrs[i] = &sdata->sd_attrs[2].dev_attr.attr;
+			pdata->sensors[i++] = sdata;
+		}
 	}
 
+	pdata->attr_group.attrs = pdata->attrs;
+	pdata->groups[0] = &pdata->attr_group;
+
 exit_put_node:
 	of_node_put(opal);
 	return err;
 }
 
-static int ibmpowernv_probe(struct platform_device *pdev)
+static int __init ibmpowernv_probe(struct platform_device *pdev)
 {
 	struct platform_data *pdata;
 	struct device *hwmon_dev;
@@ -288,15 +317,10 @@ static int ibmpowernv_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
-	/* Create sysfs attribute data for each sensor found in the DT */
-	err = create_device_attrs(pdev);
-	if (err)
-		return err;
-
 	/* Finally, register with hwmon */
 	hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, DRVNAME,
 							   pdata,
-							   pdata->attr_groups);
+							   pdata->groups);
 
 	return PTR_ERR_OR_ZERO(hwmon_dev);
 }
-- 
1.7.10.4


_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors





[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux