linux/drivers/power/twl4030_charger.c
<<
>>
Prefs
   1/*
   2 * TWL4030/TPS65950 BCI (Battery Charger Interface) driver
   3 *
   4 * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com>
   5 *
   6 * based on twl4030_bci_battery.c by TI
   7 * Copyright (C) 2008 Texas Instruments, Inc.
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 */
  14
  15#include <linux/init.h>
  16#include <linux/module.h>
  17#include <linux/slab.h>
  18#include <linux/platform_device.h>
  19#include <linux/interrupt.h>
  20#include <linux/i2c/twl.h>
  21#include <linux/power_supply.h>
  22#include <linux/notifier.h>
  23#include <linux/usb/otg.h>
  24
  25#define TWL4030_BCIMSTATEC      0x02
  26#define TWL4030_BCIICHG         0x08
  27#define TWL4030_BCIVAC          0x0a
  28#define TWL4030_BCIVBUS         0x0c
  29#define TWL4030_BCIMFSTS4       0x10
  30#define TWL4030_BCICTL1         0x23
  31
  32#define TWL4030_BCIAUTOWEN      BIT(5)
  33#define TWL4030_CONFIG_DONE     BIT(4)
  34#define TWL4030_BCIAUTOUSB      BIT(1)
  35#define TWL4030_BCIAUTOAC       BIT(0)
  36#define TWL4030_CGAIN           BIT(5)
  37#define TWL4030_USBFASTMCHG     BIT(2)
  38#define TWL4030_STS_VBUS        BIT(7)
  39#define TWL4030_STS_USB_ID      BIT(2)
  40
  41/* BCI interrupts */
  42#define TWL4030_WOVF            BIT(0) /* Watchdog overflow */
  43#define TWL4030_TMOVF           BIT(1) /* Timer overflow */
  44#define TWL4030_ICHGHIGH        BIT(2) /* Battery charge current high */
  45#define TWL4030_ICHGLOW         BIT(3) /* Battery cc. low / FSM state change */
  46#define TWL4030_ICHGEOC         BIT(4) /* Battery current end-of-charge */
  47#define TWL4030_TBATOR2         BIT(5) /* Battery temperature out of range 2 */
  48#define TWL4030_TBATOR1         BIT(6) /* Battery temperature out of range 1 */
  49#define TWL4030_BATSTS          BIT(7) /* Battery status */
  50
  51#define TWL4030_VBATLVL         BIT(0) /* VBAT level */
  52#define TWL4030_VBATOV          BIT(1) /* VBAT overvoltage */
  53#define TWL4030_VBUSOV          BIT(2) /* VBUS overvoltage */
  54#define TWL4030_ACCHGOV         BIT(3) /* Ac charger overvoltage */
  55
  56#define TWL4030_MSTATEC_USB             BIT(4)
  57#define TWL4030_MSTATEC_AC              BIT(5)
  58#define TWL4030_MSTATEC_MASK            0x0f
  59#define TWL4030_MSTATEC_QUICK1          0x02
  60#define TWL4030_MSTATEC_QUICK7          0x07
  61#define TWL4030_MSTATEC_COMPLETE1       0x0b
  62#define TWL4030_MSTATEC_COMPLETE4       0x0e
  63
  64static bool allow_usb;
  65module_param(allow_usb, bool, 1);
  66MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
  67
  68struct twl4030_bci {
  69        struct device           *dev;
  70        struct power_supply     ac;
  71        struct power_supply     usb;
  72        struct otg_transceiver  *transceiver;
  73        struct notifier_block   otg_nb;
  74        struct work_struct      work;
  75        int                     irq_chg;
  76        int                     irq_bci;
  77
  78        unsigned long           event;
  79};
  80
  81/*
  82 * clear and set bits on an given register on a given module
  83 */
  84static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg)
  85{
  86        u8 val = 0;
  87        int ret;
  88
  89        ret = twl_i2c_read_u8(mod_no, &val, reg);
  90        if (ret)
  91                return ret;
  92
  93        val &= ~clear;
  94        val |= set;
  95
  96        return twl_i2c_write_u8(mod_no, val, reg);
  97}
  98
  99static int twl4030_bci_read(u8 reg, u8 *val)
 100{
 101        return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg);
 102}
 103
 104static int twl4030_clear_set_boot_bci(u8 clear, u8 set)
 105{
 106        return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0,
 107                        TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set,
 108                        TWL4030_PM_MASTER_BOOT_BCI);
 109}
 110
 111static int twl4030bci_read_adc_val(u8 reg)
 112{
 113        int ret, temp;
 114        u8 val;
 115
 116        /* read MSB */
 117        ret = twl4030_bci_read(reg + 1, &val);
 118        if (ret)
 119                return ret;
 120
 121        temp = (int)(val & 0x03) << 8;
 122
 123        /* read LSB */
 124        ret = twl4030_bci_read(reg, &val);
 125        if (ret)
 126                return ret;
 127
 128        return temp | val;
 129}
 130
 131/*
 132 * Check if VBUS power is present
 133 */
 134static int twl4030_bci_have_vbus(struct twl4030_bci *bci)
 135{
 136        int ret;
 137        u8 hwsts;
 138
 139        ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts,
 140                              TWL4030_PM_MASTER_STS_HW_CONDITIONS);
 141        if (ret < 0)
 142                return 0;
 143
 144        dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts);
 145
 146        /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */
 147        if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID))
 148                return 1;
 149
 150        return 0;
 151}
 152
 153/*
 154 * Enable/Disable USB Charge funtionality.
 155 */
 156static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable)
 157{
 158        int ret;
 159
 160        if (enable) {
 161                /* Check for USB charger conneted */
 162                if (!twl4030_bci_have_vbus(bci))
 163                        return -ENODEV;
 164
 165                /*
 166                 * Until we can find out what current the device can provide,
 167                 * require a module param to enable USB charging.
 168                 */
 169                if (!allow_usb) {
 170                        dev_warn(bci->dev, "USB charging is disabled.\n");
 171                        return -EACCES;
 172                }
 173
 174                /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
 175                ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB);
 176                if (ret < 0)
 177                        return ret;
 178
 179                /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
 180                ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0,
 181                        TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4);
 182        } else {
 183                ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0);
 184        }
 185
 186        return ret;
 187}
 188
 189/*
 190 * Enable/Disable AC Charge funtionality.
 191 */
 192static int twl4030_charger_enable_ac(bool enable)
 193{
 194        int ret;
 195
 196        if (enable)
 197                ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC);
 198        else
 199                ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0);
 200
 201        return ret;
 202}
 203
 204/*
 205 * TWL4030 CHG_PRES (AC charger presence) events
 206 */
 207static irqreturn_t twl4030_charger_interrupt(int irq, void *arg)
 208{
 209        struct twl4030_bci *bci = arg;
 210
 211        dev_dbg(bci->dev, "CHG_PRES irq\n");
 212        power_supply_changed(&bci->ac);
 213        power_supply_changed(&bci->usb);
 214
 215        return IRQ_HANDLED;
 216}
 217
 218/*
 219 * TWL4030 BCI monitoring events
 220 */
 221static irqreturn_t twl4030_bci_interrupt(int irq, void *arg)
 222{
 223        struct twl4030_bci *bci = arg;
 224        u8 irqs1, irqs2;
 225        int ret;
 226
 227        ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1,
 228                              TWL4030_INTERRUPTS_BCIISR1A);
 229        if (ret < 0)
 230                return IRQ_HANDLED;
 231
 232        ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2,
 233                              TWL4030_INTERRUPTS_BCIISR2A);
 234        if (ret < 0)
 235                return IRQ_HANDLED;
 236
 237        dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1);
 238
 239        if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) {
 240                /* charger state change, inform the core */
 241                power_supply_changed(&bci->ac);
 242                power_supply_changed(&bci->usb);
 243        }
 244
 245        /* various monitoring events, for now we just log them here */
 246        if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1))
 247                dev_warn(bci->dev, "battery temperature out of range\n");
 248
 249        if (irqs1 & TWL4030_BATSTS)
 250                dev_crit(bci->dev, "battery disconnected\n");
 251
 252        if (irqs2 & TWL4030_VBATOV)
 253                dev_crit(bci->dev, "VBAT overvoltage\n");
 254
 255        if (irqs2 & TWL4030_VBUSOV)
 256                dev_crit(bci->dev, "VBUS overvoltage\n");
 257
 258        if (irqs2 & TWL4030_ACCHGOV)
 259                dev_crit(bci->dev, "Ac charger overvoltage\n");
 260
 261        return IRQ_HANDLED;
 262}
 263
 264static void twl4030_bci_usb_work(struct work_struct *data)
 265{
 266        struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work);
 267
 268        switch (bci->event) {
 269        case USB_EVENT_VBUS:
 270        case USB_EVENT_CHARGER:
 271                twl4030_charger_enable_usb(bci, true);
 272                break;
 273        case USB_EVENT_NONE:
 274                twl4030_charger_enable_usb(bci, false);
 275                break;
 276        }
 277}
 278
 279static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val,
 280                               void *priv)
 281{
 282        struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb);
 283
 284        dev_dbg(bci->dev, "OTG notify %lu\n", val);
 285
 286        bci->event = val;
 287        schedule_work(&bci->work);
 288
 289        return NOTIFY_OK;
 290}
 291
 292/*
 293 * TI provided formulas:
 294 * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
 295 * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7
 296 * Here we use integer approximation of:
 297 * CGAIN == 0: val * 1.6618 - 0.85
 298 * CGAIN == 1: (val * 1.6618 - 0.85) * 2
 299 */
 300static int twl4030_charger_get_current(void)
 301{
 302        int curr;
 303        int ret;
 304        u8 bcictl1;
 305
 306        curr = twl4030bci_read_adc_val(TWL4030_BCIICHG);
 307        if (curr < 0)
 308                return curr;
 309
 310        ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
 311        if (ret)
 312                return ret;
 313
 314        ret = (curr * 16618 - 850 * 10000) / 10;
 315        if (bcictl1 & TWL4030_CGAIN)
 316                ret *= 2;
 317
 318        return ret;
 319}
 320
 321/*
 322 * Returns the main charge FSM state
 323 * Or < 0 on failure.
 324 */
 325static int twl4030bci_state(struct twl4030_bci *bci)
 326{
 327        int ret;
 328        u8 state;
 329
 330        ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state);
 331        if (ret) {
 332                pr_err("twl4030_bci: error reading BCIMSTATEC\n");
 333                return ret;
 334        }
 335
 336        dev_dbg(bci->dev, "state: %02x\n", state);
 337
 338        return state;
 339}
 340
 341static int twl4030_bci_state_to_status(int state)
 342{
 343        state &= TWL4030_MSTATEC_MASK;
 344        if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7)
 345                return POWER_SUPPLY_STATUS_CHARGING;
 346        else if (TWL4030_MSTATEC_COMPLETE1 <= state &&
 347                                        state <= TWL4030_MSTATEC_COMPLETE4)
 348                return POWER_SUPPLY_STATUS_FULL;
 349        else
 350                return POWER_SUPPLY_STATUS_NOT_CHARGING;
 351}
 352
 353static int twl4030_bci_get_property(struct power_supply *psy,
 354                                    enum power_supply_property psp,
 355                                    union power_supply_propval *val)
 356{
 357        struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent);
 358        int is_charging;
 359        int state;
 360        int ret;
 361
 362        state = twl4030bci_state(bci);
 363        if (state < 0)
 364                return state;
 365
 366        if (psy->type == POWER_SUPPLY_TYPE_USB)
 367                is_charging = state & TWL4030_MSTATEC_USB;
 368        else
 369                is_charging = state & TWL4030_MSTATEC_AC;
 370
 371        switch (psp) {
 372        case POWER_SUPPLY_PROP_STATUS:
 373                if (is_charging)
 374                        val->intval = twl4030_bci_state_to_status(state);
 375                else
 376                        val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 377                break;
 378        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 379                /* charging must be active for meaningful result */
 380                if (!is_charging)
 381                        return -ENODATA;
 382                if (psy->type == POWER_SUPPLY_TYPE_USB) {
 383                        ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS);
 384                        if (ret < 0)
 385                                return ret;
 386                        /* BCIVBUS uses ADCIN8, 7/1023 V/step */
 387                        val->intval = ret * 6843;
 388                } else {
 389                        ret = twl4030bci_read_adc_val(TWL4030_BCIVAC);
 390                        if (ret < 0)
 391                                return ret;
 392                        /* BCIVAC uses ADCIN11, 10/1023 V/step */
 393                        val->intval = ret * 9775;
 394                }
 395                break;
 396        case POWER_SUPPLY_PROP_CURRENT_NOW:
 397                if (!is_charging)
 398                        return -ENODATA;
 399                /* current measurement is shared between AC and USB */
 400                ret = twl4030_charger_get_current();
 401                if (ret < 0)
 402                        return ret;
 403                val->intval = ret;
 404                break;
 405        case POWER_SUPPLY_PROP_ONLINE:
 406                val->intval = is_charging &&
 407                        twl4030_bci_state_to_status(state) !=
 408                                POWER_SUPPLY_STATUS_NOT_CHARGING;
 409                break;
 410        default:
 411                return -EINVAL;
 412        }
 413
 414        return 0;
 415}
 416
 417static enum power_supply_property twl4030_charger_props[] = {
 418        POWER_SUPPLY_PROP_STATUS,
 419        POWER_SUPPLY_PROP_ONLINE,
 420        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 421        POWER_SUPPLY_PROP_CURRENT_NOW,
 422};
 423
 424static int __init twl4030_bci_probe(struct platform_device *pdev)
 425{
 426        struct twl4030_bci *bci;
 427        int ret;
 428        int reg;
 429
 430        bci = kzalloc(sizeof(*bci), GFP_KERNEL);
 431        if (bci == NULL)
 432                return -ENOMEM;
 433
 434        bci->dev = &pdev->dev;
 435        bci->irq_chg = platform_get_irq(pdev, 0);
 436        bci->irq_bci = platform_get_irq(pdev, 1);
 437
 438        platform_set_drvdata(pdev, bci);
 439
 440        bci->ac.name = "twl4030_ac";
 441        bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
 442        bci->ac.properties = twl4030_charger_props;
 443        bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props);
 444        bci->ac.get_property = twl4030_bci_get_property;
 445
 446        ret = power_supply_register(&pdev->dev, &bci->ac);
 447        if (ret) {
 448                dev_err(&pdev->dev, "failed to register ac: %d\n", ret);
 449                goto fail_register_ac;
 450        }
 451
 452        bci->usb.name = "twl4030_usb";
 453        bci->usb.type = POWER_SUPPLY_TYPE_USB;
 454        bci->usb.properties = twl4030_charger_props;
 455        bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props);
 456        bci->usb.get_property = twl4030_bci_get_property;
 457
 458        ret = power_supply_register(&pdev->dev, &bci->usb);
 459        if (ret) {
 460                dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
 461                goto fail_register_usb;
 462        }
 463
 464        ret = request_threaded_irq(bci->irq_chg, NULL,
 465                        twl4030_charger_interrupt, 0, pdev->name, bci);
 466        if (ret < 0) {
 467                dev_err(&pdev->dev, "could not request irq %d, status %d\n",
 468                        bci->irq_chg, ret);
 469                goto fail_chg_irq;
 470        }
 471
 472        ret = request_threaded_irq(bci->irq_bci, NULL,
 473                        twl4030_bci_interrupt, 0, pdev->name, bci);
 474        if (ret < 0) {
 475                dev_err(&pdev->dev, "could not request irq %d, status %d\n",
 476                        bci->irq_bci, ret);
 477                goto fail_bci_irq;
 478        }
 479
 480        INIT_WORK(&bci->work, twl4030_bci_usb_work);
 481
 482        bci->transceiver = otg_get_transceiver();
 483        if (bci->transceiver != NULL) {
 484                bci->otg_nb.notifier_call = twl4030_bci_usb_ncb;
 485                otg_register_notifier(bci->transceiver, &bci->otg_nb);
 486        }
 487
 488        /* Enable interrupts now. */
 489        reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
 490                TWL4030_TBATOR1 | TWL4030_BATSTS);
 491        ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
 492                               TWL4030_INTERRUPTS_BCIIMR1A);
 493        if (ret < 0) {
 494                dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
 495                goto fail_unmask_interrupts;
 496        }
 497
 498        reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
 499        ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
 500                               TWL4030_INTERRUPTS_BCIIMR2A);
 501        if (ret < 0)
 502                dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret);
 503
 504        twl4030_charger_enable_ac(true);
 505        twl4030_charger_enable_usb(bci, true);
 506
 507        return 0;
 508
 509fail_unmask_interrupts:
 510        if (bci->transceiver != NULL) {
 511                otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
 512                otg_put_transceiver(bci->transceiver);
 513        }
 514        free_irq(bci->irq_bci, bci);
 515fail_bci_irq:
 516        free_irq(bci->irq_chg, bci);
 517fail_chg_irq:
 518        power_supply_unregister(&bci->usb);
 519fail_register_usb:
 520        power_supply_unregister(&bci->ac);
 521fail_register_ac:
 522        platform_set_drvdata(pdev, NULL);
 523        kfree(bci);
 524
 525        return ret;
 526}
 527
 528static int __exit twl4030_bci_remove(struct platform_device *pdev)
 529{
 530        struct twl4030_bci *bci = platform_get_drvdata(pdev);
 531
 532        twl4030_charger_enable_ac(false);
 533        twl4030_charger_enable_usb(bci, false);
 534
 535        /* mask interrupts */
 536        twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
 537                         TWL4030_INTERRUPTS_BCIIMR1A);
 538        twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
 539                         TWL4030_INTERRUPTS_BCIIMR2A);
 540
 541        if (bci->transceiver != NULL) {
 542                otg_unregister_notifier(bci->transceiver, &bci->otg_nb);
 543                otg_put_transceiver(bci->transceiver);
 544        }
 545        free_irq(bci->irq_bci, bci);
 546        free_irq(bci->irq_chg, bci);
 547        power_supply_unregister(&bci->usb);
 548        power_supply_unregister(&bci->ac);
 549        platform_set_drvdata(pdev, NULL);
 550        kfree(bci);
 551
 552        return 0;
 553}
 554
 555static struct platform_driver twl4030_bci_driver = {
 556        .driver = {
 557                .name   = "twl4030_bci",
 558                .owner  = THIS_MODULE,
 559        },
 560        .remove = __exit_p(twl4030_bci_remove),
 561};
 562
 563static int __init twl4030_bci_init(void)
 564{
 565        return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe);
 566}
 567module_init(twl4030_bci_init);
 568
 569static void __exit twl4030_bci_exit(void)
 570{
 571        platform_driver_unregister(&twl4030_bci_driver);
 572}
 573module_exit(twl4030_bci_exit);
 574
 575MODULE_AUTHOR("Gražydas Ignotas");
 576MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
 577MODULE_LICENSE("GPL");
 578MODULE_ALIAS("platform:twl4030_bci");
 579