Commit 5bf2b994bfe11bfe86231050897b2d881ca544d9
Committed by
Anton Vorontsov
1 parent
cc52a29e62
Exists in
master
and in
39 other branches
pda_power: Add optional OTG transceiver and voltage regulator support
This patch allows machines to use an OTG transceiver driver instead of supplying a custom is_usb_online callback to check USB power. Also, in the case that the OTG transceiver handles charger control when connected to USB, a regulator named "ac_draw" can be supplied instead of the custom set_charge callback to control the charger when connected to AC. The check for (transceiver->state == OTG_STATE_B_PERIPHERAL) in otg_is_usb_online is probably too simple, I'm just using this with a peripheral only device and gpio_vbus + bq24022. I'm not sure which other OTG states can supply power. Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
Showing 2 changed files with 79 additions and 12 deletions Side-by-side Diff
drivers/power/pda_power.c
... | ... | @@ -12,11 +12,14 @@ |
12 | 12 | |
13 | 13 | #include <linux/module.h> |
14 | 14 | #include <linux/platform_device.h> |
15 | +#include <linux/err.h> | |
15 | 16 | #include <linux/interrupt.h> |
16 | 17 | #include <linux/power_supply.h> |
17 | 18 | #include <linux/pda_power.h> |
19 | +#include <linux/regulator/consumer.h> | |
18 | 20 | #include <linux/timer.h> |
19 | 21 | #include <linux/jiffies.h> |
22 | +#include <linux/usb/otg.h> | |
20 | 23 | |
21 | 24 | static inline unsigned int get_irq_flags(struct resource *res) |
22 | 25 | { |
... | ... | @@ -35,6 +38,11 @@ |
35 | 38 | static struct timer_list polling_timer; |
36 | 39 | static int polling; |
37 | 40 | |
41 | +#ifdef CONFIG_USB_OTG_UTILS | |
42 | +static struct otg_transceiver *transceiver; | |
43 | +#endif | |
44 | +static struct regulator *ac_draw; | |
45 | + | |
38 | 46 | enum { |
39 | 47 | PDA_PSY_OFFLINE = 0, |
40 | 48 | PDA_PSY_ONLINE = 1, |
41 | 49 | |
... | ... | @@ -104,18 +112,35 @@ |
104 | 112 | |
105 | 113 | static void update_charger(void) |
106 | 114 | { |
107 | - if (!pdata->set_charge) | |
108 | - return; | |
115 | + static int regulator_enabled; | |
116 | + int max_uA = pdata->ac_max_uA; | |
109 | 117 | |
110 | - if (new_ac_status > 0) { | |
111 | - dev_dbg(dev, "charger on (AC)\n"); | |
112 | - pdata->set_charge(PDA_POWER_CHARGE_AC); | |
113 | - } else if (new_usb_status > 0) { | |
114 | - dev_dbg(dev, "charger on (USB)\n"); | |
115 | - pdata->set_charge(PDA_POWER_CHARGE_USB); | |
116 | - } else { | |
117 | - dev_dbg(dev, "charger off\n"); | |
118 | - pdata->set_charge(0); | |
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 | + } | |
119 | 144 | } |
120 | 145 | } |
121 | 146 | |
... | ... | @@ -194,6 +219,13 @@ |
194 | 219 | jiffies + msecs_to_jiffies(pdata->polling_interval)); |
195 | 220 | } |
196 | 221 | |
222 | +#ifdef CONFIG_USB_OTG_UTILS | |
223 | +static int otg_is_usb_online(void) | |
224 | +{ | |
225 | + return (transceiver->state == OTG_STATE_B_PERIPHERAL); | |
226 | +} | |
227 | +#endif | |
228 | + | |
197 | 229 | static int pda_power_probe(struct platform_device *pdev) |
198 | 230 | { |
199 | 231 | int ret = 0; |
... | ... | @@ -227,6 +259,9 @@ |
227 | 259 | if (!pdata->polling_interval) |
228 | 260 | pdata->polling_interval = 2000; |
229 | 261 | |
262 | + if (!pdata->ac_max_uA) | |
263 | + pdata->ac_max_uA = 500000; | |
264 | + | |
230 | 265 | setup_timer(&charger_timer, charger_timer_func, 0); |
231 | 266 | setup_timer(&supply_timer, supply_timer_func, 0); |
232 | 267 | |
... | ... | @@ -240,6 +275,13 @@ |
240 | 275 | pda_psy_usb.num_supplicants = pdata->num_supplicants; |
241 | 276 | } |
242 | 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 | + | |
243 | 285 | if (pdata->is_ac_online) { |
244 | 286 | ret = power_supply_register(&pdev->dev, &pda_psy_ac); |
245 | 287 | if (ret) { |
... | ... | @@ -261,6 +303,13 @@ |
261 | 303 | } |
262 | 304 | } |
263 | 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 | + | |
264 | 313 | if (pdata->is_usb_online) { |
265 | 314 | ret = power_supply_register(&pdev->dev, &pda_psy_usb); |
266 | 315 | if (ret) { |
267 | 316 | |
... | ... | @@ -300,10 +349,18 @@ |
300 | 349 | usb_supply_failed: |
301 | 350 | if (pdata->is_ac_online && ac_irq) |
302 | 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 | |
303 | 356 | ac_irq_failed: |
304 | 357 | if (pdata->is_ac_online) |
305 | 358 | power_supply_unregister(&pda_psy_ac); |
306 | 359 | ac_supply_failed: |
360 | + if (ac_draw) { | |
361 | + regulator_put(ac_draw); | |
362 | + ac_draw = NULL; | |
363 | + } | |
307 | 364 | if (pdata->exit) |
308 | 365 | pdata->exit(dev); |
309 | 366 | init_failed: |
... | ... | @@ -327,6 +384,14 @@ |
327 | 384 | power_supply_unregister(&pda_psy_usb); |
328 | 385 | if (pdata->is_ac_online) |
329 | 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 | + } | |
330 | 395 | if (pdata->exit) |
331 | 396 | pdata->exit(dev); |
332 | 397 |
include/linux/pda_power.h
... | ... | @@ -31,6 +31,8 @@ |
31 | 31 | unsigned int wait_for_status; /* msecs, default is 500 */ |
32 | 32 | unsigned int wait_for_charger; /* msecs, default is 500 */ |
33 | 33 | unsigned int polling_interval; /* msecs, default is 2000 */ |
34 | + | |
35 | + unsigned long ac_max_uA; /* current to draw when on AC */ | |
34 | 36 | }; |
35 | 37 | |
36 | 38 | #endif /* __PDA_POWER_H__ */ |