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