18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL Sensor-groups interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2017 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "opal-sensor-groups: " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/kobject.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <asm/opal.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sg_mutex); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic struct kobject *sg_kobj; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct sg_attr { 218c2ecf20Sopenharmony_ci u32 handle; 228c2ecf20Sopenharmony_ci struct kobj_attribute attr; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic struct sensor_group { 268c2ecf20Sopenharmony_ci char name[20]; 278c2ecf20Sopenharmony_ci struct attribute_group sg; 288c2ecf20Sopenharmony_ci struct sg_attr *sgattrs; 298c2ecf20Sopenharmony_ci} *sgs; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciint sensor_group_enable(u32 handle, bool enable) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct opal_msg msg; 348c2ecf20Sopenharmony_ci int token, ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci token = opal_async_get_token_interruptible(); 378c2ecf20Sopenharmony_ci if (token < 0) 388c2ecf20Sopenharmony_ci return token; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ret = opal_sensor_group_enable(handle, token, enable); 418c2ecf20Sopenharmony_ci if (ret == OPAL_ASYNC_COMPLETION) { 428c2ecf20Sopenharmony_ci ret = opal_async_wait_response(token, &msg); 438c2ecf20Sopenharmony_ci if (ret) { 448c2ecf20Sopenharmony_ci pr_devel("Failed to wait for the async response\n"); 458c2ecf20Sopenharmony_ci ret = -EIO; 468c2ecf20Sopenharmony_ci goto out; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci ret = opal_error_code(opal_get_async_rc(msg)); 498c2ecf20Sopenharmony_ci } else { 508c2ecf20Sopenharmony_ci ret = opal_error_code(ret); 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ciout: 548c2ecf20Sopenharmony_ci opal_async_release_token(token); 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(sensor_group_enable); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic ssize_t sg_store(struct kobject *kobj, struct kobj_attribute *attr, 608c2ecf20Sopenharmony_ci const char *buf, size_t count) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct sg_attr *sattr = container_of(attr, struct sg_attr, attr); 638c2ecf20Sopenharmony_ci struct opal_msg msg; 648c2ecf20Sopenharmony_ci u32 data; 658c2ecf20Sopenharmony_ci int ret, token; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ret = kstrtoint(buf, 0, &data); 688c2ecf20Sopenharmony_ci if (ret) 698c2ecf20Sopenharmony_ci return ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (data != 1) 728c2ecf20Sopenharmony_ci return -EINVAL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci token = opal_async_get_token_interruptible(); 758c2ecf20Sopenharmony_ci if (token < 0) { 768c2ecf20Sopenharmony_ci pr_devel("Failed to get token\n"); 778c2ecf20Sopenharmony_ci return token; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&sg_mutex); 818c2ecf20Sopenharmony_ci if (ret) 828c2ecf20Sopenharmony_ci goto out_token; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = opal_sensor_group_clear(sattr->handle, token); 858c2ecf20Sopenharmony_ci switch (ret) { 868c2ecf20Sopenharmony_ci case OPAL_ASYNC_COMPLETION: 878c2ecf20Sopenharmony_ci ret = opal_async_wait_response(token, &msg); 888c2ecf20Sopenharmony_ci if (ret) { 898c2ecf20Sopenharmony_ci pr_devel("Failed to wait for the async response\n"); 908c2ecf20Sopenharmony_ci ret = -EIO; 918c2ecf20Sopenharmony_ci goto out; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci ret = opal_error_code(opal_get_async_rc(msg)); 948c2ecf20Sopenharmony_ci if (!ret) 958c2ecf20Sopenharmony_ci ret = count; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci case OPAL_SUCCESS: 988c2ecf20Sopenharmony_ci ret = count; 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci default: 1018c2ecf20Sopenharmony_ci ret = opal_error_code(ret); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciout: 1058c2ecf20Sopenharmony_ci mutex_unlock(&sg_mutex); 1068c2ecf20Sopenharmony_ciout_token: 1078c2ecf20Sopenharmony_ci opal_async_release_token(token); 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic struct sg_ops_info { 1128c2ecf20Sopenharmony_ci int opal_no; 1138c2ecf20Sopenharmony_ci const char *attr_name; 1148c2ecf20Sopenharmony_ci ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, 1158c2ecf20Sopenharmony_ci const char *buf, size_t count); 1168c2ecf20Sopenharmony_ci} ops_info[] = { 1178c2ecf20Sopenharmony_ci { OPAL_SENSOR_GROUP_CLEAR, "clear", sg_store }, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void add_attr(int handle, struct sg_attr *attr, int index) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci attr->handle = handle; 1238c2ecf20Sopenharmony_ci sysfs_attr_init(&attr->attr.attr); 1248c2ecf20Sopenharmony_ci attr->attr.attr.name = ops_info[index].attr_name; 1258c2ecf20Sopenharmony_ci attr->attr.attr.mode = 0220; 1268c2ecf20Sopenharmony_ci attr->attr.store = ops_info[index].store; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int add_attr_group(const __be32 *ops, int len, struct sensor_group *sg, 1308c2ecf20Sopenharmony_ci u32 handle) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int i, j; 1338c2ecf20Sopenharmony_ci int count = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 1368c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(ops_info); j++) 1378c2ecf20Sopenharmony_ci if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) { 1388c2ecf20Sopenharmony_ci add_attr(handle, &sg->sgattrs[count], j); 1398c2ecf20Sopenharmony_ci sg->sg.attrs[count] = 1408c2ecf20Sopenharmony_ci &sg->sgattrs[count].attr.attr; 1418c2ecf20Sopenharmony_ci count++; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return sysfs_create_group(sg_kobj, &sg->sg); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int get_nr_attrs(const __be32 *ops, int len) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci int i, j; 1508c2ecf20Sopenharmony_ci int nr_attrs = 0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 1538c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(ops_info); j++) 1548c2ecf20Sopenharmony_ci if (be32_to_cpu(ops[i]) == ops_info[j].opal_no) 1558c2ecf20Sopenharmony_ci nr_attrs++; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return nr_attrs; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_civoid __init opal_sensor_groups_init(void) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct device_node *sg, *node; 1638c2ecf20Sopenharmony_ci int i = 0; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci sg = of_find_compatible_node(NULL, NULL, "ibm,opal-sensor-group"); 1668c2ecf20Sopenharmony_ci if (!sg) { 1678c2ecf20Sopenharmony_ci pr_devel("Sensor groups node not found\n"); 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sgs = kcalloc(of_get_child_count(sg), sizeof(*sgs), GFP_KERNEL); 1728c2ecf20Sopenharmony_ci if (!sgs) 1738c2ecf20Sopenharmony_ci return; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci sg_kobj = kobject_create_and_add("sensor_groups", opal_kobj); 1768c2ecf20Sopenharmony_ci if (!sg_kobj) { 1778c2ecf20Sopenharmony_ci pr_warn("Failed to create sensor group kobject\n"); 1788c2ecf20Sopenharmony_ci goto out_sgs; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for_each_child_of_node(sg, node) { 1828c2ecf20Sopenharmony_ci const __be32 *ops; 1838c2ecf20Sopenharmony_ci u32 sgid, len, nr_attrs, chipid; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ops = of_get_property(node, "ops", &len); 1868c2ecf20Sopenharmony_ci if (!ops) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci nr_attrs = get_nr_attrs(ops, len); 1908c2ecf20Sopenharmony_ci if (!nr_attrs) 1918c2ecf20Sopenharmony_ci continue; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci sgs[i].sgattrs = kcalloc(nr_attrs, sizeof(*sgs[i].sgattrs), 1948c2ecf20Sopenharmony_ci GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!sgs[i].sgattrs) 1968c2ecf20Sopenharmony_ci goto out_sgs_sgattrs; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci sgs[i].sg.attrs = kcalloc(nr_attrs + 1, 1998c2ecf20Sopenharmony_ci sizeof(*sgs[i].sg.attrs), 2008c2ecf20Sopenharmony_ci GFP_KERNEL); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!sgs[i].sg.attrs) { 2038c2ecf20Sopenharmony_ci kfree(sgs[i].sgattrs); 2048c2ecf20Sopenharmony_ci goto out_sgs_sgattrs; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "sensor-group-id", &sgid)) { 2088c2ecf20Sopenharmony_ci pr_warn("sensor-group-id property not found\n"); 2098c2ecf20Sopenharmony_ci goto out_sgs_sgattrs; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!of_property_read_u32(node, "ibm,chip-id", &chipid)) 2138c2ecf20Sopenharmony_ci sprintf(sgs[i].name, "%pOFn%d", node, chipid); 2148c2ecf20Sopenharmony_ci else 2158c2ecf20Sopenharmony_ci sprintf(sgs[i].name, "%pOFn", node); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci sgs[i].sg.name = sgs[i].name; 2188c2ecf20Sopenharmony_ci if (add_attr_group(ops, len, &sgs[i], sgid)) { 2198c2ecf20Sopenharmony_ci pr_warn("Failed to create sensor attribute group %s\n", 2208c2ecf20Sopenharmony_ci sgs[i].sg.name); 2218c2ecf20Sopenharmony_ci goto out_sgs_sgattrs; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci i++; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciout_sgs_sgattrs: 2298c2ecf20Sopenharmony_ci while (--i >= 0) { 2308c2ecf20Sopenharmony_ci kfree(sgs[i].sgattrs); 2318c2ecf20Sopenharmony_ci kfree(sgs[i].sg.attrs); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci kobject_put(sg_kobj); 2348c2ecf20Sopenharmony_ciout_sgs: 2358c2ecf20Sopenharmony_ci kfree(sgs); 2368c2ecf20Sopenharmony_ci} 237