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
powercap_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)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
powercap_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)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
powercap_add_attr(int handle,const char * name,struct powercap_attr * attr)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
opal_powercap_init(void)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 = kzalloc_objs(*pcaps, of_get_child_count(powercap));
154 if (!pcaps)
155 goto out_put_powercap;
156
157 powercap_kobj = kobject_create_and_add("powercap", opal_kobj);
158 if (!powercap_kobj) {
159 pr_warn("Failed to create powercap kobject\n");
160 goto out_pcaps;
161 }
162
163 i = 0;
164 for_each_child_of_node(powercap, node) {
165 u32 cur, min, max;
166 int j = 0;
167 bool has_cur = false, has_min = false, has_max = false;
168
169 if (!of_property_read_u32(node, "powercap-min", &min)) {
170 j++;
171 has_min = true;
172 }
173
174 if (!of_property_read_u32(node, "powercap-max", &max)) {
175 j++;
176 has_max = true;
177 }
178
179 if (!of_property_read_u32(node, "powercap-current", &cur)) {
180 j++;
181 has_cur = true;
182 }
183
184 pcaps[i].pattrs = kzalloc_objs(struct powercap_attr, j);
185 if (!pcaps[i].pattrs)
186 goto out_pcaps_pattrs;
187
188 pcaps[i].pg.attrs = kzalloc_objs(struct attribute *, j + 1);
189 if (!pcaps[i].pg.attrs) {
190 kfree(pcaps[i].pattrs);
191 goto out_pcaps_pattrs;
192 }
193
194 j = 0;
195 pcaps[i].pg.name = kasprintf(GFP_KERNEL, "%pOFn", node);
196 if (!pcaps[i].pg.name) {
197 kfree(pcaps[i].pattrs);
198 kfree(pcaps[i].pg.attrs);
199 goto out_pcaps_pattrs;
200 }
201
202 if (has_min) {
203 powercap_add_attr(min, "powercap-min",
204 &pcaps[i].pattrs[j]);
205 pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr;
206 j++;
207 }
208
209 if (has_max) {
210 powercap_add_attr(max, "powercap-max",
211 &pcaps[i].pattrs[j]);
212 pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr;
213 j++;
214 }
215
216 if (has_cur) {
217 powercap_add_attr(cur, "powercap-current",
218 &pcaps[i].pattrs[j]);
219 pcaps[i].pattrs[j].attr.attr.mode |= 0220;
220 pcaps[i].pattrs[j].attr.store = powercap_store;
221 pcaps[i].pg.attrs[j] = &pcaps[i].pattrs[j].attr.attr;
222 j++;
223 }
224
225 if (sysfs_create_group(powercap_kobj, &pcaps[i].pg)) {
226 pr_warn("Failed to create powercap attribute group %s\n",
227 pcaps[i].pg.name);
228 goto out_pcaps_pattrs;
229 }
230 i++;
231 }
232 of_node_put(powercap);
233
234 return;
235
236 out_pcaps_pattrs:
237 while (--i >= 0) {
238 kfree(pcaps[i].pattrs);
239 kfree(pcaps[i].pg.attrs);
240 kfree(pcaps[i].pg.name);
241 }
242 kobject_put(powercap_kobj);
243 of_node_put(node);
244 out_pcaps:
245 kfree(pcaps);
246 out_put_powercap:
247 of_node_put(powercap);
248 }
249