1 /* 2 * PowerNV OPAL Power-Shift-Ratio interface 3 * 4 * Copyright 2017 IBM Corp. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12 #define pr_fmt(fmt) "opal-psr: " fmt 13 14 #include <linux/of.h> 15 #include <linux/kobject.h> 16 #include <linux/slab.h> 17 18 #include <asm/opal.h> 19 20 DEFINE_MUTEX(psr_mutex); 21 22 static struct kobject *psr_kobj; 23 24 struct psr_attr { 25 u32 handle; 26 struct kobj_attribute attr; 27 } *psr_attrs; 28 29 static ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr, 30 char *buf) 31 { 32 struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr); 33 struct opal_msg msg; 34 int psr, ret, token; 35 36 token = opal_async_get_token_interruptible(); 37 if (token < 0) { 38 pr_devel("Failed to get token\n"); 39 return token; 40 } 41 42 ret = mutex_lock_interruptible(&psr_mutex); 43 if (ret) 44 goto out_token; 45 46 ret = opal_get_power_shift_ratio(psr_attr->handle, token, 47 (u32 *)__pa(&psr)); 48 switch (ret) { 49 case OPAL_ASYNC_COMPLETION: 50 ret = opal_async_wait_response(token, &msg); 51 if (ret) { 52 pr_devel("Failed to wait for the async response\n"); 53 ret = -EIO; 54 goto out; 55 } 56 ret = opal_error_code(opal_get_async_rc(msg)); 57 if (!ret) { 58 ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); 59 if (ret < 0) 60 ret = -EIO; 61 } 62 break; 63 case OPAL_SUCCESS: 64 ret = sprintf(buf, "%u\n", be32_to_cpu(psr)); 65 if (ret < 0) 66 ret = -EIO; 67 break; 68 default: 69 ret = opal_error_code(ret); 70 } 71 72 out: 73 mutex_unlock(&psr_mutex); 74 out_token: 75 opal_async_release_token(token); 76 return ret; 77 } 78 79 static ssize_t psr_store(struct kobject *kobj, struct kobj_attribute *attr, 80 const char *buf, size_t count) 81 { 82 struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr); 83 struct opal_msg msg; 84 int psr, ret, token; 85 86 ret = kstrtoint(buf, 0, &psr); 87 if (ret) 88 return ret; 89 90 token = opal_async_get_token_interruptible(); 91 if (token < 0) { 92 pr_devel("Failed to get token\n"); 93 return token; 94 } 95 96 ret = mutex_lock_interruptible(&psr_mutex); 97 if (ret) 98 goto out_token; 99 100 ret = opal_set_power_shift_ratio(psr_attr->handle, token, psr); 101 switch (ret) { 102 case OPAL_ASYNC_COMPLETION: 103 ret = opal_async_wait_response(token, &msg); 104 if (ret) { 105 pr_devel("Failed to wait for the async response\n"); 106 ret = -EIO; 107 goto out; 108 } 109 ret = opal_error_code(opal_get_async_rc(msg)); 110 if (!ret) 111 ret = count; 112 break; 113 case OPAL_SUCCESS: 114 ret = count; 115 break; 116 default: 117 ret = opal_error_code(ret); 118 } 119 120 out: 121 mutex_unlock(&psr_mutex); 122 out_token: 123 opal_async_release_token(token); 124 return ret; 125 } 126 127 void __init opal_psr_init(void) 128 { 129 struct device_node *psr, *node; 130 int i = 0; 131 132 psr = of_find_compatible_node(NULL, NULL, 133 "ibm,opal-power-shift-ratio"); 134 if (!psr) { 135 pr_devel("Power-shift-ratio node not found\n"); 136 return; 137 } 138 139 psr_attrs = kcalloc(of_get_child_count(psr), sizeof(*psr_attrs), 140 GFP_KERNEL); 141 if (!psr_attrs) 142 return; 143 144 psr_kobj = kobject_create_and_add("psr", opal_kobj); 145 if (!psr_kobj) { 146 pr_warn("Failed to create psr kobject\n"); 147 goto out; 148 } 149 150 for_each_child_of_node(psr, node) { 151 if (of_property_read_u32(node, "handle", 152 &psr_attrs[i].handle)) 153 goto out_kobj; 154 155 sysfs_attr_init(&psr_attrs[i].attr.attr); 156 if (of_property_read_string(node, "label", 157 &psr_attrs[i].attr.attr.name)) 158 goto out_kobj; 159 psr_attrs[i].attr.attr.mode = 0664; 160 psr_attrs[i].attr.show = psr_show; 161 psr_attrs[i].attr.store = psr_store; 162 if (sysfs_create_file(psr_kobj, &psr_attrs[i].attr.attr)) { 163 pr_devel("Failed to create psr sysfs file %s\n", 164 psr_attrs[i].attr.attr.name); 165 goto out_kobj; 166 } 167 i++; 168 } 169 170 return; 171 out_kobj: 172 kobject_put(psr_kobj); 173 out: 174 kfree(psr_attrs); 175 } 176