linux/drivers/power/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
 350#define REQUEST_IRQ(_irq, _name)                                        \
 351do {                                                                    \
 352        ret = request_threaded_irq(chip->irq_base + _irq, NULL,         \
 353                                    max8925_charger_handler,            \
 354                                    IRQF_ONESHOT, _name, info);         \
 355        if (ret)                                                        \
 356                dev_err(chip->dev, "Failed to request IRQ #%d: %d\n",   \
 357                        _irq, ret);                                     \
 358} while (0)
 359
 360static int max8925_init_charger(struct max8925_chip *chip,
 361                                          struct max8925_power_info *info)
 362{
 363        int ret;
 364
 365        REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
 366        if (!info->no_insert_detect) {
 367                REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
 368                REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
 369        }
 370        if (!info->no_temp_support) {
 371                REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
 372                REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
 373        }
 374        REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
 375        REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
 376        REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
 377        REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done");
 378        REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
 379        REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
 380
 381        info->usb_online = 0;
 382        info->bat_online = 0;
 383
 384        /* check for power - can miss interrupt at boot time */
 385        if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
 386                info->ac_online = 1;
 387        else
 388                info->ac_online = 0;
 389
 390        ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
 391        if (ret >= 0) {
 392                /*
 393                 * If battery detection is enabled, ID pin of battery is
 394                 * connected to MBDET pin of MAX8925. It could be used to
 395                 * detect battery presence.
 396                 * Otherwise, we have to assume that battery is always on.
 397                 */
 398                if (info->batt_detect)
 399                        info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1;
 400                else
 401                        info->bat_online = 1;
 402                if (ret & MAX8925_CHG_AC_RANGE_MASK)
 403                        info->ac_online = 1;
 404                else
 405                        info->ac_online = 0;
 406        }
 407        /* disable charge */
 408        max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7);
 409        /* set charging current in charge topoff mode */
 410        max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5,
 411                         info->topoff_threshold << 5);
 412        /* set charing current in fast charge mode */
 413        max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge);
 414
 415        return 0;
 416}
 417
 418static int max8925_deinit_charger(struct max8925_power_info *info)
 419{
 420        struct max8925_chip *chip = info->chip;
 421        int irq;
 422
 423        irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP;
 424        for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++)
 425                free_irq(irq, info);
 426
 427        return 0;
 428}
 429
 430#ifdef CONFIG_OF
 431static struct max8925_power_pdata *
 432max8925_power_dt_init(struct platform_device *pdev)
 433{
 434        struct device_node *nproot = pdev->dev.parent->of_node;
 435        struct device_node *np;
 436        int batt_detect;
 437        int topoff_threshold;
 438        int fast_charge;
 439        int no_temp_support;
 440        int no_insert_detect;
 441        struct max8925_power_pdata *pdata;
 442
 443        if (!nproot)
 444                return pdev->dev.platform_data;
 445
 446        np = of_find_node_by_name(nproot, "charger");
 447        if (!np) {
 448                dev_err(&pdev->dev, "failed to find charger node\n");
 449                return NULL;
 450        }
 451
 452        pdata = devm_kzalloc(&pdev->dev,
 453                        sizeof(struct max8925_power_pdata),
 454                        GFP_KERNEL);
 455
 456        of_property_read_u32(np, "topoff-threshold", &topoff_threshold);
 457        of_property_read_u32(np, "batt-detect", &batt_detect);
 458        of_property_read_u32(np, "fast-charge", &fast_charge);
 459        of_property_read_u32(np, "no-insert-detect", &no_insert_detect);
 460        of_property_read_u32(np, "no-temp-support", &no_temp_support);
 461        of_node_put(np);
 462
 463        pdata->batt_detect = batt_detect;
 464        pdata->fast_charge = fast_charge;
 465        pdata->topoff_threshold = topoff_threshold;
 466        pdata->no_insert_detect = no_insert_detect;
 467        pdata->no_temp_support = no_temp_support;
 468
 469        return pdata;
 470}
 471#else
 472static struct max8925_power_pdata *
 473max8925_power_dt_init(struct platform_device *pdev)
 474{
 475        return pdev->dev.platform_data;
 476}
 477#endif
 478
 479static int max8925_power_probe(struct platform_device *pdev)
 480{
 481        struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
 482        struct max8925_power_pdata *pdata = NULL;
 483        struct max8925_power_info *info;
 484        int ret;
 485
 486        pdata = max8925_power_dt_init(pdev);
 487        if (!pdata) {
 488                dev_err(&pdev->dev, "platform data isn't assigned to "
 489                        "power supply\n");
 490                return -EINVAL;
 491        }
 492
 493        info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_power_info),
 494                                GFP_KERNEL);
 495        if (!info)
 496                return -ENOMEM;
 497        info->chip = chip;
 498        info->gpm = chip->i2c;
 499        info->adc = chip->adc;
 500        platform_set_drvdata(pdev, info);
 501
 502        info->ac.name = "max8925-ac";
 503        info->ac.type = POWER_SUPPLY_TYPE_MAINS;
 504        info->ac.properties = max8925_ac_props;
 505        info->ac.num_properties = ARRAY_SIZE(max8925_ac_props);
 506        info->ac.get_property = max8925_ac_get_prop;
 507        info->ac.supplied_to = pdata->supplied_to;
 508        info->ac.num_supplicants = pdata->num_supplicants;
 509        ret = power_supply_register(&pdev->dev, &info->ac);
 510        if (ret)
 511                goto out;
 512        info->ac.dev->parent = &pdev->dev;
 513
 514        info->usb.name = "max8925-usb";
 515        info->usb.type = POWER_SUPPLY_TYPE_USB;
 516        info->usb.properties = max8925_usb_props;
 517        info->usb.num_properties = ARRAY_SIZE(max8925_usb_props);
 518        info->usb.get_property = max8925_usb_get_prop;
 519        info->usb.supplied_to = pdata->supplied_to;
 520        info->usb.num_supplicants = pdata->num_supplicants;
 521
 522        ret = power_supply_register(&pdev->dev, &info->usb);
 523        if (ret)
 524                goto out_usb;
 525        info->usb.dev->parent = &pdev->dev;
 526
 527        info->battery.name = "max8925-battery";
 528        info->battery.type = POWER_SUPPLY_TYPE_BATTERY;
 529        info->battery.properties = max8925_battery_props;
 530        info->battery.num_properties = ARRAY_SIZE(max8925_battery_props);
 531        info->battery.get_property = max8925_bat_get_prop;
 532        ret = power_supply_register(&pdev->dev, &info->battery);
 533        if (ret)
 534                goto out_battery;
 535        info->battery.dev->parent = &pdev->dev;
 536
 537        info->batt_detect = pdata->batt_detect;
 538        info->topoff_threshold = pdata->topoff_threshold;
 539        info->fast_charge = pdata->fast_charge;
 540        info->set_charger = pdata->set_charger;
 541        info->no_temp_support = pdata->no_temp_support;
 542        info->no_insert_detect = pdata->no_insert_detect;
 543
 544        max8925_init_charger(chip, info);
 545        return 0;
 546out_battery:
 547        power_supply_unregister(&info->battery);
 548out_usb:
 549        power_supply_unregister(&info->ac);
 550out:
 551        return ret;
 552}
 553
 554static int max8925_power_remove(struct platform_device *pdev)
 555{
 556        struct max8925_power_info *info = platform_get_drvdata(pdev);
 557
 558        if (info) {
 559                power_supply_unregister(&info->ac);
 560                power_supply_unregister(&info->usb);
 561                power_supply_unregister(&info->battery);
 562                max8925_deinit_charger(info);
 563        }
 564        return 0;
 565}
 566
 567static struct platform_driver max8925_power_driver = {
 568        .probe  = max8925_power_probe,
 569        .remove = max8925_power_remove,
 570        .driver = {
 571                .name   = "max8925-power",
 572        },
 573};
 574
 575module_platform_driver(max8925_power_driver);
 576
 577MODULE_LICENSE("GPL");
 578MODULE_DESCRIPTION("Power supply driver for MAX8925");
 579MODULE_ALIAS("platform:max8925-power");
 580
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.