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 int 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 
57 out:
58 	opal_async_release_token(token);
59 	return ret;
60 }
61 EXPORT_SYMBOL_GPL(sensor_group_enable);
62 
63 static 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 
108 out:
109 	mutex_unlock(&sg_mutex);
110 out_token:
111 	opal_async_release_token(token);
112 	return ret;
113 }
114 
115 static 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 
124 static 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 
133 static 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 
151 static 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 
164 void __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 
232 out_sgs_sgattrs:
233 	while (--i >= 0) {
234 		kfree(sgs[i].sgattrs);
235 		kfree(sgs[i].sg.attrs);
236 	}
237 	kobject_put(sg_kobj);
238 out_sgs:
239 	kfree(sgs);
240 }
241