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 }
213