root/drivers/power/supply/pcf50633-charger.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcf50633_mbc_usb_curlim_set
  2. pcf50633_mbc_get_status
  3. pcf50633_mbc_get_usb_online_status
  4. show_chgmode
  5. show_usblim
  6. set_usblim
  7. show_chglim
  8. set_chglim
  9. pcf50633_mbc_irq_handler
  10. adapter_get_property
  11. usb_get_property
  12. ac_get_property
  13. pcf50633_mbc_probe
  14. pcf50633_mbc_remove

   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 
  25 struct 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 
  36 int 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 }
 104 EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
 105 
 106 int 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 }
 135 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
 136 
 137 int 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 }
 146 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
 147 
 148 static ssize_t
 149 show_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 }
 158 static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
 159 
 160 static ssize_t
 161 show_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 
 180 static 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 
 196 static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
 197 
 198 static ssize_t
 199 show_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 
 213 static 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  */
 241 static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
 242 
 243 static 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 
 250 ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs);
 251 
 252 static void
 253 pcf50633_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 
 279 static 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 
 297 static 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 
 318 static 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 
 339 static enum power_supply_property power_props[] = {
 340         POWER_SUPPLY_PROP_ONLINE,
 341 };
 342 
 343 static 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 
 358 static 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 
 366 static 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 
 374 static 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 
 382 static 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 
 444 static 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 
 460 static 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 
 468 module_platform_driver(pcf50633_mbc_driver);
 469 
 470 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
 471 MODULE_DESCRIPTION("PCF50633 mbc driver");
 472 MODULE_LICENSE("GPL");
 473 MODULE_ALIAS("platform:pcf50633-mbc");

/* [<][>][^][v][top][bottom][index][help] */