linux/drivers/power/supply/pcf50633-charger.c
<<
>>
Prefs
   1/* NXP PCF50633 Main Battery Charger Driver
   2 *
   3 * (C) 2006-2008 by Openmoko, Inc.
   4 * Author: Balaji Rao <balajirrao@openmoko.org>
   5 * All rights reserved.
   6 *
   7 * Broken down from monstrous PCF50633 driver mainly by
   8 * Harald Welte, Andy Green and Werner Almesberger
   9 *
  10 *  This program is free software; you can redistribute  it and/or modify it
  11 *  under  the terms of  the GNU General  Public License as published by the
  12 *  Free Software Foundation;  either version 2 of the  License, or (at your
  13 *  option) any later version.
  14 *
  15 */
  16
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/slab.h>
  20#include <linux/init.h>
  21#include <linux/types.h>
  22#include <linux/device.h>
  23#include <linux/sysfs.h>
  24#include <linux/platform_device.h>
  25#include <linux/power_supply.h>
  26
  27#include <linux/mfd/pcf50633/core.h>
  28#include <linux/mfd/pcf50633/mbc.h>
  29
  30struct pcf50633_mbc {
  31        struct pcf50633 *pcf;
  32
  33        int adapter_online;
  34        int usb_online;
  35
  36        struct power_supply *usb;
  37        struct power_supply *adapter;
  38        struct power_supply *ac;
  39};
  40
  41int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
  42{
  43        struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
  44        int ret = 0;
  45        u8 bits;
  46        u8 mbcs2, chgmod;
  47        unsigned int mbcc5;
  48
  49        if (ma >= 1000) {
  50                bits = PCF50633_MBCC7_USB_1000mA;
  51                ma = 1000;
  52        } else if (ma >= 500) {
  53                bits = PCF50633_MBCC7_USB_500mA;
  54                ma = 500;
  55        } else if (ma >= 100) {
  56                bits = PCF50633_MBCC7_USB_100mA;
  57                ma = 100;
  58        } else {
  59                bits = PCF50633_MBCC7_USB_SUSPEND;
  60                ma = 0;
  61        }
  62
  63        ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
  64                                        PCF50633_MBCC7_USB_MASK, bits);
  65        if (ret)
  66                dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
  67        else
  68                dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
  69
  70        /*
  71         * We limit the charging current to be the USB current limit.
  72         * The reason is that on pcf50633, when it enters PMU Standby mode,
  73         * which it does when the device goes "off", the USB current limit
  74         * reverts to the variant default.  In at least one common case, that
  75         * default is 500mA.  By setting the charging current to be the same
  76         * as the USB limit we set here before PMU standby, we enforce it only
  77         * using the correct amount of current even when the USB current limit
  78         * gets reset to the wrong thing
  79         */
  80
  81        if (mbc->pcf->pdata->charger_reference_current_ma) {
  82                mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
  83                if (mbcc5 > 255)
  84                        mbcc5 = 255;
  85                pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
  86        }
  87
  88        mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
  89        chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
  90
  91        /* If chgmod == BATFULL, setting chgena has no effect.
  92         * Datasheet says we need to set resume instead but when autoresume is
  93         * used resume doesn't work. Clear and set chgena instead.
  94         */
  95        if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
  96                pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
  97                                PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
  98        else {
  99                pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
 100                                PCF50633_MBCC1_CHGENA);
 101                pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
 102                                PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
 103        }
 104
 105        power_supply_changed(mbc->usb);
 106
 107        return ret;
 108}
 109EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
 110
 111int pcf50633_mbc_get_status(struct pcf50633 *pcf)
 112{
 113        struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
 114        int status = 0;
 115        u8 chgmod;
 116
 117        if (!mbc)
 118                return 0;
 119
 120        chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
 121                & PCF50633_MBCS2_MBC_MASK;
 122
 123        if (mbc->usb_online)
 124                status |= PCF50633_MBC_USB_ONLINE;
 125        if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
 126            chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
 127            chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
 128            chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
 129                status |= PCF50633_MBC_USB_ACTIVE;
 130        if (mbc->adapter_online)
 131                status |= PCF50633_MBC_ADAPTER_ONLINE;
 132        if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
 133            chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
 134            chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
 135            chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
 136                status |= PCF50633_MBC_ADAPTER_ACTIVE;
 137
 138        return status;
 139}
 140EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
 141
 142int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
 143{
 144        struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
 145
 146        if (!mbc)
 147                return 0;
 148
 149        return mbc->usb_online;
 150}
 151EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
 152
 153static ssize_t
 154show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
 155{
 156        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 157
 158        u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
 159        u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
 160
 161        return sprintf(buf, "%d\n", chgmod);
 162}
 163static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
 164
 165static ssize_t
 166show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
 167{
 168        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 169        u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
 170                                                PCF50633_MBCC7_USB_MASK;
 171        unsigned int ma;
 172
 173        if (usblim == PCF50633_MBCC7_USB_1000mA)
 174                ma = 1000;
 175        else if (usblim == PCF50633_MBCC7_USB_500mA)
 176                ma = 500;
 177        else if (usblim == PCF50633_MBCC7_USB_100mA)
 178                ma = 100;
 179        else
 180                ma = 0;
 181
 182        return sprintf(buf, "%u\n", ma);
 183}
 184
 185static ssize_t set_usblim(struct device *dev,
 186                struct device_attribute *attr, const char *buf, size_t count)
 187{
 188        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 189        unsigned long ma;
 190        int ret;
 191
 192        ret = kstrtoul(buf, 10, &ma);
 193        if (ret)
 194                return ret;
 195
 196        pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
 197
 198        return count;
 199}
 200
 201static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
 202
 203static ssize_t
 204show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
 205{
 206        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 207        u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
 208        unsigned int ma;
 209
 210        if (!mbc->pcf->pdata->charger_reference_current_ma)
 211                return -ENODEV;
 212
 213        ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
 214
 215        return sprintf(buf, "%u\n", ma);
 216}
 217
 218static ssize_t set_chglim(struct device *dev,
 219                struct device_attribute *attr, const char *buf, size_t count)
 220{
 221        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 222        unsigned long ma;
 223        unsigned int mbcc5;
 224        int ret;
 225
 226        if (!mbc->pcf->pdata->charger_reference_current_ma)
 227                return -ENODEV;
 228
 229        ret = kstrtoul(buf, 10, &ma);
 230        if (ret)
 231                return ret;
 232
 233        mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
 234        if (mbcc5 > 255)
 235                mbcc5 = 255;
 236        pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
 237
 238        return count;
 239}
 240
 241/*
 242 * This attribute allows to change MBC charging limit on the fly
 243 * independently of usb current limit. It also gets set automatically every
 244 * time usb current limit is changed.
 245 */
 246static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
 247
 248static struct attribute *pcf50633_mbc_sysfs_entries[] = {
 249        &dev_attr_chgmode.attr,
 250        &dev_attr_usb_curlim.attr,
 251        &dev_attr_chg_curlim.attr,
 252        NULL,
 253};
 254
 255static const struct attribute_group mbc_attr_group = {
 256        .name   = NULL,                 /* put in device directory */
 257        .attrs  = pcf50633_mbc_sysfs_entries,
 258};
 259
 260static void
 261pcf50633_mbc_irq_handler(int irq, void *data)
 262{
 263        struct pcf50633_mbc *mbc = data;
 264
 265        /* USB */
 266        if (irq == PCF50633_IRQ_USBINS) {
 267                mbc->usb_online = 1;
 268        } else if (irq == PCF50633_IRQ_USBREM) {
 269                mbc->usb_online = 0;
 270                pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
 271        }
 272
 273        /* Adapter */
 274        if (irq == PCF50633_IRQ_ADPINS)
 275                mbc->adapter_online = 1;
 276        else if (irq == PCF50633_IRQ_ADPREM)
 277                mbc->adapter_online = 0;
 278
 279        power_supply_changed(mbc->ac);
 280        power_supply_changed(mbc->usb);
 281        power_supply_changed(mbc->adapter);
 282
 283        if (mbc->pcf->pdata->mbc_event_callback)
 284                mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
 285}
 286
 287static int adapter_get_property(struct power_supply *psy,
 288                        enum power_supply_property psp,
 289                        union power_supply_propval *val)
 290{
 291        struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
 292        int ret = 0;
 293
 294        switch (psp) {
 295        case POWER_SUPPLY_PROP_ONLINE:
 296                val->intval =  mbc->adapter_online;
 297                break;
 298        default:
 299                ret = -EINVAL;
 300                break;
 301        }
 302        return ret;
 303}
 304
 305static int usb_get_property(struct power_supply *psy,
 306                        enum power_supply_property psp,
 307                        union power_supply_propval *val)
 308{
 309        struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
 310        int ret = 0;
 311        u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
 312                                                PCF50633_MBCC7_USB_MASK;
 313
 314        switch (psp) {
 315        case POWER_SUPPLY_PROP_ONLINE:
 316                val->intval = mbc->usb_online &&
 317                                (usblim <= PCF50633_MBCC7_USB_500mA);
 318                break;
 319        default:
 320                ret = -EINVAL;
 321                break;
 322        }
 323        return ret;
 324}
 325
 326static int ac_get_property(struct power_supply *psy,
 327                        enum power_supply_property psp,
 328                        union power_supply_propval *val)
 329{
 330        struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
 331        int ret = 0;
 332        u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
 333                                                PCF50633_MBCC7_USB_MASK;
 334
 335        switch (psp) {
 336        case POWER_SUPPLY_PROP_ONLINE:
 337                val->intval = mbc->usb_online &&
 338                                (usblim == PCF50633_MBCC7_USB_1000mA);
 339                break;
 340        default:
 341                ret = -EINVAL;
 342                break;
 343        }
 344        return ret;
 345}
 346
 347static enum power_supply_property power_props[] = {
 348        POWER_SUPPLY_PROP_ONLINE,
 349};
 350
 351static const u8 mbc_irq_handlers[] = {
 352        PCF50633_IRQ_ADPINS,
 353        PCF50633_IRQ_ADPREM,
 354        PCF50633_IRQ_USBINS,
 355        PCF50633_IRQ_USBREM,
 356        PCF50633_IRQ_BATFULL,
 357        PCF50633_IRQ_CHGHALT,
 358        PCF50633_IRQ_THLIMON,
 359        PCF50633_IRQ_THLIMOFF,
 360        PCF50633_IRQ_USBLIMON,
 361        PCF50633_IRQ_USBLIMOFF,
 362        PCF50633_IRQ_LOWSYS,
 363        PCF50633_IRQ_LOWBAT,
 364};
 365
 366static const struct power_supply_desc pcf50633_mbc_adapter_desc = {
 367        .name           = "adapter",
 368        .type           = POWER_SUPPLY_TYPE_MAINS,
 369        .properties     = power_props,
 370        .num_properties = ARRAY_SIZE(power_props),
 371        .get_property   = &adapter_get_property,
 372};
 373
 374static const struct power_supply_desc pcf50633_mbc_usb_desc = {
 375        .name           = "usb",
 376        .type           = POWER_SUPPLY_TYPE_USB,
 377        .properties     = power_props,
 378        .num_properties = ARRAY_SIZE(power_props),
 379        .get_property   = usb_get_property,
 380};
 381
 382static const struct power_supply_desc pcf50633_mbc_ac_desc = {
 383        .name           = "ac",
 384        .type           = POWER_SUPPLY_TYPE_MAINS,
 385        .properties     = power_props,
 386        .num_properties = ARRAY_SIZE(power_props),
 387        .get_property   = ac_get_property,
 388};
 389
 390static int pcf50633_mbc_probe(struct platform_device *pdev)
 391{
 392        struct power_supply_config psy_cfg = {};
 393        struct pcf50633_mbc *mbc;
 394        int i;
 395        u8 mbcs1;
 396
 397        mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
 398        if (!mbc)
 399                return -ENOMEM;
 400
 401        platform_set_drvdata(pdev, mbc);
 402        mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
 403
 404        /* Set up IRQ handlers */
 405        for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
 406                pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
 407                                        pcf50633_mbc_irq_handler, mbc);
 408
 409        psy_cfg.supplied_to             = mbc->pcf->pdata->batteries;
 410        psy_cfg.num_supplicants         = mbc->pcf->pdata->num_batteries;
 411        psy_cfg.drv_data                = mbc;
 412
 413        /* Create power supplies */
 414        mbc->adapter = power_supply_register(&pdev->dev,
 415                                             &pcf50633_mbc_adapter_desc,
 416                                             &psy_cfg);
 417        if (IS_ERR(mbc->adapter)) {
 418                dev_err(mbc->pcf->dev, "failed to register adapter\n");
 419                return PTR_ERR(mbc->adapter);
 420        }
 421
 422        mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
 423                                         &psy_cfg);
 424        if (IS_ERR(mbc->usb)) {
 425                dev_err(mbc->pcf->dev, "failed to register usb\n");
 426                power_supply_unregister(mbc->adapter);
 427                return PTR_ERR(mbc->usb);
 428        }
 429
 430        mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
 431                                        &psy_cfg);
 432        if (IS_ERR(mbc->ac)) {
 433                dev_err(mbc->pcf->dev, "failed to register ac\n");
 434                power_supply_unregister(mbc->adapter);
 435                power_supply_unregister(mbc->usb);
 436                return PTR_ERR(mbc->ac);
 437        }
 438
 439        if (sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group))
 440                dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
 441
 442        mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
 443        if (mbcs1 & PCF50633_MBCS1_USBPRES)
 444                pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
 445        if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
 446                pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
 447
 448        return 0;
 449}
 450
 451static int pcf50633_mbc_remove(struct platform_device *pdev)
 452{
 453        struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
 454        int i;
 455
 456        /* Remove IRQ handlers */
 457        for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
 458                pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
 459
 460        sysfs_remove_group(&pdev->dev.kobj, &mbc_attr_group);
 461        power_supply_unregister(mbc->usb);
 462        power_supply_unregister(mbc->adapter);
 463        power_supply_unregister(mbc->ac);
 464
 465        return 0;
 466}
 467
 468static struct platform_driver pcf50633_mbc_driver = {
 469        .driver = {
 470                .name = "pcf50633-mbc",
 471        },
 472        .probe = pcf50633_mbc_probe,
 473        .remove = pcf50633_mbc_remove,
 474};
 475
 476module_platform_driver(pcf50633_mbc_driver);
 477
 478MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
 479MODULE_DESCRIPTION("PCF50633 mbc driver");
 480MODULE_LICENSE("GPL");
 481MODULE_ALIAS("platform:pcf50633-mbc");
 482