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