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 		goto out_token;
60 
61 	ret = opal_async_wait_response(token, &msg);
62 	if (ret) {
63 		pr_err("%s: Failed to wait for the async response, %zd\n",
64 				__func__, ret);
65 		goto out_token;
66 	}
67 
68 	ret = be64_to_cpu(msg.params[1]);
69 
70 out_token:
71 	opal_async_release_token(token);
72 out:
73 	return ret;
74 }
75 
76 static int opal_set_sys_param(u32 param_id, u32 length, void *buffer)
77 {
78 	struct opal_msg msg;
79 	int ret, token;
80 
81 	token = opal_async_get_token_interruptible();
82 	if (token < 0) {
83 		if (token != -ERESTARTSYS)
84 			pr_err("%s: Couldn't get the token, returning\n",
85 					__func__);
86 		ret = token;
87 		goto out;
88 	}
89 
90 	ret = opal_set_param(token, param_id, (u64)buffer, length);
91 
92 	if (ret != OPAL_ASYNC_COMPLETION)
93 		goto out_token;
94 
95 	ret = opal_async_wait_response(token, &msg);
96 	if (ret) {
97 		pr_err("%s: Failed to wait for the async response, %d\n",
98 				__func__, ret);
99 		goto out_token;
100 	}
101 
102 	ret = be64_to_cpu(msg.params[1]);
103 
104 out_token:
105 	opal_async_release_token(token);
106 out:
107 	return ret;
108 }
109 
110 static ssize_t sys_param_show(struct kobject *kobj,
111 		struct kobj_attribute *kobj_attr, char *buf)
112 {
113 	struct param_attr *attr = container_of(kobj_attr, struct param_attr,
114 			kobj_attr);
115 	ssize_t ret;
116 
117 	mutex_lock(&opal_sysparam_mutex);
118 	ret = opal_get_sys_param(attr->param_id, attr->param_size,
119 			param_data_buf);
120 	if (ret)
121 		goto out;
122 
123 	memcpy(buf, param_data_buf, attr->param_size);
124 
125 	ret = attr->param_size;
126 out:
127 	mutex_unlock(&opal_sysparam_mutex);
128 	return ret;
129 }
130 
131 static ssize_t sys_param_store(struct kobject *kobj,
132 		struct kobj_attribute *kobj_attr, const char *buf, size_t count)
133 {
134 	struct param_attr *attr = container_of(kobj_attr, struct param_attr,
135 			kobj_attr);
136 	ssize_t ret;
137 
138         /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */
139         if (count > MAX_PARAM_DATA_LEN)
140                 count = MAX_PARAM_DATA_LEN;
141 
142 	mutex_lock(&opal_sysparam_mutex);
143 	memcpy(param_data_buf, buf, count);
144 	ret = opal_set_sys_param(attr->param_id, attr->param_size,
145 			param_data_buf);
146 	mutex_unlock(&opal_sysparam_mutex);
147 	if (!ret)
148 		ret = count;
149 	return ret;
150 }
151 
152 void __init opal_sys_param_init(void)
153 {
154 	struct device_node *sysparam;
155 	struct param_attr *attr;
156 	u32 *id, *size;
157 	int count, i;
158 	u8 *perm;
159 
160 	if (!opal_kobj) {
161 		pr_warn("SYSPARAM: opal kobject is not available\n");
162 		goto out;
163 	}
164 
165 	sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj);
166 	if (!sysparam_kobj) {
167 		pr_err("SYSPARAM: Failed to create sysparam kobject\n");
168 		goto out;
169 	}
170 
171 	/* Allocate big enough buffer for any get/set transactions */
172 	param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL);
173 	if (!param_data_buf) {
174 		pr_err("SYSPARAM: Failed to allocate memory for param data "
175 				"buf\n");
176 		goto out_kobj_put;
177 	}
178 
179 	sysparam = of_find_node_by_path("/ibm,opal/sysparams");
180 	if (!sysparam) {
181 		pr_err("SYSPARAM: Opal sysparam node not found\n");
182 		goto out_param_buf;
183 	}
184 
185 	if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) {
186 		pr_err("SYSPARAM: Opal sysparam node not compatible\n");
187 		goto out_node_put;
188 	}
189 
190 	/* Number of parameters exposed through DT */
191 	count = of_property_count_strings(sysparam, "param-name");
192 	if (count < 0) {
193 		pr_err("SYSPARAM: No string found of property param-name in "
194 				"the node %s\n", sysparam->name);
195 		goto out_node_put;
196 	}
197 
198 	id = kzalloc(sizeof(*id) * count, GFP_KERNEL);
199 	if (!id) {
200 		pr_err("SYSPARAM: Failed to allocate memory to read parameter "
201 				"id\n");
202 		goto out_node_put;
203 	}
204 
205 	size = kzalloc(sizeof(*size) * count, GFP_KERNEL);
206 	if (!size) {
207 		pr_err("SYSPARAM: Failed to allocate memory to read parameter "
208 				"size\n");
209 		goto out_free_id;
210 	}
211 
212 	perm = kzalloc(sizeof(*perm) * count, GFP_KERNEL);
213 	if (!perm) {
214 		pr_err("SYSPARAM: Failed to allocate memory to read supported "
215 				"action on the parameter");
216 		goto out_free_size;
217 	}
218 
219 	if (of_property_read_u32_array(sysparam, "param-id", id, count)) {
220 		pr_err("SYSPARAM: Missing property param-id in the DT\n");
221 		goto out_free_perm;
222 	}
223 
224 	if (of_property_read_u32_array(sysparam, "param-len", size, count)) {
225 		pr_err("SYSPARAM: Missing property param-len in the DT\n");
226 		goto out_free_perm;
227 	}
228 
229 
230 	if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) {
231 		pr_err("SYSPARAM: Missing property param-perm in the DT\n");
232 		goto out_free_perm;
233 	}
234 
235 	attr = kzalloc(sizeof(*attr) * count, GFP_KERNEL);
236 	if (!attr) {
237 		pr_err("SYSPARAM: Failed to allocate memory for parameter "
238 				"attributes\n");
239 		goto out_free_perm;
240 	}
241 
242 	/* For each of the parameters, populate the parameter attributes */
243 	for (i = 0; i < count; i++) {
244 		if (size[i] > MAX_PARAM_DATA_LEN) {
245 			pr_warn("SYSPARAM: Not creating parameter %d as size "
246 				"exceeds buffer length\n", i);
247 			continue;
248 		}
249 
250 		sysfs_attr_init(&attr[i].kobj_attr.attr);
251 		attr[i].param_id = id[i];
252 		attr[i].param_size = size[i];
253 		if (of_property_read_string_index(sysparam, "param-name", i,
254 				&attr[i].kobj_attr.attr.name))
255 			continue;
256 
257 		/* If the parameter is read-only or read-write */
258 		switch (perm[i] & 3) {
259 		case OPAL_SYSPARAM_READ:
260 			attr[i].kobj_attr.attr.mode = S_IRUGO;
261 			break;
262 		case OPAL_SYSPARAM_WRITE:
263 			attr[i].kobj_attr.attr.mode = S_IWUSR;
264 			break;
265 		case OPAL_SYSPARAM_RW:
266 			attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUSR;
267 			break;
268 		default:
269 			break;
270 		}
271 
272 		attr[i].kobj_attr.show = sys_param_show;
273 		attr[i].kobj_attr.store = sys_param_store;
274 
275 		if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) {
276 			pr_err("SYSPARAM: Failed to create sysfs file %s\n",
277 					attr[i].kobj_attr.attr.name);
278 			goto out_free_attr;
279 		}
280 	}
281 
282 	kfree(perm);
283 	kfree(size);
284 	kfree(id);
285 	of_node_put(sysparam);
286 	return;
287 
288 out_free_attr:
289 	kfree(attr);
290 out_free_perm:
291 	kfree(perm);
292 out_free_size:
293 	kfree(size);
294 out_free_id:
295 	kfree(id);
296 out_node_put:
297 	of_node_put(sysparam);
298 out_param_buf:
299 	kfree(param_data_buf);
300 out_kobj_put:
301 	kobject_put(sysparam_kobj);
302 out:
303 	return;
304 }
305