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