Branch data Line data Source code
1 : : /*
2 : : * Universal power supply monitor class
3 : : *
4 : : * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
5 : : * Copyright © 2004 Szabolcs Gyurko
6 : : * Copyright © 2003 Ian Molton <spyro@f2s.com>
7 : : *
8 : : * Modified: 2004, Oct Szabolcs Gyurko
9 : : *
10 : : * You may use this code as per GPL version 2
11 : : */
12 : :
13 : : #include <linux/module.h>
14 : : #include <linux/types.h>
15 : : #include <linux/init.h>
16 : : #include <linux/slab.h>
17 : : #include <linux/device.h>
18 : : #include <linux/notifier.h>
19 : : #include <linux/err.h>
20 : : #include <linux/power_supply.h>
21 : : #include <linux/thermal.h>
22 : : #include "power_supply.h"
23 : :
24 : : /* exported for the APM Power driver, APM emulation */
25 : : struct class *power_supply_class;
26 : : EXPORT_SYMBOL_GPL(power_supply_class);
27 : :
28 : : ATOMIC_NOTIFIER_HEAD(power_supply_notifier);
29 : : EXPORT_SYMBOL_GPL(power_supply_notifier);
30 : :
31 : : static struct device_type power_supply_dev_type;
32 : :
33 : 0 : static bool __power_supply_is_supplied_by(struct power_supply *supplier,
34 : : struct power_supply *supply)
35 : : {
36 : : int i;
37 : :
38 [ # # ][ # # ]: 0 : if (!supply->supplied_from && !supplier->supplied_to)
39 : : return false;
40 : :
41 : : /* Support both supplied_to and supplied_from modes */
42 [ # # ]: 0 : if (supply->supplied_from) {
43 [ # # ]: 0 : if (!supplier->name)
44 : : return false;
45 [ # # ]: 0 : for (i = 0; i < supply->num_supplies; i++)
46 [ # # ]: 0 : if (!strcmp(supplier->name, supply->supplied_from[i]))
47 : : return true;
48 : : } else {
49 [ # # ]: 0 : if (!supply->name)
50 : : return false;
51 [ # # ]: 0 : for (i = 0; i < supplier->num_supplicants; i++)
52 [ # # ]: 0 : if (!strcmp(supplier->supplied_to[i], supply->name))
53 : : return true;
54 : : }
55 : :
56 : : return false;
57 : : }
58 : :
59 : 0 : static int __power_supply_changed_work(struct device *dev, void *data)
60 : : {
61 : : struct power_supply *psy = (struct power_supply *)data;
62 : 0 : struct power_supply *pst = dev_get_drvdata(dev);
63 : :
64 [ # # ]: 0 : if (__power_supply_is_supplied_by(psy, pst)) {
65 [ # # ]: 0 : if (pst->external_power_changed)
66 : 0 : pst->external_power_changed(pst);
67 : : }
68 : :
69 : 0 : return 0;
70 : : }
71 : :
72 : 0 : static void power_supply_changed_work(struct work_struct *work)
73 : : {
74 : : unsigned long flags;
75 : 0 : struct power_supply *psy = container_of(work, struct power_supply,
76 : : changed_work);
77 : :
78 : : dev_dbg(psy->dev, "%s\n", __func__);
79 : :
80 : 0 : spin_lock_irqsave(&psy->changed_lock, flags);
81 [ # # ]: 0 : if (psy->changed) {
82 : 0 : psy->changed = false;
83 : : spin_unlock_irqrestore(&psy->changed_lock, flags);
84 : 0 : class_for_each_device(power_supply_class, NULL, psy,
85 : : __power_supply_changed_work);
86 : 0 : power_supply_update_leds(psy);
87 : 0 : atomic_notifier_call_chain(&power_supply_notifier,
88 : : PSY_EVENT_PROP_CHANGED, psy);
89 : 0 : kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
90 : 0 : spin_lock_irqsave(&psy->changed_lock, flags);
91 : : }
92 : : /*
93 : : * Dependent power supplies (e.g. battery) may have changed state
94 : : * as a result of this event, so poll again and hold the
95 : : * wakeup_source until all events are processed.
96 : : */
97 [ # # ]: 0 : if (!psy->changed)
98 : 0 : pm_relax(psy->dev);
99 : : spin_unlock_irqrestore(&psy->changed_lock, flags);
100 : 0 : }
101 : :
102 : 0 : void power_supply_changed(struct power_supply *psy)
103 : : {
104 : : unsigned long flags;
105 : :
106 : : dev_dbg(psy->dev, "%s\n", __func__);
107 : :
108 : 0 : spin_lock_irqsave(&psy->changed_lock, flags);
109 : 0 : psy->changed = true;
110 : 0 : pm_stay_awake(psy->dev);
111 : : spin_unlock_irqrestore(&psy->changed_lock, flags);
112 : 0 : schedule_work(&psy->changed_work);
113 : 0 : }
114 : : EXPORT_SYMBOL_GPL(power_supply_changed);
115 : :
116 : : #ifdef CONFIG_OF
117 : : #include <linux/of.h>
118 : :
119 : 0 : static int __power_supply_populate_supplied_from(struct device *dev,
120 : : void *data)
121 : : {
122 : : struct power_supply *psy = (struct power_supply *)data;
123 : 0 : struct power_supply *epsy = dev_get_drvdata(dev);
124 : : struct device_node *np;
125 : : int i = 0;
126 : :
127 : : do {
128 : 0 : np = of_parse_phandle(psy->of_node, "power-supplies", i++);
129 [ # # ]: 0 : if (!np)
130 : 0 : continue;
131 : :
132 [ # # ]: 0 : if (np == epsy->of_node) {
133 : 0 : dev_info(psy->dev, "%s: Found supply : %s\n",
134 : : psy->name, epsy->name);
135 : 0 : psy->supplied_from[i-1] = (char *)epsy->name;
136 : 0 : psy->num_supplies++;
137 : : of_node_put(np);
138 : : break;
139 : : }
140 : : of_node_put(np);
141 [ # # ]: 0 : } while (np);
142 : :
143 : 0 : return 0;
144 : : }
145 : :
146 : : static int power_supply_populate_supplied_from(struct power_supply *psy)
147 : : {
148 : : int error;
149 : :
150 : 0 : error = class_for_each_device(power_supply_class, NULL, psy,
151 : : __power_supply_populate_supplied_from);
152 : :
153 : : dev_dbg(psy->dev, "%s %d\n", __func__, error);
154 : :
155 : : return error;
156 : : }
157 : :
158 : 0 : static int __power_supply_find_supply_from_node(struct device *dev,
159 : : void *data)
160 : : {
161 : : struct device_node *np = (struct device_node *)data;
162 : 0 : struct power_supply *epsy = dev_get_drvdata(dev);
163 : :
164 : : /* return error breaks out of class_for_each_device loop */
165 [ # # ]: 0 : if (epsy->of_node == np)
166 : : return -EINVAL;
167 : :
168 : 0 : return 0;
169 : : }
170 : :
171 : 0 : static int power_supply_find_supply_from_node(struct device_node *supply_node)
172 : : {
173 : : int error;
174 : : struct device *dev;
175 : : struct class_dev_iter iter;
176 : :
177 : : /*
178 : : * Use iterator to see if any other device is registered.
179 : : * This is required since class_for_each_device returns 0
180 : : * if there are no devices registered.
181 : : */
182 : 0 : class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
183 : 0 : dev = class_dev_iter_next(&iter);
184 : :
185 [ # # ]: 0 : if (!dev)
186 : : return -EPROBE_DEFER;
187 : :
188 : : /*
189 : : * We have to treat the return value as inverted, because if
190 : : * we return error on not found, then it won't continue looking.
191 : : * So we trick it by returning error on success to stop looking
192 : : * once the matching device is found.
193 : : */
194 : 0 : error = class_for_each_device(power_supply_class, NULL, supply_node,
195 : : __power_supply_find_supply_from_node);
196 : :
197 [ # # ]: 0 : return error ? 0 : -EPROBE_DEFER;
198 : : }
199 : :
200 : 0 : static int power_supply_check_supplies(struct power_supply *psy)
201 : : {
202 : : struct device_node *np;
203 : : int cnt = 0;
204 : :
205 : : /* If there is already a list honor it */
206 [ # # ][ # # ]: 0 : if (psy->supplied_from && psy->num_supplies > 0)
207 : : return 0;
208 : :
209 : : /* No device node found, nothing to do */
210 [ # # ]: 0 : if (!psy->of_node)
211 : : return 0;
212 : :
213 : : do {
214 : : int ret;
215 : :
216 : 0 : np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
217 [ # # ]: 0 : if (!np)
218 : 0 : continue;
219 : :
220 : 0 : ret = power_supply_find_supply_from_node(np);
221 [ # # ]: 0 : if (ret) {
222 : : dev_dbg(psy->dev, "Failed to find supply, defer!\n");
223 : : of_node_put(np);
224 : : return -EPROBE_DEFER;
225 : : }
226 : : of_node_put(np);
227 [ # # ]: 0 : } while (np);
228 : :
229 : : /* All supplies found, allocate char ** array for filling */
230 : 0 : psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from),
231 : : GFP_KERNEL);
232 [ # # ]: 0 : if (!psy->supplied_from) {
233 : 0 : dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
234 : 0 : return -ENOMEM;
235 : : }
236 : :
237 : 0 : *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt,
238 : : GFP_KERNEL);
239 [ # # ]: 0 : if (!*psy->supplied_from) {
240 : 0 : dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
241 : 0 : return -ENOMEM;
242 : : }
243 : :
244 : 0 : return power_supply_populate_supplied_from(psy);
245 : : }
246 : : #else
247 : : static inline int power_supply_check_supplies(struct power_supply *psy)
248 : : {
249 : : return 0;
250 : : }
251 : : #endif
252 : :
253 : 0 : static int __power_supply_am_i_supplied(struct device *dev, void *data)
254 : : {
255 : 0 : union power_supply_propval ret = {0,};
256 : : struct power_supply *psy = (struct power_supply *)data;
257 : 0 : struct power_supply *epsy = dev_get_drvdata(dev);
258 : :
259 [ # # ]: 0 : if (__power_supply_is_supplied_by(epsy, psy))
260 [ # # ]: 0 : if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) {
261 [ # # ]: 0 : if (ret.intval)
262 : 0 : return ret.intval;
263 : : }
264 : :
265 : : return 0;
266 : : }
267 : :
268 : 0 : int power_supply_am_i_supplied(struct power_supply *psy)
269 : : {
270 : : int error;
271 : :
272 : 0 : error = class_for_each_device(power_supply_class, NULL, psy,
273 : : __power_supply_am_i_supplied);
274 : :
275 : : dev_dbg(psy->dev, "%s %d\n", __func__, error);
276 : :
277 : 0 : return error;
278 : : }
279 : : EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
280 : :
281 : 0 : static int __power_supply_is_system_supplied(struct device *dev, void *data)
282 : : {
283 : 0 : union power_supply_propval ret = {0,};
284 : 0 : struct power_supply *psy = dev_get_drvdata(dev);
285 : : unsigned int *count = data;
286 : :
287 : 0 : (*count)++;
288 [ # # ]: 0 : if (psy->type != POWER_SUPPLY_TYPE_BATTERY) {
289 [ # # ]: 0 : if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
290 : : return 0;
291 [ # # ]: 0 : if (ret.intval)
292 : 0 : return ret.intval;
293 : : }
294 : : return 0;
295 : : }
296 : :
297 : 0 : int power_supply_is_system_supplied(void)
298 : : {
299 : : int error;
300 : 0 : unsigned int count = 0;
301 : :
302 : 0 : error = class_for_each_device(power_supply_class, NULL, &count,
303 : : __power_supply_is_system_supplied);
304 : :
305 : : /*
306 : : * If no power class device was found at all, most probably we are
307 : : * running on a desktop system, so assume we are on mains power.
308 : : */
309 [ # # ]: 0 : if (count == 0)
310 : : return 1;
311 : :
312 : 0 : return error;
313 : : }
314 : : EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
315 : :
316 : 0 : int power_supply_set_battery_charged(struct power_supply *psy)
317 : : {
318 [ # # ][ # # ]: 0 : if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) {
319 : 0 : psy->set_charged(psy);
320 : 0 : return 0;
321 : : }
322 : :
323 : : return -EINVAL;
324 : : }
325 : : EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
326 : :
327 : 0 : static int power_supply_match_device_by_name(struct device *dev, const void *data)
328 : : {
329 : : const char *name = data;
330 : 0 : struct power_supply *psy = dev_get_drvdata(dev);
331 : :
332 : 0 : return strcmp(psy->name, name) == 0;
333 : : }
334 : :
335 : 0 : struct power_supply *power_supply_get_by_name(const char *name)
336 : : {
337 : 0 : struct device *dev = class_find_device(power_supply_class, NULL, name,
338 : : power_supply_match_device_by_name);
339 : :
340 [ # # ]: 0 : return dev ? dev_get_drvdata(dev) : NULL;
341 : : }
342 : : EXPORT_SYMBOL_GPL(power_supply_get_by_name);
343 : :
344 : : #ifdef CONFIG_OF
345 : 0 : static int power_supply_match_device_node(struct device *dev, const void *data)
346 : : {
347 [ # # ][ # # ]: 0 : return dev->parent && dev->parent->of_node == data;
348 : : }
349 : :
350 : 0 : struct power_supply *power_supply_get_by_phandle(struct device_node *np,
351 : : const char *property)
352 : : {
353 : : struct device_node *power_supply_np;
354 : : struct device *dev;
355 : :
356 : 0 : power_supply_np = of_parse_phandle(np, property, 0);
357 [ # # ]: 0 : if (!power_supply_np)
358 : : return ERR_PTR(-ENODEV);
359 : :
360 : 0 : dev = class_find_device(power_supply_class, NULL, power_supply_np,
361 : : power_supply_match_device_node);
362 : :
363 : : of_node_put(power_supply_np);
364 : :
365 [ # # ]: 0 : return dev ? dev_get_drvdata(dev) : NULL;
366 : : }
367 : : EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
368 : : #endif /* CONFIG_OF */
369 : :
370 : 0 : int power_supply_powers(struct power_supply *psy, struct device *dev)
371 : : {
372 : 0 : return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
373 : : }
374 : : EXPORT_SYMBOL_GPL(power_supply_powers);
375 : :
376 : 0 : static void power_supply_dev_release(struct device *dev)
377 : : {
378 : : pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
379 : 0 : kfree(dev);
380 : 0 : }
381 : :
382 : 0 : int power_supply_reg_notifier(struct notifier_block *nb)
383 : : {
384 : 0 : return atomic_notifier_chain_register(&power_supply_notifier, nb);
385 : : }
386 : : EXPORT_SYMBOL_GPL(power_supply_reg_notifier);
387 : :
388 : 0 : void power_supply_unreg_notifier(struct notifier_block *nb)
389 : : {
390 : 0 : atomic_notifier_chain_unregister(&power_supply_notifier, nb);
391 : 0 : }
392 : : EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
393 : :
394 : : #ifdef CONFIG_THERMAL
395 : : static int power_supply_read_temp(struct thermal_zone_device *tzd,
396 : : unsigned long *temp)
397 : : {
398 : : struct power_supply *psy;
399 : : union power_supply_propval val;
400 : : int ret;
401 : :
402 : : WARN_ON(tzd == NULL);
403 : : psy = tzd->devdata;
404 : : ret = psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
405 : :
406 : : /* Convert tenths of degree Celsius to milli degree Celsius. */
407 : : if (!ret)
408 : : *temp = val.intval * 100;
409 : :
410 : : return ret;
411 : : }
412 : :
413 : : static struct thermal_zone_device_ops psy_tzd_ops = {
414 : : .get_temp = power_supply_read_temp,
415 : : };
416 : :
417 : : static int psy_register_thermal(struct power_supply *psy)
418 : : {
419 : : int i;
420 : :
421 : : /* Register battery zone device psy reports temperature */
422 : : for (i = 0; i < psy->num_properties; i++) {
423 : : if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
424 : : psy->tzd = thermal_zone_device_register(psy->name, 0, 0,
425 : : psy, &psy_tzd_ops, NULL, 0, 0);
426 : : if (IS_ERR(psy->tzd))
427 : : return PTR_ERR(psy->tzd);
428 : : break;
429 : : }
430 : : }
431 : : return 0;
432 : : }
433 : :
434 : : static void psy_unregister_thermal(struct power_supply *psy)
435 : : {
436 : : if (IS_ERR_OR_NULL(psy->tzd))
437 : : return;
438 : : thermal_zone_device_unregister(psy->tzd);
439 : : }
440 : :
441 : : /* thermal cooling device callbacks */
442 : : static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
443 : : unsigned long *state)
444 : : {
445 : : struct power_supply *psy;
446 : : union power_supply_propval val;
447 : : int ret;
448 : :
449 : : psy = tcd->devdata;
450 : : ret = psy->get_property(psy,
451 : : POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
452 : : if (!ret)
453 : : *state = val.intval;
454 : :
455 : : return ret;
456 : : }
457 : :
458 : : static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd,
459 : : unsigned long *state)
460 : : {
461 : : struct power_supply *psy;
462 : : union power_supply_propval val;
463 : : int ret;
464 : :
465 : : psy = tcd->devdata;
466 : : ret = psy->get_property(psy,
467 : : POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
468 : : if (!ret)
469 : : *state = val.intval;
470 : :
471 : : return ret;
472 : : }
473 : :
474 : : static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
475 : : unsigned long state)
476 : : {
477 : : struct power_supply *psy;
478 : : union power_supply_propval val;
479 : : int ret;
480 : :
481 : : psy = tcd->devdata;
482 : : val.intval = state;
483 : : ret = psy->set_property(psy,
484 : : POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
485 : :
486 : : return ret;
487 : : }
488 : :
489 : : static struct thermal_cooling_device_ops psy_tcd_ops = {
490 : : .get_max_state = ps_get_max_charge_cntl_limit,
491 : : .get_cur_state = ps_get_cur_chrage_cntl_limit,
492 : : .set_cur_state = ps_set_cur_charge_cntl_limit,
493 : : };
494 : :
495 : : static int psy_register_cooler(struct power_supply *psy)
496 : : {
497 : : int i;
498 : :
499 : : /* Register for cooling device if psy can control charging */
500 : : for (i = 0; i < psy->num_properties; i++) {
501 : : if (psy->properties[i] ==
502 : : POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) {
503 : : psy->tcd = thermal_cooling_device_register(
504 : : (char *)psy->name,
505 : : psy, &psy_tcd_ops);
506 : : if (IS_ERR(psy->tcd))
507 : : return PTR_ERR(psy->tcd);
508 : : break;
509 : : }
510 : : }
511 : : return 0;
512 : : }
513 : :
514 : : static void psy_unregister_cooler(struct power_supply *psy)
515 : : {
516 : : if (IS_ERR_OR_NULL(psy->tcd))
517 : : return;
518 : : thermal_cooling_device_unregister(psy->tcd);
519 : : }
520 : : #else
521 : : static int psy_register_thermal(struct power_supply *psy)
522 : : {
523 : : return 0;
524 : : }
525 : :
526 : : static void psy_unregister_thermal(struct power_supply *psy)
527 : : {
528 : : }
529 : :
530 : : static int psy_register_cooler(struct power_supply *psy)
531 : : {
532 : : return 0;
533 : : }
534 : :
535 : : static void psy_unregister_cooler(struct power_supply *psy)
536 : : {
537 : : }
538 : : #endif
539 : :
540 : 0 : int power_supply_register(struct device *parent, struct power_supply *psy)
541 : : {
542 : : struct device *dev;
543 : : int rc;
544 : :
545 : : dev = kzalloc(sizeof(*dev), GFP_KERNEL);
546 [ # # ]: 0 : if (!dev)
547 : : return -ENOMEM;
548 : :
549 : 0 : device_initialize(dev);
550 : :
551 : 0 : dev->class = power_supply_class;
552 : 0 : dev->type = &power_supply_dev_type;
553 : 0 : dev->parent = parent;
554 : 0 : dev->release = power_supply_dev_release;
555 : 0 : dev_set_drvdata(dev, psy);
556 : 0 : psy->dev = dev;
557 : :
558 : 0 : rc = dev_set_name(dev, "%s", psy->name);
559 [ # # ]: 0 : if (rc)
560 : : goto dev_set_name_failed;
561 : :
562 : 0 : INIT_WORK(&psy->changed_work, power_supply_changed_work);
563 : :
564 : 0 : rc = power_supply_check_supplies(psy);
565 [ # # ]: 0 : if (rc) {
566 : 0 : dev_info(dev, "Not all required supplies found, defer probe\n");
567 : 0 : goto check_supplies_failed;
568 : : }
569 : :
570 : 0 : spin_lock_init(&psy->changed_lock);
571 : 0 : rc = device_init_wakeup(dev, true);
572 [ # # ]: 0 : if (rc)
573 : : goto wakeup_init_failed;
574 : :
575 : 0 : rc = device_add(dev);
576 [ # # ]: 0 : if (rc)
577 : : goto device_add_failed;
578 : :
579 : : rc = psy_register_thermal(psy);
580 : : if (rc)
581 : : goto register_thermal_failed;
582 : :
583 : : rc = psy_register_cooler(psy);
584 : : if (rc)
585 : : goto register_cooler_failed;
586 : :
587 : 0 : rc = power_supply_create_triggers(psy);
588 [ # # ]: 0 : if (rc)
589 : : goto create_triggers_failed;
590 : :
591 : 0 : power_supply_changed(psy);
592 : :
593 : 0 : goto success;
594 : :
595 : : create_triggers_failed:
596 : : psy_unregister_cooler(psy);
597 : : register_cooler_failed:
598 : : psy_unregister_thermal(psy);
599 : : register_thermal_failed:
600 : 0 : device_del(dev);
601 : : device_add_failed:
602 : : wakeup_init_failed:
603 : : check_supplies_failed:
604 : : dev_set_name_failed:
605 : 0 : put_device(dev);
606 : : success:
607 : 0 : return rc;
608 : : }
609 : : EXPORT_SYMBOL_GPL(power_supply_register);
610 : :
611 : 0 : void power_supply_unregister(struct power_supply *psy)
612 : : {
613 : 0 : cancel_work_sync(&psy->changed_work);
614 : 0 : sysfs_remove_link(&psy->dev->kobj, "powers");
615 : 0 : power_supply_remove_triggers(psy);
616 : : psy_unregister_cooler(psy);
617 : : psy_unregister_thermal(psy);
618 : 0 : device_init_wakeup(psy->dev, false);
619 : 0 : device_unregister(psy->dev);
620 : 0 : }
621 : : EXPORT_SYMBOL_GPL(power_supply_unregister);
622 : :
623 : 0 : static int __init power_supply_class_init(void)
624 : : {
625 : 0 : power_supply_class = class_create(THIS_MODULE, "power_supply");
626 : :
627 [ # # ]: 0 : if (IS_ERR(power_supply_class))
628 : 0 : return PTR_ERR(power_supply_class);
629 : :
630 : 0 : power_supply_class->dev_uevent = power_supply_uevent;
631 : 0 : power_supply_init_attrs(&power_supply_dev_type);
632 : :
633 : 0 : return 0;
634 : : }
635 : :
636 : 0 : static void __exit power_supply_class_exit(void)
637 : : {
638 : 0 : class_destroy(power_supply_class);
639 : 0 : }
640 : :
641 : : subsys_initcall(power_supply_class_init);
642 : : module_exit(power_supply_class_exit);
643 : :
644 : : MODULE_DESCRIPTION("Universal power supply monitor class");
645 : : MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
646 : : "Szabolcs Gyurko, "
647 : : "Anton Vorontsov <cbou@mail.ru>");
648 : : MODULE_LICENSE("GPL");
|