linux/drivers/power/lp8727_charger.c
<<
>>
Prefs
   1/*
   2 * Driver for LP8727 Micro/Mini USB IC with integrated charger
   3 *
   4 *                      Copyright (C) 2011 Texas Instruments
   5 *                      Copyright (C) 2011 National Semiconductor
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/slab.h>
  15#include <linux/interrupt.h>
  16#include <linux/i2c.h>
  17#include <linux/power_supply.h>
  18#include <linux/platform_data/lp8727.h>
  19
  20#define LP8788_NUM_INTREGS      2
  21#define DEFAULT_DEBOUNCE_MSEC   270
  22
  23/* Registers */
  24#define LP8727_CTRL1            0x1
  25#define LP8727_CTRL2            0x2
  26#define LP8727_SWCTRL           0x3
  27#define LP8727_INT1             0x4
  28#define LP8727_INT2             0x5
  29#define LP8727_STATUS1          0x6
  30#define LP8727_STATUS2          0x7
  31#define LP8727_CHGCTRL2         0x9
  32
  33/* CTRL1 register */
  34#define LP8727_CP_EN            BIT(0)
  35#define LP8727_ADC_EN           BIT(1)
  36#define LP8727_ID200_EN         BIT(4)
  37
  38/* CTRL2 register */
  39#define LP8727_CHGDET_EN        BIT(1)
  40#define LP8727_INT_EN           BIT(6)
  41
  42/* SWCTRL register */
  43#define LP8727_SW_DM1_DM        (0x0 << 0)
  44#define LP8727_SW_DM1_HiZ       (0x7 << 0)
  45#define LP8727_SW_DP2_DP        (0x0 << 3)
  46#define LP8727_SW_DP2_HiZ       (0x7 << 3)
  47
  48/* INT1 register */
  49#define LP8727_IDNO             (0xF << 0)
  50#define LP8727_VBUS             BIT(4)
  51
  52/* STATUS1 register */
  53#define LP8727_CHGSTAT          (3 << 4)
  54#define LP8727_CHPORT           BIT(6)
  55#define LP8727_DCPORT           BIT(7)
  56#define LP8727_STAT_EOC         0x30
  57
  58/* STATUS2 register */
  59#define LP8727_TEMP_STAT        (3 << 5)
  60#define LP8727_TEMP_SHIFT       5
  61
  62/* CHGCTRL2 register */
  63#define LP8727_ICHG_SHIFT       4
  64
  65enum lp8727_dev_id {
  66        LP8727_ID_NONE,
  67        LP8727_ID_TA,
  68        LP8727_ID_DEDICATED_CHG,
  69        LP8727_ID_USB_CHG,
  70        LP8727_ID_USB_DS,
  71        LP8727_ID_MAX,
  72};
  73
  74enum lp8727_die_temp {
  75        LP8788_TEMP_75C,
  76        LP8788_TEMP_95C,
  77        LP8788_TEMP_115C,
  78        LP8788_TEMP_135C,
  79};
  80
  81struct lp8727_psy {
  82        struct power_supply ac;
  83        struct power_supply usb;
  84        struct power_supply batt;
  85};
  86
  87struct lp8727_chg {
  88        struct device *dev;
  89        struct i2c_client *client;
  90        struct mutex xfer_lock;
  91        struct lp8727_psy *psy;
  92        struct lp8727_platform_data *pdata;
  93
  94        /* Charger Data */
  95        enum lp8727_dev_id devid;
  96        struct lp8727_chg_param *chg_param;
  97
  98        /* Interrupt Handling */
  99        int irq;
 100        struct delayed_work work;
 101        unsigned long debounce_jiffies;
 102};
 103
 104static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
 105{
 106        s32 ret;
 107
 108        mutex_lock(&pchg->xfer_lock);
 109        ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);
 110        mutex_unlock(&pchg->xfer_lock);
 111
 112        return (ret != len) ? -EIO : 0;
 113}
 114
 115static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data)
 116{
 117        return lp8727_read_bytes(pchg, reg, data, 1);
 118}
 119
 120static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data)
 121{
 122        int ret;
 123
 124        mutex_lock(&pchg->xfer_lock);
 125        ret = i2c_smbus_write_byte_data(pchg->client, reg, data);
 126        mutex_unlock(&pchg->xfer_lock);
 127
 128        return ret;
 129}
 130
 131static bool lp8727_is_charger_attached(const char *name, int id)
 132{
 133        if (!strcmp(name, "ac"))
 134                return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG;
 135        else if (!strcmp(name, "usb"))
 136                return id == LP8727_ID_USB_CHG;
 137
 138        return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG;
 139}
 140
 141static int lp8727_init_device(struct lp8727_chg *pchg)
 142{
 143        u8 val;
 144        int ret;
 145        u8 intstat[LP8788_NUM_INTREGS];
 146
 147        /* clear interrupts */
 148        ret = lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS);
 149        if (ret)
 150                return ret;
 151
 152        val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN;
 153        ret = lp8727_write_byte(pchg, LP8727_CTRL1, val);
 154        if (ret)
 155                return ret;
 156
 157        val = LP8727_INT_EN | LP8727_CHGDET_EN;
 158        return lp8727_write_byte(pchg, LP8727_CTRL2, val);
 159}
 160
 161static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
 162{
 163        u8 val;
 164
 165        lp8727_read_byte(pchg, LP8727_STATUS1, &val);
 166        return val & LP8727_DCPORT;
 167}
 168
 169static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
 170{
 171        u8 val;
 172
 173        lp8727_read_byte(pchg, LP8727_STATUS1, &val);
 174        return val & LP8727_CHPORT;
 175}
 176
 177static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
 178{
 179        lp8727_write_byte(pchg, LP8727_SWCTRL, sw);
 180}
 181
 182static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
 183{
 184        struct lp8727_platform_data *pdata = pchg->pdata;
 185        u8 devid = LP8727_ID_NONE;
 186        u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ;
 187
 188        switch (id) {
 189        case 0x5:
 190                devid = LP8727_ID_TA;
 191                pchg->chg_param = pdata ? pdata->ac : NULL;
 192                break;
 193        case 0xB:
 194                if (lp8727_is_dedicated_charger(pchg)) {
 195                        pchg->chg_param = pdata ? pdata->ac : NULL;
 196                        devid = LP8727_ID_DEDICATED_CHG;
 197                } else if (lp8727_is_usb_charger(pchg)) {
 198                        pchg->chg_param = pdata ? pdata->usb : NULL;
 199                        devid = LP8727_ID_USB_CHG;
 200                        swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
 201                } else if (vbusin) {
 202                        devid = LP8727_ID_USB_DS;
 203                        swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP;
 204                }
 205                break;
 206        default:
 207                devid = LP8727_ID_NONE;
 208                pchg->chg_param = NULL;
 209                break;
 210        }
 211
 212        pchg->devid = devid;
 213        lp8727_ctrl_switch(pchg, swctrl);
 214}
 215
 216static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
 217{
 218        u8 val;
 219
 220        lp8727_read_byte(pchg, LP8727_CTRL2, &val);
 221        val |= LP8727_CHGDET_EN;
 222        lp8727_write_byte(pchg, LP8727_CTRL2, val);
 223}
 224
 225static void lp8727_delayed_func(struct work_struct *_work)
 226{
 227        struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg,
 228                                                work.work);
 229        u8 intstat[LP8788_NUM_INTREGS];
 230        u8 idno;
 231        u8 vbus;
 232
 233        if (lp8727_read_bytes(pchg, LP8727_INT1, intstat, LP8788_NUM_INTREGS)) {
 234                dev_err(pchg->dev, "can not read INT registers\n");
 235                return;
 236        }
 237
 238        idno = intstat[0] & LP8727_IDNO;
 239        vbus = intstat[0] & LP8727_VBUS;
 240
 241        lp8727_id_detection(pchg, idno, vbus);
 242        lp8727_enable_chgdet(pchg);
 243
 244        power_supply_changed(&pchg->psy->ac);
 245        power_supply_changed(&pchg->psy->usb);
 246        power_supply_changed(&pchg->psy->batt);
 247}
 248
 249static irqreturn_t lp8727_isr_func(int irq, void *ptr)
 250{
 251        struct lp8727_chg *pchg = ptr;
 252
 253        schedule_delayed_work(&pchg->work, pchg->debounce_jiffies);
 254        return IRQ_HANDLED;
 255}
 256
 257static int lp8727_setup_irq(struct lp8727_chg *pchg)
 258{
 259        int ret;
 260        int irq = pchg->client->irq;
 261        unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec :
 262                                                DEFAULT_DEBOUNCE_MSEC;
 263
 264        INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
 265
 266        if (irq <= 0) {
 267                dev_warn(pchg->dev, "invalid irq number: %d\n", irq);
 268                return 0;
 269        }
 270
 271        ret = request_threaded_irq(irq, NULL, lp8727_isr_func,
 272                                IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 273                                "lp8727_irq", pchg);
 274
 275        if (ret)
 276                return ret;
 277
 278        pchg->irq = irq;
 279        pchg->debounce_jiffies = msecs_to_jiffies(delay_msec);
 280
 281        return 0;
 282}
 283
 284static void lp8727_release_irq(struct lp8727_chg *pchg)
 285{
 286        cancel_delayed_work_sync(&pchg->work);
 287
 288        if (pchg->irq)
 289                free_irq(pchg->irq, pchg);
 290}
 291
 292static enum power_supply_property lp8727_charger_prop[] = {
 293        POWER_SUPPLY_PROP_ONLINE,
 294};
 295
 296static enum power_supply_property lp8727_battery_prop[] = {
 297        POWER_SUPPLY_PROP_STATUS,
 298        POWER_SUPPLY_PROP_HEALTH,
 299        POWER_SUPPLY_PROP_PRESENT,
 300        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 301        POWER_SUPPLY_PROP_CAPACITY,
 302        POWER_SUPPLY_PROP_TEMP,
 303};
 304
 305static char *battery_supplied_to[] = {
 306        "main_batt",
 307};
 308
 309static int lp8727_charger_get_property(struct power_supply *psy,
 310                                       enum power_supply_property psp,
 311                                       union power_supply_propval *val)
 312{
 313        struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
 314
 315        if (psp != POWER_SUPPLY_PROP_ONLINE)
 316                return -EINVAL;
 317
 318        val->intval = lp8727_is_charger_attached(psy->name, pchg->devid);
 319
 320        return 0;
 321}
 322
 323static bool lp8727_is_high_temperature(enum lp8727_die_temp temp)
 324{
 325        switch (temp) {
 326        case LP8788_TEMP_95C:
 327        case LP8788_TEMP_115C:
 328        case LP8788_TEMP_135C:
 329                return true;
 330        default:
 331                return false;
 332        }
 333}
 334
 335static int lp8727_battery_get_property(struct power_supply *psy,
 336                                       enum power_supply_property psp,
 337                                       union power_supply_propval *val)
 338{
 339        struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
 340        struct lp8727_platform_data *pdata = pchg->pdata;
 341        enum lp8727_die_temp temp;
 342        u8 read;
 343
 344        switch (psp) {
 345        case POWER_SUPPLY_PROP_STATUS:
 346                if (!lp8727_is_charger_attached(psy->name, pchg->devid)) {
 347                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 348                        return 0;
 349                }
 350
 351                lp8727_read_byte(pchg, LP8727_STATUS1, &read);
 352
 353                val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ?
 354                                POWER_SUPPLY_STATUS_FULL :
 355                                POWER_SUPPLY_STATUS_CHARGING;
 356                break;
 357        case POWER_SUPPLY_PROP_HEALTH:
 358                lp8727_read_byte(pchg, LP8727_STATUS2, &read);
 359                temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT;
 360
 361                val->intval = lp8727_is_high_temperature(temp) ?
 362                        POWER_SUPPLY_HEALTH_OVERHEAT :
 363                        POWER_SUPPLY_HEALTH_GOOD;
 364                break;
 365        case POWER_SUPPLY_PROP_PRESENT:
 366                if (!pdata)
 367                        return -EINVAL;
 368
 369                if (pdata->get_batt_present)
 370                        val->intval = pdata->get_batt_present();
 371                break;
 372        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 373                if (!pdata)
 374                        return -EINVAL;
 375
 376                if (pdata->get_batt_level)
 377                        val->intval = pdata->get_batt_level();
 378                break;
 379        case POWER_SUPPLY_PROP_CAPACITY:
 380                if (!pdata)
 381                        return -EINVAL;
 382
 383                if (pdata->get_batt_capacity)
 384                        val->intval = pdata->get_batt_capacity();
 385                break;
 386        case POWER_SUPPLY_PROP_TEMP:
 387                if (!pdata)
 388                        return -EINVAL;
 389
 390                if (pdata->get_batt_temp)
 391                        val->intval = pdata->get_batt_temp();
 392                break;
 393        default:
 394                break;
 395        }
 396
 397        return 0;
 398}
 399
 400static void lp8727_charger_changed(struct power_supply *psy)
 401{
 402        struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
 403        u8 eoc_level;
 404        u8 ichg;
 405        u8 val;
 406
 407        /* skip if no charger exists */
 408        if (!lp8727_is_charger_attached(psy->name, pchg->devid))
 409                return;
 410
 411        /* update charging parameters */
 412        if (pchg->chg_param) {
 413                eoc_level = pchg->chg_param->eoc_level;
 414                ichg = pchg->chg_param->ichg;
 415                val = (ichg << LP8727_ICHG_SHIFT) | eoc_level;
 416                lp8727_write_byte(pchg, LP8727_CHGCTRL2, val);
 417        }
 418}
 419
 420static int lp8727_register_psy(struct lp8727_chg *pchg)
 421{
 422        struct lp8727_psy *psy;
 423
 424        psy = devm_kzalloc(pchg->dev, sizeof(*psy), GFP_KERNEL);
 425        if (!psy)
 426                return -ENOMEM;
 427
 428        pchg->psy = psy;
 429
 430        psy->ac.name = "ac";
 431        psy->ac.type = POWER_SUPPLY_TYPE_MAINS;
 432        psy->ac.properties = lp8727_charger_prop;
 433        psy->ac.num_properties = ARRAY_SIZE(lp8727_charger_prop);
 434        psy->ac.get_property = lp8727_charger_get_property;
 435        psy->ac.supplied_to = battery_supplied_to;
 436        psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to);
 437
 438        if (power_supply_register(pchg->dev, &psy->ac))
 439                goto err_psy_ac;
 440
 441        psy->usb.name = "usb";
 442        psy->usb.type = POWER_SUPPLY_TYPE_USB;
 443        psy->usb.properties = lp8727_charger_prop;
 444        psy->usb.num_properties = ARRAY_SIZE(lp8727_charger_prop);
 445        psy->usb.get_property = lp8727_charger_get_property;
 446        psy->usb.supplied_to = battery_supplied_to;
 447        psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to);
 448
 449        if (power_supply_register(pchg->dev, &psy->usb))
 450                goto err_psy_usb;
 451
 452        psy->batt.name = "main_batt";
 453        psy->batt.type = POWER_SUPPLY_TYPE_BATTERY;
 454        psy->batt.properties = lp8727_battery_prop;
 455        psy->batt.num_properties = ARRAY_SIZE(lp8727_battery_prop);
 456        psy->batt.get_property = lp8727_battery_get_property;
 457        psy->batt.external_power_changed = lp8727_charger_changed;
 458
 459        if (power_supply_register(pchg->dev, &psy->batt))
 460                goto err_psy_batt;
 461
 462        return 0;
 463
 464err_psy_batt:
 465        power_supply_unregister(&psy->usb);
 466err_psy_usb:
 467        power_supply_unregister(&psy->ac);
 468err_psy_ac:
 469        return -EPERM;
 470}
 471
 472static void lp8727_unregister_psy(struct lp8727_chg *pchg)
 473{
 474        struct lp8727_psy *psy = pchg->psy;
 475
 476        if (!psy)
 477                return;
 478
 479        power_supply_unregister(&psy->ac);
 480        power_supply_unregister(&psy->usb);
 481        power_supply_unregister(&psy->batt);
 482}
 483
 484static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 485{
 486        struct lp8727_chg *pchg;
 487        int ret;
 488
 489        if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
 490                return -EIO;
 491
 492        pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);
 493        if (!pchg)
 494                return -ENOMEM;
 495
 496        pchg->client = cl;
 497        pchg->dev = &cl->dev;
 498        pchg->pdata = cl->dev.platform_data;
 499        i2c_set_clientdata(cl, pchg);
 500
 501        mutex_init(&pchg->xfer_lock);
 502
 503        ret = lp8727_init_device(pchg);
 504        if (ret) {
 505                dev_err(pchg->dev, "i2c communication err: %d", ret);
 506                return ret;
 507        }
 508
 509        ret = lp8727_register_psy(pchg);
 510        if (ret) {
 511                dev_err(pchg->dev, "power supplies register err: %d", ret);
 512                return ret;
 513        }
 514
 515        ret = lp8727_setup_irq(pchg);
 516        if (ret) {
 517                dev_err(pchg->dev, "irq handler err: %d", ret);
 518                lp8727_unregister_psy(pchg);
 519                return ret;
 520        }
 521
 522        return 0;
 523}
 524
 525static int lp8727_remove(struct i2c_client *cl)
 526{
 527        struct lp8727_chg *pchg = i2c_get_clientdata(cl);
 528
 529        lp8727_release_irq(pchg);
 530        lp8727_unregister_psy(pchg);
 531        return 0;
 532}
 533
 534static const struct i2c_device_id lp8727_ids[] = {
 535        {"lp8727", 0},
 536        { }
 537};
 538MODULE_DEVICE_TABLE(i2c, lp8727_ids);
 539
 540static struct i2c_driver lp8727_driver = {
 541        .driver = {
 542                   .name = "lp8727",
 543                   },
 544        .probe = lp8727_probe,
 545        .remove = lp8727_remove,
 546        .id_table = lp8727_ids,
 547};
 548module_i2c_driver(lp8727_driver);
 549
 550MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver");
 551MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
 552MODULE_LICENSE("GPL");
 553
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.