linux/arch/powerpc/platforms/powernv/opal-powercap.c
<<
>>
Prefs
   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
  16DEFINE_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 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}
 242