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