1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PowerNV OPAL Powercap interface 4 * 5 * Copyright 2017 IBM Corp. 6 */ 7 8 #define pr_fmt(fmt) "opal-powercap: " fmt 9 10 #include <linux/of.h> 11 #include <linux/kobject.h> 12 #include <linux/slab.h> 13 #include <linux/sysfs.h> 14 15 #include <asm/opal.h> 16 17 static DEFINE_MUTEX(powercap_mutex); 18 19 static struct kobject *powercap_kobj; 20 21 struct powercap_attr { 22 u32 handle; 23 struct kobj_attribute attr; 24 }; 25 26 static struct pcap { 27 struct attribute_group pg; 28 struct powercap_attr *pattrs; 29 } *pcaps; 30 31 static ssize_t powercap_show(struct kobject *kobj, struct kobj_attribute *attr, 32 char *buf) 33 { 34 struct powercap_attr *pcap_attr = container_of(attr, 35 struct powercap_attr, attr); 36 struct opal_msg msg; 37 u32 pcap; 38 int ret, token; 39 40 token = opal_async_get_token_interruptible(); 41 if (token < 0) { 42 pr_devel("Failed to get token\n"); 43 return token; 44 } 45 46 ret = mutex_lock_interruptible(&powercap_mutex); 47 if (ret) 48 goto out_token; 49 50 ret = opal_get_powercap(pcap_attr->handle, token, (u32 *)__pa(&pcap)); 51 switch (ret) { 52 case OPAL_ASYNC_COMPLETION: 53 ret = opal_async_wait_response(token, &msg); 54 if (ret) { 55 pr_devel("Failed to wait for the async response\n"); 56 ret = -EIO; 57 goto out; 58 } 59 ret = opal_error_code(opal_get_async_rc(msg)); 60 if (!ret) 61 ret = sysfs_emit(buf, "%u\n", be32_to_cpu(pcap)); 62 break; 63 case OPAL_SUCCESS: 64 ret = sysfs_emit(buf, "%u\n", be32_to_cpu(pcap)); 65 break; 66 default: 67 ret = opal_error_code(ret); 68 } 69 70 out: 71 mutex_unlock(&powercap_mutex); 72 out_token: 73 opal_async_release_token(token); 74 return ret; 75 } 76 77 static ssize_t powercap_store(struct kobject *kobj, 78 struct kobj_attribute *attr, const char *buf, 79 size_t count) 80 { 81 struct powercap_attr *pcap_attr = container_of(attr, 82 struct powercap_attr, attr); 83 struct opal_msg msg; 84 u32 pcap; 85 int ret, token; 86 87 ret = kstrtoint(buf, 0, &pcap); 88 if (ret) 89 return ret; 90 91 token = opal_async_get_token_interruptible(); 92 if (token < 0) { 93 pr_devel("Failed to get token\n"); 94 return token; 95 } 96 97 ret = mutex_lock_interruptible(&powercap_mutex); 98 if (ret) 99 goto out_token; 100 101 ret = opal_set_powercap(pcap_attr->handle, token, pcap); 102 switch (ret) { 103 case OPAL_ASYNC_COMPLETION: 104 ret = opal_async_wait_response(token, &msg); 105 if (ret) { 106 pr_devel("Failed to wait for the async response\n"); 107 ret = -EIO; 108 goto out; 109 } 110 ret = opal_error_code(opal_get_async_rc(msg)); 111 if (!ret) 112 ret = count; 113 break; 114 case OPAL_SUCCESS: 115 ret = count; 116 break; 117 default: 118 ret = opal_error_code(ret); 119 } 120 121 out: 122 mutex_unlock(&powercap_mutex); 123 out_token: 124 opal_async_release_token(token); 125 return ret; 126 } 127 128 static void __init powercap_add_attr(int handle, const char *name, 129 struct powercap_attr *attr) 130 { 131 attr->handle = handle; 132 sysfs_attr_init(&attr->attr.attr); 133 attr->attr.attr.name = name; 134 attr->attr.attr.mode = 0444; 135 attr->attr.show = powercap_show; 136 } 137 138 void __init opal_powercap_init(void) 139 { 140 struct device_node *powercap, *node; 141 int i = 0; 142 143 powercap = of_find_compatible_node(NULL, NULL, "ibm,opal-powercap"); 144 if (!powercap) { 145 pr_devel("Powercap node not found\n"); 146 return; 147 } 148 149 pcaps = kzalloc_objs(*pcaps, of_get_child_count(powercap)); 150 if (!pcaps) 151 goto out_put_powercap; 152 153 powercap_kobj = kobject_create_and_add("powercap", opal_kobj); 154 if (!powercap_kobj) { 155 pr_warn("Failed to create powercap kobject\n"); 156 goto out_pcaps; 157 } 158 159 i = 0; 160 for_each_child_of_node(powercap, node) { 161 u32 cur, min, max; 162 int j = 0; 163 bool has_cur = false, has_min = false, has_max = false; 164 165 if (!of_property_read_u32(node, "powercap-min", &min)) { 166 j++; 167 has_min = true; 168 } 169 170 if (!of_property_read_u32(node, "powercap-max", &max)) { 171 j++; 172 has_max = true; 173 } 174 175 if (!of_property_read_u32(node, "powercap-current", &cur)) { 176 j++; 177 has_cur = true; 178 } 179 180 pcaps[i].pattrs = kzalloc_objs(struct powercap_attr, j); 181 if (!pcaps[i].pattrs) 182 goto out_pcaps_pattrs; 183 184 pcaps[i].pg.attrs = kzalloc_objs(struct attribute *, j + 1); 185 if (!pcaps[i].pg.attrs) { 186 kfree(pcaps[i].pattrs); 187 goto out_pcaps_pattrs; 188 } 189 190 j = 0; 191 pcaps[i].pg.name = kasprintf(GFP_KERNEL, "%pOFn", node); 192 if (!pcaps[i].pg.name) { 193 kfree(pcaps[i].pattrs); 194 kfree(pcaps[i].pg.attrs); 195 goto out_pcaps_pattrs; 196 } 197 198 if (has_min) { 199 powercap_add_attr(min, "powercap-min", 200 &pcaps[i].pattrs[j]); 201 pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr; 202 j++; 203 } 204 205 if (has_max) { 206 powercap_add_attr(max, "powercap-max", 207 &pcaps[i].pattrs[j]); 208 pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr; 209 j++; 210 } 211 212 if (has_cur) { 213 powercap_add_attr(cur, "powercap-current", 214 &pcaps[i].pattrs[j]); 215 pcaps[i].pattrs[j].attr.attr.mode |= 0220; 216 pcaps[i].pattrs[j].attr.store = powercap_store; 217 pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr; 218 j++; 219 } 220 221 if (sysfs_create_group(powercap_kobj, &pcaps[i].pg)) { 222 pr_warn("Failed to create powercap attribute group %s\n", 223 pcaps[i].pg.name); 224 goto out_pcaps_pattrs; 225 } 226 i++; 227 } 228 of_node_put(powercap); 229 230 return; 231 232 out_pcaps_pattrs: 233 while (--i >= 0) { 234 kfree(pcaps[i].pattrs); 235 kfree(pcaps[i].pg.attrs); 236 kfree(pcaps[i].pg.name); 237 } 238 kobject_put(powercap_kobj); 239 of_node_put(node); 240 out_pcaps: 241 kfree(pcaps); 242 out_put_powercap: 243 of_node_put(powercap); 244 } 245