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