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(be64_to_cpu(msg.params[1])); 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(be64_to_cpu(msg.params[1])); 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 %s\n", sysparam->name); 198 goto out_param_buf; 199 } 200 201 id = kzalloc(sizeof(*id) * count, 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 = kzalloc(sizeof(*size) * count, 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 = kzalloc(sizeof(*perm) * count, 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 = kzalloc(sizeof(*attr) * count, 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 = S_IRUGO; 264 break; 265 case OPAL_SYSPARAM_WRITE: 266 attr[i].kobj_attr.attr.mode = S_IWUSR; 267 break; 268 case OPAL_SYSPARAM_RW: 269 attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUSR; 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