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