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