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.30-rc2 413 lines 11 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_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");