18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV system parameter code 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 IBM 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kobject.h> 98c2ecf20Sopenharmony_ci#include <linux/mutex.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/gfp.h> 138c2ecf20Sopenharmony_ci#include <linux/stat.h> 148c2ecf20Sopenharmony_ci#include <asm/opal.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MAX_PARAM_DATA_LEN 64 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(opal_sysparam_mutex); 198c2ecf20Sopenharmony_cistatic struct kobject *sysparam_kobj; 208c2ecf20Sopenharmony_cistatic void *param_data_buf; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct param_attr { 238c2ecf20Sopenharmony_ci struct list_head list; 248c2ecf20Sopenharmony_ci u32 param_id; 258c2ecf20Sopenharmony_ci u32 param_size; 268c2ecf20Sopenharmony_ci struct kobj_attribute kobj_attr; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct opal_msg msg; 328c2ecf20Sopenharmony_ci ssize_t ret; 338c2ecf20Sopenharmony_ci int token; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci token = opal_async_get_token_interruptible(); 368c2ecf20Sopenharmony_ci if (token < 0) { 378c2ecf20Sopenharmony_ci if (token != -ERESTARTSYS) 388c2ecf20Sopenharmony_ci pr_err("%s: Couldn't get the token, returning\n", 398c2ecf20Sopenharmony_ci __func__); 408c2ecf20Sopenharmony_ci ret = token; 418c2ecf20Sopenharmony_ci goto out; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci ret = opal_get_param(token, param_id, (u64)buffer, length); 458c2ecf20Sopenharmony_ci if (ret != OPAL_ASYNC_COMPLETION) { 468c2ecf20Sopenharmony_ci ret = opal_error_code(ret); 478c2ecf20Sopenharmony_ci goto out_token; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci ret = opal_async_wait_response(token, &msg); 518c2ecf20Sopenharmony_ci if (ret) { 528c2ecf20Sopenharmony_ci pr_err("%s: Failed to wait for the async response, %zd\n", 538c2ecf20Sopenharmony_ci __func__, ret); 548c2ecf20Sopenharmony_ci goto out_token; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci ret = opal_error_code(opal_get_async_rc(msg)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciout_token: 608c2ecf20Sopenharmony_ci opal_async_release_token(token); 618c2ecf20Sopenharmony_ciout: 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int opal_set_sys_param(u32 param_id, u32 length, void *buffer) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct opal_msg msg; 688c2ecf20Sopenharmony_ci int ret, token; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci token = opal_async_get_token_interruptible(); 718c2ecf20Sopenharmony_ci if (token < 0) { 728c2ecf20Sopenharmony_ci if (token != -ERESTARTSYS) 738c2ecf20Sopenharmony_ci pr_err("%s: Couldn't get the token, returning\n", 748c2ecf20Sopenharmony_ci __func__); 758c2ecf20Sopenharmony_ci ret = token; 768c2ecf20Sopenharmony_ci goto out; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ret = opal_set_param(token, param_id, (u64)buffer, length); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (ret != OPAL_ASYNC_COMPLETION) { 828c2ecf20Sopenharmony_ci ret = opal_error_code(ret); 838c2ecf20Sopenharmony_ci goto out_token; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = opal_async_wait_response(token, &msg); 878c2ecf20Sopenharmony_ci if (ret) { 888c2ecf20Sopenharmony_ci pr_err("%s: Failed to wait for the async response, %d\n", 898c2ecf20Sopenharmony_ci __func__, ret); 908c2ecf20Sopenharmony_ci goto out_token; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = opal_error_code(opal_get_async_rc(msg)); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciout_token: 968c2ecf20Sopenharmony_ci opal_async_release_token(token); 978c2ecf20Sopenharmony_ciout: 988c2ecf20Sopenharmony_ci return ret; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic ssize_t sys_param_show(struct kobject *kobj, 1028c2ecf20Sopenharmony_ci struct kobj_attribute *kobj_attr, char *buf) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct param_attr *attr = container_of(kobj_attr, struct param_attr, 1058c2ecf20Sopenharmony_ci kobj_attr); 1068c2ecf20Sopenharmony_ci ssize_t ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci mutex_lock(&opal_sysparam_mutex); 1098c2ecf20Sopenharmony_ci ret = opal_get_sys_param(attr->param_id, attr->param_size, 1108c2ecf20Sopenharmony_ci param_data_buf); 1118c2ecf20Sopenharmony_ci if (ret) 1128c2ecf20Sopenharmony_ci goto out; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci memcpy(buf, param_data_buf, attr->param_size); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = attr->param_size; 1178c2ecf20Sopenharmony_ciout: 1188c2ecf20Sopenharmony_ci mutex_unlock(&opal_sysparam_mutex); 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic ssize_t sys_param_store(struct kobject *kobj, 1238c2ecf20Sopenharmony_ci struct kobj_attribute *kobj_attr, const char *buf, size_t count) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct param_attr *attr = container_of(kobj_attr, struct param_attr, 1268c2ecf20Sopenharmony_ci kobj_attr); 1278c2ecf20Sopenharmony_ci ssize_t ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* MAX_PARAM_DATA_LEN is sizeof(param_data_buf) */ 1308c2ecf20Sopenharmony_ci if (count > MAX_PARAM_DATA_LEN) 1318c2ecf20Sopenharmony_ci count = MAX_PARAM_DATA_LEN; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci mutex_lock(&opal_sysparam_mutex); 1348c2ecf20Sopenharmony_ci memcpy(param_data_buf, buf, count); 1358c2ecf20Sopenharmony_ci ret = opal_set_sys_param(attr->param_id, attr->param_size, 1368c2ecf20Sopenharmony_ci param_data_buf); 1378c2ecf20Sopenharmony_ci mutex_unlock(&opal_sysparam_mutex); 1388c2ecf20Sopenharmony_ci if (!ret) 1398c2ecf20Sopenharmony_ci ret = count; 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_civoid __init opal_sys_param_init(void) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct device_node *sysparam; 1468c2ecf20Sopenharmony_ci struct param_attr *attr; 1478c2ecf20Sopenharmony_ci u32 *id, *size; 1488c2ecf20Sopenharmony_ci int count, i; 1498c2ecf20Sopenharmony_ci u8 *perm; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (!opal_kobj) { 1528c2ecf20Sopenharmony_ci pr_warn("SYSPARAM: opal kobject is not available\n"); 1538c2ecf20Sopenharmony_ci goto out; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Some systems do not use sysparams; this is not an error */ 1578c2ecf20Sopenharmony_ci sysparam = of_find_node_by_path("/ibm,opal/sysparams"); 1588c2ecf20Sopenharmony_ci if (!sysparam) 1598c2ecf20Sopenharmony_ci goto out; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) { 1628c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Opal sysparam node not compatible\n"); 1638c2ecf20Sopenharmony_ci goto out_node_put; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj); 1678c2ecf20Sopenharmony_ci if (!sysparam_kobj) { 1688c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to create sysparam kobject\n"); 1698c2ecf20Sopenharmony_ci goto out_node_put; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Allocate big enough buffer for any get/set transactions */ 1738c2ecf20Sopenharmony_ci param_data_buf = kzalloc(MAX_PARAM_DATA_LEN, GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!param_data_buf) { 1758c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to allocate memory for param data " 1768c2ecf20Sopenharmony_ci "buf\n"); 1778c2ecf20Sopenharmony_ci goto out_kobj_put; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Number of parameters exposed through DT */ 1818c2ecf20Sopenharmony_ci count = of_property_count_strings(sysparam, "param-name"); 1828c2ecf20Sopenharmony_ci if (count < 0) { 1838c2ecf20Sopenharmony_ci pr_err("SYSPARAM: No string found of property param-name in " 1848c2ecf20Sopenharmony_ci "the node %pOFn\n", sysparam); 1858c2ecf20Sopenharmony_ci goto out_param_buf; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci id = kcalloc(count, sizeof(*id), GFP_KERNEL); 1898c2ecf20Sopenharmony_ci if (!id) { 1908c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to allocate memory to read parameter " 1918c2ecf20Sopenharmony_ci "id\n"); 1928c2ecf20Sopenharmony_ci goto out_param_buf; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci size = kcalloc(count, sizeof(*size), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!size) { 1978c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to allocate memory to read parameter " 1988c2ecf20Sopenharmony_ci "size\n"); 1998c2ecf20Sopenharmony_ci goto out_free_id; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci perm = kcalloc(count, sizeof(*perm), GFP_KERNEL); 2038c2ecf20Sopenharmony_ci if (!perm) { 2048c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to allocate memory to read supported " 2058c2ecf20Sopenharmony_ci "action on the parameter"); 2068c2ecf20Sopenharmony_ci goto out_free_size; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (of_property_read_u32_array(sysparam, "param-id", id, count)) { 2108c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Missing property param-id in the DT\n"); 2118c2ecf20Sopenharmony_ci goto out_free_perm; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (of_property_read_u32_array(sysparam, "param-len", size, count)) { 2158c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Missing property param-len in the DT\n"); 2168c2ecf20Sopenharmony_ci goto out_free_perm; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (of_property_read_u8_array(sysparam, "param-perm", perm, count)) { 2218c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Missing property param-perm in the DT\n"); 2228c2ecf20Sopenharmony_ci goto out_free_perm; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci attr = kcalloc(count, sizeof(*attr), GFP_KERNEL); 2268c2ecf20Sopenharmony_ci if (!attr) { 2278c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to allocate memory for parameter " 2288c2ecf20Sopenharmony_ci "attributes\n"); 2298c2ecf20Sopenharmony_ci goto out_free_perm; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* For each of the parameters, populate the parameter attributes */ 2338c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2348c2ecf20Sopenharmony_ci if (size[i] > MAX_PARAM_DATA_LEN) { 2358c2ecf20Sopenharmony_ci pr_warn("SYSPARAM: Not creating parameter %d as size " 2368c2ecf20Sopenharmony_ci "exceeds buffer length\n", i); 2378c2ecf20Sopenharmony_ci continue; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci sysfs_attr_init(&attr[i].kobj_attr.attr); 2418c2ecf20Sopenharmony_ci attr[i].param_id = id[i]; 2428c2ecf20Sopenharmony_ci attr[i].param_size = size[i]; 2438c2ecf20Sopenharmony_ci if (of_property_read_string_index(sysparam, "param-name", i, 2448c2ecf20Sopenharmony_ci &attr[i].kobj_attr.attr.name)) 2458c2ecf20Sopenharmony_ci continue; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* If the parameter is read-only or read-write */ 2488c2ecf20Sopenharmony_ci switch (perm[i] & 3) { 2498c2ecf20Sopenharmony_ci case OPAL_SYSPARAM_READ: 2508c2ecf20Sopenharmony_ci attr[i].kobj_attr.attr.mode = 0444; 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case OPAL_SYSPARAM_WRITE: 2538c2ecf20Sopenharmony_ci attr[i].kobj_attr.attr.mode = 0200; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case OPAL_SYSPARAM_RW: 2568c2ecf20Sopenharmony_ci attr[i].kobj_attr.attr.mode = 0644; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci default: 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci attr[i].kobj_attr.show = sys_param_show; 2638c2ecf20Sopenharmony_ci attr[i].kobj_attr.store = sys_param_store; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (sysfs_create_file(sysparam_kobj, &attr[i].kobj_attr.attr)) { 2668c2ecf20Sopenharmony_ci pr_err("SYSPARAM: Failed to create sysfs file %s\n", 2678c2ecf20Sopenharmony_ci attr[i].kobj_attr.attr.name); 2688c2ecf20Sopenharmony_ci goto out_free_attr; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci kfree(perm); 2738c2ecf20Sopenharmony_ci kfree(size); 2748c2ecf20Sopenharmony_ci kfree(id); 2758c2ecf20Sopenharmony_ci of_node_put(sysparam); 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciout_free_attr: 2798c2ecf20Sopenharmony_ci kfree(attr); 2808c2ecf20Sopenharmony_ciout_free_perm: 2818c2ecf20Sopenharmony_ci kfree(perm); 2828c2ecf20Sopenharmony_ciout_free_size: 2838c2ecf20Sopenharmony_ci kfree(size); 2848c2ecf20Sopenharmony_ciout_free_id: 2858c2ecf20Sopenharmony_ci kfree(id); 2868c2ecf20Sopenharmony_ciout_param_buf: 2878c2ecf20Sopenharmony_ci kfree(param_data_buf); 2888c2ecf20Sopenharmony_ciout_kobj_put: 2898c2ecf20Sopenharmony_ci kobject_put(sysparam_kobj); 2908c2ecf20Sopenharmony_ciout_node_put: 2918c2ecf20Sopenharmony_ci of_node_put(sysparam); 2928c2ecf20Sopenharmony_ciout: 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci} 295