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