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
  35int sensor_group_enable(u32 handle, bool enable)
  36{
  37        struct opal_msg msg;
  38        int token, ret;
  39
  40        token = opal_async_get_token_interruptible();
  41        if (token < 0)
  42                return token;
  43
  44        ret = opal_sensor_group_enable(handle, token, enable);
  45        if (ret == OPAL_ASYNC_COMPLETION) {
  46                ret = opal_async_wait_response(token, &msg);
  47                if (ret) {
  48                        pr_devel("Failed to wait for the async response\n");
  49                        ret = -EIO;
  50                        goto out;
  51                }
  52                ret = opal_error_code(opal_get_async_rc(msg));
  53        } else {
  54                ret = opal_error_code(ret);
  55        }
  56
  57out:
  58        opal_async_release_token(token);
  59        return ret;
  60}
  61EXPORT_SYMBOL_GPL(sensor_group_enable);
  62
  63static ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr,
  64                        const char *buf, size_t count)
  65{
  66        struct sg_attr *sattr = container_of(attr, struct sg_attr, attr);
  67        struct opal_msg msg;
  68        u32 data;
  69        int ret, token;
  70
  71        ret = kstrtoint(buf, 0, &data);
  72        if (ret)
  73                return ret;
  74
  75        if (data != 1)
  76                return -EINVAL;
  77
  78        token = opal_async_get_token_interruptible();
  79        if (token < 0) {
  80                pr_devel("Failed to get token\n");
  81                return token;
  82        }
  83
  84        ret = mutex_lock_interruptible(&sg_mutex);
  85        if (ret)
  86                goto out_token;
  87
  88        ret = opal_sensor_group_clear(sattr->handle, token);
  89        switch (ret) {
  90        case OPAL_ASYNC_COMPLETION:
  91                ret = opal_async_wait_response(token, &msg);
  92                if (ret) {
  93                        pr_devel("Failed to wait for the async response\n");
  94                        ret = -EIO;
  95                        goto out;
  96                }
  97                ret = opal_error_code(opal_get_async_rc(msg));
  98                if (!ret)
  99                        ret = count;
 100                break;
 101        case OPAL_SUCCESS:
 102                ret = count;
 103                break;
 104        default:
 105                ret = opal_error_code(ret);
 106        }
 107
 108out:
 109        mutex_unlock(&sg_mutex);
 110out_token:
 111        opal_async_release_token(token);
 112        return ret;
 113}
 114
 115static struct sg_ops_info {
 116        int opal_no;
 117        const char *attr_name;
 118        ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
 119                        const char *buf, size_t count);
 120} ops_info[] = {
 121        { OPAL_SENSOR_GROUP_CLEAR, "clear", sg_store },
 122};
 123
 124static void add_attr(int handle, struct sg_attr *attr, int index)
 125{
 126        attr->handle = handle;
 127        sysfs_attr_init(&attr->attr.attr);
 128        attr->attr.attr.name = ops_info[index].attr_name;
 129        attr->attr.attr.mode = 0220;
 130        attr->attr.store = ops_info[index].store;
 131}
 132
 133static int add_attr_group(const __be32 *ops, int len, struct sensor_group *sg,
 134                           u32 handle)
 135{
 136        int i, j;
 137        int count = 0;
 138
 139        for (i = 0; i < len; i++)
 140                for (j = 0; j < ARRAY_SIZE(ops_info); j++)
 141                        if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) {
 142                                add_attr(handle, &sg->sgattrs[count], j);
 143                                sg->sg.attrs[count] =
 144                                        &sg->sgattrs[count].attr.attr;
 145                                count++;
 146                        }
 147
 148        return sysfs_create_group(sg_kobj, &sg->sg);
 149}
 150
 151static int get_nr_attrs(const __be32 *ops, int len)
 152{
 153        int i, j;
 154        int nr_attrs = 0;
 155
 156        for (i = 0; i < len; i++)
 157                for (j = 0; j < ARRAY_SIZE(ops_info); j++)
 158                        if (be32_to_cpu(ops[i]) == ops_info[j].opal_no)
 159                                nr_attrs++;
 160
 161        return nr_attrs;
 162}
 163
 164void __init opal_sensor_groups_init(void)
 165{
 166        struct device_node *sg, *node;
 167        int i = 0;
 168
 169        sg = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group");
 170        if (!sg) {
 171                pr_devel("Sensor groups node not found\n");
 172                return;
 173        }
 174
 175        sgs = kcalloc(of_get_child_count(sg), sizeof(*sgs), GFP_KERNEL);
 176        if (!sgs)
 177                return;
 178
 179        sg_kobj = kobject_create_and_add("sensor_groups", opal_kobj);
 180        if (!sg_kobj) {
 181                pr_warn("Failed to create sensor group kobject\n");
 182                goto out_sgs;
 183        }
 184
 185        for_each_child_of_node(sg, node) {
 186                const __be32 *ops;
 187                u32 sgid, len, nr_attrs, chipid;
 188
 189                ops = of_get_property(node, "ops", &len);
 190                if (!ops)
 191                        continue;
 192
 193                nr_attrs = get_nr_attrs(ops, len);
 194                if (!nr_attrs)
 195                        continue;
 196
 197                sgs[i].sgattrs = kcalloc(nr_attrs, sizeof(*sgs[i].sgattrs),
 198                                         GFP_KERNEL);
 199                if (!sgs[i].sgattrs)
 200                        goto out_sgs_sgattrs;
 201
 202                sgs[i].sg.attrs = kcalloc(nr_attrs + 1,
 203                                          sizeof(*sgs[i].sg.attrs),
 204                                          GFP_KERNEL);
 205
 206                if (!sgs[i].sg.attrs) {
 207                        kfree(sgs[i].sgattrs);
 208                        goto out_sgs_sgattrs;
 209                }
 210
 211                if (of_property_read_u32(node, "sensor-group-id", &sgid)) {
 212                        pr_warn("sensor-group-id property not found\n");
 213                        goto out_sgs_sgattrs;
 214                }
 215
 216                if (!of_property_read_u32(node, "ibm,chip-id", &chipid))
 217                        sprintf(sgs[i].name, "%s%d", node->name, chipid);
 218                else
 219                        sprintf(sgs[i].name, "%s", node->name);
 220
 221                sgs[i].sg.name = sgs[i].name;
 222                if (add_attr_group(ops, len, &sgs[i], sgid)) {
 223                        pr_warn("Failed to create sensor attribute group %s\n",
 224                                sgs[i].sg.name);
 225                        goto out_sgs_sgattrs;
 226                }
 227                i++;
 228        }
 229
 230        return;
 231
 232out_sgs_sgattrs:
 233        while (--i >= 0) {
 234                kfree(sgs[i].sgattrs);
 235                kfree(sgs[i].sg.attrs);
 236        }
 237        kobject_put(sg_kobj);
 238out_sgs:
 239        kfree(sgs);
 240}
 241