File Index
Files and directories
Sign in
FILE

opal-powercap.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/powernv/opal-powercap.c C
  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}
~/ Read-only file preview.