pcf50633-charger.c (12288B)
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");