Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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