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