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/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#ifdef CONFIG_USB_OTG_UTILS
  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#ifdef CONFIG_USB_OTG_UTILS
 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        update_status();
 285        update_charger();
 286
 287        if (!pdata->wait_for_status)
 288                pdata->wait_for_status = 500;
 289
 290        if (!pdata->wait_for_charger)
 291                pdata->wait_for_charger = 500;
 292
 293        if (!pdata->polling_interval)
 294                pdata->polling_interval = 2000;
 295
 296        if (!pdata->ac_max_uA)
 297                pdata->ac_max_uA = 500000;
 298
 299        setup_timer(&charger_timer, charger_timer_func, 0);
 300        setup_timer(&supply_timer, supply_timer_func, 0);
 301
 302        ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
 303        usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
 304
 305        if (pdata->supplied_to) {
 306                pda_psy_ac.supplied_to = pdata->supplied_to;
 307                pda_psy_ac.num_supplicants = pdata->num_supplicants;
 308                pda_psy_usb.supplied_to = pdata->supplied_to;
 309                pda_psy_usb.num_supplicants = pdata->num_supplicants;
 310        }
 311
 312        ac_draw = regulator_get(dev, "ac_draw");
 313        if (IS_ERR(ac_draw)) {
 314                dev_dbg(dev, "couldn't get ac_draw regulator\n");
 315                ac_draw = NULL;
 316                ret = PTR_ERR(ac_draw);
 317        }
 318
 319#ifdef CONFIG_USB_OTG_UTILS
 320        transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
 321        if (!IS_ERR_OR_NULL(transceiver)) {
 322                if (!pdata->is_usb_online)
 323                        pdata->is_usb_online = otg_is_usb_online;
 324                if (!pdata->is_ac_online)
 325                        pdata->is_ac_online = otg_is_ac_online;
 326        }
 327#endif
 328
 329        if (pdata->is_ac_online) {
 330                ret = power_supply_register(&pdev->dev, &pda_psy_ac);
 331                if (ret) {
 332                        dev_err(dev, "failed to register %s power supply\n",
 333                                pda_psy_ac.name);
 334                        goto ac_supply_failed;
 335                }
 336
 337                if (ac_irq) {
 338                        ret = request_irq(ac_irq->start, power_changed_isr,
 339                                          get_irq_flags(ac_irq), ac_irq->name,
 340                                          &pda_psy_ac);
 341                        if (ret) {
 342                                dev_err(dev, "request ac irq failed\n");
 343                                goto ac_irq_failed;
 344                        }
 345                } else {
 346                        polling = 1;
 347                }
 348        }
 349
 350        if (pdata->is_usb_online) {
 351                ret = power_supply_register(&pdev->dev, &pda_psy_usb);
 352                if (ret) {
 353                        dev_err(dev, "failed to register %s power supply\n",
 354                                pda_psy_usb.name);
 355                        goto usb_supply_failed;
 356                }
 357
 358                if (usb_irq) {
 359                        ret = request_irq(usb_irq->start, power_changed_isr,
 360                                          get_irq_flags(usb_irq),
 361                                          usb_irq->name, &pda_psy_usb);
 362                        if (ret) {
 363                                dev_err(dev, "request usb irq failed\n");
 364                                goto usb_irq_failed;
 365                        }
 366                } else {
 367                        polling = 1;
 368                }
 369        }
 370
 371#ifdef CONFIG_USB_OTG_UTILS
 372        if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) {
 373                otg_nb.notifier_call = otg_handle_notification;
 374                ret = usb_register_notifier(transceiver, &otg_nb);
 375                if (ret) {
 376                        dev_err(dev, "failure to register otg notifier\n");
 377                        goto otg_reg_notifier_failed;
 378                }
 379                polling = 0;
 380        }
 381#endif
 382
 383        if (polling) {
 384                dev_dbg(dev, "will poll for status\n");
 385                setup_timer(&polling_timer, polling_timer_func, 0);
 386                mod_timer(&polling_timer,
 387                          jiffies + msecs_to_jiffies(pdata->polling_interval));
 388        }
 389
 390        if (ac_irq || usb_irq)
 391                device_init_wakeup(&pdev->dev, 1);
 392
 393        return 0;
 394
 395#ifdef CONFIG_USB_OTG_UTILS
 396otg_reg_notifier_failed:
 397        if (pdata->is_usb_online && usb_irq)
 398                free_irq(usb_irq->start, &pda_psy_usb);
 399#endif
 400usb_irq_failed:
 401        if (pdata->is_usb_online)
 402                power_supply_unregister(&pda_psy_usb);
 403usb_supply_failed:
 404        if (pdata->is_ac_online && ac_irq)
 405                free_irq(ac_irq->start, &pda_psy_ac);
 406#ifdef CONFIG_USB_OTG_UTILS
 407        if (!IS_ERR_OR_NULL(transceiver))
 408                usb_put_phy(transceiver);
 409#endif
 410ac_irq_failed:
 411        if (pdata->is_ac_online)
 412                power_supply_unregister(&pda_psy_ac);
 413ac_supply_failed:
 414        if (ac_draw) {
 415                regulator_put(ac_draw);
 416                ac_draw = NULL;
 417        }
 418        if (pdata->exit)
 419                pdata->exit(dev);
 420init_failed:
 421wrongid:
 422        return ret;
 423}
 424
 425static int pda_power_remove(struct platform_device *pdev)
 426{
 427        if (pdata->is_usb_online && usb_irq)
 428                free_irq(usb_irq->start, &pda_psy_usb);
 429        if (pdata->is_ac_online && ac_irq)
 430                free_irq(ac_irq->start, &pda_psy_ac);
 431
 432        if (polling)
 433                del_timer_sync(&polling_timer);
 434        del_timer_sync(&charger_timer);
 435        del_timer_sync(&supply_timer);
 436
 437        if (pdata->is_usb_online)
 438                power_supply_unregister(&pda_psy_usb);
 439        if (pdata->is_ac_online)
 440                power_supply_unregister(&pda_psy_ac);
 441#ifdef CONFIG_USB_OTG_UTILS
 442        if (!IS_ERR_OR_NULL(transceiver))
 443                usb_put_phy(transceiver);
 444#endif
 445        if (ac_draw) {
 446                regulator_put(ac_draw);
 447                ac_draw = NULL;
 448        }
 449        if (pdata->exit)
 450                pdata->exit(dev);
 451
 452        return 0;
 453}
 454
 455#ifdef CONFIG_PM
 456static int ac_wakeup_enabled;
 457static int usb_wakeup_enabled;
 458
 459static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
 460{
 461        if (pdata->suspend) {
 462                int ret = pdata->suspend(state);
 463
 464                if (ret)
 465                        return ret;
 466        }
 467
 468        if (device_may_wakeup(&pdev->dev)) {
 469                if (ac_irq)
 470                        ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
 471                if (usb_irq)
 472                        usb_wakeup_enabled = !enable_irq_wake(usb_irq->start);
 473        }
 474
 475        return 0;
 476}
 477
 478static int pda_power_resume(struct platform_device *pdev)
 479{
 480        if (device_may_wakeup(&pdev->dev)) {
 481                if (usb_irq && usb_wakeup_enabled)
 482                        disable_irq_wake(usb_irq->start);
 483                if (ac_irq && ac_wakeup_enabled)
 484                        disable_irq_wake(ac_irq->start);
 485        }
 486
 487        if (pdata->resume)
 488                return pdata->resume();
 489
 490        return 0;
 491}
 492#else
 493#define pda_power_suspend NULL
 494#define pda_power_resume NULL
 495#endif /* CONFIG_PM */
 496
 497static struct platform_driver pda_power_pdrv = {
 498        .driver = {
 499                .name = "pda-power",
 500        },
 501        .probe = pda_power_probe,
 502        .remove = pda_power_remove,
 503        .suspend = pda_power_suspend,
 504        .resume = pda_power_resume,
 505};
 506
 507module_platform_driver(pda_power_pdrv);
 508
 509MODULE_LICENSE("GPL");
 510MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
 511MODULE_ALIAS("platform:pda-power");
 512
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.