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 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 } 291