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#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");
 511
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.