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