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
;
119 static int power_supply_populate_supplied_from(struct power_supply
*psy
)
123 error
= class_for_each_device(power_supply_class
, NULL
, psy
,
124 __power_supply_populate_supplied_from
);
126 dev_dbg(psy
->dev
, "%s %d\n", __func__
, error
);
131 static int __power_supply_find_supply_from_node(struct device
*dev
,
134 struct device_node
*np
= (struct device_node
*)data
;
135 struct power_supply
*epsy
= dev_get_drvdata(dev
);
137 /* return error breaks out of class_for_each_device loop */
138 if (epsy
->of_node
== np
)
144 static int power_supply_find_supply_from_node(struct device_node
*supply_node
)
148 struct class_dev_iter iter
;
151 * Use iterator to see if any other device is registered.
152 * This is required since class_for_each_device returns 0
153 * if there are no devices registered.
155 class_dev_iter_init(&iter
, power_supply_class
, NULL
, NULL
);
156 dev
= class_dev_iter_next(&iter
);
159 return -EPROBE_DEFER
;
162 * We have to treat the return value as inverted, because if
163 * we return error on not found, then it won't continue looking.
164 * So we trick it by returning error on success to stop looking
165 * once the matching device is found.
167 error
= class_for_each_device(power_supply_class
, NULL
, supply_node
,
168 __power_supply_find_supply_from_node
);
170 return error
? 0 : -EPROBE_DEFER
;
173 static int power_supply_check_supplies(struct power_supply
*psy
)
175 struct device_node
*np
;
178 /* If there is already a list honor it */
179 if (psy
->supplied_from
&& psy
->num_supplies
> 0)
182 /* No device node found, nothing to do */
189 np
= of_parse_phandle(psy
->of_node
, "power-supplies", cnt
++);
193 ret
= power_supply_find_supply_from_node(np
);
195 dev_dbg(psy
->dev
, "Failed to find supply, defer!\n");
196 return -EPROBE_DEFER
;
200 /* All supplies found, allocate char ** array for filling */
201 psy
->supplied_from
= devm_kzalloc(psy
->dev
, sizeof(psy
->supplied_from
),
203 if (!psy
->supplied_from
) {
204 dev_err(psy
->dev
, "Couldn't allocate memory for supply list\n");
208 *psy
->supplied_from
= devm_kzalloc(psy
->dev
, sizeof(char *) * cnt
,
210 if (!*psy
->supplied_from
) {
211 dev_err(psy
->dev
, "Couldn't allocate memory for supply list\n");
215 return power_supply_populate_supplied_from(psy
);
218 static inline int power_supply_check_supplies(struct power_supply
*psy
)
224 static int __power_supply_am_i_supplied(struct device
*dev
, void *data
)
226 union power_supply_propval ret
= {0,};
227 struct power_supply
*psy
= (struct power_supply
*)data
;
228 struct power_supply
*epsy
= dev_get_drvdata(dev
);
230 if (__power_supply_is_supplied_by(epsy
, psy
))
231 if (!epsy
->get_property(epsy
, POWER_SUPPLY_PROP_ONLINE
, &ret
)) {
239 int power_supply_am_i_supplied(struct power_supply
*psy
)
243 error
= class_for_each_device(power_supply_class
, NULL
, psy
,
244 __power_supply_am_i_supplied
);
246 dev_dbg(psy
->dev
, "%s %d\n", __func__
, error
);
250 EXPORT_SYMBOL_GPL(power_supply_am_i_supplied
);
252 static int __power_supply_is_system_supplied(struct device
*dev
, void *data
)
254 union power_supply_propval ret
= {0,};
255 struct power_supply
*psy
= dev_get_drvdata(dev
);
256 unsigned int *count
= data
;
259 if (psy
->type
!= POWER_SUPPLY_TYPE_BATTERY
) {
260 if (psy
->get_property(psy
, POWER_SUPPLY_PROP_ONLINE
, &ret
))
268 int power_supply_is_system_supplied(void)
271 unsigned int count
= 0;
273 error
= class_for_each_device(power_supply_class
, NULL
, &count
,
274 __power_supply_is_system_supplied
);
277 * If no power class device was found at all, most probably we are
278 * running on a desktop system, so assume we are on mains power.
285 EXPORT_SYMBOL_GPL(power_supply_is_system_supplied
);
287 int power_supply_set_battery_charged(struct power_supply
*psy
)
289 if (psy
->type
== POWER_SUPPLY_TYPE_BATTERY
&& psy
->set_charged
) {
290 psy
->set_charged(psy
);
296 EXPORT_SYMBOL_GPL(power_supply_set_battery_charged
);
298 static int power_supply_match_device_by_name(struct device
*dev
, const void *data
)
300 const char *name
= data
;
301 struct power_supply
*psy
= dev_get_drvdata(dev
);
303 return strcmp(psy
->name
, name
) == 0;
306 struct power_supply
*power_supply_get_by_name(const char *name
)
308 struct device
*dev
= class_find_device(power_supply_class
, NULL
, name
,
309 power_supply_match_device_by_name
);
311 return dev
? dev_get_drvdata(dev
) : NULL
;
313 EXPORT_SYMBOL_GPL(power_supply_get_by_name
);
315 int power_supply_powers(struct power_supply
*psy
, struct device
*dev
)
317 return sysfs_create_link(&psy
->dev
->kobj
, &dev
->kobj
, "powers");
319 EXPORT_SYMBOL_GPL(power_supply_powers
);
321 static void power_supply_dev_release(struct device
*dev
)
323 pr_debug("device: '%s': %s\n", dev_name(dev
), __func__
);
327 #ifdef CONFIG_THERMAL
328 static int power_supply_read_temp(struct thermal_zone_device
*tzd
,
331 struct power_supply
*psy
;
332 union power_supply_propval val
;
335 WARN_ON(tzd
== NULL
);
337 ret
= psy
->get_property(psy
, POWER_SUPPLY_PROP_TEMP
, &val
);
339 /* Convert tenths of degree Celsius to milli degree Celsius. */
341 *temp
= val
.intval
* 100;
346 static struct thermal_zone_device_ops psy_tzd_ops
= {
347 .get_temp
= power_supply_read_temp
,
350 static int psy_register_thermal(struct power_supply
*psy
)
354 /* Register battery zone device psy reports temperature */
355 for (i
= 0; i
< psy
->num_properties
; i
++) {
356 if (psy
->properties
[i
] == POWER_SUPPLY_PROP_TEMP
) {
357 psy
->tzd
= thermal_zone_device_register(psy
->name
, 0, 0,
358 psy
, &psy_tzd_ops
, NULL
, 0, 0);
359 if (IS_ERR(psy
->tzd
))
360 return PTR_ERR(psy
->tzd
);
367 static void psy_unregister_thermal(struct power_supply
*psy
)
369 if (IS_ERR_OR_NULL(psy
->tzd
))
371 thermal_zone_device_unregister(psy
->tzd
);
374 /* thermal cooling device callbacks */
375 static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device
*tcd
,
376 unsigned long *state
)
378 struct power_supply
*psy
;
379 union power_supply_propval val
;
383 ret
= psy
->get_property(psy
,
384 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX
, &val
);
391 static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device
*tcd
,
392 unsigned long *state
)
394 struct power_supply
*psy
;
395 union power_supply_propval val
;
399 ret
= psy
->get_property(psy
,
400 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT
, &val
);
407 static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device
*tcd
,
410 struct power_supply
*psy
;
411 union power_supply_propval val
;
416 ret
= psy
->set_property(psy
,
417 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT
, &val
);
422 static struct thermal_cooling_device_ops psy_tcd_ops
= {
423 .get_max_state
= ps_get_max_charge_cntl_limit
,
424 .get_cur_state
= ps_get_cur_chrage_cntl_limit
,
425 .set_cur_state
= ps_set_cur_charge_cntl_limit
,
428 static int psy_register_cooler(struct power_supply
*psy
)
432 /* Register for cooling device if psy can control charging */
433 for (i
= 0; i
< psy
->num_properties
; i
++) {
434 if (psy
->properties
[i
] ==
435 POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT
) {
436 psy
->tcd
= thermal_cooling_device_register(
439 if (IS_ERR(psy
->tcd
))
440 return PTR_ERR(psy
->tcd
);
447 static void psy_unregister_cooler(struct power_supply
*psy
)
449 if (IS_ERR_OR_NULL(psy
->tcd
))
451 thermal_cooling_device_unregister(psy
->tcd
);
454 static int psy_register_thermal(struct power_supply
*psy
)
459 static void psy_unregister_thermal(struct power_supply
*psy
)
463 static int psy_register_cooler(struct power_supply
*psy
)
468 static void psy_unregister_cooler(struct power_supply
*psy
)
473 int power_supply_register(struct device
*parent
, struct power_supply
*psy
)
478 dev
= kzalloc(sizeof(*dev
), GFP_KERNEL
);
482 device_initialize(dev
);
484 dev
->class = power_supply_class
;
485 dev
->type
= &power_supply_dev_type
;
486 dev
->parent
= parent
;
487 dev
->release
= power_supply_dev_release
;
488 dev_set_drvdata(dev
, psy
);
491 INIT_WORK(&psy
->changed_work
, power_supply_changed_work
);
493 rc
= power_supply_check_supplies(psy
);
495 dev_info(dev
, "Not all required supplies found, defer probe\n");
496 goto check_supplies_failed
;
499 rc
= kobject_set_name(&dev
->kobj
, "%s", psy
->name
);
501 goto kobject_set_name_failed
;
503 rc
= device_add(dev
);
505 goto device_add_failed
;
507 rc
= psy_register_thermal(psy
);
509 goto register_thermal_failed
;
511 rc
= psy_register_cooler(psy
);
513 goto register_cooler_failed
;
515 rc
= power_supply_create_triggers(psy
);
517 goto create_triggers_failed
;
519 power_supply_changed(psy
);
523 create_triggers_failed
:
524 psy_unregister_cooler(psy
);
525 register_cooler_failed
:
526 psy_unregister_thermal(psy
);
527 register_thermal_failed
:
529 kobject_set_name_failed
:
531 check_supplies_failed
:
536 EXPORT_SYMBOL_GPL(power_supply_register
);
538 void power_supply_unregister(struct power_supply
*psy
)
540 cancel_work_sync(&psy
->changed_work
);
541 sysfs_remove_link(&psy
->dev
->kobj
, "powers");
542 power_supply_remove_triggers(psy
);
543 psy_unregister_cooler(psy
);
544 psy_unregister_thermal(psy
);
545 device_unregister(psy
->dev
);
547 EXPORT_SYMBOL_GPL(power_supply_unregister
);
549 static int __init
power_supply_class_init(void)
551 power_supply_class
= class_create(THIS_MODULE
, "power_supply");
553 if (IS_ERR(power_supply_class
))
554 return PTR_ERR(power_supply_class
);
556 power_supply_class
->dev_uevent
= power_supply_uevent
;
557 power_supply_init_attrs(&power_supply_dev_type
);
562 static void __exit
power_supply_class_exit(void)
564 class_destroy(power_supply_class
);
567 subsys_initcall(power_supply_class_init
);
568 module_exit(power_supply_class_exit
);
570 MODULE_DESCRIPTION("Universal power supply monitor class");
571 MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
573 "Anton Vorontsov <cbou@mail.ru>");
574 MODULE_LICENSE("GPL");