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