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
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 __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
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		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
239out_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);
247out_pcaps:
248	kfree(pcaps);
249out_put_powercap:
250	of_node_put(powercap);
251}
252