Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1/*
  2 * Common power driver for PDAs and phones with one or two external
  3 * power supplies (AC/USB) connected to main and backup batteries,
  4 * and optional builtin charger.
  5 *
  6 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
  7 *
  8 * This program is free software; you can redistribute it and/or modify
  9 * it under the terms of the GNU General Public License version 2 as
 10 * published by the Free Software Foundation.
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/platform_device.h>
 15#include <linux/err.h>
 16#include <linux/interrupt.h>
 17#include <linux/notifier.h>
 18#include <linux/power_supply.h>
 19#include <linux/pda_power.h>
 20#include <linux/regulator/consumer.h>
 21#include <linux/timer.h>
 22#include <linux/jiffies.h>
 23#include <linux/usb/otg.h>
 24
 25static inline unsigned int get_irq_flags(struct resource *res)
 26{
 27	unsigned int flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED;
 28
 29	flags |= res->flags & IRQF_TRIGGER_MASK;
 30
 31	return flags;
 32}
 33
 34static struct device *dev;
 35static struct pda_power_pdata *pdata;
 36static struct resource *ac_irq, *usb_irq;
 37static struct timer_list charger_timer;
 38static struct timer_list supply_timer;
 39static struct timer_list polling_timer;
 40static int polling;
 41
 42#ifdef CONFIG_USB_OTG_UTILS
 43static struct usb_phy *transceiver;
 44static struct notifier_block otg_nb;
 45#endif
 46
 47static struct regulator *ac_draw;
 48
 49enum {
 50	PDA_PSY_OFFLINE = 0,
 51	PDA_PSY_ONLINE = 1,
 52	PDA_PSY_TO_CHANGE,
 53};
 54static int new_ac_status = -1;
 55static int new_usb_status = -1;
 56static int ac_status = -1;
 57static int usb_status = -1;
 58
 59static int pda_power_get_property(struct power_supply *psy,
 60				  enum power_supply_property psp,
 61				  union power_supply_propval *val)
 62{
 63	switch (psp) {
 64	case POWER_SUPPLY_PROP_ONLINE:
 65		if (psy->type == POWER_SUPPLY_TYPE_MAINS)
 66			val->intval = pdata->is_ac_online ?
 67				      pdata->is_ac_online() : 0;
 68		else
 69			val->intval = pdata->is_usb_online ?
 70				      pdata->is_usb_online() : 0;
 71		break;
 72	default:
 73		return -EINVAL;
 74	}
 75	return 0;
 76}
 77
 78static enum power_supply_property pda_power_props[] = {
 79	POWER_SUPPLY_PROP_ONLINE,
 80};
 81
 82static char *pda_power_supplied_to[] = {
 83	"main-battery",
 84	"backup-battery",
 85};
 86
 87static struct power_supply pda_psy_ac = {
 88	.name = "ac",
 89	.type = POWER_SUPPLY_TYPE_MAINS,
 90	.supplied_to = pda_power_supplied_to,
 91	.num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
 92	.properties = pda_power_props,
 93	.num_properties = ARRAY_SIZE(pda_power_props),
 94	.get_property = pda_power_get_property,
 95};
 96
 97static struct power_supply pda_psy_usb = {
 98	.name = "usb",
 99	.type = POWER_SUPPLY_TYPE_USB,
100	.supplied_to = pda_power_supplied_to,
101	.num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
102	.properties = pda_power_props,
103	.num_properties = ARRAY_SIZE(pda_power_props),
104	.get_property = pda_power_get_property,
105};
106
107static void update_status(void)
108{
109	if (pdata->is_ac_online)
110		new_ac_status = !!pdata->is_ac_online();
111
112	if (pdata->is_usb_online)
113		new_usb_status = !!pdata->is_usb_online();
114}
115
116static void update_charger(void)
117{
118	static int regulator_enabled;
119	int max_uA = pdata->ac_max_uA;
120
121	if (pdata->set_charge) {
122		if (new_ac_status > 0) {
123			dev_dbg(dev, "charger on (AC)\n");
124			pdata->set_charge(PDA_POWER_CHARGE_AC);
125		} else if (new_usb_status > 0) {
126			dev_dbg(dev, "charger on (USB)\n");
127			pdata->set_charge(PDA_POWER_CHARGE_USB);
128		} else {
129			dev_dbg(dev, "charger off\n");
130			pdata->set_charge(0);
131		}
132	} else if (ac_draw) {
133		if (new_ac_status > 0) {
134			regulator_set_current_limit(ac_draw, max_uA, max_uA);
135			if (!regulator_enabled) {
136				dev_dbg(dev, "charger on (AC)\n");
137				regulator_enable(ac_draw);
138				regulator_enabled = 1;
139			}
140		} else {
141			if (regulator_enabled) {
142				dev_dbg(dev, "charger off\n");
143				regulator_disable(ac_draw);
144				regulator_enabled = 0;
145			}
146		}
147	}
148}
149
150static void supply_timer_func(unsigned long unused)
151{
152	if (ac_status == PDA_PSY_TO_CHANGE) {
153		ac_status = new_ac_status;
154		power_supply_changed(&pda_psy_ac);
155	}
156
157	if (usb_status == PDA_PSY_TO_CHANGE) {
158		usb_status = new_usb_status;
159		power_supply_changed(&pda_psy_usb);
160	}
161}
162
163static void psy_changed(void)
164{
165	update_charger();
166
167	/*
168	 * Okay, charger set. Now wait a bit before notifying supplicants,
169	 * charge power should stabilize.
170	 */
171	mod_timer(&supply_timer,
172		  jiffies + msecs_to_jiffies(pdata->wait_for_charger));
173}
174
175static void charger_timer_func(unsigned long unused)
176{
177	update_status();
178	psy_changed();
179}
180
181static irqreturn_t power_changed_isr(int irq, void *power_supply)
182{
183	if (power_supply == &pda_psy_ac)
184		ac_status = PDA_PSY_TO_CHANGE;
185	else if (power_supply == &pda_psy_usb)
186		usb_status = PDA_PSY_TO_CHANGE;
187	else
188		return IRQ_NONE;
189
190	/*
191	 * Wait a bit before reading ac/usb line status and setting charger,
192	 * because ac/usb status readings may lag from irq.
193	 */
194	mod_timer(&charger_timer,
195		  jiffies + msecs_to_jiffies(pdata->wait_for_status));
196
197	return IRQ_HANDLED;
198}
199
200static void polling_timer_func(unsigned long unused)
201{
202	int changed = 0;
203
204	dev_dbg(dev, "polling...\n");
205
206	update_status();
207
208	if (!ac_irq && new_ac_status != ac_status) {
209		ac_status = PDA_PSY_TO_CHANGE;
210		changed = 1;
211	}
212
213	if (!usb_irq && new_usb_status != usb_status) {
214		usb_status = PDA_PSY_TO_CHANGE;
215		changed = 1;
216	}
217
218	if (changed)
219		psy_changed();
220
221	mod_timer(&polling_timer,
222		  jiffies + msecs_to_jiffies(pdata->polling_interval));
223}
224
225#ifdef CONFIG_USB_OTG_UTILS
226static int otg_is_usb_online(void)
227{
228	return (transceiver->last_event == USB_EVENT_VBUS ||
229		transceiver->last_event == USB_EVENT_ENUMERATED);
230}
231
232static int otg_is_ac_online(void)
233{
234	return (transceiver->last_event == USB_EVENT_CHARGER);
235}
236
237static int otg_handle_notification(struct notifier_block *nb,
238		unsigned long event, void *unused)
239{
240	switch (event) {
241	case USB_EVENT_CHARGER:
242		ac_status = PDA_PSY_TO_CHANGE;
243		break;
244	case USB_EVENT_VBUS:
245	case USB_EVENT_ENUMERATED:
246		usb_status = PDA_PSY_TO_CHANGE;
247		break;
248	case USB_EVENT_NONE:
249		ac_status = PDA_PSY_TO_CHANGE;
250		usb_status = PDA_PSY_TO_CHANGE;
251		break;
252	default:
253		return NOTIFY_OK;
254	}
255
256	/*
257	 * Wait a bit before reading ac/usb line status and setting charger,
258	 * because ac/usb status readings may lag from irq.
259	 */
260	mod_timer(&charger_timer,
261		  jiffies + msecs_to_jiffies(pdata->wait_for_status));
262
263	return NOTIFY_OK;
264}
265#endif
266
267static int pda_power_probe(struct platform_device *pdev)
268{
269	int ret = 0;
270
271	dev = &pdev->dev;
272
273	if (pdev->id != -1) {
274		dev_err(dev, "it's meaningless to register several "
275			"pda_powers; use id = -1\n");
276		ret = -EINVAL;
277		goto wrongid;
278	}
279
280	pdata = pdev->dev.platform_data;
281
282	if (pdata->init) {
283		ret = pdata->init(dev);
284		if (ret < 0)
285			goto init_failed;
286	}
287
288	update_status();
289	update_charger();
290
291	if (!pdata->wait_for_status)
292		pdata->wait_for_status = 500;
293
294	if (!pdata->wait_for_charger)
295		pdata->wait_for_charger = 500;
296
297	if (!pdata->polling_interval)
298		pdata->polling_interval = 2000;
299
300	if (!pdata->ac_max_uA)
301		pdata->ac_max_uA = 500000;
302
303	setup_timer(&charger_timer, charger_timer_func, 0);
304	setup_timer(&supply_timer, supply_timer_func, 0);
305
306	ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
307	usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
308
309	if (pdata->supplied_to) {
310		pda_psy_ac.supplied_to = pdata->supplied_to;
311		pda_psy_ac.num_supplicants = pdata->num_supplicants;
312		pda_psy_usb.supplied_to = pdata->supplied_to;
313		pda_psy_usb.num_supplicants = pdata->num_supplicants;
314	}
315
316	ac_draw = regulator_get(dev, "ac_draw");
317	if (IS_ERR(ac_draw)) {
318		dev_dbg(dev, "couldn't get ac_draw regulator\n");
319		ac_draw = NULL;
320		ret = PTR_ERR(ac_draw);
321	}
322
323#ifdef CONFIG_USB_OTG_UTILS
324	transceiver = usb_get_transceiver();
325	if (transceiver && !pdata->is_usb_online) {
326		pdata->is_usb_online = otg_is_usb_online;
327	}
328	if (transceiver && !pdata->is_ac_online) {
329		pdata->is_ac_online = otg_is_ac_online;
330	}
331#endif
332
333	if (pdata->is_ac_online) {
334		ret = power_supply_register(&pdev->dev, &pda_psy_ac);
335		if (ret) {
336			dev_err(dev, "failed to register %s power supply\n",
337				pda_psy_ac.name);
338			goto ac_supply_failed;
339		}
340
341		if (ac_irq) {
342			ret = request_irq(ac_irq->start, power_changed_isr,
343					  get_irq_flags(ac_irq), ac_irq->name,
344					  &pda_psy_ac);
345			if (ret) {
346				dev_err(dev, "request ac irq failed\n");
347				goto ac_irq_failed;
348			}
349		} else {
350			polling = 1;
351		}
352	}
353
354	if (pdata->is_usb_online) {
355		ret = power_supply_register(&pdev->dev, &pda_psy_usb);
356		if (ret) {
357			dev_err(dev, "failed to register %s power supply\n",
358				pda_psy_usb.name);
359			goto usb_supply_failed;
360		}
361
362		if (usb_irq) {
363			ret = request_irq(usb_irq->start, power_changed_isr,
364					  get_irq_flags(usb_irq),
365					  usb_irq->name, &pda_psy_usb);
366			if (ret) {
367				dev_err(dev, "request usb irq failed\n");
368				goto usb_irq_failed;
369			}
370		} else {
371			polling = 1;
372		}
373	}
374
375#ifdef CONFIG_USB_OTG_UTILS
376	if (transceiver && pdata->use_otg_notifier) {
377		otg_nb.notifier_call = otg_handle_notification;
378		ret = usb_register_notifier(transceiver, &otg_nb);
379		if (ret) {
380			dev_err(dev, "failure to register otg notifier\n");
381			goto otg_reg_notifier_failed;
382		}
383		polling = 0;
384	}
385#endif
386
387	if (polling) {
388		dev_dbg(dev, "will poll for status\n");
389		setup_timer(&polling_timer, polling_timer_func, 0);
390		mod_timer(&polling_timer,
391			  jiffies + msecs_to_jiffies(pdata->polling_interval));
392	}
393
394	if (ac_irq || usb_irq)
395		device_init_wakeup(&pdev->dev, 1);
396
397	return 0;
398
399#ifdef CONFIG_USB_OTG_UTILS
400otg_reg_notifier_failed:
401	if (pdata->is_usb_online && usb_irq)
402		free_irq(usb_irq->start, &pda_psy_usb);
403#endif
404usb_irq_failed:
405	if (pdata->is_usb_online)
406		power_supply_unregister(&pda_psy_usb);
407usb_supply_failed:
408	if (pdata->is_ac_online && ac_irq)
409		free_irq(ac_irq->start, &pda_psy_ac);
410#ifdef CONFIG_USB_OTG_UTILS
411	if (transceiver)
412		usb_put_transceiver(transceiver);
413#endif
414ac_irq_failed:
415	if (pdata->is_ac_online)
416		power_supply_unregister(&pda_psy_ac);
417ac_supply_failed:
418	if (ac_draw) {
419		regulator_put(ac_draw);
420		ac_draw = NULL;
421	}
422	if (pdata->exit)
423		pdata->exit(dev);
424init_failed:
425wrongid:
426	return ret;
427}
428
429static int pda_power_remove(struct platform_device *pdev)
430{
431	if (pdata->is_usb_online && usb_irq)
432		free_irq(usb_irq->start, &pda_psy_usb);
433	if (pdata->is_ac_online && ac_irq)
434		free_irq(ac_irq->start, &pda_psy_ac);
435
436	if (polling)
437		del_timer_sync(&polling_timer);
438	del_timer_sync(&charger_timer);
439	del_timer_sync(&supply_timer);
440
441	if (pdata->is_usb_online)
442		power_supply_unregister(&pda_psy_usb);
443	if (pdata->is_ac_online)
444		power_supply_unregister(&pda_psy_ac);
445#ifdef CONFIG_USB_OTG_UTILS
446	if (transceiver)
447		usb_put_transceiver(transceiver);
448#endif
449	if (ac_draw) {
450		regulator_put(ac_draw);
451		ac_draw = NULL;
452	}
453	if (pdata->exit)
454		pdata->exit(dev);
455
456	return 0;
457}
458
459#ifdef CONFIG_PM
460static int ac_wakeup_enabled;
461static int usb_wakeup_enabled;
462
463static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
464{
465	if (pdata->suspend) {
466		int ret = pdata->suspend(state);
467
468		if (ret)
469			return ret;
470	}
471
472	if (device_may_wakeup(&pdev->dev)) {
473		if (ac_irq)
474			ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
475		if (usb_irq)
476			usb_wakeup_enabled = !enable_irq_wake(usb_irq->start);
477	}
478
479	return 0;
480}
481
482static int pda_power_resume(struct platform_device *pdev)
483{
484	if (device_may_wakeup(&pdev->dev)) {
485		if (usb_irq && usb_wakeup_enabled)
486			disable_irq_wake(usb_irq->start);
487		if (ac_irq && ac_wakeup_enabled)
488			disable_irq_wake(ac_irq->start);
489	}
490
491	if (pdata->resume)
492		return pdata->resume();
493
494	return 0;
495}
496#else
497#define pda_power_suspend NULL
498#define pda_power_resume NULL
499#endif /* CONFIG_PM */
500
501static struct platform_driver pda_power_pdrv = {
502	.driver = {
503		.name = "pda-power",
504	},
505	.probe = pda_power_probe,
506	.remove = pda_power_remove,
507	.suspend = pda_power_suspend,
508	.resume = pda_power_resume,
509};
510
511module_platform_driver(pda_power_pdrv);
512
513MODULE_LICENSE("GPL");
514MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
515MODULE_ALIAS("platform:pda-power");