[PATCH v2] powernv: sensor-groups: Add debugfs file to enable/disable sensor groups

From: Shilpasri G Bhat
Date: Tue Oct 23 2018 - 06:37:05 EST


This patch provides support to disable and enable platform specific
sensor groups like performance, utilization and frequency which are
currently not supported in hwmon.

Signed-off-by: Shilpasri G Bhat <shilpa.bhat@xxxxxxxxxxxxxxxxxx>
---
Changes from V1:
- As per Michael Ellerman's suggestion, adding the "enable" files to debugfs.
The original code had been written in mind to accomodate the "enable" file
in the same path as "clear" attribute. As this is not required anymore
the code is cleaned up to bifurcate the functions adding "enable" and
"clear" attribute.

.../powerpc/platforms/powernv/opal-sensor-groups.c | 284 ++++++++++++++-------
1 file changed, 186 insertions(+), 98 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/opal-sensor-groups.c b/arch/powerpc/platforms/powernv/opal-sensor-groups.c
index f7d04b6..e270dba 100644
--- a/arch/powerpc/platforms/powernv/opal-sensor-groups.c
+++ b/arch/powerpc/platforms/powernv/opal-sensor-groups.c
@@ -15,22 +15,21 @@
#include <linux/kobject.h>
#include <linux/slab.h>

+#include <asm/debugfs.h>
#include <asm/opal.h>

-DEFINE_MUTEX(sg_mutex);
+#define SENSOR_GROUPS_DIR_STR "sensor_groups"

static struct kobject *sg_kobj;

-struct sg_attr {
- u32 handle;
- struct kobj_attribute attr;
-};
-
static struct sensor_group {
- char name[20];
+ struct kobj_attribute sattr;
struct attribute_group sg;
- struct sg_attr *sgattrs;
-} *sgs;
+ struct mutex mutex;
+ char name[20];
+ u32 handle;
+ u32 enable;
+} *sgroups;

int sensor_group_enable(u32 handle, bool enable)
{
@@ -60,10 +59,12 @@ int sensor_group_enable(u32 handle, bool enable)
}
EXPORT_SYMBOL_GPL(sensor_group_enable);

-static ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count)
+static ssize_t sgroup_clear_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
{
- struct sg_attr *sattr = container_of(attr, struct sg_attr, attr);
+ struct sensor_group *sgroup = container_of(attr, struct sensor_group,
+ sattr);
struct opal_msg msg;
u32 data;
int ret, token;
@@ -81,11 +82,11 @@ static ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr,
return token;
}

- ret = mutex_lock_interruptible(&sg_mutex);
+ ret = mutex_lock_interruptible(&sgroup->mutex);
if (ret)
goto out_token;

- ret = opal_sensor_group_clear(sattr->handle, token);
+ ret = opal_sensor_group_clear(sgroup->handle, token);
switch (ret) {
case OPAL_ASYNC_COMPLETION:
ret = opal_async_wait_response(token, &msg);
@@ -106,135 +107,222 @@ static ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr,
}

out:
- mutex_unlock(&sg_mutex);
+ mutex_unlock(&sgroup->mutex);
out_token:
opal_async_release_token(token);
return ret;
}

-static struct sg_ops_info {
- int opal_no;
- const char *attr_name;
- ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t count);
-} ops_info[] = {
- { OPAL_SENSOR_GROUP_CLEAR, "clear", sg_store },
-};
+static int find_nr_groups(struct device_node *sensor_group_node, int opal_no)
+{
+ struct devce_node *node;
+ int count = 0;
+
+ for_each_child_of_node(sensor_group_node, node) {
+ u32 sgid, op;
+
+ if (of_device_is_compatible(node, "ibm,opal-sensor"))
+ continue;
+
+ if (of_property_read_u32(node, "ops", &op))
+ continue;
+
+ if (op != opal_no)
+ continue;
+
+ if (of_property_read_u32(node, "sensor-group-id", &sgid))
+ continue;
+ count++;
+ }
+
+ return count;
+}

-static void add_attr(int handle, struct sg_attr *attr, int index)
+#ifdef CONFIG_DEBUG_FS
+static int sgroup_enable_get(void *data, u64 *val)
{
- attr->handle = handle;
- sysfs_attr_init(&attr->attr.attr);
- attr->attr.attr.name = ops_info[index].attr_name;
- attr->attr.attr.mode = 0220;
- attr->attr.store = ops_info[index].store;
+ struct sensor_group *sgroup = (struct sensor_group *)data;
+
+ *val = sgroup->enable;
+ return 0;
}

-static int add_attr_group(const __be32 *ops, int len, struct sensor_group *sg,
- u32 handle)
+static int sgroup_enable_set(void *data, u64 val)
{
- int i, j;
- int count = 0;
+ struct sensor_group *sgroup = (struct sensor_group *)data;
+ int rc;
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ rc = mutex_lock_interruptible(&sgroup->mutex);
+ if (rc)
+ return rc;

- for (i = 0; i < len; i++)
- for (j = 0; j < ARRAY_SIZE(ops_info); j++)
- if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) {
- add_attr(handle, &sg->sgattrs[count], j);
- sg->sg.attrs[count] =
- &sg->sgattrs[count].attr.attr;
- count++;
- }
+ if (val != sgroup->enable) {
+ rc = sensor_group_enable(sgroup->handle, val);
+ if (!rc)
+ sgroup->enable = val;
+ }

- return sysfs_create_group(sg_kobj, &sg->sg);
+ mutex_unlock(&sgroup->mutex);
+ return rc;
}

-static int get_nr_attrs(const __be32 *ops, int len)
+DEFINE_DEBUGFS_ATTRIBUTE(sensor_debugfs_ops, sgroup_enable_get,
+ sgroup_enable_set, "%llx\n");
+static struct dentry *sensor_debugfs_parent;
+static struct sensor_group *dgroups;
+
+static void add_debugfs_attrs(struct device_node *sensor_group_node)
{
- int i, j;
- int nr_attrs = 0;
+ struct device_node *node;
+ int i = 0, count;
+
+ count = find_nr_groups(sensor_group_node, OPAL_SENSOR_GROUP_ENABLE);
+ if (!count)
+ return;

- for (i = 0; i < len; i++)
- for (j = 0; j < ARRAY_SIZE(ops_info); j++)
- if (be32_to_cpu(ops[i]) == ops_info[j].opal_no)
- nr_attrs++;
+ dgroups = kcalloc(count, sizeof(*dgroups), GFP_KERNEL);
+ if (!sgroups)
+ return;
+
+ sensor_debugfs_parent = debugfs_create_dir(SENSOR_GROUPS_DIR_STR,
+ powerpc_debugfs_root);
+ if (!sensor_debugfs_parent)
+ goto out_dgroups;

- return nr_attrs;
+ for_each_child_of_node(sensor_group_node, node) {
+ struct dentry *group;
+ u32 op, sgid, chipid;
+
+ if (of_device_is_compatible(node, "ibm,opal-sensor"))
+ continue;
+
+ if (of_property_read_u32(node, "ops", &op))
+ continue;
+
+ if (op != OPAL_SENSOR_GROUP_ENABLE)
+ continue;
+
+ if (of_property_read_u32(node, "sensor-group-id", &sgid))
+ continue;
+
+ if (!of_property_read_u32(node, "ibm,chip-id", &chipid))
+ sprintf(dgroups[i].name, "%s%d", node->name, chipid);
+ else
+ sprintf(dgroups[i].name, "%s", node->name);
+
+ dgroups[i].handle = sgid;
+ dgroups[i].enable = 1;
+ mutex_init(&dgroups[i].mutex);
+ group = debugfs_create_dir(dgroups[i].name,
+ sensor_debugfs_parent);
+ if (!group) {
+ of_node_put(node);
+ goto out_debugfs;
+ }
+
+ if (!debugfs_create_file("enable", 0664, group, &dgroups[i],
+ &sensor_debugfs_ops)) {
+ of_node_put(node);
+ goto out_debugfs;
+ }
+ i++;
+ }
+
+ return;
+
+out_debugfs:
+ debugfs_remove_recursive(sensor_debugfs_parent);
+out_dgroups:
+ kfree(dgroups);
}
+#endif /* CONFIG_DEBUG_FS */

void __init opal_sensor_groups_init(void)
{
- struct device_node *sg, *node;
- int i = 0;
+ struct device_node *sensor_group_node, *node;
+ int i = 0, count;

- sg = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
- if (!sg) {
+ sensor_group_node = of_find_compatible_node(NULL, NULL,
+ "ibm,opal-sensor-group");
+ if (!sensor_group_node) {
pr_devel("Sensor groups node not found\n");
return;
}

- sgs = kcalloc(of_get_child_count(sg), sizeof(*sgs), GFP_KERNEL);
- if (!sgs)
- return;
+ count = find_nr_groups(sensor_group_node, OPAL_SENSOR_GROUP_CLEAR);
+ if (!count)
+ goto out_node_put;

- sg_kobj = kobject_create_and_add("sensor_groups", opal_kobj);
+ sgroups = kcalloc(count, sizeof(*sgroups), GFP_KERNEL);
+ if (!sgroups)
+ goto out_node_put;
+
+ sg_kobj = kobject_create_and_add(SENSOR_GROUPS_DIR_STR, opal_kobj);
if (!sg_kobj) {
pr_warn("Failed to create sensor group kobject\n");
- goto out_sgs;
+ goto out_sgroups;
}

- for_each_child_of_node(sg, node) {
- const __be32 *ops;
- u32 sgid, len, nr_attrs, chipid;
+ for_each_child_of_node(sensor_group_node, node) {
+ u32 sgid, chipid, op;

- ops = of_get_property(node, "ops", &len);
- if (!ops)
+ if (of_property_read_u32(node, "ops", &op))
continue;

- nr_attrs = get_nr_attrs(ops, len);
- if (!nr_attrs)
+ if (op != OPAL_SENSOR_GROUP_CLEAR)
continue;

- sgs[i].sgattrs = kcalloc(nr_attrs, sizeof(*sgs[i].sgattrs),
- GFP_KERNEL);
- if (!sgs[i].sgattrs)
- goto out_sgs_sgattrs;
-
- sgs[i].sg.attrs = kcalloc(nr_attrs + 1,
- sizeof(*sgs[i].sg.attrs),
- GFP_KERNEL);
-
- if (!sgs[i].sg.attrs) {
- kfree(sgs[i].sgattrs);
- goto out_sgs_sgattrs;
- }
-
- if (of_property_read_u32(node, "sensor-group-id", &sgid)) {
- pr_warn("sensor-group-id property not found\n");
- goto out_sgs_sgattrs;
- }
+ if (of_property_read_u32(node, "sensor-group-id", &sgid))
+ continue;

if (!of_property_read_u32(node, "ibm,chip-id", &chipid))
- sprintf(sgs[i].name, "%s%d", node->name, chipid);
+ sprintf(sgroups[i].name, "%s%d", node->name, chipid);
else
- sprintf(sgs[i].name, "%s", node->name);
+ sprintf(sgroups[i].name, "%s", node->name);
+
+ sgroups[i].sg.attrs = kzalloc(sizeof(struct attribute *) * 2,
+ GFP_KERNEL);
+ if (!sgroups[i].sg.attrs) {
+ of_node_put(node);
+ goto out_sg_attrs;
+ }

- sgs[i].sg.name = sgs[i].name;
- if (add_attr_group(ops, len, &sgs[i], sgid)) {
+ sgroups[i].sg.name = sgroups[i].name;
+ sgroups[i].handle = sgid;
+ sysfs_attr_init(sgroups[i].sattr.attr);
+ sgroups[i].sattr.attr.name = "clear";
+ sgroups[i].sattr.attr.mode = 0220;
+ sgroups[i].sattr.store = sgroup_clear_store;
+ sgroups[i].sg.attrs[0] = &sgroups[i].sattr.attr;
+ sgroups[i].sg.attrs[1] = NULL;
+ mutex_init(&sgroups[i].mutex);
+
+ if (sysfs_create_group(sg_kobj, &sgroups[i].sg)) {
pr_warn("Failed to create sensor attribute group %s\n",
- sgs[i].sg.name);
- goto out_sgs_sgattrs;
+ sgroups[i].sg.name);
+ kfree(sgroups[i].sg.attrs);
+ of_node_put(node);
+ goto out_sg_attrs;
}
+
i++;
}

- return;
+#ifdef CONFIG_DEBUG_FS
+ add_debugfs_attrs(sensor_group_node);
+#endif /* CONFIG_DEBUG_FS */

-out_sgs_sgattrs:
- while (--i >= 0) {
- kfree(sgs[i].sgattrs);
- kfree(sgs[i].sg.attrs);
- }
+ goto out_node_put;
+
+out_sg_attrs:
+ while (i-- > 0)
+ kfree(sgroups[i].sg.attrs);
kobject_put(sg_kobj);
-out_sgs:
- kfree(sgs);
+out_sgroups:
+ kfree(sgroups);
+out_node_put:
+ of_node_put(sensor_group_node);
}
--
1.8.3.1