GNU Linux-libre 4.14.259-gnu1
[releases.git] / arch / powerpc / platforms / powernv / opal-sensor-groups.c
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
20 DEFINE_MUTEX(sg_mutex);
21
22 static struct kobject *sg_kobj;
23
24 struct sg_attr {
25         u32 handle;
26         struct kobj_attribute attr;
27 };
28
29 static struct sensor_group {
30         char name[20];
31         struct attribute_group sg;
32         struct sg_attr *sgattrs;
33 } *sgs;
34
35 static 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
80 out:
81         mutex_unlock(&sg_mutex);
82 out_token:
83         opal_async_release_token(token);
84         return ret;
85 }
86
87 static 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
96 static 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
105 static 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
123 static 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
136 void __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
204 out_sgs_sgattrs:
205         while (--i >= 0) {
206                 kfree(sgs[i].sgattrs);
207                 kfree(sgs[i].sg.attrs);
208         }
209         kobject_put(sg_kobj);
210 out_sgs:
211         kfree(sgs);
212 }