linux/drivers/power/supply/max8925_power.c
<<
>>
Prefs
   1/*
   2 * Battery driver for Maxim MAX8925
   3 *
   4 * Copyright (c) 2009-2010 Marvell International Ltd.
   5 *      Haojian Zhuang <haojian.zhuang@marvell.com>
   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#include <linux/module.h>
  13#include <linux/err.h>
  14#include <linux/slab.h>
  15#include <linux/of.h>
  16#include <linux/i2c.h>
  17#include <linux/interrupt.h>
  18#include <linux/platform_device.h>
  19#include <linux/power_supply.h>
  20#include <linux/mfd/max8925.h>
  21
  22/* registers in GPM */
  23#define MAX8925_OUT5VEN                 0x54
  24#define MAX8925_OUT3VEN                 0x58
  25#define MAX8925_CHG_CNTL1               0x7c
  26
  27/* bits definition */
  28#define MAX8925_CHG_STAT_VSYSLOW        (1 << 0)
  29#define MAX8925_CHG_STAT_MODE_MASK      (3 << 2)
  30#define MAX8925_CHG_STAT_EN_MASK        (1 << 4)
  31#define MAX8925_CHG_MBDET               (1 << 1)
  32#define MAX8925_CHG_AC_RANGE_MASK       (3 << 6)
  33
  34/* registers in ADC */
  35#define MAX8925_ADC_RES_CNFG1           0x06
  36#define MAX8925_ADC_AVG_CNFG1           0x07
  37#define MAX8925_ADC_ACQ_CNFG1           0x08
  38#define MAX8925_ADC_ACQ_CNFG2           0x09
  39/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */
  40#define MAX8925_ADC_AUX2                0x62
  41#define MAX8925_ADC_VCHG                0x64
  42#define MAX8925_ADC_VBBATT              0x66
  43#define MAX8925_ADC_VMBATT              0x68
  44#define MAX8925_ADC_ISNS                0x6a
  45#define MAX8925_ADC_THM                 0x6c
  46#define MAX8925_ADC_TDIE                0x6e
  47#define MAX8925_CMD_AUX2                0xc8
  48#define MAX8925_CMD_VCHG                0xd0
  49#define MAX8925_CMD_VBBATT              0xd8
  50#define MAX8925_CMD_VMBATT              0xe0
  51#define MAX8925_CMD_ISNS                0xe8
  52#define MAX8925_CMD_THM                 0xf0
  53#define MAX8925_CMD_TDIE                0xf8
  54
  55enum {
  56        MEASURE_AUX2,
  57        MEASURE_VCHG,
  58        MEASURE_VBBATT,
  59        MEASURE_VMBATT,
  60        MEASURE_ISNS,
  61        MEASURE_THM,
  62        MEASURE_TDIE,
  63        MEASURE_MAX,
  64};
  65
  66struct max8925_power_info {
  67        struct max8925_chip     *chip;
  68        struct i2c_client       *gpm;
  69        struct i2c_client       *adc;
  70
  71        struct power_supply     *ac;
  72        struct power_supply     *usb;
  73        struct power_supply     *battery;
  74        int                     irq_base;
  75        unsigned                ac_online:1;
  76        unsigned                usb_online:1;
  77        unsigned                bat_online:1;
  78        unsigned                chg_mode:2;
  79        unsigned                batt_detect:1;  /* detecing MB by ID pin */
  80        unsigned                topoff_threshold:2;
  81        unsigned                fast_charge:3;
  82        unsigned                no_temp_support:1;
  83        unsigned                no_insert_detect:1;
  84
  85        int (*set_charger) (int);
  86};
  87
  88static int __set_charger(struct max8925_power_info *info, int enable)
  89{
  90        struct max8925_chip *chip = info->chip;
  91        if (enable) {
  92                /* enable charger in platform */
  93                if (info->set_charger)
  94                        info->set_charger(1);
  95                /* enable charger */
  96                max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0);
  97        } else {
  98                /* disable charge */
  99                max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
 100                if (info->set_charger)
 101                        info->set_charger(0);
 102        }
 103        dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger"
 104                : "Disable charger");
 105        return 0;
 106}
 107
 108static irqreturn_t max8925_charger_handler(int irq, void *data)
 109{
 110        struct max8925_power_info *info = (struct max8925_power_info *)data;
 111        struct max8925_chip *chip = info->chip;
 112
 113        switch (irq - chip->irq_base) {
 114        case MAX8925_IRQ_VCHG_DC_R:
 115                info->ac_online = 1;
 116                __set_charger(info, 1);
 117                dev_dbg(chip->dev, "Adapter inserted\n");
 118                break;
 119        case MAX8925_IRQ_VCHG_DC_F:
 120                info->ac_online = 0;
 121                __set_charger(info, 0);
 122                dev_dbg(chip->dev, "Adapter removed\n");
 123                break;
 124        case MAX8925_IRQ_VCHG_THM_OK_F:
 125                /* Battery is not ready yet */
 126                dev_dbg(chip->dev, "Battery temperature is out of range\n");
 127        case MAX8925_IRQ_VCHG_DC_OVP:
 128                dev_dbg(chip->dev, "Error detection\n");
 129                __set_charger(info, 0);
 130                break;
 131        case MAX8925_IRQ_VCHG_THM_OK_R:
 132                /* Battery is ready now */
 133                dev_dbg(chip->dev, "Battery temperature is in range\n");
 134                break;
 135        case MAX8925_IRQ_VCHG_SYSLOW_R:
 136                /* VSYS is low */
 137                dev_info(chip->dev, "Sys power is too low\n");
 138                break;
 139        case MAX8925_IRQ_VCHG_SYSLOW_F:
 140                dev_dbg(chip->dev, "Sys power is above low threshold\n");
 141                break;
 142        case MAX8925_IRQ_VCHG_DONE:
 143                __set_charger(info, 0);
 144                dev_dbg(chip->dev, "Charging is done\n");
 145                break;
 146        case MAX8925_IRQ_VCHG_TOPOFF:
 147                dev_dbg(chip->dev, "Charging in top-off mode\n");
 148                break;
 149        case MAX8925_IRQ_VCHG_TMR_FAULT:
 150                __set_charger(info, 0);
 151                dev_dbg(chip->dev, "Safe timer is expired\n");
 152                break;
 153        case MAX8925_IRQ_VCHG_RST:
 154                __set_charger(info, 0);
 155                dev_dbg(chip->dev, "Charger is reset\n");
 156                break;
 157        }
 158        return IRQ_HANDLED;
 159}
 160
 161static int start_measure(struct max8925_power_info *info, int type)
 162{
 163        unsigned char buf[2] = {0, 0};
 164        int meas_cmd;
 165        int meas_reg = 0, ret;
 166
 167        switch (type) {
 168        case MEASURE_VCHG:
 169                meas_cmd = MAX8925_CMD_VCHG;
 170                meas_reg = MAX8925_ADC_VCHG;
 171                break;
 172        case MEASURE_VBBATT:
 173                meas_cmd = MAX8925_CMD_VBBATT;
 174                meas_reg = MAX8925_ADC_VBBATT;
 175                break;
 176        case MEASURE_VMBATT:
 177                meas_cmd = MAX8925_CMD_VMBATT;
 178                meas_reg = MAX8925_ADC_VMBATT;
 179                break;
 180        case MEASURE_ISNS:
 181                meas_cmd = MAX8925_CMD_ISNS;
 182                meas_reg = MAX8925_ADC_ISNS;
 183                break;
 184        default:
 185                return -EINVAL;
 186        }
 187
 188        max8925_reg_write(info->adc, meas_cmd, 0);
 189        max8925_bulk_read(info->adc, meas_reg, 2, buf);
 190        ret = ((buf[0]<<8) | buf[1]) >> 4;
 191
 192        return ret;
 193}
 194
 195static int max8925_ac_get_prop(struct power_supply *psy,
 196                               enum power_supply_property psp,
 197                               union power_supply_propval *val)
 198{
 199        struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
 200        int ret = 0;
 201
 202        switch (psp) {
 203        case POWER_SUPPLY_PROP_ONLINE:
 204                val->intval = info->ac_online;
 205                break;
 206        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 207                if (info->ac_online) {
 208                        ret = start_measure(info, MEASURE_VCHG);
 209                        if (ret >= 0) {
 210                                val->intval = ret * 2000;       /* unit is uV */
 211                                goto out;
 212                        }
 213                }
 214                ret = -ENODATA;
 215                break;
 216        default:
 217                ret = -ENODEV;
 218                break;
 219        }
 220out:
 221        return ret;
 222}
 223
 224static enum power_supply_property max8925_ac_props[] = {
 225        POWER_SUPPLY_PROP_ONLINE,
 226        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 227};
 228
 229static int max8925_usb_get_prop(struct power_supply *psy,
 230                                enum power_supply_property psp,
 231                                union power_supply_propval *val)
 232{
 233        struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
 234        int ret = 0;
 235
 236        switch (psp) {
 237        case POWER_SUPPLY_PROP_ONLINE:
 238                val->intval = info->usb_online;
 239                break;
 240        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 241                if (info->usb_online) {
 242                        ret = start_measure(info, MEASURE_VCHG);
 243                        if (ret >= 0) {
 244                                val->intval = ret * 2000;       /* unit is uV */
 245                                goto out;
 246                        }
 247                }
 248                ret = -ENODATA;
 249                break;
 250        default:
 251                ret = -ENODEV;
 252                break;
 253        }
 254out:
 255        return ret;
 256}
 257
 258static enum power_supply_property max8925_usb_props[] = {
 259        POWER_SUPPLY_PROP_ONLINE,
 260        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 261};
 262
 263static int max8925_bat_get_prop(struct power_supply *psy,
 264                                enum power_supply_property psp,
 265                                union power_supply_propval *val)
 266{
 267        struct max8925_power_info *info = dev_get_drvdata(psy->dev.parent);
 268        int ret = 0;
 269
 270        switch (psp) {
 271        case POWER_SUPPLY_PROP_ONLINE:
 272                val->intval = info->bat_online;
 273                break;
 274        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 275                if (info->bat_online) {
 276                        ret = start_measure(info, MEASURE_VMBATT);
 277                        if (ret >= 0) {
 278                                val->intval = ret * 2000;       /* unit is uV */
 279                                ret = 0;
 280                                break;
 281                        }
 282                }
 283                ret = -ENODATA;
 284                break;
 285        case POWER_SUPPLY_PROP_CURRENT_NOW:
 286                if (info->bat_online) {
 287                        ret = start_measure(info, MEASURE_ISNS);
 288                        if (ret >= 0) {
 289                                /* assume r_sns is 0.02 */
 290                                ret = ((ret * 6250) - 3125) /* uA */;
 291                                val->intval = 0;
 292                                if (ret > 0)
 293                                        val->intval = ret; /* unit is mA */
 294                                ret = 0;
 295                                break;
 296                        }
 297                }
 298                ret = -ENODATA;
 299                break;
 300        case POWER_SUPPLY_PROP_CHARGE_TYPE:
 301                if (!info->bat_online) {
 302                        ret = -ENODATA;
 303                        break;
 304                }
 305                ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
 306                ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2;
 307                switch (ret) {
 308                case 1:
 309                        val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
 310                        break;
 311                case 0:
 312                case 2:
 313                        val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
 314                        break;
 315                case 3:
 316                        val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
 317                        break;
 318                }
 319                ret = 0;
 320                break;
 321        case POWER_SUPPLY_PROP_STATUS:
 322                if (!info->bat_online) {
 323                        ret = -ENODATA;
 324                        break;
 325                }
 326                ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
 327                if (info->usb_online || info->ac_online) {
 328                        val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 329                        if (ret & MAX8925_CHG_STAT_EN_MASK)
 330                                val->intval = POWER_SUPPLY_STATUS_CHARGING;
 331                } else
 332                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 333                ret = 0;
 334                break;
 335        default:
 336                ret = -ENODEV;
 337                break;
 338        }
 339        return ret;
 340}
 341
 342static enum power_supply_property max8925_battery_props[] = {
 343        POWER_SUPPLY_PROP_ONLINE,
 344        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 345        POWER_SUPPLY_PROP_CURRENT_NOW,
 346        POWER_SUPPLY_PROP_CHARGE_TYPE,
 347        POWER_SUPPLY_PROP_STATUS,
 348};
 349
 350static const struct power_supply_desc ac_desc = {
 351        .name           = "max8925-ac",
 352        .type           = POWER_SUPPLY_TYPE_MAINS,
 353        .properties     = max8925_ac_props,
 354        .num_properties = ARRAY_SIZE(max8925_ac_props),
 355        .get_property   = max8925_ac_get_prop,
 356};
 357
 358static const struct power_supply_desc usb_desc = {
 359        .name           = "max8925-usb",
 360        .type           = POWER_SUPPLY_TYPE_USB,
 361        .properties     = max8925_usb_props,
 362        .num_properties = ARRAY_SIZE(max8925_usb_props),
 363        .get_property   = max8925_usb_get_prop,
 364};
 365
 366static const struct power_supply_desc battery_desc = {
 367        .name           = "max8925-battery",
 368        .type           = POWER_SUPPLY_TYPE_BATTERY,
 369        .properties     = max8925_battery_props,
 370        .num_properties = ARRAY_SIZE(max8925_battery_props),
 371        .get_property   = max8925_bat_get_prop,
 372};
 373
 374#define REQUEST_IRQ(_irq, _name)                                        \
 375do {                                                                    \
 376        ret = request_threaded_irq(chip->irq_base + _irq, NULL,         \
 377                                    max8925_charger_handler,            \
 378                                    IRQF_ONESHOT, _name, info);         \
 379        if (ret)                                                        \
 380                dev_err(chip->dev, "Failed to request IRQ #%d: %d\n",   \
 381                        _irq, ret);                                     \
 382} while (0)
 383
 384static int max8925_init_charger(struct max8925_chip *chip,
 385                                          struct max8925_power_info *info)
 386{
 387        int ret;
 388
 389        REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
 390        if (!info->no_insert_detect) {
 391                REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
 392                REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
 393        }
 394        if (!info->no_temp_support) {
 395                REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
 396                REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
 397        }
 398        REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
 399        REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
 400        REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
 401        REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
 402        REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
 403        REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
 404
 405        info->usb_online = 0;
 406        info->bat_online = 0;
 407
 408        /* check for power - can miss interrupt at boot time */
 409        if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
 410                info->ac_online = 1;
 411        else
 412                info->ac_online = 0;
 413
 414        ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
 415        if (ret >= 0) {
 416                /*
 417                 * If battery detection is enabled, ID pin of battery is
 418                 * connected to MBDET pin of MAX8925. It could be used to
 419                 * detect battery presence.
 420                 * Otherwise, we have to assume that battery is always on.
 421                 */
 422                if (info->batt_detect)
 423                        info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
 424                else
 425                        info->bat_online = 1;
 426                if (ret & MAX8925_CHG_AC_RANGE_MASK)
 427                        info->ac_online = 1;
 428                else
 429                        info->ac_online = 0;
 430        }
 431        /* disable charge */
 432        max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
 433        /* set charging current in charge topoff mode */
 434        max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
 435                         info->topoff_threshold << 5);
 436        /* set charing current in fast charge mode */
 437        max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
 438
 439        return 0;
 440}
 441
 442static int max8925_deinit_charger(struct max8925_power_info *info)
 443{
 444        struct max8925_chip *chip = info->chip;
 445        int irq;
 446
 447        irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP;
 448        for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++)
 449                free_irq(irq, info);
 450
 451        return 0;
 452}
 453
 454#ifdef CONFIG_OF
 455static struct max8925_power_pdata *
 456max8925_power_dt_init(struct platform_device *pdev)
 457{
 458        struct device_node *nproot = pdev->dev.parent->of_node;
 459        struct device_node *np;
 460        int batt_detect;
 461        int topoff_threshold;
 462        int fast_charge;
 463        int no_temp_support;
 464        int no_insert_detect;
 465        struct max8925_power_pdata *pdata;
 466
 467        if (!nproot)
 468                return pdev->dev.platform_data;
 469
 470        np = of_get_child_by_name(nproot, "charger");
 471        if (!np) {
 472                dev_err(&pdev->dev, "failed to find charger node\n");
 473                return NULL;
 474        }
 475
 476        pdata = devm_kzalloc(&pdev->dev,
 477                        sizeof(struct max8925_power_pdata),
 478                        GFP_KERNEL);
 479        if (!pdata)
 480                goto ret;
 481
 482        of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
 483        of_property_read_u32(np, "batt-detect", &batt_detect);
 484        of_property_read_u32(np, "fast-charge", &fast_charge);
 485        of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
 486        of_property_read_u32(np, "no-temp-support", &no_temp_support);
 487
 488        pdata->batt_detect = batt_detect;
 489        pdata->fast_charge = fast_charge;
 490        pdata->topoff_threshold = topoff_threshold;
 491        pdata->no_insert_detect = no_insert_detect;
 492        pdata->no_temp_support = no_temp_support;
 493
 494ret:
 495        of_node_put(np);
 496        return pdata;
 497}
 498#else
 499static struct max8925_power_pdata *
 500max8925_power_dt_init(struct platform_device *pdev)
 501{
 502        return pdev->dev.platform_data;
 503}
 504#endif
 505
 506static int max8925_power_probe(struct platform_device *pdev)
 507{
 508        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 509        struct power_supply_config psy_cfg = {}; /* Only for ac and usb */
 510        struct max8925_power_pdata *pdata = NULL;
 511        struct max8925_power_info *info;
 512        int ret;
 513
 514        pdata = max8925_power_dt_init(pdev);
 515        if (!pdata) {
 516                dev_err(&pdev->dev, "platform data isn't assigned to "
 517                        "power supply\n");
 518                return -EINVAL;
 519        }
 520
 521        info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
 522                                GFP_KERNEL);
 523        if (!info)
 524                return -ENOMEM;
 525        info->chip = chip;
 526        info->gpm = chip->i2c;
 527        info->adc = chip->adc;
 528        platform_set_drvdata(pdev, info);
 529
 530        psy_cfg.supplied_to = pdata->supplied_to;
 531        psy_cfg.num_supplicants = pdata->num_supplicants;
 532
 533        info->ac = power_supply_register(&pdev->dev, &ac_desc, &psy_cfg);
 534        if (IS_ERR(info->ac)) {
 535                ret = PTR_ERR(info->ac);
 536                goto out;
 537        }
 538        info->ac->dev.parent = &pdev->dev;
 539
 540        info->usb = power_supply_register(&pdev->dev, &usb_desc, &psy_cfg);
 541        if (IS_ERR(info->usb)) {
 542                ret = PTR_ERR(info->usb);
 543                goto out_unregister_ac;
 544        }
 545        info->usb->dev.parent = &pdev->dev;
 546
 547        info->battery = power_supply_register(&pdev->dev, &battery_desc, NULL);
 548        if (IS_ERR(info->battery)) {
 549                ret = PTR_ERR(info->battery);
 550                goto out_unregister_usb;
 551        }
 552        info->battery->dev.parent = &pdev->dev;
 553
 554        info->batt_detect = pdata->batt_detect;
 555        info->topoff_threshold = pdata->topoff_threshold;
 556        info->fast_charge = pdata->fast_charge;
 557        info->set_charger = pdata->set_charger;
 558        info->no_temp_support = pdata->no_temp_support;
 559        info->no_insert_detect = pdata->no_insert_detect;
 560
 561        max8925_init_charger(chip, info);
 562        return 0;
 563out_unregister_usb:
 564        power_supply_unregister(info->usb);
 565out_unregister_ac:
 566        power_supply_unregister(info->ac);
 567out:
 568        return ret;
 569}
 570
 571static int max8925_power_remove(struct platform_device *pdev)
 572{
 573        struct max8925_power_info *info = platform_get_drvdata(pdev);
 574
 575        if (info) {
 576                power_supply_unregister(info->ac);
 577                power_supply_unregister(info->usb);
 578                power_supply_unregister(info->battery);
 579                max8925_deinit_charger(info);
 580        }
 581        return 0;
 582}
 583
 584static struct platform_driver max8925_power_driver = {
 585        .probe  = max8925_power_probe,
 586        .remove = max8925_power_remove,
 587        .driver = {
 588                .name   = "max8925-power",
 589        },
 590};
 591
 592module_platform_driver(max8925_power_driver);
 593
 594MODULE_LICENSE("GPL");
 595MODULE_DESCRIPTION("Power supply driver for MAX8925");
 596MODULE_ALIAS("platform:max8925-power");
 597