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