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