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 
16 static DEFINE_MUTEX(sg_mutex);
17 
18 static struct kobject *sg_kobj;
19 
20 struct sg_attr {
21 	u32 handle;
22 	struct kobj_attribute attr;
23 };
24 
25 static struct sensor_group {
26 	char name[20];
27 	struct attribute_group sg;
28 	struct sg_attr *sgattrs;
29 } *sgs;
30 
sensor_group_enable(u32 handle,bool enable)31 int 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 
53 out:
54 	opal_async_release_token(token);
55 	return ret;
56 }
57 EXPORT_SYMBOL_GPL(sensor_group_enable);
58 
sg_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)59 static 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 
104 out:
105 	mutex_unlock(&sg_mutex);
106 out_token:
107 	opal_async_release_token(token);
108 	return ret;
109 }
110 
111 static 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 
add_attr(int handle,struct sg_attr * attr,int index)120 static 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 
add_attr_group(const __be32 * ops,int len,struct sensor_group * sg,u32 handle)129 static 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 
get_nr_attrs(const __be32 * ops,int len)147 static 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 
opal_sensor_groups_init(void)160 void __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 
229 out_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);
236 out_sgs:
237 	kfree(sgs);
238 out_sg_put:
239 	of_node_put(sg);
240 }
241