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