Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powernv/opal-powercap.c
26481 views
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
16
static DEFINE_MUTEX(powercap_mutex);
17
18
static struct kobject *powercap_kobj;
19
20
struct powercap_attr {
21
u32 handle;
22
struct kobj_attribute attr;
23
};
24
25
static struct pcap {
26
struct attribute_group pg;
27
struct powercap_attr *pattrs;
28
} *pcaps;
29
30
static 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
74
out:
75
mutex_unlock(&powercap_mutex);
76
out_token:
77
opal_async_release_token(token);
78
return ret;
79
}
80
81
static 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
125
out:
126
mutex_unlock(&powercap_mutex);
127
out_token:
128
opal_async_release_token(token);
129
return ret;
130
}
131
132
static void __init 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
142
void __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
goto out_put_powercap;
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 (!pcaps[i].pg.name) {
200
kfree(pcaps[i].pattrs);
201
kfree(pcaps[i].pg.attrs);
202
goto out_pcaps_pattrs;
203
}
204
205
if (has_min) {
206
powercap_add_attr(min, "powercap-min",
207
&pcaps[i].pattrs[j]);
208
pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr;
209
j++;
210
}
211
212
if (has_max) {
213
powercap_add_attr(max, "powercap-max",
214
&pcaps[i].pattrs[j]);
215
pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr;
216
j++;
217
}
218
219
if (has_cur) {
220
powercap_add_attr(cur, "powercap-current",
221
&pcaps[i].pattrs[j]);
222
pcaps[i].pattrs[j].attr.attr.mode |= 0220;
223
pcaps[i].pattrs[j].attr.store = powercap_store;
224
pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr;
225
j++;
226
}
227
228
if (sysfs_create_group(powercap_kobj, &pcaps[i].pg)) {
229
pr_warn("Failed to create powercap attribute group %s\n",
230
pcaps[i].pg.name);
231
goto out_pcaps_pattrs;
232
}
233
i++;
234
}
235
of_node_put(powercap);
236
237
return;
238
239
out_pcaps_pattrs:
240
while (--i >= 0) {
241
kfree(pcaps[i].pattrs);
242
kfree(pcaps[i].pg.attrs);
243
kfree(pcaps[i].pg.name);
244
}
245
kobject_put(powercap_kobj);
246
of_node_put(node);
247
out_pcaps:
248
kfree(pcaps);
249
out_put_powercap:
250
of_node_put(powercap);
251
}
252
253