linux/arch/powerpc/platforms/powernv/opal-psr.c
<<
>>
Prefs
   1/*
   2 * PowerNV OPAL Power-Shift-Ratio 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-psr: " fmt
  13
  14#include <linux/of.h>
  15#include <linux/kobject.h>
  16#include <linux/slab.h>
  17
  18#include <asm/opal.h>
  19
  20DEFINE_MUTEX(psr_mutex);
  21
  22static struct kobject *psr_kobj;
  23
  24struct psr_attr {
  25        u32 handle;
  26        struct kobj_attribute attr;
  27} *psr_attrs;
  28
  29static ssize_t psr_show(struct kobject *kobj, struct kobj_attribute *attr,
  30                        char *buf)
  31{
  32        struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr);
  33        struct opal_msg msg;
  34        int psr, ret, token;
  35
  36        token = opal_async_get_token_interruptible();
  37        if (token < 0) {
  38                pr_devel("Failed to get token\n");
  39                return token;
  40        }
  41
  42        ret = mutex_lock_interruptible(&psr_mutex);
  43        if (ret)
  44                goto out_token;
  45
  46        ret = opal_get_power_shift_ratio(psr_attr->handle, token,
  47                                            (u32 *)__pa(&psr));
  48        switch (ret) {
  49        case OPAL_ASYNC_COMPLETION:
  50                ret = opal_async_wait_response(token, &msg);
  51                if (ret) {
  52                        pr_devel("Failed to wait for the async response\n");
  53                        ret = -EIO;
  54                        goto out;
  55                }
  56                ret = opal_error_code(opal_get_async_rc(msg));
  57                if (!ret) {
  58                        ret = sprintf(buf, "%u\n", be32_to_cpu(psr));
  59                        if (ret < 0)
  60                                ret = -EIO;
  61                }
  62                break;
  63        case OPAL_SUCCESS:
  64                ret = sprintf(buf, "%u\n", be32_to_cpu(psr));
  65                if (ret < 0)
  66                        ret = -EIO;
  67                break;
  68        default:
  69                ret = opal_error_code(ret);
  70        }
  71
  72out:
  73        mutex_unlock(&psr_mutex);
  74out_token:
  75        opal_async_release_token(token);
  76        return ret;
  77}
  78
  79static ssize_t psr_store(struct kobject *kobj, struct kobj_attribute *attr,
  80                         const char *buf, size_t count)
  81{
  82        struct psr_attr *psr_attr = container_of(attr, struct psr_attr, attr);
  83        struct opal_msg msg;
  84        int psr, ret, token;
  85
  86        ret = kstrtoint(buf, 0, &psr);
  87        if (ret)
  88                return ret;
  89
  90        token = opal_async_get_token_interruptible();
  91        if (token < 0) {
  92                pr_devel("Failed to get token\n");
  93                return token;
  94        }
  95
  96        ret = mutex_lock_interruptible(&psr_mutex);
  97        if (ret)
  98                goto out_token;
  99
 100        ret = opal_set_power_shift_ratio(psr_attr->handle, token, psr);
 101        switch (ret) {
 102        case OPAL_ASYNC_COMPLETION:
 103                ret = opal_async_wait_response(token, &msg);
 104                if (ret) {
 105                        pr_devel("Failed to wait for the async response\n");
 106                        ret = -EIO;
 107                        goto out;
 108                }
 109                ret = opal_error_code(opal_get_async_rc(msg));
 110                if (!ret)
 111                        ret = count;
 112                break;
 113        case OPAL_SUCCESS:
 114                ret = count;
 115                break;
 116        default:
 117                ret = opal_error_code(ret);
 118        }
 119
 120out:
 121        mutex_unlock(&psr_mutex);
 122out_token:
 123        opal_async_release_token(token);
 124        return ret;
 125}
 126
 127void __init opal_psr_init(void)
 128{
 129        struct device_node *psr, *node;
 130        int i = 0;
 131
 132        psr = of_find_compatible_node(NULL, NULL,
 133                                      "ibm,opal-power-shift-ratio");
 134        if (!psr) {
 135                pr_devel("Power-shift-ratio node not found\n");
 136                return;
 137        }
 138
 139        psr_attrs = kcalloc(of_get_child_count(psr), sizeof(*psr_attrs),
 140                            GFP_KERNEL);
 141        if (!psr_attrs)
 142                return;
 143
 144        psr_kobj = kobject_create_and_add("psr", opal_kobj);
 145        if (!psr_kobj) {
 146                pr_warn("Failed to create psr kobject\n");
 147                goto out;
 148        }
 149
 150        for_each_child_of_node(psr, node) {
 151                if (of_property_read_u32(node, "handle",
 152                                         &psr_attrs[i].handle))
 153                        goto out_kobj;
 154
 155                sysfs_attr_init(&psr_attrs[i].attr.attr);
 156                if (of_property_read_string(node, "label",
 157                                            &psr_attrs[i].attr.attr.name))
 158                        goto out_kobj;
 159                psr_attrs[i].attr.attr.mode = 0664;
 160                psr_attrs[i].attr.show = psr_show;
 161                psr_attrs[i].attr.store = psr_store;
 162                if (sysfs_create_file(psr_kobj, &psr_attrs[i].attr.attr)) {
 163                        pr_devel("Failed to create psr sysfs file %s\n",
 164                                 psr_attrs[i].attr.attr.name);
 165                        goto out_kobj;
 166                }
 167                i++;
 168        }
 169
 170        return;
 171out_kobj:
 172        kobject_put(psr_kobj);
 173out:
 174        kfree(psr_attrs);
 175}
 176