2 * Universal power supply monitor class
4 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
5 * Copyright © 2004 Szabolcs Gyurko
6 * Copyright © 2003 Ian Molton <spyro@f2s.com>
8 * Modified: 2004, Oct Szabolcs Gyurko
10 * You may use this code as per GPL version 2
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/err.h>
19 #include <linux/power_supply.h>
20 #include <linux/thermal.h>
21 #include "power_supply.h"
23 /* exported for the APM Power driver, APM emulation */
24 struct class *power_supply_class
;
25 EXPORT_SYMBOL_GPL(power_supply_class
);
27 static struct device_type power_supply_dev_type
;
29 static bool __power_supply_is_supplied_by(struct power_supply
*supplier
,
30 struct power_supply
*supply
)
34 if (!supply
->supplied_from
&& !supplier
->supplied_to
)
37 /* Support both supplied_to and supplied_from modes */
38 if (supply
->supplied_from
) {
41 for (i
= 0; i
< supply
->num_supplies
; i
++)
42 if (!strcmp(supplier
->name
, supply
->supplied_from
[i
]))
47 for (i
= 0; i
< supplier
->num_supplicants
; i
++)
48 if (!strcmp(supplier
->supplied_to
[i
], supply
->name
))
55 static int __power_supply_changed_work(struct device
*dev
, void *data
)
57 struct power_supply
*psy
= (struct power_supply
*)data
;
58 struct power_supply
*pst
= dev_get_drvdata(dev
);
60 if (__power_supply_is_supplied_by(psy
, pst
)) {
61 if (pst
->external_power_changed
)
62 pst
->external_power_changed(pst
);
68 static void power_supply_changed_work(struct work_struct
*work
)
70 struct power_supply
*psy
= container_of(work
, struct power_supply
,
73 dev_dbg(psy
->dev
, "%s\n", __func__
);
75 class_for_each_device(power_supply_class
, NULL
, psy
,
76 __power_supply_changed_work
);
78 power_supply_update_leds(psy
);
80 kobject_uevent(&psy
->dev
->kobj
, KOBJ_CHANGE
);
83 void power_supply_changed(struct power_supply
*psy
)
85 dev_dbg(psy
->dev
, "%s\n", __func__
);
87 schedule_work(&psy
->changed_work
);
89 EXPORT_SYMBOL_GPL(power_supply_changed
);
94 static int __power_supply_populate_supplied_from(struct device
*dev
,
97 struct power_supply
*psy
= (struct power_supply
*)data
;
98 struct power_supply
*epsy
= dev_get_drvdata(dev
);
99 struct device_node
*np
;
103 np
= of_parse_phandle(psy
->of_node
, "power-supplies", i
++);
107 if (np
== epsy
->of_node
) {
108 dev_info(psy
->dev
, "%s: Found supply : %s\n",
109 psy
->name
, epsy
->name
);
110 psy
->supplied_from
[i
-1] = (char *)epsy
->name
;
121 static int power_supply_populate_supplied_from(struct power_supply
*psy
)
125 error
= class_for_each_device(power_supply_class
, NULL
, psy
,
126 __power_supply_populate_supplied_from
);
128 dev_dbg(psy
->dev
, "%s %d\n", __func__
, error
);
133 static int __power_supply_find_supply_from_node(struct device
*dev
,
136 struct device_node
*np
= (struct device_node
*)data
;
137 struct power_supply
*epsy
= dev_get_drvdata(dev
);
139 /* return error breaks out of class_for_each_device loop */
140 if (epsy
->of_node
== np
)
146 static int power_supply_find_supply_from_node(struct device_node
*supply_node
)
150 struct class_dev_iter iter
;
153 * Use iterator to see if any other device is registered.
154 * This is required since class_for_each_device returns 0
155 * if there are no devices registered.
157 class_dev_iter_init(&iter
, power_supply_class
, NULL
, NULL
);
158 dev
= class_dev_iter_next(&iter
);
161 return -EPROBE_DEFER
;
164 * We have to treat the return value as inverted, because if
165 * we return error on not found, then it won't continue looking.
166 * So we trick it by returning error on success to stop looking
167 * once the matching device is found.
169 error
= class_for_each_device(power_supply_class
, NULL
, supply_node
,
170 __power_supply_find_supply_from_node
);
172 return error
? 0 : -EPROBE_DEFER
;
175 static int power_supply_check_supplies(struct power_supply
*psy
)
177 struct device_node
*np
;
180 /* If there is already a list honor it */
181 if (psy
->supplied_from
&& psy
->num_supplies
> 0)
184 /* No device node found, nothing to do */
191 np
= of_parse_phandle(psy
->of_node
, "power-supplies", cnt
++);
195 ret
= power_supply_find_supply_from_node(np
);
197 dev_dbg(psy
->dev
, "Failed to find supply, defer!\n");
199 return -EPROBE_DEFER
;
204 /* All supplies found, allocate char ** array for filling */
205 psy
->supplied_from
= devm_kzalloc(psy
->dev
, sizeof(psy
->supplied_from
),
207 if (!psy
->supplied_from
) {
208 dev_err(psy
->dev
, "Couldn't allocate memory for supply list\n");
212 *psy
->supplied_from
= devm_kzalloc(psy
->dev
, sizeof(char *) * cnt
,
214 if (!*psy
->supplied_from
) {
215 dev_err(psy
->dev
, "Couldn't allocate memory for supply list\n");
219 return power_supply_populate_supplied_from(psy
);
222 static inline int power_supply_check_supplies(struct power_supply
*psy
)
228 static int __power_supply_am_i_supplied(struct device
*dev
, void *data
)
230 union power_supply_propval ret
= {0,};
231 struct power_supply
*psy
= (struct power_supply
*)data
;
232 struct power_supply
*epsy
= dev_get_drvdata(dev
);
234 if (__power_supply_is_supplied_by(epsy
, psy
))
235 if (!epsy
->get_property(epsy
, POWER_SUPPLY_PROP_ONLINE
, &ret
)) {
243 int power_supply_am_i_supplied(struct power_supply
*psy
)
247 error
= class_for_each_device(power_supply_class
, NULL
, psy
,
248 __power_supply_am_i_supplied
);
250 dev_dbg(psy
->dev
, "%s %d\n", __func__
, error
);
254 EXPORT_SYMBOL_GPL(power_supply_am_i_supplied
);
256 static int __power_supply_is_system_supplied(struct device
*dev
, void *data
)
258 union power_supply_propval ret
= {0,};
259 struct power_supply
*psy
= dev_get_drvdata(dev
);
260 unsigned int *count
= data
;
263 if (psy
->type
!= POWER_SUPPLY_TYPE_BATTERY
) {
264 if (psy
->get_property(psy
, POWER_SUPPLY_PROP_ONLINE
, &ret
))
272 int power_supply_is_system_supplied(void)
275 unsigned int count
= 0;
277 error
= class_for_each_device(power_supply_class
, NULL
, &count
,
278 __power_supply_is_system_supplied
);
281 * If no power class device was found at all, most probably we are
282 * running on a desktop system, so assume we are on mains power.
289 EXPORT_SYMBOL_GPL(power_supply_is_system_supplied
);
291 int power_supply_set_battery_charged(struct power_supply
*psy
)
293 if (psy
->type
== POWER_SUPPLY_TYPE_BATTERY
&& psy
->set_charged
) {
294 psy
->set_charged(psy
);
300 EXPORT_SYMBOL_GPL(power_supply_set_battery_charged
);
302 static int power_supply_match_device_by_name(struct device
*dev
, const void *data
)
304 const char *name
= data
;
305 struct power_supply
*psy
= dev_get_drvdata(dev
);
307 return strcmp(psy
->name
, name
) == 0;
310 struct power_supply
*power_supply_get_by_name(const char *name
)
312 struct device
*dev
= class_find_device(power_supply_class
, NULL
, name
,
313 power_supply_match_device_by_name
);
315 return dev
? dev_get_drvdata(dev
) : NULL
;
317 EXPORT_SYMBOL_GPL(power_supply_get_by_name
);
319 int power_supply_powers(struct power_supply
*psy
, struct device
*dev
)
321 return sysfs_create_link(&psy
->dev
->kobj
, &dev
->kobj
, "powers");
323 EXPORT_SYMBOL_GPL(power_supply_powers
);
325 static void power_supply_dev_release(struct device
*dev
)
327 pr_debug("device: '%s': %s\n", dev_name(dev
), __func__
);
331 #ifdef CONFIG_THERMAL
332 static int power_supply_read_temp(struct thermal_zone_device
*tzd
,
335 struct power_supply
*psy
;
336 union power_supply_propval val
;
339 WARN_ON(tzd
== NULL
);
341 ret
= psy
->get_property(psy
, POWER_SUPPLY_PROP_TEMP
, &val
);
343 /* Convert tenths of degree Celsius to milli degree Celsius. */
345 *temp
= val
.intval
* 100;
350 static struct thermal_zone_device_ops psy_tzd_ops
= {
351 .get_temp
= power_supply_read_temp
,
354 static int psy_register_thermal(struct power_supply
*psy
)
358 /* Register battery zone device psy reports temperature */
359 for (i
= 0; i
< psy
->num_properties
; i
++) {
360 if (psy
->properties
[i
] == POWER_SUPPLY_PROP_TEMP
) {
361 psy
->tzd
= thermal_zone_device_register(psy
->name
, 0, 0,
362 psy
, &psy_tzd_ops
, NULL
, 0, 0);
363 if (IS_ERR(psy
->tzd
))
364 return PTR_ERR(psy
->tzd
);
371 static void psy_unregister_thermal(struct power_supply
*psy
)
373 if (IS_ERR_OR_NULL(psy
->tzd
))
375 thermal_zone_device_unregister(psy
->tzd
);
378 /* thermal cooling device callbacks */
379 static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device
*tcd
,
380 unsigned long *state
)
382 struct power_supply
*psy
;
383 union power_supply_propval val
;
387 ret
= psy
->get_property(psy
,
388 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX
, &val
);
395 static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device
*tcd
,
396 unsigned long *state
)
398 struct power_supply
*psy
;
399 union power_supply_propval val
;
403 ret
= psy
->get_property(psy
,
404 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT
, &val
);
411 static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device
*tcd
,
414 struct power_supply
*psy
;
415 union power_supply_propval val
;
420 ret
= psy
->set_property(psy
,
421 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT
, &val
);
426 static struct thermal_cooling_device_ops psy_tcd_ops
= {
427 .get_max_state
= ps_get_max_charge_cntl_limit
,
428 .get_cur_state
= ps_get_cur_chrage_cntl_limit
,
429 .set_cur_state
= ps_set_cur_charge_cntl_limit
,
432 static int psy_register_cooler(struct power_supply
*psy
)
436 /* Register for cooling device if psy can control charging */
437 for (i
= 0; i
< psy
->num_properties
; i
++) {
438 if (psy
->properties
[i
] ==
439 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT
) {
440 psy
->tcd
= thermal_cooling_device_register(
443 if (IS_ERR(psy
->tcd
))
444 return PTR_ERR(psy
->tcd
);
451 static void psy_unregister_cooler(struct power_supply
*psy
)
453 if (IS_ERR_OR_NULL(psy
->tcd
))
455 thermal_cooling_device_unregister(psy
->tcd
);
458 static int psy_register_thermal(struct power_supply
*psy
)
463 static void psy_unregister_thermal(struct power_supply
*psy
)
467 static int psy_register_cooler(struct power_supply
*psy
)
472 static void psy_unregister_cooler(struct power_supply
*psy
)
477 int power_supply_register(struct device
*parent
, struct power_supply
*psy
)
482 dev
= kzalloc(sizeof(*dev
), GFP_KERNEL
);
486 device_initialize(dev
);
488 dev
->class = power_supply_class
;
489 dev
->type
= &power_supply_dev_type
;
490 dev
->parent
= parent
;
491 dev
->release
= power_supply_dev_release
;
492 dev_set_drvdata(dev
, psy
);
495 INIT_WORK(&psy
->changed_work
, power_supply_changed_work
);
497 rc
= power_supply_check_supplies(psy
);
499 dev_info(dev
, "Not all required supplies found, defer probe\n");
500 goto check_supplies_failed
;
503 rc
= kobject_set_name(&dev
->kobj
, "%s", psy
->name
);
505 goto kobject_set_name_failed
;
507 rc
= device_add(dev
);
509 goto device_add_failed
;
511 rc
= psy_register_thermal(psy
);
513 goto register_thermal_failed
;
515 rc
= psy_register_cooler(psy
);
517 goto register_cooler_failed
;
519 rc
= power_supply_create_triggers(psy
);
521 goto create_triggers_failed
;
523 power_supply_changed(psy
);
527 create_triggers_failed
:
528 psy_unregister_cooler(psy
);
529 register_cooler_failed
:
530 psy_unregister_thermal(psy
);
531 register_thermal_failed
:
533 kobject_set_name_failed
:
535 check_supplies_failed
:
540 EXPORT_SYMBOL_GPL(power_supply_register
);
542 void power_supply_unregister(struct power_supply
*psy
)
544 cancel_work_sync(&psy
->changed_work
);
545 sysfs_remove_link(&psy
->dev
->kobj
, "powers");
546 power_supply_remove_triggers(psy
);
547 psy_unregister_cooler(psy
);
548 psy_unregister_thermal(psy
);
549 device_unregister(psy
->dev
);
551 EXPORT_SYMBOL_GPL(power_supply_unregister
);
553 static int __init
power_supply_class_init(void)
555 power_supply_class
= class_create(THIS_MODULE
, "power_supply");
557 if (IS_ERR(power_supply_class
))
558 return PTR_ERR(power_supply_class
);
560 power_supply_class
->dev_uevent
= power_supply_uevent
;
561 power_supply_init_attrs(&power_supply_dev_type
);
566 static void __exit
power_supply_class_exit(void)
568 class_destroy(power_supply_class
);
571 subsys_initcall(power_supply_class_init
);
572 module_exit(power_supply_class_exit
);
574 MODULE_DESCRIPTION("Universal power supply monitor class");
575 MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
577 "Anton Vorontsov <cbou@mail.ru>");
578 MODULE_LICENSE("GPL");