aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAnton Vorontsov <cbou@mail.ru>2007-05-03 16:32:17 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2007-07-10 06:25:59 -0400
commitb2998049cfae4f4a81c4bb048814d34912017bb9 (patch)
treece56af8b6600a31bd78de03787848e1cecc044c8 /drivers
parent4a11b59d8283662193a9c6a9c14c58d1b9bf0617 (diff)
[BATTERY] pda_power platform driver
Common power driver for PDAs and phones with one or two external power supplies (AC/USB) connected to main and backup batteries, and optional builtin charger. It's used to stop logic duplication through different embedded devices. So, power supply *logic* is here. pda_power register power supplies, and will take care about notifying batteries about power changes through external power interface. Currently, power consumption legal limits (including USB power consumption) should be handled by platform code, inside set_charge function. Signed-off-by: Anton Vorontsov <cbou@mail.ru> Signed-off-by: Roman Moravcik <roman.moravcik@gmail.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/pda_power.c261
3 files changed, 270 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 7811fa627412..cc70644db947 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -14,4 +14,11 @@ config POWER_SUPPLY_DEBUG
14 Say Y here to enable debugging messages for power supply class 14 Say Y here to enable debugging messages for power supply class
15 and drivers. 15 and drivers.
16 16
17config PDA_POWER
18 tristate "Generic PDA/phone power driver"
19 help
20 Say Y here to enable generic power driver for PDAs and phones with
21 one or two external power supplies (AC/USB) connected to main and
22 backup batteries, and optional builtin charger.
23
17endif # POWER_SUPPLY 24endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 3c88148983a2..1ff4b6806381 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -13,3 +13,5 @@ EXTRA_CFLAGS += -DDEBUG
13endif 13endif
14 14
15obj-$(CONFIG_POWER_SUPPLY) += power_supply.o 15obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
16
17obj-$(CONFIG_PDA_POWER) += pda_power.o
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
new file mode 100644
index 000000000000..4e1eb040e148
--- /dev/null
+++ b/drivers/power/pda_power.c
@@ -0,0 +1,261 @@
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/interrupt.h>
16#include <linux/power_supply.h>
17#include <linux/pda_power.h>
18#include <linux/timer.h>
19#include <linux/jiffies.h>
20
21static inline unsigned int get_irq_flags(struct resource *res)
22{
23 unsigned int flags = IRQF_DISABLED | IRQF_SHARED;
24
25 flags |= res->flags & IRQF_TRIGGER_MASK;
26
27 return flags;
28}
29
30static struct device *dev;
31static struct pda_power_pdata *pdata;
32static struct resource *ac_irq, *usb_irq;
33static struct timer_list charger_timer;
34static struct timer_list supply_timer;
35
36static int pda_power_get_property(struct power_supply *psy,
37 enum power_supply_property psp,
38 union power_supply_propval *val)
39{
40 switch (psp) {
41 case POWER_SUPPLY_PROP_ONLINE:
42 if (psy->type == POWER_SUPPLY_TYPE_MAINS)
43 val->intval = pdata->is_ac_online ?
44 pdata->is_ac_online() : 0;
45 else
46 val->intval = pdata->is_usb_online ?
47 pdata->is_usb_online() : 0;
48 break;
49 default:
50 return -EINVAL;
51 }
52 return 0;
53}
54
55static enum power_supply_property pda_power_props[] = {
56 POWER_SUPPLY_PROP_ONLINE,
57};
58
59static char *pda_power_supplied_to[] = {
60 "main-battery",
61 "backup-battery",
62};
63
64static struct power_supply pda_power_supplies[] = {
65 {
66 .name = "ac",
67 .type = POWER_SUPPLY_TYPE_MAINS,
68 .supplied_to = pda_power_supplied_to,
69 .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
70 .properties = pda_power_props,
71 .num_properties = ARRAY_SIZE(pda_power_props),
72 .get_property = pda_power_get_property,
73 },
74 {
75 .name = "usb",
76 .type = POWER_SUPPLY_TYPE_USB,
77 .supplied_to = pda_power_supplied_to,
78 .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
79 .properties = pda_power_props,
80 .num_properties = ARRAY_SIZE(pda_power_props),
81 .get_property = pda_power_get_property,
82 },
83};
84
85static void update_charger(void)
86{
87 if (!pdata->set_charge)
88 return;
89
90 if (pdata->is_ac_online && pdata->is_ac_online()) {
91 dev_dbg(dev, "charger on (AC)\n");
92 pdata->set_charge(PDA_POWER_CHARGE_AC);
93 } else if (pdata->is_usb_online && pdata->is_usb_online()) {
94 dev_dbg(dev, "charger on (USB)\n");
95 pdata->set_charge(PDA_POWER_CHARGE_USB);
96 } else {
97 dev_dbg(dev, "charger off\n");
98 pdata->set_charge(0);
99 }
100
101 return;
102}
103
104static void supply_timer_func(unsigned long irq)
105{
106 if (ac_irq && irq == ac_irq->start)
107 power_supply_changed(&pda_power_supplies[0]);
108 else if (usb_irq && irq == usb_irq->start)
109 power_supply_changed(&pda_power_supplies[1]);
110 return;
111}
112
113static void charger_timer_func(unsigned long irq)
114{
115 update_charger();
116
117 /* Okay, charger set. Now wait a bit before notifying supplicants,
118 * charge power should stabilize. */
119 supply_timer.data = irq;
120 mod_timer(&supply_timer,
121 jiffies + msecs_to_jiffies(pdata->wait_for_charger));
122 return;
123}
124
125static irqreturn_t power_changed_isr(int irq, void *unused)
126{
127 /* Wait a bit before reading ac/usb line status and setting charger,
128 * because ac/usb status readings may lag from irq. */
129 charger_timer.data = irq;
130 mod_timer(&charger_timer,
131 jiffies + msecs_to_jiffies(pdata->wait_for_status));
132 return IRQ_HANDLED;
133}
134
135static int pda_power_probe(struct platform_device *pdev)
136{
137 int ret = 0;
138
139 dev = &pdev->dev;
140
141 if (pdev->id != -1) {
142 dev_err(dev, "it's meaningless to register several "
143 "pda_powers; use id = -1\n");
144 ret = -EINVAL;
145 goto wrongid;
146 }
147
148 pdata = pdev->dev.platform_data;
149
150 update_charger();
151
152 if (!pdata->wait_for_status)
153 pdata->wait_for_status = 500;
154
155 if (!pdata->wait_for_charger)
156 pdata->wait_for_charger = 500;
157
158 setup_timer(&charger_timer, charger_timer_func, 0);
159 setup_timer(&supply_timer, supply_timer_func, 0);
160
161 ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
162 usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
163 if (!ac_irq && !usb_irq) {
164 dev_err(dev, "no ac/usb irq specified\n");
165 ret = -ENODEV;
166 goto noirqs;
167 }
168
169 if (pdata->supplied_to) {
170 pda_power_supplies[0].supplied_to = pdata->supplied_to;
171 pda_power_supplies[1].supplied_to = pdata->supplied_to;
172 pda_power_supplies[0].num_supplicants = pdata->num_supplicants;
173 pda_power_supplies[1].num_supplicants = pdata->num_supplicants;
174 }
175
176 ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
177 if (ret) {
178 dev_err(dev, "failed to register %s power supply\n",
179 pda_power_supplies[0].name);
180 goto supply0_failed;
181 }
182
183 ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
184 if (ret) {
185 dev_err(dev, "failed to register %s power supply\n",
186 pda_power_supplies[1].name);
187 goto supply1_failed;
188 }
189
190 if (ac_irq) {
191 ret = request_irq(ac_irq->start, power_changed_isr,
192 get_irq_flags(ac_irq), ac_irq->name,
193 &pda_power_supplies[0]);
194 if (ret) {
195 dev_err(dev, "request ac irq failed\n");
196 goto ac_irq_failed;
197 }
198 }
199
200 if (usb_irq) {
201 ret = request_irq(usb_irq->start, power_changed_isr,
202 get_irq_flags(usb_irq), usb_irq->name,
203 &pda_power_supplies[1]);
204 if (ret) {
205 dev_err(dev, "request usb irq failed\n");
206 goto usb_irq_failed;
207 }
208 }
209
210 goto success;
211
212usb_irq_failed:
213 if (ac_irq)
214 free_irq(ac_irq->start, &pda_power_supplies[0]);
215ac_irq_failed:
216 power_supply_unregister(&pda_power_supplies[1]);
217supply1_failed:
218 power_supply_unregister(&pda_power_supplies[0]);
219supply0_failed:
220noirqs:
221wrongid:
222success:
223 return ret;
224}
225
226static int pda_power_remove(struct platform_device *pdev)
227{
228 if (usb_irq)
229 free_irq(usb_irq->start, &pda_power_supplies[1]);
230 if (ac_irq)
231 free_irq(ac_irq->start, &pda_power_supplies[0]);
232 del_timer_sync(&charger_timer);
233 del_timer_sync(&supply_timer);
234 power_supply_unregister(&pda_power_supplies[1]);
235 power_supply_unregister(&pda_power_supplies[0]);
236 return 0;
237}
238
239static struct platform_driver pda_power_pdrv = {
240 .driver = {
241 .name = "pda-power",
242 },
243 .probe = pda_power_probe,
244 .remove = pda_power_remove,
245};
246
247static int __init pda_power_init(void)
248{
249 return platform_driver_register(&pda_power_pdrv);
250}
251
252static void __exit pda_power_exit(void)
253{
254 platform_driver_unregister(&pda_power_pdrv);
255 return;
256}
257
258module_init(pda_power_init);
259module_exit(pda_power_exit);
260MODULE_LICENSE("GPL");
261MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");