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 /* Some systems do not use sysparams; this is not an error */ 166 sysparam = of_find_node_by_path("/ibm,opal/sysparams"); 167 if (!sysparam) 168 goto out; 169 170 if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) { 171 pr_err("SYSPARAM: Opal sysparam node not compatible\n"); 172 goto out_node_put; 173 } 174 175 sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj); 176 if (!sysparam_kobj) { 177 pr_err("SYSPARAM: Failed to create sysparam kobject\n"); 178 goto out_node_put; 179 } 180 181 /* Allocate big enough buffer for any get/set transactions */ 182 param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL); 183 if (!param_data_buf) { 184 pr_err("SYSPARAM: Failed to allocate memory for param data " 185 "buf\n"); 186 goto out_kobj_put; 187 } 188 189 /* Number of parameters exposed through DT */ 190 count = of_property_count_strings(sysparam, "param-name"); 191 if (count < 0) { 192 pr_err("SYSPARAM: No string found of property param-name in " 193 "the node %s\n", sysparam->name); 194 goto out_param_buf; 195 } 196 197 id = kzalloc(sizeof(*id) * count, GFP_KERNEL); 198 if (!id) { 199 pr_err("SYSPARAM: Failed to allocate memory to read parameter " 200 "id\n"); 201 goto out_param_buf; 202 } 203 204 size = kzalloc(sizeof(*size) * count, GFP_KERNEL); 205 if (!size) { 206 pr_err("SYSPARAM: Failed to allocate memory to read parameter " 207 "size\n"); 208 goto out_free_id; 209 } 210 211 perm = kzalloc(sizeof(*perm) * count, GFP_KERNEL); 212 if (!perm) { 213 pr_err("SYSPARAM: Failed to allocate memory to read supported " 214 "action on the parameter"); 215 goto out_free_size; 216 } 217 218 if (of_property_read_u32_array(sysparam, "param-id", id, count)) { 219 pr_err("SYSPARAM: Missing property param-id in the DT\n"); 220 goto out_free_perm; 221 } 222 223 if (of_property_read_u32_array(sysparam, "param-len", size, count)) { 224 pr_err("SYSPARAM: Missing property param-len in the DT\n"); 225 goto out_free_perm; 226 } 227 228 229 if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) { 230 pr_err("SYSPARAM: Missing property param-perm in the DT\n"); 231 goto out_free_perm; 232 } 233 234 attr = kzalloc(sizeof(*attr) * count, GFP_KERNEL); 235 if (!attr) { 236 pr_err("SYSPARAM: Failed to allocate memory for parameter " 237 "attributes\n"); 238 goto out_free_perm; 239 } 240 241 /* For each of the parameters, populate the parameter attributes */ 242 for (i = 0; i < count; i++) { 243 if (size[i] > MAX_PARAM_DATA_LEN) { 244 pr_warn("SYSPARAM: Not creating parameter %d as size " 245 "exceeds buffer length\n", i); 246 continue; 247 } 248 249 sysfs_attr_init(&attr[i].kobj_attr.attr); 250 attr[i].param_id = id[i]; 251 attr[i].param_size = size[i]; 252 if (of_property_read_string_index(sysparam, "param-name", i, 253 &attr[i].kobj_attr.attr.name)) 254 continue; 255 256 /* If the parameter is read-only or read-write */ 257 switch (perm[i] & 3) { 258 case OPAL_SYSPARAM_READ: 259 attr[i].kobj_attr.attr.mode = S_IRUGO; 260 break; 261 case OPAL_SYSPARAM_WRITE: 262 attr[i].kobj_attr.attr.mode = S_IWUSR; 263 break; 264 case OPAL_SYSPARAM_RW: 265 attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUSR; 266 break; 267 default: 268 break; 269 } 270 271 attr[i].kobj_attr.show = sys_param_show; 272 attr[i].kobj_attr.store = sys_param_store; 273 274 if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) { 275 pr_err("SYSPARAM: Failed to create sysfs file %s\n", 276 attr[i].kobj_attr.attr.name); 277 goto out_free_attr; 278 } 279 } 280 281 kfree(perm); 282 kfree(size); 283 kfree(id); 284 of_node_put(sysparam); 285 return; 286 287 out_free_attr: 288 kfree(attr); 289 out_free_perm: 290 kfree(perm); 291 out_free_size: 292 kfree(size); 293 out_free_id: 294 kfree(id); 295 out_param_buf: 296 kfree(param_data_buf); 297 out_kobj_put: 298 kobject_put(sysparam_kobj); 299 out_node_put: 300 of_node_put(sysparam); 301 out: 302 return; 303 } 304