1
2
3
4
5
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
16static DEFINE_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