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 = be64_to_cpu(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 = be64_to_cpu(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 ret = attr->param_size; 125 out: 126 mutex_unlock(&opal_sysparam_mutex); 127 return ret; 128 } 129 130 static ssize_t sys_param_store(struct kobject *kobj, 131 struct kobj_attribute *kobj_attr, const char *buf, size_t count) 132 { 133 struct param_attr *attr = container_of(kobj_attr, struct param_attr, 134 kobj_attr); 135 int ret; 136 137 mutex_lock(&opal_sysparam_mutex); 138 memcpy(param_data_buf, buf, count); 139 ret = opal_set_sys_param(attr->param_id, attr->param_size, 140 param_data_buf); 141 mutex_unlock(&opal_sysparam_mutex); 142 if (!ret) 143 ret = count; 144 return ret; 145 } 146 147 void __init opal_sys_param_init(void) 148 { 149 struct device_node *sysparam; 150 struct param_attr *attr; 151 u32 *id, *size; 152 int count, i; 153 u8 *perm; 154 155 if (!opal_kobj) { 156 pr_warn("SYSPARAM: opal kobject is not available\n"); 157 goto out; 158 } 159 160 sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj); 161 if (!sysparam_kobj) { 162 pr_err("SYSPARAM: Failed to create sysparam kobject\n"); 163 goto out; 164 } 165 166 /* Allocate big enough buffer for any get/set transactions */ 167 param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL); 168 if (!param_data_buf) { 169 pr_err("SYSPARAM: Failed to allocate memory for param data " 170 "buf\n"); 171 goto out_kobj_put; 172 } 173 174 sysparam = of_find_node_by_path("/ibm,opal/sysparams"); 175 if (!sysparam) { 176 pr_err("SYSPARAM: Opal sysparam node not found\n"); 177 goto out_param_buf; 178 } 179 180 if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) { 181 pr_err("SYSPARAM: Opal sysparam node not compatible\n"); 182 goto out_node_put; 183 } 184 185 /* Number of parameters exposed through DT */ 186 count = of_property_count_strings(sysparam, "param-name"); 187 if (count < 0) { 188 pr_err("SYSPARAM: No string found of property param-name in " 189 "the node %s\n", sysparam->name); 190 goto out_node_put; 191 } 192 193 id = kzalloc(sizeof(*id) * count, GFP_KERNEL); 194 if (!id) { 195 pr_err("SYSPARAM: Failed to allocate memory to read parameter " 196 "id\n"); 197 goto out_node_put; 198 } 199 200 size = kzalloc(sizeof(*size) * count, GFP_KERNEL); 201 if (!size) { 202 pr_err("SYSPARAM: Failed to allocate memory to read parameter " 203 "size\n"); 204 goto out_free_id; 205 } 206 207 perm = kzalloc(sizeof(*perm) * count, GFP_KERNEL); 208 if (!perm) { 209 pr_err("SYSPARAM: Failed to allocate memory to read supported " 210 "action on the parameter"); 211 goto out_free_size; 212 } 213 214 if (of_property_read_u32_array(sysparam, "param-id", id, count)) { 215 pr_err("SYSPARAM: Missing property param-id in the DT\n"); 216 goto out_free_perm; 217 } 218 219 if (of_property_read_u32_array(sysparam, "param-len", size, count)) { 220 pr_err("SYSPARAM: Missing propery param-len in the DT\n"); 221 goto out_free_perm; 222 } 223 224 225 if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) { 226 pr_err("SYSPARAM: Missing propery param-perm in the DT\n"); 227 goto out_free_perm; 228 } 229 230 attr = kzalloc(sizeof(*attr) * count, GFP_KERNEL); 231 if (!attr) { 232 pr_err("SYSPARAM: Failed to allocate memory for parameter " 233 "attributes\n"); 234 goto out_free_perm; 235 } 236 237 /* For each of the parameters, populate the parameter attributes */ 238 for (i = 0; i < count; i++) { 239 sysfs_attr_init(&attr[i].kobj_attr.attr); 240 attr[i].param_id = id[i]; 241 attr[i].param_size = size[i]; 242 if (of_property_read_string_index(sysparam, "param-name", i, 243 &attr[i].kobj_attr.attr.name)) 244 continue; 245 246 /* If the parameter is read-only or read-write */ 247 switch (perm[i] & 3) { 248 case OPAL_SYSPARAM_READ: 249 attr[i].kobj_attr.attr.mode = S_IRUGO; 250 break; 251 case OPAL_SYSPARAM_WRITE: 252 attr[i].kobj_attr.attr.mode = S_IWUGO; 253 break; 254 case OPAL_SYSPARAM_RW: 255 attr[i].kobj_attr.attr.mode = S_IRUGO | S_IWUGO; 256 break; 257 default: 258 break; 259 } 260 261 attr[i].kobj_attr.show = sys_param_show; 262 attr[i].kobj_attr.store = sys_param_store; 263 264 if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) { 265 pr_err("SYSPARAM: Failed to create sysfs file %s\n", 266 attr[i].kobj_attr.attr.name); 267 goto out_free_attr; 268 } 269 } 270 271 kfree(perm); 272 kfree(size); 273 kfree(id); 274 of_node_put(sysparam); 275 return; 276 277 out_free_attr: 278 kfree(attr); 279 out_free_perm: 280 kfree(perm); 281 out_free_size: 282 kfree(size); 283 out_free_id: 284 kfree(id); 285 out_node_put: 286 of_node_put(sysparam); 287 out_param_buf: 288 kfree(param_data_buf); 289 out_kobj_put: 290 kobject_put(sysparam_kobj); 291 out: 292 return; 293 } 294