18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL Power-Shift-Ratio interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2017 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "opal-psr: " 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(psr_mutex); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic struct kobject *psr_kobj; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct psr_attr { 218c2ecf20Sopenharmony_ci u32 handle; 228c2ecf20Sopenharmony_ci struct kobj_attribute attr; 238c2ecf20Sopenharmony_ci} *psr_attrs; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr, 268c2ecf20Sopenharmony_ci char *buf) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr); 298c2ecf20Sopenharmony_ci struct opal_msg msg; 308c2ecf20Sopenharmony_ci int psr, ret, token; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci token = opal_async_get_token_interruptible(); 338c2ecf20Sopenharmony_ci if (token < 0) { 348c2ecf20Sopenharmony_ci pr_devel("Failed to get token\n"); 358c2ecf20Sopenharmony_ci return token; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&psr_mutex); 398c2ecf20Sopenharmony_ci if (ret) 408c2ecf20Sopenharmony_ci goto out_token; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci ret = opal_get_power_shift_ratio(psr_attr->handle, token, 438c2ecf20Sopenharmony_ci (u32 *)__pa(&psr)); 448c2ecf20Sopenharmony_ci switch (ret) { 458c2ecf20Sopenharmony_ci case OPAL_ASYNC_COMPLETION: 468c2ecf20Sopenharmony_ci ret = opal_async_wait_response(token, &msg); 478c2ecf20Sopenharmony_ci if (ret) { 488c2ecf20Sopenharmony_ci pr_devel("Failed to wait for the async response\n"); 498c2ecf20Sopenharmony_ci ret = -EIO; 508c2ecf20Sopenharmony_ci goto out; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci ret = opal_error_code(opal_get_async_rc(msg)); 538c2ecf20Sopenharmony_ci if (!ret) { 548c2ecf20Sopenharmony_ci ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); 558c2ecf20Sopenharmony_ci if (ret < 0) 568c2ecf20Sopenharmony_ci ret = -EIO; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci case OPAL_SUCCESS: 608c2ecf20Sopenharmony_ci ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); 618c2ecf20Sopenharmony_ci if (ret < 0) 628c2ecf20Sopenharmony_ci ret = -EIO; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci default: 658c2ecf20Sopenharmony_ci ret = opal_error_code(ret); 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciout: 698c2ecf20Sopenharmony_ci mutex_unlock(&psr_mutex); 708c2ecf20Sopenharmony_ciout_token: 718c2ecf20Sopenharmony_ci opal_async_release_token(token); 728c2ecf20Sopenharmony_ci return ret; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic ssize_t psr_store(struct kobject *kobj, struct kobj_attribute *attr, 768c2ecf20Sopenharmony_ci const char *buf, size_t count) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr); 798c2ecf20Sopenharmony_ci struct opal_msg msg; 808c2ecf20Sopenharmony_ci int psr, ret, token; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret = kstrtoint(buf, 0, &psr); 838c2ecf20Sopenharmony_ci if (ret) 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci token = opal_async_get_token_interruptible(); 878c2ecf20Sopenharmony_ci if (token < 0) { 888c2ecf20Sopenharmony_ci pr_devel("Failed to get token\n"); 898c2ecf20Sopenharmony_ci return token; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = mutex_lock_interruptible(&psr_mutex); 938c2ecf20Sopenharmony_ci if (ret) 948c2ecf20Sopenharmony_ci goto out_token; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = opal_set_power_shift_ratio(psr_attr->handle, token, psr); 978c2ecf20Sopenharmony_ci switch (ret) { 988c2ecf20Sopenharmony_ci case OPAL_ASYNC_COMPLETION: 998c2ecf20Sopenharmony_ci ret = opal_async_wait_response(token, &msg); 1008c2ecf20Sopenharmony_ci if (ret) { 1018c2ecf20Sopenharmony_ci pr_devel("Failed to wait for the async response\n"); 1028c2ecf20Sopenharmony_ci ret = -EIO; 1038c2ecf20Sopenharmony_ci goto out; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci ret = opal_error_code(opal_get_async_rc(msg)); 1068c2ecf20Sopenharmony_ci if (!ret) 1078c2ecf20Sopenharmony_ci ret = count; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci case OPAL_SUCCESS: 1108c2ecf20Sopenharmony_ci ret = count; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci default: 1138c2ecf20Sopenharmony_ci ret = opal_error_code(ret); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciout: 1178c2ecf20Sopenharmony_ci mutex_unlock(&psr_mutex); 1188c2ecf20Sopenharmony_ciout_token: 1198c2ecf20Sopenharmony_ci opal_async_release_token(token); 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_civoid __init opal_psr_init(void) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct device_node *psr, *node; 1268c2ecf20Sopenharmony_ci int i = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci psr = of_find_compatible_node(NULL, NULL, 1298c2ecf20Sopenharmony_ci "ibm,opal-power-shift-ratio"); 1308c2ecf20Sopenharmony_ci if (!psr) { 1318c2ecf20Sopenharmony_ci pr_devel("Power-shift-ratio node not found\n"); 1328c2ecf20Sopenharmony_ci return; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci psr_attrs = kcalloc(of_get_child_count(psr), sizeof(*psr_attrs), 1368c2ecf20Sopenharmony_ci GFP_KERNEL); 1378c2ecf20Sopenharmony_ci if (!psr_attrs) 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci psr_kobj = kobject_create_and_add("psr", opal_kobj); 1418c2ecf20Sopenharmony_ci if (!psr_kobj) { 1428c2ecf20Sopenharmony_ci pr_warn("Failed to create psr kobject\n"); 1438c2ecf20Sopenharmony_ci goto out; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for_each_child_of_node(psr, node) { 1478c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "handle", 1488c2ecf20Sopenharmony_ci &psr_attrs[i].handle)) 1498c2ecf20Sopenharmony_ci goto out_kobj; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci sysfs_attr_init(&psr_attrs[i].attr.attr); 1528c2ecf20Sopenharmony_ci if (of_property_read_string(node, "label", 1538c2ecf20Sopenharmony_ci &psr_attrs[i].attr.attr.name)) 1548c2ecf20Sopenharmony_ci goto out_kobj; 1558c2ecf20Sopenharmony_ci psr_attrs[i].attr.attr.mode = 0664; 1568c2ecf20Sopenharmony_ci psr_attrs[i].attr.show = psr_show; 1578c2ecf20Sopenharmony_ci psr_attrs[i].attr.store = psr_store; 1588c2ecf20Sopenharmony_ci if (sysfs_create_file(psr_kobj, &psr_attrs[i].attr.attr)) { 1598c2ecf20Sopenharmony_ci pr_devel("Failed to create psr sysfs file %s\n", 1608c2ecf20Sopenharmony_ci psr_attrs[i].attr.attr.name); 1618c2ecf20Sopenharmony_ci goto out_kobj; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci i++; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ciout_kobj: 1688c2ecf20Sopenharmony_ci kobject_put(psr_kobj); 1698c2ecf20Sopenharmony_ciout: 1708c2ecf20Sopenharmony_ci kfree(psr_attrs); 1718c2ecf20Sopenharmony_ci} 172