GNU Linux-libre 4.14.328-gnu1
[releases.git] / arch / powerpc / platforms / powernv / opal-powercap.c
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 = node->name;
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         }
241         kobject_put(powercap_kobj);
242 out_pcaps:
243         kfree(pcaps);
244 }