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