Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.11-rc4 510 lines 12 kB view raw
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 25static inline unsigned int get_irq_flags(struct resource *res) 26{ 27 return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK); 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; 35static struct timer_list polling_timer; 36static int polling; 37 38#if IS_ENABLED(CONFIG_USB_PHY) 39static struct usb_phy *transceiver; 40static struct notifier_block otg_nb; 41#endif 42 43static struct regulator *ac_draw; 44 45enum { 46 PDA_PSY_OFFLINE = 0, 47 PDA_PSY_ONLINE = 1, 48 PDA_PSY_TO_CHANGE, 49}; 50static int new_ac_status = -1; 51static int new_usb_status = -1; 52static int ac_status = -1; 53static int usb_status = -1; 54 55static 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 74static enum power_supply_property pda_power_props[] = { 75 POWER_SUPPLY_PROP_ONLINE, 76}; 77 78static char *pda_power_supplied_to[] = { 79 "main-battery", 80 "backup-battery", 81}; 82 83static 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 93static 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 103static 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 112static 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 146static 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 159static 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 171static void charger_timer_func(unsigned long unused) 172{ 173 update_status(); 174 psy_changed(); 175} 176 177static 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 196static 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) 222static int otg_is_usb_online(void) 223{ 224 return (transceiver->last_event == USB_EVENT_VBUS || 225 transceiver->last_event == USB_EVENT_ENUMERATED); 226} 227 228static int otg_is_ac_online(void) 229{ 230 return (transceiver->last_event == USB_EVENT_CHARGER); 231} 232 233static 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 263static 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) 395otg_reg_notifier_failed: 396 if (pdata->is_usb_online && usb_irq) 397 free_irq(usb_irq->start, &pda_psy_usb); 398#endif 399usb_irq_failed: 400 if (pdata->is_usb_online) 401 power_supply_unregister(&pda_psy_usb); 402usb_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 409ac_irq_failed: 410 if (pdata->is_ac_online) 411 power_supply_unregister(&pda_psy_ac); 412ac_supply_failed: 413 if (ac_draw) { 414 regulator_put(ac_draw); 415 ac_draw = NULL; 416 } 417 if (pdata->exit) 418 pdata->exit(dev); 419init_failed: 420wrongid: 421 return ret; 422} 423 424static 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 455static int ac_wakeup_enabled; 456static int usb_wakeup_enabled; 457 458static 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 477static 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 496static 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 506module_platform_driver(pda_power_pdrv); 507 508MODULE_LICENSE("GPL"); 509MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); 510MODULE_ALIAS("platform:pda-power");