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 42static 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 70out_token: 71 opal_async_release_token(token); 72out: 73 return ret; 74} 75 76static 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 104out_token: 105 opal_async_release_token(token); 106out: 107 return ret; 108} 109 110static 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; 126out: 127 mutex_unlock(&opal_sysparam_mutex); 128 return ret; 129} 130 131static 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 152void __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 288out_free_attr: 289 kfree(attr); 290out_free_perm: 291 kfree(perm); 292out_free_size: 293 kfree(size); 294out_free_id: 295 kfree(id); 296out_node_put: 297 of_node_put(sysparam); 298out_param_buf: 299 kfree(param_data_buf); 300out_kobj_put: 301 kobject_put(sysparam_kobj); 302out: 303 return; 304} 305