treewide: kzalloc() -> kcalloc()
[linux-2.6-block.git] / arch / powerpc / platforms / powernv / opal-sysparam.c
CommitLineData
4029cd66
NG
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
31static DEFINE_MUTEX(opal_sysparam_mutex);
32static struct kobject *sysparam_kobj;
33static void *param_data_buf;
34
35struct param_attr {
36 struct list_head list;
37 u32 param_id;
38 u32 param_size;
39 struct kobj_attribute kobj_attr;
40};
41
b8569d23 42static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer)
4029cd66
NG
43{
44 struct opal_msg msg;
b8569d23
JS
45 ssize_t ret;
46 int token;
4029cd66
NG
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);
14aae78f
CLG
58 if (ret != OPAL_ASYNC_COMPLETION) {
59 ret = opal_error_code(ret);
4029cd66 60 goto out_token;
14aae78f 61 }
4029cd66
NG
62
63 ret = opal_async_wait_response(token, &msg);
64 if (ret) {
b8569d23 65 pr_err("%s: Failed to wait for the async response, %zd\n",
4029cd66
NG
66 __func__, ret);
67 goto out_token;
68 }
69
d0226d31 70 ret = opal_error_code(opal_get_async_rc(msg));
4029cd66
NG
71
72out_token:
73 opal_async_release_token(token);
74out:
75 return ret;
76}
77
78static 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
14aae78f
CLG
94 if (ret != OPAL_ASYNC_COMPLETION) {
95 ret = opal_error_code(ret);
4029cd66 96 goto out_token;
14aae78f 97 }
4029cd66
NG
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
d0226d31 106 ret = opal_error_code(opal_get_async_rc(msg));
4029cd66
NG
107
108out_token:
109 opal_async_release_token(token);
110out:
111 return ret;
112}
113
114static 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);
b8569d23 119 ssize_t ret;
4029cd66
NG
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
ba9a32b1 129 ret = attr->param_size;
4029cd66
NG
130out:
131 mutex_unlock(&opal_sysparam_mutex);
ba9a32b1 132 return ret;
4029cd66
NG
133}
134
135static 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);
b8569d23 140 ssize_t ret;
4029cd66 141
85390378
JS
142 /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */
143 if (count > MAX_PARAM_DATA_LEN)
144 count = MAX_PARAM_DATA_LEN;
145
4029cd66
NG
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);
ba9a32b1
JS
151 if (!ret)
152 ret = count;
153 return ret;
4029cd66
NG
154}
155
156void __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
38c04887
JS
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
4029cd66
NG
179 sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj);
180 if (!sysparam_kobj) {
181 pr_err("SYSPARAM: Failed to create sysparam kobject\n");
38c04887 182 goto out_node_put;
4029cd66
NG
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
4029cd66
NG
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 %s\n", sysparam->name);
38c04887 198 goto out_param_buf;
4029cd66
NG
199 }
200
6396bb22 201 id = kcalloc(count, sizeof(*id), GFP_KERNEL);
4029cd66
NG
202 if (!id) {
203 pr_err("SYSPARAM: Failed to allocate memory to read parameter "
204 "id\n");
38c04887 205 goto out_param_buf;
4029cd66
NG
206 }
207
6396bb22 208 size = kcalloc(count, sizeof(*size), GFP_KERNEL);
4029cd66
NG
209 if (!size) {
210 pr_err("SYSPARAM: Failed to allocate memory to read parameter "
211 "size\n");
212 goto out_free_id;
213 }
214
6396bb22 215 perm = kcalloc(count, sizeof(*perm), GFP_KERNEL);
4029cd66
NG
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)) {
16003d23 228 pr_err("SYSPARAM: Missing property param-len in the DT\n");
4029cd66
NG
229 goto out_free_perm;
230 }
231
232
233 if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) {
16003d23 234 pr_err("SYSPARAM: Missing property param-perm in the DT\n");
4029cd66
NG
235 goto out_free_perm;
236 }
237
6396bb22 238 attr = kcalloc(count, sizeof(*attr), GFP_KERNEL);
4029cd66
NG
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++) {
63aecfb2
JS
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
4029cd66
NG
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:
57ad583f 263 attr[i].kobj_attr.attr.mode = 0444;
4029cd66
NG
264 break;
265 case OPAL_SYSPARAM_WRITE:
57ad583f 266 attr[i].kobj_attr.attr.mode = 0200;
4029cd66
NG
267 break;
268 case OPAL_SYSPARAM_RW:
57ad583f 269 attr[i].kobj_attr.attr.mode = 0644;
4029cd66
NG
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
291out_free_attr:
292 kfree(attr);
293out_free_perm:
294 kfree(perm);
295out_free_size:
296 kfree(size);
297out_free_id:
298 kfree(id);
4029cd66
NG
299out_param_buf:
300 kfree(param_data_buf);
301out_kobj_put:
302 kobject_put(sysparam_kobj);
38c04887
JS
303out_node_put:
304 of_node_put(sysparam);
4029cd66
NG
305out:
306 return;
307}