1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Power capping class
4*4882a593Smuzhiyun * Copyright (c) 2013, Intel Corporation.
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/device.h>
9*4882a593Smuzhiyun #include <linux/err.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/powercap.h>
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #define to_powercap_zone(n) container_of(n, struct powercap_zone, dev)
14*4882a593Smuzhiyun #define to_powercap_control_type(n) \
15*4882a593Smuzhiyun container_of(n, struct powercap_control_type, dev)
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun /* Power zone show function */
18*4882a593Smuzhiyun #define define_power_zone_show(_attr) \
19*4882a593Smuzhiyun static ssize_t _attr##_show(struct device *dev, \
20*4882a593Smuzhiyun struct device_attribute *dev_attr,\
21*4882a593Smuzhiyun char *buf) \
22*4882a593Smuzhiyun { \
23*4882a593Smuzhiyun u64 value; \
24*4882a593Smuzhiyun ssize_t len = -EINVAL; \
25*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev); \
26*4882a593Smuzhiyun \
27*4882a593Smuzhiyun if (power_zone->ops->get_##_attr) { \
28*4882a593Smuzhiyun if (!power_zone->ops->get_##_attr(power_zone, &value)) \
29*4882a593Smuzhiyun len = sprintf(buf, "%lld\n", value); \
30*4882a593Smuzhiyun } \
31*4882a593Smuzhiyun \
32*4882a593Smuzhiyun return len; \
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /* The only meaningful input is 0 (reset), others are silently ignored */
36*4882a593Smuzhiyun #define define_power_zone_store(_attr) \
37*4882a593Smuzhiyun static ssize_t _attr##_store(struct device *dev,\
38*4882a593Smuzhiyun struct device_attribute *dev_attr, \
39*4882a593Smuzhiyun const char *buf, size_t count) \
40*4882a593Smuzhiyun { \
41*4882a593Smuzhiyun int err; \
42*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev); \
43*4882a593Smuzhiyun u64 value; \
44*4882a593Smuzhiyun \
45*4882a593Smuzhiyun err = kstrtoull(buf, 10, &value); \
46*4882a593Smuzhiyun if (err) \
47*4882a593Smuzhiyun return -EINVAL; \
48*4882a593Smuzhiyun if (value) \
49*4882a593Smuzhiyun return count; \
50*4882a593Smuzhiyun if (power_zone->ops->reset_##_attr) { \
51*4882a593Smuzhiyun if (!power_zone->ops->reset_##_attr(power_zone)) \
52*4882a593Smuzhiyun return count; \
53*4882a593Smuzhiyun } \
54*4882a593Smuzhiyun \
55*4882a593Smuzhiyun return -EINVAL; \
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun /* Power zone constraint show function */
59*4882a593Smuzhiyun #define define_power_zone_constraint_show(_attr) \
60*4882a593Smuzhiyun static ssize_t show_constraint_##_attr(struct device *dev, \
61*4882a593Smuzhiyun struct device_attribute *dev_attr,\
62*4882a593Smuzhiyun char *buf) \
63*4882a593Smuzhiyun { \
64*4882a593Smuzhiyun u64 value; \
65*4882a593Smuzhiyun ssize_t len = -ENODATA; \
66*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev); \
67*4882a593Smuzhiyun int id; \
68*4882a593Smuzhiyun struct powercap_zone_constraint *pconst;\
69*4882a593Smuzhiyun \
70*4882a593Smuzhiyun if (!sscanf(dev_attr->attr.name, "constraint_%d_", &id)) \
71*4882a593Smuzhiyun return -EINVAL; \
72*4882a593Smuzhiyun if (id >= power_zone->const_id_cnt) \
73*4882a593Smuzhiyun return -EINVAL; \
74*4882a593Smuzhiyun pconst = &power_zone->constraints[id]; \
75*4882a593Smuzhiyun if (pconst && pconst->ops && pconst->ops->get_##_attr) { \
76*4882a593Smuzhiyun if (!pconst->ops->get_##_attr(power_zone, id, &value)) \
77*4882a593Smuzhiyun len = sprintf(buf, "%lld\n", value); \
78*4882a593Smuzhiyun } \
79*4882a593Smuzhiyun \
80*4882a593Smuzhiyun return len; \
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* Power zone constraint store function */
84*4882a593Smuzhiyun #define define_power_zone_constraint_store(_attr) \
85*4882a593Smuzhiyun static ssize_t store_constraint_##_attr(struct device *dev,\
86*4882a593Smuzhiyun struct device_attribute *dev_attr, \
87*4882a593Smuzhiyun const char *buf, size_t count) \
88*4882a593Smuzhiyun { \
89*4882a593Smuzhiyun int err; \
90*4882a593Smuzhiyun u64 value; \
91*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev); \
92*4882a593Smuzhiyun int id; \
93*4882a593Smuzhiyun struct powercap_zone_constraint *pconst;\
94*4882a593Smuzhiyun \
95*4882a593Smuzhiyun if (!sscanf(dev_attr->attr.name, "constraint_%d_", &id)) \
96*4882a593Smuzhiyun return -EINVAL; \
97*4882a593Smuzhiyun if (id >= power_zone->const_id_cnt) \
98*4882a593Smuzhiyun return -EINVAL; \
99*4882a593Smuzhiyun pconst = &power_zone->constraints[id]; \
100*4882a593Smuzhiyun err = kstrtoull(buf, 10, &value); \
101*4882a593Smuzhiyun if (err) \
102*4882a593Smuzhiyun return -EINVAL; \
103*4882a593Smuzhiyun if (pconst && pconst->ops && pconst->ops->set_##_attr) { \
104*4882a593Smuzhiyun if (!pconst->ops->set_##_attr(power_zone, id, value)) \
105*4882a593Smuzhiyun return count; \
106*4882a593Smuzhiyun } \
107*4882a593Smuzhiyun \
108*4882a593Smuzhiyun return -ENODATA; \
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun /* Power zone information callbacks */
112*4882a593Smuzhiyun define_power_zone_show(power_uw);
113*4882a593Smuzhiyun define_power_zone_show(max_power_range_uw);
114*4882a593Smuzhiyun define_power_zone_show(energy_uj);
115*4882a593Smuzhiyun define_power_zone_store(energy_uj);
116*4882a593Smuzhiyun define_power_zone_show(max_energy_range_uj);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun /* Power zone attributes */
119*4882a593Smuzhiyun static DEVICE_ATTR_RO(max_power_range_uw);
120*4882a593Smuzhiyun static DEVICE_ATTR_RO(power_uw);
121*4882a593Smuzhiyun static DEVICE_ATTR_RO(max_energy_range_uj);
122*4882a593Smuzhiyun static DEVICE_ATTR_RW(energy_uj);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun /* Power zone constraint attributes callbacks */
125*4882a593Smuzhiyun define_power_zone_constraint_show(power_limit_uw);
126*4882a593Smuzhiyun define_power_zone_constraint_store(power_limit_uw);
127*4882a593Smuzhiyun define_power_zone_constraint_show(time_window_us);
128*4882a593Smuzhiyun define_power_zone_constraint_store(time_window_us);
129*4882a593Smuzhiyun define_power_zone_constraint_show(max_power_uw);
130*4882a593Smuzhiyun define_power_zone_constraint_show(min_power_uw);
131*4882a593Smuzhiyun define_power_zone_constraint_show(max_time_window_us);
132*4882a593Smuzhiyun define_power_zone_constraint_show(min_time_window_us);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun /* For one time seeding of constraint device attributes */
135*4882a593Smuzhiyun struct powercap_constraint_attr {
136*4882a593Smuzhiyun struct device_attribute power_limit_attr;
137*4882a593Smuzhiyun struct device_attribute time_window_attr;
138*4882a593Smuzhiyun struct device_attribute max_power_attr;
139*4882a593Smuzhiyun struct device_attribute min_power_attr;
140*4882a593Smuzhiyun struct device_attribute max_time_window_attr;
141*4882a593Smuzhiyun struct device_attribute min_time_window_attr;
142*4882a593Smuzhiyun struct device_attribute name_attr;
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun static struct powercap_constraint_attr
146*4882a593Smuzhiyun constraint_attrs[MAX_CONSTRAINTS_PER_ZONE];
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun /* A list of powercap control_types */
149*4882a593Smuzhiyun static LIST_HEAD(powercap_cntrl_list);
150*4882a593Smuzhiyun /* Mutex to protect list of powercap control_types */
151*4882a593Smuzhiyun static DEFINE_MUTEX(powercap_cntrl_list_lock);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun #define POWERCAP_CONSTRAINT_NAME_LEN 30 /* Some limit to avoid overflow */
show_constraint_name(struct device * dev,struct device_attribute * dev_attr,char * buf)154*4882a593Smuzhiyun static ssize_t show_constraint_name(struct device *dev,
155*4882a593Smuzhiyun struct device_attribute *dev_attr,
156*4882a593Smuzhiyun char *buf)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun const char *name;
159*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev);
160*4882a593Smuzhiyun int id;
161*4882a593Smuzhiyun ssize_t len = -ENODATA;
162*4882a593Smuzhiyun struct powercap_zone_constraint *pconst;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun if (!sscanf(dev_attr->attr.name, "constraint_%d_", &id))
165*4882a593Smuzhiyun return -EINVAL;
166*4882a593Smuzhiyun if (id >= power_zone->const_id_cnt)
167*4882a593Smuzhiyun return -EINVAL;
168*4882a593Smuzhiyun pconst = &power_zone->constraints[id];
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun if (pconst && pconst->ops && pconst->ops->get_name) {
171*4882a593Smuzhiyun name = pconst->ops->get_name(power_zone, id);
172*4882a593Smuzhiyun if (name) {
173*4882a593Smuzhiyun snprintf(buf, POWERCAP_CONSTRAINT_NAME_LEN,
174*4882a593Smuzhiyun "%s\n", name);
175*4882a593Smuzhiyun buf[POWERCAP_CONSTRAINT_NAME_LEN] = '\0';
176*4882a593Smuzhiyun len = strlen(buf);
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun return len;
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
create_constraint_attribute(int id,const char * name,int mode,struct device_attribute * dev_attr,ssize_t (* show)(struct device *,struct device_attribute *,char *),ssize_t (* store)(struct device *,struct device_attribute *,const char *,size_t))183*4882a593Smuzhiyun static int create_constraint_attribute(int id, const char *name,
184*4882a593Smuzhiyun int mode,
185*4882a593Smuzhiyun struct device_attribute *dev_attr,
186*4882a593Smuzhiyun ssize_t (*show)(struct device *,
187*4882a593Smuzhiyun struct device_attribute *, char *),
188*4882a593Smuzhiyun ssize_t (*store)(struct device *,
189*4882a593Smuzhiyun struct device_attribute *,
190*4882a593Smuzhiyun const char *, size_t)
191*4882a593Smuzhiyun )
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun dev_attr->attr.name = kasprintf(GFP_KERNEL, "constraint_%d_%s",
195*4882a593Smuzhiyun id, name);
196*4882a593Smuzhiyun if (!dev_attr->attr.name)
197*4882a593Smuzhiyun return -ENOMEM;
198*4882a593Smuzhiyun dev_attr->attr.mode = mode;
199*4882a593Smuzhiyun dev_attr->show = show;
200*4882a593Smuzhiyun dev_attr->store = store;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun return 0;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
free_constraint_attributes(void)205*4882a593Smuzhiyun static void free_constraint_attributes(void)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun int i;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun for (i = 0; i < MAX_CONSTRAINTS_PER_ZONE; ++i) {
210*4882a593Smuzhiyun kfree(constraint_attrs[i].power_limit_attr.attr.name);
211*4882a593Smuzhiyun kfree(constraint_attrs[i].time_window_attr.attr.name);
212*4882a593Smuzhiyun kfree(constraint_attrs[i].name_attr.attr.name);
213*4882a593Smuzhiyun kfree(constraint_attrs[i].max_power_attr.attr.name);
214*4882a593Smuzhiyun kfree(constraint_attrs[i].min_power_attr.attr.name);
215*4882a593Smuzhiyun kfree(constraint_attrs[i].max_time_window_attr.attr.name);
216*4882a593Smuzhiyun kfree(constraint_attrs[i].min_time_window_attr.attr.name);
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
seed_constraint_attributes(void)220*4882a593Smuzhiyun static int seed_constraint_attributes(void)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun int i;
223*4882a593Smuzhiyun int ret;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun for (i = 0; i < MAX_CONSTRAINTS_PER_ZONE; ++i) {
226*4882a593Smuzhiyun ret = create_constraint_attribute(i, "power_limit_uw",
227*4882a593Smuzhiyun S_IWUSR | S_IRUGO,
228*4882a593Smuzhiyun &constraint_attrs[i].power_limit_attr,
229*4882a593Smuzhiyun show_constraint_power_limit_uw,
230*4882a593Smuzhiyun store_constraint_power_limit_uw);
231*4882a593Smuzhiyun if (ret)
232*4882a593Smuzhiyun goto err_alloc;
233*4882a593Smuzhiyun ret = create_constraint_attribute(i, "time_window_us",
234*4882a593Smuzhiyun S_IWUSR | S_IRUGO,
235*4882a593Smuzhiyun &constraint_attrs[i].time_window_attr,
236*4882a593Smuzhiyun show_constraint_time_window_us,
237*4882a593Smuzhiyun store_constraint_time_window_us);
238*4882a593Smuzhiyun if (ret)
239*4882a593Smuzhiyun goto err_alloc;
240*4882a593Smuzhiyun ret = create_constraint_attribute(i, "name", S_IRUGO,
241*4882a593Smuzhiyun &constraint_attrs[i].name_attr,
242*4882a593Smuzhiyun show_constraint_name,
243*4882a593Smuzhiyun NULL);
244*4882a593Smuzhiyun if (ret)
245*4882a593Smuzhiyun goto err_alloc;
246*4882a593Smuzhiyun ret = create_constraint_attribute(i, "max_power_uw", S_IRUGO,
247*4882a593Smuzhiyun &constraint_attrs[i].max_power_attr,
248*4882a593Smuzhiyun show_constraint_max_power_uw,
249*4882a593Smuzhiyun NULL);
250*4882a593Smuzhiyun if (ret)
251*4882a593Smuzhiyun goto err_alloc;
252*4882a593Smuzhiyun ret = create_constraint_attribute(i, "min_power_uw", S_IRUGO,
253*4882a593Smuzhiyun &constraint_attrs[i].min_power_attr,
254*4882a593Smuzhiyun show_constraint_min_power_uw,
255*4882a593Smuzhiyun NULL);
256*4882a593Smuzhiyun if (ret)
257*4882a593Smuzhiyun goto err_alloc;
258*4882a593Smuzhiyun ret = create_constraint_attribute(i, "max_time_window_us",
259*4882a593Smuzhiyun S_IRUGO,
260*4882a593Smuzhiyun &constraint_attrs[i].max_time_window_attr,
261*4882a593Smuzhiyun show_constraint_max_time_window_us,
262*4882a593Smuzhiyun NULL);
263*4882a593Smuzhiyun if (ret)
264*4882a593Smuzhiyun goto err_alloc;
265*4882a593Smuzhiyun ret = create_constraint_attribute(i, "min_time_window_us",
266*4882a593Smuzhiyun S_IRUGO,
267*4882a593Smuzhiyun &constraint_attrs[i].min_time_window_attr,
268*4882a593Smuzhiyun show_constraint_min_time_window_us,
269*4882a593Smuzhiyun NULL);
270*4882a593Smuzhiyun if (ret)
271*4882a593Smuzhiyun goto err_alloc;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun return 0;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun err_alloc:
278*4882a593Smuzhiyun free_constraint_attributes();
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun return ret;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
create_constraints(struct powercap_zone * power_zone,int nr_constraints,const struct powercap_zone_constraint_ops * const_ops)283*4882a593Smuzhiyun static int create_constraints(struct powercap_zone *power_zone,
284*4882a593Smuzhiyun int nr_constraints,
285*4882a593Smuzhiyun const struct powercap_zone_constraint_ops *const_ops)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun int i;
288*4882a593Smuzhiyun int ret = 0;
289*4882a593Smuzhiyun int count;
290*4882a593Smuzhiyun struct powercap_zone_constraint *pconst;
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun if (!power_zone || !const_ops || !const_ops->get_power_limit_uw ||
293*4882a593Smuzhiyun !const_ops->set_power_limit_uw ||
294*4882a593Smuzhiyun !const_ops->get_time_window_us ||
295*4882a593Smuzhiyun !const_ops->set_time_window_us)
296*4882a593Smuzhiyun return -EINVAL;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun count = power_zone->zone_attr_count;
299*4882a593Smuzhiyun for (i = 0; i < nr_constraints; ++i) {
300*4882a593Smuzhiyun pconst = &power_zone->constraints[i];
301*4882a593Smuzhiyun pconst->ops = const_ops;
302*4882a593Smuzhiyun pconst->id = power_zone->const_id_cnt;
303*4882a593Smuzhiyun power_zone->const_id_cnt++;
304*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
305*4882a593Smuzhiyun &constraint_attrs[i].power_limit_attr.attr;
306*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
307*4882a593Smuzhiyun &constraint_attrs[i].time_window_attr.attr;
308*4882a593Smuzhiyun if (pconst->ops->get_name)
309*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
310*4882a593Smuzhiyun &constraint_attrs[i].name_attr.attr;
311*4882a593Smuzhiyun if (pconst->ops->get_max_power_uw)
312*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
313*4882a593Smuzhiyun &constraint_attrs[i].max_power_attr.attr;
314*4882a593Smuzhiyun if (pconst->ops->get_min_power_uw)
315*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
316*4882a593Smuzhiyun &constraint_attrs[i].min_power_attr.attr;
317*4882a593Smuzhiyun if (pconst->ops->get_max_time_window_us)
318*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
319*4882a593Smuzhiyun &constraint_attrs[i].max_time_window_attr.attr;
320*4882a593Smuzhiyun if (pconst->ops->get_min_time_window_us)
321*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
322*4882a593Smuzhiyun &constraint_attrs[i].min_time_window_attr.attr;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun power_zone->zone_attr_count = count;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun return ret;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
control_type_valid(void * control_type)329*4882a593Smuzhiyun static bool control_type_valid(void *control_type)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun struct powercap_control_type *pos = NULL;
332*4882a593Smuzhiyun bool found = false;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun mutex_lock(&powercap_cntrl_list_lock);
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun list_for_each_entry(pos, &powercap_cntrl_list, node) {
337*4882a593Smuzhiyun if (pos == control_type) {
338*4882a593Smuzhiyun found = true;
339*4882a593Smuzhiyun break;
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun mutex_unlock(&powercap_cntrl_list_lock);
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun return found;
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun
name_show(struct device * dev,struct device_attribute * attr,char * buf)347*4882a593Smuzhiyun static ssize_t name_show(struct device *dev,
348*4882a593Smuzhiyun struct device_attribute *attr,
349*4882a593Smuzhiyun char *buf)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev);
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun return sprintf(buf, "%s\n", power_zone->name);
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun static DEVICE_ATTR_RO(name);
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun /* Create zone and attributes in sysfs */
create_power_zone_common_attributes(struct powercap_zone * power_zone)359*4882a593Smuzhiyun static void create_power_zone_common_attributes(
360*4882a593Smuzhiyun struct powercap_zone *power_zone)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun int count = 0;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] = &dev_attr_name.attr;
365*4882a593Smuzhiyun if (power_zone->ops->get_max_energy_range_uj)
366*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
367*4882a593Smuzhiyun &dev_attr_max_energy_range_uj.attr;
368*4882a593Smuzhiyun if (power_zone->ops->get_energy_uj) {
369*4882a593Smuzhiyun if (power_zone->ops->reset_energy_uj)
370*4882a593Smuzhiyun dev_attr_energy_uj.attr.mode = S_IWUSR | S_IRUSR;
371*4882a593Smuzhiyun else
372*4882a593Smuzhiyun dev_attr_energy_uj.attr.mode = S_IRUSR;
373*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
374*4882a593Smuzhiyun &dev_attr_energy_uj.attr;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun if (power_zone->ops->get_power_uw)
377*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
378*4882a593Smuzhiyun &dev_attr_power_uw.attr;
379*4882a593Smuzhiyun if (power_zone->ops->get_max_power_range_uw)
380*4882a593Smuzhiyun power_zone->zone_dev_attrs[count++] =
381*4882a593Smuzhiyun &dev_attr_max_power_range_uw.attr;
382*4882a593Smuzhiyun power_zone->zone_dev_attrs[count] = NULL;
383*4882a593Smuzhiyun power_zone->zone_attr_count = count;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun
powercap_release(struct device * dev)386*4882a593Smuzhiyun static void powercap_release(struct device *dev)
387*4882a593Smuzhiyun {
388*4882a593Smuzhiyun bool allocated;
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun if (dev->parent) {
391*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev);
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun /* Store flag as the release() may free memory */
394*4882a593Smuzhiyun allocated = power_zone->allocated;
395*4882a593Smuzhiyun /* Remove id from parent idr struct */
396*4882a593Smuzhiyun idr_remove(power_zone->parent_idr, power_zone->id);
397*4882a593Smuzhiyun /* Destroy idrs allocated for this zone */
398*4882a593Smuzhiyun idr_destroy(&power_zone->idr);
399*4882a593Smuzhiyun kfree(power_zone->name);
400*4882a593Smuzhiyun kfree(power_zone->zone_dev_attrs);
401*4882a593Smuzhiyun kfree(power_zone->constraints);
402*4882a593Smuzhiyun if (power_zone->ops->release)
403*4882a593Smuzhiyun power_zone->ops->release(power_zone);
404*4882a593Smuzhiyun if (allocated)
405*4882a593Smuzhiyun kfree(power_zone);
406*4882a593Smuzhiyun } else {
407*4882a593Smuzhiyun struct powercap_control_type *control_type =
408*4882a593Smuzhiyun to_powercap_control_type(dev);
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun /* Store flag as the release() may free memory */
411*4882a593Smuzhiyun allocated = control_type->allocated;
412*4882a593Smuzhiyun idr_destroy(&control_type->idr);
413*4882a593Smuzhiyun mutex_destroy(&control_type->lock);
414*4882a593Smuzhiyun if (control_type->ops && control_type->ops->release)
415*4882a593Smuzhiyun control_type->ops->release(control_type);
416*4882a593Smuzhiyun if (allocated)
417*4882a593Smuzhiyun kfree(control_type);
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
enabled_show(struct device * dev,struct device_attribute * attr,char * buf)421*4882a593Smuzhiyun static ssize_t enabled_show(struct device *dev,
422*4882a593Smuzhiyun struct device_attribute *attr,
423*4882a593Smuzhiyun char *buf)
424*4882a593Smuzhiyun {
425*4882a593Smuzhiyun bool mode = true;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun /* Default is enabled */
428*4882a593Smuzhiyun if (dev->parent) {
429*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev);
430*4882a593Smuzhiyun if (power_zone->ops->get_enable)
431*4882a593Smuzhiyun if (power_zone->ops->get_enable(power_zone, &mode))
432*4882a593Smuzhiyun mode = false;
433*4882a593Smuzhiyun } else {
434*4882a593Smuzhiyun struct powercap_control_type *control_type =
435*4882a593Smuzhiyun to_powercap_control_type(dev);
436*4882a593Smuzhiyun if (control_type->ops && control_type->ops->get_enable)
437*4882a593Smuzhiyun if (control_type->ops->get_enable(control_type, &mode))
438*4882a593Smuzhiyun mode = false;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun return sprintf(buf, "%d\n", mode);
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
enabled_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)444*4882a593Smuzhiyun static ssize_t enabled_store(struct device *dev,
445*4882a593Smuzhiyun struct device_attribute *attr,
446*4882a593Smuzhiyun const char *buf, size_t len)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun bool mode;
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun if (strtobool(buf, &mode))
451*4882a593Smuzhiyun return -EINVAL;
452*4882a593Smuzhiyun if (dev->parent) {
453*4882a593Smuzhiyun struct powercap_zone *power_zone = to_powercap_zone(dev);
454*4882a593Smuzhiyun if (power_zone->ops->set_enable)
455*4882a593Smuzhiyun if (!power_zone->ops->set_enable(power_zone, mode))
456*4882a593Smuzhiyun return len;
457*4882a593Smuzhiyun } else {
458*4882a593Smuzhiyun struct powercap_control_type *control_type =
459*4882a593Smuzhiyun to_powercap_control_type(dev);
460*4882a593Smuzhiyun if (control_type->ops && control_type->ops->set_enable)
461*4882a593Smuzhiyun if (!control_type->ops->set_enable(control_type, mode))
462*4882a593Smuzhiyun return len;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun return -ENOSYS;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun static DEVICE_ATTR_RW(enabled);
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun static struct attribute *powercap_attrs[] = {
471*4882a593Smuzhiyun &dev_attr_enabled.attr,
472*4882a593Smuzhiyun NULL,
473*4882a593Smuzhiyun };
474*4882a593Smuzhiyun ATTRIBUTE_GROUPS(powercap);
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun static struct class powercap_class = {
477*4882a593Smuzhiyun .name = "powercap",
478*4882a593Smuzhiyun .dev_release = powercap_release,
479*4882a593Smuzhiyun .dev_groups = powercap_groups,
480*4882a593Smuzhiyun };
481*4882a593Smuzhiyun
powercap_register_zone(struct powercap_zone * power_zone,struct powercap_control_type * control_type,const char * name,struct powercap_zone * parent,const struct powercap_zone_ops * ops,int nr_constraints,const struct powercap_zone_constraint_ops * const_ops)482*4882a593Smuzhiyun struct powercap_zone *powercap_register_zone(
483*4882a593Smuzhiyun struct powercap_zone *power_zone,
484*4882a593Smuzhiyun struct powercap_control_type *control_type,
485*4882a593Smuzhiyun const char *name,
486*4882a593Smuzhiyun struct powercap_zone *parent,
487*4882a593Smuzhiyun const struct powercap_zone_ops *ops,
488*4882a593Smuzhiyun int nr_constraints,
489*4882a593Smuzhiyun const struct powercap_zone_constraint_ops *const_ops)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun int result;
492*4882a593Smuzhiyun int nr_attrs;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun if (!name || !control_type || !ops ||
495*4882a593Smuzhiyun nr_constraints > MAX_CONSTRAINTS_PER_ZONE ||
496*4882a593Smuzhiyun (!ops->get_energy_uj && !ops->get_power_uw) ||
497*4882a593Smuzhiyun !control_type_valid(control_type))
498*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
499*4882a593Smuzhiyun
500*4882a593Smuzhiyun if (power_zone) {
501*4882a593Smuzhiyun if (!ops->release)
502*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
503*4882a593Smuzhiyun memset(power_zone, 0, sizeof(*power_zone));
504*4882a593Smuzhiyun } else {
505*4882a593Smuzhiyun power_zone = kzalloc(sizeof(*power_zone), GFP_KERNEL);
506*4882a593Smuzhiyun if (!power_zone)
507*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
508*4882a593Smuzhiyun power_zone->allocated = true;
509*4882a593Smuzhiyun }
510*4882a593Smuzhiyun power_zone->ops = ops;
511*4882a593Smuzhiyun power_zone->control_type_inst = control_type;
512*4882a593Smuzhiyun if (!parent) {
513*4882a593Smuzhiyun power_zone->dev.parent = &control_type->dev;
514*4882a593Smuzhiyun power_zone->parent_idr = &control_type->idr;
515*4882a593Smuzhiyun } else {
516*4882a593Smuzhiyun power_zone->dev.parent = &parent->dev;
517*4882a593Smuzhiyun power_zone->parent_idr = &parent->idr;
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun power_zone->dev.class = &powercap_class;
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun mutex_lock(&control_type->lock);
522*4882a593Smuzhiyun /* Using idr to get the unique id */
523*4882a593Smuzhiyun result = idr_alloc(power_zone->parent_idr, NULL, 0, 0, GFP_KERNEL);
524*4882a593Smuzhiyun if (result < 0)
525*4882a593Smuzhiyun goto err_idr_alloc;
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun power_zone->id = result;
528*4882a593Smuzhiyun idr_init(&power_zone->idr);
529*4882a593Smuzhiyun result = -ENOMEM;
530*4882a593Smuzhiyun power_zone->name = kstrdup(name, GFP_KERNEL);
531*4882a593Smuzhiyun if (!power_zone->name)
532*4882a593Smuzhiyun goto err_name_alloc;
533*4882a593Smuzhiyun dev_set_name(&power_zone->dev, "%s:%x",
534*4882a593Smuzhiyun dev_name(power_zone->dev.parent),
535*4882a593Smuzhiyun power_zone->id);
536*4882a593Smuzhiyun power_zone->constraints = kcalloc(nr_constraints,
537*4882a593Smuzhiyun sizeof(*power_zone->constraints),
538*4882a593Smuzhiyun GFP_KERNEL);
539*4882a593Smuzhiyun if (!power_zone->constraints)
540*4882a593Smuzhiyun goto err_const_alloc;
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun nr_attrs = nr_constraints * POWERCAP_CONSTRAINTS_ATTRS +
543*4882a593Smuzhiyun POWERCAP_ZONE_MAX_ATTRS + 1;
544*4882a593Smuzhiyun power_zone->zone_dev_attrs = kcalloc(nr_attrs, sizeof(void *),
545*4882a593Smuzhiyun GFP_KERNEL);
546*4882a593Smuzhiyun if (!power_zone->zone_dev_attrs)
547*4882a593Smuzhiyun goto err_attr_alloc;
548*4882a593Smuzhiyun create_power_zone_common_attributes(power_zone);
549*4882a593Smuzhiyun result = create_constraints(power_zone, nr_constraints, const_ops);
550*4882a593Smuzhiyun if (result)
551*4882a593Smuzhiyun goto err_dev_ret;
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun power_zone->zone_dev_attrs[power_zone->zone_attr_count] = NULL;
554*4882a593Smuzhiyun power_zone->dev_zone_attr_group.attrs = power_zone->zone_dev_attrs;
555*4882a593Smuzhiyun power_zone->dev_attr_groups[0] = &power_zone->dev_zone_attr_group;
556*4882a593Smuzhiyun power_zone->dev_attr_groups[1] = NULL;
557*4882a593Smuzhiyun power_zone->dev.groups = power_zone->dev_attr_groups;
558*4882a593Smuzhiyun result = device_register(&power_zone->dev);
559*4882a593Smuzhiyun if (result)
560*4882a593Smuzhiyun goto err_dev_ret;
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun control_type->nr_zones++;
563*4882a593Smuzhiyun mutex_unlock(&control_type->lock);
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun return power_zone;
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun err_dev_ret:
568*4882a593Smuzhiyun kfree(power_zone->zone_dev_attrs);
569*4882a593Smuzhiyun err_attr_alloc:
570*4882a593Smuzhiyun kfree(power_zone->constraints);
571*4882a593Smuzhiyun err_const_alloc:
572*4882a593Smuzhiyun kfree(power_zone->name);
573*4882a593Smuzhiyun err_name_alloc:
574*4882a593Smuzhiyun idr_remove(power_zone->parent_idr, power_zone->id);
575*4882a593Smuzhiyun err_idr_alloc:
576*4882a593Smuzhiyun if (power_zone->allocated)
577*4882a593Smuzhiyun kfree(power_zone);
578*4882a593Smuzhiyun mutex_unlock(&control_type->lock);
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun return ERR_PTR(result);
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(powercap_register_zone);
583*4882a593Smuzhiyun
powercap_unregister_zone(struct powercap_control_type * control_type,struct powercap_zone * power_zone)584*4882a593Smuzhiyun int powercap_unregister_zone(struct powercap_control_type *control_type,
585*4882a593Smuzhiyun struct powercap_zone *power_zone)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun if (!power_zone || !control_type)
588*4882a593Smuzhiyun return -EINVAL;
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun mutex_lock(&control_type->lock);
591*4882a593Smuzhiyun control_type->nr_zones--;
592*4882a593Smuzhiyun mutex_unlock(&control_type->lock);
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun device_unregister(&power_zone->dev);
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun return 0;
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(powercap_unregister_zone);
599*4882a593Smuzhiyun
powercap_register_control_type(struct powercap_control_type * control_type,const char * name,const struct powercap_control_type_ops * ops)600*4882a593Smuzhiyun struct powercap_control_type *powercap_register_control_type(
601*4882a593Smuzhiyun struct powercap_control_type *control_type,
602*4882a593Smuzhiyun const char *name,
603*4882a593Smuzhiyun const struct powercap_control_type_ops *ops)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun int result;
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun if (!name)
608*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
609*4882a593Smuzhiyun if (control_type) {
610*4882a593Smuzhiyun if (!ops || !ops->release)
611*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
612*4882a593Smuzhiyun memset(control_type, 0, sizeof(*control_type));
613*4882a593Smuzhiyun } else {
614*4882a593Smuzhiyun control_type = kzalloc(sizeof(*control_type), GFP_KERNEL);
615*4882a593Smuzhiyun if (!control_type)
616*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
617*4882a593Smuzhiyun control_type->allocated = true;
618*4882a593Smuzhiyun }
619*4882a593Smuzhiyun mutex_init(&control_type->lock);
620*4882a593Smuzhiyun control_type->ops = ops;
621*4882a593Smuzhiyun INIT_LIST_HEAD(&control_type->node);
622*4882a593Smuzhiyun control_type->dev.class = &powercap_class;
623*4882a593Smuzhiyun dev_set_name(&control_type->dev, "%s", name);
624*4882a593Smuzhiyun result = device_register(&control_type->dev);
625*4882a593Smuzhiyun if (result) {
626*4882a593Smuzhiyun if (control_type->allocated)
627*4882a593Smuzhiyun kfree(control_type);
628*4882a593Smuzhiyun return ERR_PTR(result);
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun idr_init(&control_type->idr);
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun mutex_lock(&powercap_cntrl_list_lock);
633*4882a593Smuzhiyun list_add_tail(&control_type->node, &powercap_cntrl_list);
634*4882a593Smuzhiyun mutex_unlock(&powercap_cntrl_list_lock);
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun return control_type;
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(powercap_register_control_type);
639*4882a593Smuzhiyun
powercap_unregister_control_type(struct powercap_control_type * control_type)640*4882a593Smuzhiyun int powercap_unregister_control_type(struct powercap_control_type *control_type)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun struct powercap_control_type *pos = NULL;
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun if (control_type->nr_zones) {
645*4882a593Smuzhiyun dev_err(&control_type->dev, "Zones of this type still not freed\n");
646*4882a593Smuzhiyun return -EINVAL;
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun mutex_lock(&powercap_cntrl_list_lock);
649*4882a593Smuzhiyun list_for_each_entry(pos, &powercap_cntrl_list, node) {
650*4882a593Smuzhiyun if (pos == control_type) {
651*4882a593Smuzhiyun list_del(&control_type->node);
652*4882a593Smuzhiyun mutex_unlock(&powercap_cntrl_list_lock);
653*4882a593Smuzhiyun device_unregister(&control_type->dev);
654*4882a593Smuzhiyun return 0;
655*4882a593Smuzhiyun }
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun mutex_unlock(&powercap_cntrl_list_lock);
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun return -ENODEV;
660*4882a593Smuzhiyun }
661*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(powercap_unregister_control_type);
662*4882a593Smuzhiyun
powercap_init(void)663*4882a593Smuzhiyun static int __init powercap_init(void)
664*4882a593Smuzhiyun {
665*4882a593Smuzhiyun int result;
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun result = seed_constraint_attributes();
668*4882a593Smuzhiyun if (result)
669*4882a593Smuzhiyun return result;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun return class_register(&powercap_class);
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun fs_initcall(powercap_init);
675*4882a593Smuzhiyun
676*4882a593Smuzhiyun MODULE_DESCRIPTION("PowerCap sysfs Driver");
677*4882a593Smuzhiyun MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
678*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
679