1 /*
2  * PowerNV system parameter code
3  *
4  * Copyright (C) 2013 IBM
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include <linux/kobject.h>
22 #include <linux/mutex.h>
23 #include <linux/slab.h>
24 #include <linux/of.h>
25 #include <linux/gfp.h>
26 #include <linux/stat.h>
27 #include <asm/opal.h>
28 
29 #define MAX_PARAM_DATA_LEN	64
30 
31 static DEFINE_MUTEX(opal_sysparam_mutex);
32 static struct kobject *sysparam_kobj;
33 static void *param_data_buf;
34 
35 struct param_attr {
36 	struct list_head list;
37 	u32 param_id;
38 	u32 param_size;
39 	struct kobj_attribute kobj_attr;
40 };
41 
42 static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer)
43 {
44 	struct opal_msg msg;
45 	ssize_t ret;
46 	int token;
47 
48 	token = opal_async_get_token_interruptible();
49 	if (token < 0) {
50 		if (token != -ERESTARTSYS)
51 			pr_err("%s: Couldn't get the token, returning\n",
52 					__func__);
53 		ret = token;
54 		goto out;
55 	}
56 
57 	ret = opal_get_param(token, param_id, (u64)buffer, length);
58 	if (ret != OPAL_ASYNC_COMPLETION) {
59 		ret = opal_error_code(ret);
60 		goto out_token;
61 	}
62 
63 	ret = opal_async_wait_response(token, &msg);
64 	if (ret) {
65 		pr_err("%s: Failed to wait for the async response, %zd\n",
66 				__func__, ret);
67 		goto out_token;
68 	}
69 
70 	ret = opal_error_code(opal_get_async_rc(msg));
71 
72 out_token:
73 	opal_async_release_token(token);
74 out:
75 	return ret;
76 }
77 
78 static int opal_set_sys_param(u32 param_id, u32 length, void *buffer)
79 {
80 	struct opal_msg msg;
81 	int ret, token;
82 
83 	token = opal_async_get_token_interruptible();
84 	if (token < 0) {
85 		if (token != -ERESTARTSYS)
86 			pr_err("%s: Couldn't get the token, returning\n",
87 					__func__);
88 		ret = token;
89 		goto out;
90 	}
91 
92 	ret = opal_set_param(token, param_id, (u64)buffer, length);
93 
94 	if (ret != OPAL_ASYNC_COMPLETION) {
95 		ret = opal_error_code(ret);
96 		goto out_token;
97 	}
98 
99 	ret = opal_async_wait_response(token, &msg);
100 	if (ret) {
101 		pr_err("%s: Failed to wait for the async response, %d\n",
102 				__func__, ret);
103 		goto out_token;
104 	}
105 
106 	ret = opal_error_code(opal_get_async_rc(msg));
107 
108 out_token:
109 	opal_async_release_token(token);
110 out:
111 	return ret;
112 }
113 
114 static ssize_t sys_param_show(struct kobject *kobj,
115 		struct kobj_attribute *kobj_attr, char *buf)
116 {
117 	struct param_attr *attr = container_of(kobj_attr, struct param_attr,
118 			kobj_attr);
119 	ssize_t ret;
120 
121 	mutex_lock(&opal_sysparam_mutex);
122 	ret = opal_get_sys_param(attr->param_id, attr->param_size,
123 			param_data_buf);
124 	if (ret)
125 		goto out;
126 
127 	memcpy(buf, param_data_buf, attr->param_size);
128 
129 	ret = attr->param_size;
130 out:
131 	mutex_unlock(&opal_sysparam_mutex);
132 	return ret;
133 }
134 
135 static ssize_t sys_param_store(struct kobject *kobj,
136 		struct kobj_attribute *kobj_attr, const char *buf, size_t count)
137 {
138 	struct param_attr *attr = container_of(kobj_attr, struct param_attr,
139 			kobj_attr);
140 	ssize_t ret;
141 
142         /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */
143         if (count > MAX_PARAM_DATA_LEN)
144                 count = MAX_PARAM_DATA_LEN;
145 
146 	mutex_lock(&opal_sysparam_mutex);
147 	memcpy(param_data_buf, buf, count);
148 	ret = opal_set_sys_param(attr->param_id, attr->param_size,
149 			param_data_buf);
150 	mutex_unlock(&opal_sysparam_mutex);
151 	if (!ret)
152 		ret = count;
153 	return ret;
154 }
155 
156 void __init opal_sys_param_init(void)
157 {
158 	struct device_node *sysparam;
159 	struct param_attr *attr;
160 	u32 *id, *size;
161 	int count, i;
162 	u8 *perm;
163 
164 	if (!opal_kobj) {
165 		pr_warn("SYSPARAM: opal kobject is not available\n");
166 		goto out;
167 	}
168 
169 	/* Some systems do not use sysparams; this is not an error */
170 	sysparam = of_find_node_by_path("/ibm,opal/sysparams");
171 	if (!sysparam)
172 		goto out;
173 
174 	if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) {
175 		pr_err("SYSPARAM: Opal sysparam node not compatible\n");
176 		goto out_node_put;
177 	}
178 
179 	sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj);
180 	if (!sysparam_kobj) {
181 		pr_err("SYSPARAM: Failed to create sysparam kobject\n");
182 		goto out_node_put;
183 	}
184 
185 	/* Allocate big enough buffer for any get/set transactions */
186 	param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL);
187 	if (!param_data_buf) {
188 		pr_err("SYSPARAM: Failed to allocate memory for param data "
189 				"buf\n");
190 		goto out_kobj_put;
191 	}
192 
193 	/* Number of parameters exposed through DT */
194 	count = of_property_count_strings(sysparam, "param-name");
195 	if (count < 0) {
196 		pr_err("SYSPARAM: No string found of property param-name in "
197 				"the node %pOFn\n", sysparam);
198 		goto out_param_buf;
199 	}
200 
201 	id = kcalloc(count, sizeof(*id), GFP_KERNEL);
202 	if (!id) {
203 		pr_err("SYSPARAM: Failed to allocate memory to read parameter "
204 				"id\n");
205 		goto out_param_buf;
206 	}
207 
208 	size = kcalloc(count, sizeof(*size), GFP_KERNEL);
209 	if (!size) {
210 		pr_err("SYSPARAM: Failed to allocate memory to read parameter "
211 				"size\n");
212 		goto out_free_id;
213 	}
214 
215 	perm = kcalloc(count, sizeof(*perm), GFP_KERNEL);
216 	if (!perm) {
217 		pr_err("SYSPARAM: Failed to allocate memory to read supported "
218 				"action on the parameter");
219 		goto out_free_size;
220 	}
221 
222 	if (of_property_read_u32_array(sysparam, "param-id", id, count)) {
223 		pr_err("SYSPARAM: Missing property param-id in the DT\n");
224 		goto out_free_perm;
225 	}
226 
227 	if (of_property_read_u32_array(sysparam, "param-len", size, count)) {
228 		pr_err("SYSPARAM: Missing property param-len in the DT\n");
229 		goto out_free_perm;
230 	}
231 
232 
233 	if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) {
234 		pr_err("SYSPARAM: Missing property param-perm in the DT\n");
235 		goto out_free_perm;
236 	}
237 
238 	attr = kcalloc(count, sizeof(*attr), GFP_KERNEL);
239 	if (!attr) {
240 		pr_err("SYSPARAM: Failed to allocate memory for parameter "
241 				"attributes\n");
242 		goto out_free_perm;
243 	}
244 
245 	/* For each of the parameters, populate the parameter attributes */
246 	for (i = 0; i < count; i++) {
247 		if (size[i] > MAX_PARAM_DATA_LEN) {
248 			pr_warn("SYSPARAM: Not creating parameter %d as size "
249 				"exceeds buffer length\n", i);
250 			continue;
251 		}
252 
253 		sysfs_attr_init(&attr[i].kobj_attr.attr);
254 		attr[i].param_id = id[i];
255 		attr[i].param_size = size[i];
256 		if (of_property_read_string_index(sysparam, "param-name", i,
257 				&attr[i].kobj_attr.attr.name))
258 			continue;
259 
260 		/* If the parameter is read-only or read-write */
261 		switch (perm[i] & 3) {
262 		case OPAL_SYSPARAM_READ:
263 			attr[i].kobj_attr.attr.mode = 0444;
264 			break;
265 		case OPAL_SYSPARAM_WRITE:
266 			attr[i].kobj_attr.attr.mode = 0200;
267 			break;
268 		case OPAL_SYSPARAM_RW:
269 			attr[i].kobj_attr.attr.mode = 0644;
270 			break;
271 		default:
272 			break;
273 		}
274 
275 		attr[i].kobj_attr.show = sys_param_show;
276 		attr[i].kobj_attr.store = sys_param_store;
277 
278 		if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) {
279 			pr_err("SYSPARAM: Failed to create sysfs file %s\n",
280 					attr[i].kobj_attr.attr.name);
281 			goto out_free_attr;
282 		}
283 	}
284 
285 	kfree(perm);
286 	kfree(size);
287 	kfree(id);
288 	of_node_put(sysparam);
289 	return;
290 
291 out_free_attr:
292 	kfree(attr);
293 out_free_perm:
294 	kfree(perm);
295 out_free_size:
296 	kfree(size);
297 out_free_id:
298 	kfree(id);
299 out_param_buf:
300 	kfree(param_data_buf);
301 out_kobj_put:
302 	kobject_put(sysparam_kobj);
303 out_node_put:
304 	of_node_put(sysparam);
305 out:
306 	return;
307 }
308