linux/drivers/power/pda_power.c
<<
>>
Prefs
   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