linux/arch/powerpc/platforms/powernv/opal-sensor-groups.c
<<
>>
Prefs
   1/*
   2 * PowerNV OPAL Sensor-groups interface
   3 *
   4 * Copyright 2017 IBM Corp.
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#define pr_fmt(fmt)     "opal-sensor-groups: " fmt
  13
  14#include <linux/of.h>
  15#include <linux/kobject.h>
  16#include <linux/slab.h>
  17
  18#include <asm/opal.h>
  19
  20DEFINE_MUTEX(sg_mutex);
  21
  22static struct kobject *sg_kobj;
  23
  24struct sg_attr {
  25        u32 handle;
  26        struct kobj_attribute attr;
  27};
  28
  29static struct sensor_group {
  30        char name[20];
  31        struct attribute_group sg;
  32        struct sg_attr *sgattrs;
  33} *sgs;
  34
  35static ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr,
  36                        const char *buf, size_t count)
  37{
  38        struct sg_attr *sattr = container_of(attr, struct sg_attr, attr);
  39        struct opal_msg msg;
  40        u32 data;
  41        int ret, token;
  42
  43        ret = kstrtoint(buf, 0, &data);
  44        if (ret)
  45                return ret;
  46
  47        if (data != 1)
  48                return -EINVAL;
  49
  50        token = opal_async_get_token_interruptible();
  51        if (token < 0) {
  52                pr_devel("Failed to get token\n");
  53                return token;
  54        }
  55
  56        ret = mutex_lock_interruptible(&sg_mutex);
  57        if (ret)
  58                goto out_token;
  59
  60        ret = opal_sensor_group_clear(sattr->handle, token);
  61        switch (ret) {
  62        case OPAL_ASYNC_COMPLETION:
  63                ret = opal_async_wait_response(token, &msg);
  64                if (ret) {
  65                        pr_devel("Failed to wait for the async response\n");
  66                        ret = -EIO;
  67                        goto out;
  68                }
  69                ret = opal_error_code(opal_get_async_rc(msg));
  70                if (!ret)
  71                        ret = count;
  72                break;
  73        case OPAL_SUCCESS:
  74                ret = count;
  75                break;
  76        default:
  77                ret = opal_error_code(ret);
  78        }
  79
  80out:
  81        mutex_unlock(&sg_mutex);
  82out_token:
  83        opal_async_release_token(token);
  84        return ret;
  85}
  86
  87static struct sg_ops_info {
  88        int opal_no;
  89        const char *attr_name;
  90        ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
  91                        const char *buf, size_t count);
  92} ops_info[] = {
  93        { OPAL_SENSOR_GROUP_CLEAR, "clear", sg_store },
  94};
  95
  96static void add_attr(int handle, struct sg_attr *attr, int index)
  97{
  98        attr->handle = handle;
  99        sysfs_attr_init(&attr->attr.attr);
 100        attr->attr.attr.name = ops_info[index].attr_name;
 101        attr->attr.attr.mode = 0220;
 102        attr->attr.store = ops_info[index].store;
 103}
 104
 105static int add_attr_group(const __be32 *ops, int len, struct sensor_group *sg,
 106                           u32 handle)
 107{
 108        int i, j;
 109        int count = 0;
 110
 111        for (i = 0; i < len; i++)
 112                for (j = 0; j < ARRAY_SIZE(ops_info); j++)
 113                        if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) {
 114                                add_attr(handle, &sg->sgattrs[count], j);
 115                                sg->sg.attrs[count] =
 116                                        &sg->sgattrs[count].attr.attr;
 117                                count++;
 118                        }
 119
 120        return sysfs_create_group(sg_kobj, &sg->sg);
 121}
 122
 123static int get_nr_attrs(const __be32 *ops, int len)
 124{
 125        int i, j;
 126        int nr_attrs = 0;
 127
 128        for (i = 0; i < len; i++)
 129                for (j = 0; j < ARRAY_SIZE(ops_info); j++)
 130                        if (be32_to_cpu(ops[i]) == ops_info[j].opal_no)
 131                                nr_attrs++;
 132
 133        return nr_attrs;
 134}
 135
 136void __init opal_sensor_groups_init(void)
 137{
 138        struct device_node *sg, *node;
 139        int i = 0;
 140
 141        sg = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
 142        if (!sg) {
 143                pr_devel("Sensor groups node not found\n");
 144                return;
 145        }
 146
 147        sgs = kcalloc(of_get_child_count(sg), sizeof(*sgs), GFP_KERNEL);
 148        if (!sgs)
 149                return;
 150
 151        sg_kobj = kobject_create_and_add("sensor_groups", opal_kobj);
 152        if (!sg_kobj) {
 153                pr_warn("Failed to create sensor group kobject\n");
 154                goto out_sgs;
 155        }
 156
 157        for_each_child_of_node(sg, node) {
 158                const __be32 *ops;
 159                u32 sgid, len, nr_attrs, chipid;
 160
 161                ops = of_get_property(node, "ops", &len);
 162                if (!ops)
 163                        continue;
 164
 165                nr_attrs = get_nr_attrs(ops, len);
 166                if (!nr_attrs)
 167                        continue;
 168
 169                sgs[i].sgattrs = kcalloc(nr_attrs, sizeof(struct sg_attr),
 170                                         GFP_KERNEL);
 171                if (!sgs[i].sgattrs)
 172                        goto out_sgs_sgattrs;
 173
 174                sgs[i].sg.attrs = kcalloc(nr_attrs + 1,
 175                                          sizeof(struct attribute *),
 176                                          GFP_KERNEL);
 177
 178                if (!sgs[i].sg.attrs) {
 179                        kfree(sgs[i].sgattrs);
 180                        goto out_sgs_sgattrs;
 181                }
 182
 183                if (of_property_read_u32(node, "sensor-group-id", &sgid)) {
 184                        pr_warn("sensor-group-id property not found\n");
 185                        goto out_sgs_sgattrs;
 186                }
 187
 188                if (!of_property_read_u32(node, "ibm,chip-id", &chipid))
 189                        sprintf(sgs[i].name, "%s%d", node->name, chipid);
 190                else
 191                        sprintf(sgs[i].name, "%s", node->name);
 192
 193                sgs[i].sg.name = sgs[i].name;
 194                if (add_attr_group(ops, len, &sgs[i], sgid)) {
 195                        pr_warn("Failed to create sensor attribute group %s\n",
 196                                sgs[i].sg.name);
 197                        goto out_sgs_sgattrs;
 198                }
 199                i++;
 200        }
 201
 202        return;
 203
 204out_sgs_sgattrs:
 205        while (--i >= 0) {
 206                kfree(sgs[i].sgattrs);
 207                kfree(sgs[i].sg.attrs);
 208        }
 209        kobject_put(sg_kobj);
 210out_sgs:
 211        kfree(sgs);
 212}
 213