linux/drivers/power/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/init.h>
  20#include <linux/types.h>
  21#include <linux/device.h>
  22#include <linux/sysfs.h>
  23#include <linux/platform_device.h>
  24#include <linux/power_supply.h>
  25
  26#include <linux/mfd/pcf50633/core.h>
  27#include <linux/mfd/pcf50633/mbc.h>
  28
  29struct pcf50633_mbc {
  30        struct pcf50633 *pcf;
  31
  32        int adapter_active;
  33        int adapter_online;
  34        int usb_active;
  35        int usb_online;
  36
  37        struct power_supply usb;
  38        struct power_supply adapter;
  39
  40        struct delayed_work charging_restart_work;
  41};
  42
  43int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
  44{
  45        struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
  46        int ret = 0;
  47        u8 bits;
  48        int charging_start = 1;
  49        u8 mbcs2, chgmod;
  50
  51        if (ma >= 1000)
  52                bits = PCF50633_MBCC7_USB_1000mA;
  53        else if (ma >= 500)
  54                bits = PCF50633_MBCC7_USB_500mA;
  55        else if (ma >= 100)
  56                bits = PCF50633_MBCC7_USB_100mA;
  57        else {
  58                bits = PCF50633_MBCC7_USB_SUSPEND;
  59                charging_start = 0;
  60        }
  61
  62        ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
  63                                        PCF50633_MBCC7_USB_MASK, bits);
  64        if (ret)
  65                dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
  66        else
  67                dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
  68
  69        /* Manual charging start */
  70        mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
  71        chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
  72
  73        /* If chgmod == BATFULL, setting chgena has no effect.
  74         * We need to set resume instead.
  75         */
  76        if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
  77                pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
  78                                PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
  79        else
  80                pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
  81                                PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
  82
  83        mbc->usb_active = charging_start;
  84
  85        power_supply_changed(&mbc->usb);
  86
  87        return ret;
  88}
  89EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
  90
  91int pcf50633_mbc_get_status(struct pcf50633 *pcf)
  92{
  93        struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
  94        int status = 0;
  95
  96        if (mbc->usb_online)
  97                status |= PCF50633_MBC_USB_ONLINE;
  98        if (mbc->usb_active)
  99                status |= PCF50633_MBC_USB_ACTIVE;
 100        if (mbc->adapter_online)
 101                status |= PCF50633_MBC_ADAPTER_ONLINE;
 102        if (mbc->adapter_active)
 103                status |= PCF50633_MBC_ADAPTER_ACTIVE;
 104
 105        return status;
 106}
 107EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
 108
 109static ssize_t
 110show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
 111{
 112        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 113
 114        u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
 115        u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
 116
 117        return sprintf(buf, "%d\n", chgmod);
 118}
 119static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
 120
 121static ssize_t
 122show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
 123{
 124        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 125        u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
 126                                                PCF50633_MBCC7_USB_MASK;
 127        unsigned int ma;
 128
 129        if (usblim == PCF50633_MBCC7_USB_1000mA)
 130                ma = 1000;
 131        else if (usblim == PCF50633_MBCC7_USB_500mA)
 132                ma = 500;
 133        else if (usblim == PCF50633_MBCC7_USB_100mA)
 134                ma = 100;
 135        else
 136                ma = 0;
 137
 138        return sprintf(buf, "%u\n", ma);
 139}
 140
 141static ssize_t set_usblim(struct device *dev,
 142                struct device_attribute *attr, const char *buf, size_t count)
 143{
 144        struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
 145        unsigned long ma;
 146        int ret;
 147
 148        ret = strict_strtoul(buf, 10, &ma);
 149        if (ret)
 150                return -EINVAL;
 151
 152        pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
 153
 154        return count;
 155}
 156
 157static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
 158
 159static struct attribute *pcf50633_mbc_sysfs_entries[] = {
 160        &dev_attr_chgmode.attr,
 161        &dev_attr_usb_curlim.attr,
 162        NULL,
 163};
 164
 165static struct attribute_group mbc_attr_group = {
 166        .name   = NULL,                 /* put in device directory */
 167        .attrs  = pcf50633_mbc_sysfs_entries,
 168};
 169
 170/* MBC state machine switches into charging mode when the battery voltage
 171 * falls below 96% of a battery float voltage. But the voltage drop in Li-ion
 172 * batteries is marginal(1~2 %) till about 80% of its capacity - which means,
 173 * after a BATFULL, charging won't be restarted until 80%.
 174 *
 175 * This work_struct function restarts charging at regular intervals to make
 176 * sure we don't discharge too much
 177 */
 178
 179static void pcf50633_mbc_charging_restart(struct work_struct *work)
 180{
 181        struct pcf50633_mbc *mbc;
 182        u8 mbcs2, chgmod;
 183
 184        mbc = container_of(work, struct pcf50633_mbc,
 185                                charging_restart_work.work);
 186
 187        mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
 188        chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
 189
 190        if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
 191                return;
 192
 193        /* Restart charging */
 194        pcf50633_reg_set_bit_mask(mbc->pcf, PCF50633_REG_MBCC1,
 195                                PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
 196        mbc->usb_active = 1;
 197        power_supply_changed(&mbc->usb);
 198
 199        dev_info(mbc->pcf->dev, "Charging restarted\n");
 200}
 201
 202static void
 203pcf50633_mbc_irq_handler(int irq, void *data)
 204{
 205        struct pcf50633_mbc *mbc = data;
 206        int chg_restart_interval =
 207                        mbc->pcf->pdata->charging_restart_interval;
 208
 209        /* USB */
 210        if (irq == PCF50633_IRQ_USBINS) {
 211                mbc->usb_online = 1;
 212        } else if (irq == PCF50633_IRQ_USBREM) {
 213                mbc->usb_online = 0;
 214                mbc->usb_active = 0;
 215                pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
 216                cancel_delayed_work_sync(&mbc->charging_restart_work);
 217        }
 218
 219        /* Adapter */
 220        if (irq == PCF50633_IRQ_ADPINS) {
 221                mbc->adapter_online = 1;
 222                mbc->adapter_active = 1;
 223        } else if (irq == PCF50633_IRQ_ADPREM) {
 224                mbc->adapter_online = 0;
 225                mbc->adapter_active = 0;
 226        }
 227
 228        if (irq == PCF50633_IRQ_BATFULL) {
 229                mbc->usb_active = 0;
 230                mbc->adapter_active = 0;
 231
 232                if (chg_restart_interval > 0)
 233                        schedule_delayed_work(&mbc->charging_restart_work,
 234                                                        chg_restart_interval);
 235        } else if (irq == PCF50633_IRQ_USBLIMON)
 236                mbc->usb_active = 0;
 237        else if (irq == PCF50633_IRQ_USBLIMOFF)
 238                mbc->usb_active = 1;
 239
 240        power_supply_changed(&mbc->usb);
 241        power_supply_changed(&mbc->adapter);
 242
 243        if (mbc->pcf->pdata->mbc_event_callback)
 244                mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
 245}
 246
 247static int adapter_get_property(struct power_supply *psy,
 248                        enum power_supply_property psp,
 249                        union power_supply_propval *val)
 250{
 251        struct pcf50633_mbc *mbc = container_of(psy,
 252                                struct pcf50633_mbc, adapter);
 253        int ret = 0;
 254
 255        switch (psp) {
 256        case POWER_SUPPLY_PROP_ONLINE:
 257                val->intval =  mbc->adapter_online;
 258                break;
 259        default:
 260                ret = -EINVAL;
 261                break;
 262        }
 263        return ret;
 264}
 265
 266static int usb_get_property(struct power_supply *psy,
 267                        enum power_supply_property psp,
 268                        union power_supply_propval *val)
 269{
 270        struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
 271        int ret = 0;
 272
 273        switch (psp) {
 274        case POWER_SUPPLY_PROP_ONLINE:
 275                val->intval = mbc->usb_online;
 276                break;
 277        default:
 278                ret = -EINVAL;
 279                break;
 280        }
 281        return ret;
 282}
 283
 284static enum power_supply_property power_props[] = {
 285        POWER_SUPPLY_PROP_ONLINE,
 286};
 287
 288static const u8 mbc_irq_handlers[] = {
 289        PCF50633_IRQ_ADPINS,
 290        PCF50633_IRQ_ADPREM,
 291        PCF50633_IRQ_USBINS,
 292        PCF50633_IRQ_USBREM,
 293        PCF50633_IRQ_BATFULL,
 294        PCF50633_IRQ_CHGHALT,
 295        PCF50633_IRQ_THLIMON,
 296        PCF50633_IRQ_THLIMOFF,
 297        PCF50633_IRQ_USBLIMON,
 298        PCF50633_IRQ_USBLIMOFF,
 299        PCF50633_IRQ_LOWSYS,
 300        PCF50633_IRQ_LOWBAT,
 301};
 302
 303static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
 304{
 305        struct pcf50633_mbc *mbc;
 306        struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
 307        int ret;
 308        int i;
 309        u8 mbcs1;
 310
 311        mbc = kzalloc(sizeof(*mbc), GFP_KERNEL);
 312        if (!mbc)
 313                return -ENOMEM;
 314
 315        platform_set_drvdata(pdev, mbc);
 316        mbc->pcf = pdata->pcf;
 317
 318        /* Set up IRQ handlers */
 319        for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
 320                pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
 321                                        pcf50633_mbc_irq_handler, mbc);
 322
 323        /* Create power supplies */
 324        mbc->adapter.name               = "adapter";
 325        mbc->adapter.type               = POWER_SUPPLY_TYPE_MAINS;
 326        mbc->adapter.properties         = power_props;
 327        mbc->adapter.num_properties     = ARRAY_SIZE(power_props);
 328        mbc->adapter.get_property       = &adapter_get_property;
 329        mbc->adapter.supplied_to        = mbc->pcf->pdata->batteries;
 330        mbc->adapter.num_supplicants    = mbc->pcf->pdata->num_batteries;
 331
 332        mbc->usb.name                   = "usb";
 333        mbc->usb.type                   = POWER_SUPPLY_TYPE_USB;
 334        mbc->usb.properties             = power_props;
 335        mbc->usb.num_properties         = ARRAY_SIZE(power_props);
 336        mbc->usb.get_property           = usb_get_property;
 337        mbc->usb.supplied_to            = mbc->pcf->pdata->batteries;
 338        mbc->usb.num_supplicants        = mbc->pcf->pdata->num_batteries;
 339
 340        ret = power_supply_register(&pdev->dev, &mbc->adapter);
 341        if (ret) {
 342                dev_err(mbc->pcf->dev, "failed to register adapter\n");
 343                kfree(mbc);
 344                return ret;
 345        }
 346
 347        ret = power_supply_register(&pdev->dev, &mbc->usb);
 348        if (ret) {
 349                dev_err(mbc->pcf->dev, "failed to register usb\n");
 350                power_supply_unregister(&mbc->adapter);
 351                kfree(mbc);
 352                return ret;
 353        }
 354
 355        INIT_DELAYED_WORK(&mbc->charging_restart_work,
 356                                pcf50633_mbc_charging_restart);
 357
 358        ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
 359        if (ret)
 360                dev_err(mbc->pcf->dev, "failed to create sysfs entries\n");
 361
 362        mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
 363        if (mbcs1 & PCF50633_MBCS1_USBPRES)
 364                pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
 365        if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
 366                pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
 367
 368        return 0;
 369}
 370
 371static int __devexit pcf50633_mbc_remove(struct platform_device *pdev)
 372{
 373        struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
 374        int i;
 375
 376        /* Remove IRQ handlers */
 377        for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
 378                pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
 379
 380        power_supply_unregister(&mbc->usb);
 381        power_supply_unregister(&mbc->adapter);
 382
 383        cancel_delayed_work_sync(&mbc->charging_restart_work);
 384
 385        kfree(mbc);
 386
 387        return 0;
 388}
 389
 390static struct platform_driver pcf50633_mbc_driver = {
 391        .driver = {
 392                .name = "pcf50633-mbc",
 393        },
 394        .probe = pcf50633_mbc_probe,
 395        .remove = __devexit_p(pcf50633_mbc_remove),
 396};
 397
 398static int __init pcf50633_mbc_init(void)
 399{
 400        return platform_driver_register(&pcf50633_mbc_driver);
 401}
 402module_init(pcf50633_mbc_init);
 403
 404static void __exit pcf50633_mbc_exit(void)
 405{
 406        platform_driver_unregister(&pcf50633_mbc_driver);
 407}
 408module_exit(pcf50633_mbc_exit);
 409
 410MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
 411MODULE_DESCRIPTION("PCF50633 mbc driver");
 412MODULE_LICENSE("GPL");
 413MODULE_ALIAS("platform:pcf50633-mbc");
 414