Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  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 */
 25struct class *power_supply_class;
 26EXPORT_SYMBOL_GPL(power_supply_class);
 27
 28ATOMIC_NOTIFIER_HEAD(power_supply_notifier);
 29EXPORT_SYMBOL_GPL(power_supply_notifier);
 30
 31static struct device_type power_supply_dev_type;
 32
 33static bool __power_supply_is_supplied_by(struct power_supply *supplier,
 34					 struct power_supply *supply)
 35{
 36	int i;
 37
 38	if (!supply->supplied_from && !supplier->supplied_to)
 39		return false;
 40
 41	/* Support both supplied_to and supplied_from modes */
 42	if (supply->supplied_from) {
 43		if (!supplier->name)
 44			return false;
 45		for (i = 0; i < supply->num_supplies; i++)
 46			if (!strcmp(supplier->name, supply->supplied_from[i]))
 47				return true;
 48	} else {
 49		if (!supply->name)
 50			return false;
 51		for (i = 0; i < supplier->num_supplicants; i++)
 52			if (!strcmp(supplier->supplied_to[i], supply->name))
 53				return true;
 54	}
 55
 56	return false;
 57}
 58
 59static int __power_supply_changed_work(struct device *dev, void *data)
 60{
 61	struct power_supply *psy = (struct power_supply *)data;
 62	struct power_supply *pst = dev_get_drvdata(dev);
 63
 64	if (__power_supply_is_supplied_by(psy, pst)) {
 65		if (pst->external_power_changed)
 66			pst->external_power_changed(pst);
 67	}
 68
 69	return 0;
 70}
 71
 72static void power_supply_changed_work(struct work_struct *work)
 73{
 74	unsigned long flags;
 75	struct power_supply *psy = container_of(work, struct power_supply,
 76						changed_work);
 77
 78	dev_dbg(psy->dev, "%s\n", __func__);
 79
 80	spin_lock_irqsave(&psy->changed_lock, flags);
 81	if (psy->changed) {
 82		psy->changed = false;
 83		spin_unlock_irqrestore(&psy->changed_lock, flags);
 84		class_for_each_device(power_supply_class, NULL, psy,
 85				      __power_supply_changed_work);
 86		power_supply_update_leds(psy);
 87		atomic_notifier_call_chain(&power_supply_notifier,
 88				PSY_EVENT_PROP_CHANGED, psy);
 89		kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
 90		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	if (!psy->changed)
 98		pm_relax(psy->dev);
 99	spin_unlock_irqrestore(&psy->changed_lock, flags);
100}
101
102void power_supply_changed(struct power_supply *psy)
103{
104	unsigned long flags;
105
106	dev_dbg(psy->dev, "%s\n", __func__);
107
108	spin_lock_irqsave(&psy->changed_lock, flags);
109	psy->changed = true;
110	pm_stay_awake(psy->dev);
111	spin_unlock_irqrestore(&psy->changed_lock, flags);
112	schedule_work(&psy->changed_work);
113}
114EXPORT_SYMBOL_GPL(power_supply_changed);
115
116#ifdef CONFIG_OF
117#include <linux/of.h>
118
119static int __power_supply_populate_supplied_from(struct device *dev,
120						 void *data)
121{
122	struct power_supply *psy = (struct power_supply *)data;
123	struct power_supply *epsy = dev_get_drvdata(dev);
124	struct device_node *np;
125	int i = 0;
126
127	do {
128		np = of_parse_phandle(psy->of_node, "power-supplies", i++);
129		if (!np)
130			continue;
131
132		if (np == epsy->of_node) {
133			dev_info(psy->dev, "%s: Found supply : %s\n",
134				psy->name, epsy->name);
135			psy->supplied_from[i-1] = (char *)epsy->name;
136			psy->num_supplies++;
137			of_node_put(np);
138			break;
139		}
140		of_node_put(np);
141	} while (np);
142
143	return 0;
144}
145
146static int power_supply_populate_supplied_from(struct power_supply *psy)
147{
148	int error;
149
150	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
158static int  __power_supply_find_supply_from_node(struct device *dev,
159						 void *data)
160{
161	struct device_node *np = (struct device_node *)data;
162	struct power_supply *epsy = dev_get_drvdata(dev);
163
164	/* return error breaks out of class_for_each_device loop */
165	if (epsy->of_node == np)
166		return -EINVAL;
167
168	return 0;
169}
170
171static 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	class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
183	dev = class_dev_iter_next(&iter);
184
185	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	error = class_for_each_device(power_supply_class, NULL, supply_node,
195				       __power_supply_find_supply_from_node);
196
197	return error ? 0 : -EPROBE_DEFER;
198}
199
200static 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	if (psy->supplied_from && psy->num_supplies > 0)
207		return 0;
208
209	/* No device node found, nothing to do */
210	if (!psy->of_node)
211		return 0;
212
213	do {
214		int ret;
215
216		np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
217		if (!np)
218			continue;
219
220		ret = power_supply_find_supply_from_node(np);
221		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	} while (np);
228
229	/* All supplies found, allocate char ** array for filling */
230	psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from),
231					  GFP_KERNEL);
232	if (!psy->supplied_from) {
233		dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
234		return -ENOMEM;
235	}
236
237	*psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt,
238					   GFP_KERNEL);
239	if (!*psy->supplied_from) {
240		dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
241		return -ENOMEM;
242	}
243
244	return power_supply_populate_supplied_from(psy);
245}
246#else
247static inline int power_supply_check_supplies(struct power_supply *psy)
248{
249	return 0;
250}
251#endif
252
253static int __power_supply_am_i_supplied(struct device *dev, void *data)
254{
255	union power_supply_propval ret = {0,};
256	struct power_supply *psy = (struct power_supply *)data;
257	struct power_supply *epsy = dev_get_drvdata(dev);
258
259	if (__power_supply_is_supplied_by(epsy, psy))
260		if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) {
261			if (ret.intval)
262				return ret.intval;
263		}
264
265	return 0;
266}
267
268int power_supply_am_i_supplied(struct power_supply *psy)
269{
270	int error;
271
272	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	return error;
278}
279EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
280
281static int __power_supply_is_system_supplied(struct device *dev, void *data)
282{
283	union power_supply_propval ret = {0,};
284	struct power_supply *psy = dev_get_drvdata(dev);
285	unsigned int *count = data;
286
287	(*count)++;
288	if (psy->type != POWER_SUPPLY_TYPE_BATTERY) {
289		if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
290			return 0;
291		if (ret.intval)
292			return ret.intval;
293	}
294	return 0;
295}
296
297int power_supply_is_system_supplied(void)
298{
299	int error;
300	unsigned int count = 0;
301
302	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	if (count == 0)
310		return 1;
311
312	return error;
313}
314EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
315
316int power_supply_set_battery_charged(struct power_supply *psy)
317{
318	if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) {
319		psy->set_charged(psy);
320		return 0;
321	}
322
323	return -EINVAL;
324}
325EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
326
327static int power_supply_match_device_by_name(struct device *dev, const void *data)
328{
329	const char *name = data;
330	struct power_supply *psy = dev_get_drvdata(dev);
331
332	return strcmp(psy->name, name) == 0;
333}
334
335struct power_supply *power_supply_get_by_name(const char *name)
336{
337	struct device *dev = class_find_device(power_supply_class, NULL, name,
338					power_supply_match_device_by_name);
339
340	return dev ? dev_get_drvdata(dev) : NULL;
341}
342EXPORT_SYMBOL_GPL(power_supply_get_by_name);
343
344#ifdef CONFIG_OF
345static int power_supply_match_device_node(struct device *dev, const void *data)
346{
347	return dev->parent && dev->parent->of_node == data;
348}
349
350struct 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	power_supply_np = of_parse_phandle(np, property, 0);
357	if (!power_supply_np)
358		return ERR_PTR(-ENODEV);
359
360	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	return dev ? dev_get_drvdata(dev) : NULL;
366}
367EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
368#endif /* CONFIG_OF */
369
370int power_supply_powers(struct power_supply *psy, struct device *dev)
371{
372	return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
373}
374EXPORT_SYMBOL_GPL(power_supply_powers);
375
376static void power_supply_dev_release(struct device *dev)
377{
378	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
379	kfree(dev);
380}
381
382int power_supply_reg_notifier(struct notifier_block *nb)
383{
384	return atomic_notifier_chain_register(&power_supply_notifier, nb);
385}
386EXPORT_SYMBOL_GPL(power_supply_reg_notifier);
387
388void power_supply_unreg_notifier(struct notifier_block *nb)
389{
390	atomic_notifier_chain_unregister(&power_supply_notifier, nb);
391}
392EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
393
394#ifdef CONFIG_THERMAL
395static 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
413static struct thermal_zone_device_ops psy_tzd_ops = {
414	.get_temp = power_supply_read_temp,
415};
416
417static 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
434static 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 */
442static 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
458static 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
474static 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
489static 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
495static 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
514static 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
521static int psy_register_thermal(struct power_supply *psy)
522{
523	return 0;
524}
525
526static void psy_unregister_thermal(struct power_supply *psy)
527{
528}
529
530static int psy_register_cooler(struct power_supply *psy)
531{
532	return 0;
533}
534
535static void psy_unregister_cooler(struct power_supply *psy)
536{
537}
538#endif
539
540int 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	if (!dev)
547		return -ENOMEM;
548
549	device_initialize(dev);
550
551	dev->class = power_supply_class;
552	dev->type = &power_supply_dev_type;
553	dev->parent = parent;
554	dev->release = power_supply_dev_release;
555	dev_set_drvdata(dev, psy);
556	psy->dev = dev;
557
558	rc = dev_set_name(dev, "%s", psy->name);
559	if (rc)
560		goto dev_set_name_failed;
561
562	INIT_WORK(&psy->changed_work, power_supply_changed_work);
563
564	rc = power_supply_check_supplies(psy);
565	if (rc) {
566		dev_info(dev, "Not all required supplies found, defer probe\n");
567		goto check_supplies_failed;
568	}
569
570	spin_lock_init(&psy->changed_lock);
571	rc = device_init_wakeup(dev, true);
572	if (rc)
573		goto wakeup_init_failed;
574
575	rc = device_add(dev);
576	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	rc = power_supply_create_triggers(psy);
588	if (rc)
589		goto create_triggers_failed;
590
591	power_supply_changed(psy);
592
593	goto success;
594
595create_triggers_failed:
596	psy_unregister_cooler(psy);
597register_cooler_failed:
598	psy_unregister_thermal(psy);
599register_thermal_failed:
600	device_del(dev);
601device_add_failed:
602wakeup_init_failed:
603check_supplies_failed:
604dev_set_name_failed:
605	put_device(dev);
606success:
607	return rc;
608}
609EXPORT_SYMBOL_GPL(power_supply_register);
610
611void power_supply_unregister(struct power_supply *psy)
612{
613	cancel_work_sync(&psy->changed_work);
614	sysfs_remove_link(&psy->dev->kobj, "powers");
615	power_supply_remove_triggers(psy);
616	psy_unregister_cooler(psy);
617	psy_unregister_thermal(psy);
618	device_init_wakeup(psy->dev, false);
619	device_unregister(psy->dev);
620}
621EXPORT_SYMBOL_GPL(power_supply_unregister);
622
623static int __init power_supply_class_init(void)
624{
625	power_supply_class = class_create(THIS_MODULE, "power_supply");
626
627	if (IS_ERR(power_supply_class))
628		return PTR_ERR(power_supply_class);
629
630	power_supply_class->dev_uevent = power_supply_uevent;
631	power_supply_init_attrs(&power_supply_dev_type);
632
633	return 0;
634}
635
636static void __exit power_supply_class_exit(void)
637{
638	class_destroy(power_supply_class);
639}
640
641subsys_initcall(power_supply_class_init);
642module_exit(power_supply_class_exit);
643
644MODULE_DESCRIPTION("Universal power supply monitor class");
645MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
646	      "Szabolcs Gyurko, "
647	      "Anton Vorontsov <cbou@mail.ru>");
648MODULE_LICENSE("GPL");