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

power_supply: PCF50633 battery charger driver

Signed-off-by: Balaji Rao <balajirrao@openmoko.org>
Cc: Andy Green <andy@openmoko.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Acked-by: Anton Vorontsov <cbouatmailru@gmail.com>
Signed-off-by: Samuel Ortiz <sameo@openedhand.com>

authored by

Balaji Rao and committed by
Samuel Ortiz
f5714dc9 eae854b2

+499
+6
drivers/power/Kconfig
··· 82 82 Say Y here to enable support for batteries charger integrated into 83 83 DA9030 PMIC. 84 84 85 + config CHARGER_PCF50633 86 + tristate "NXP PCF50633 MBC" 87 + depends on MFD_PCF50633 88 + help 89 + Say Y to include support for NXP PCF50633 Main Battery Charger. 90 + 85 91 endif # POWER_SUPPLY
+1
drivers/power/Makefile
··· 25 25 obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o 26 26 obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o 27 27 obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o 28 + obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
+358
drivers/power/pcf50633-charger.c
··· 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 + 29 + struct 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 + 41 + int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) 42 + { 43 + struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); 44 + int ret = 0; 45 + u8 bits; 46 + 47 + if (ma >= 1000) 48 + bits = PCF50633_MBCC7_USB_1000mA; 49 + else if (ma >= 500) 50 + bits = PCF50633_MBCC7_USB_500mA; 51 + else if (ma >= 100) 52 + bits = PCF50633_MBCC7_USB_100mA; 53 + else 54 + bits = PCF50633_MBCC7_USB_SUSPEND; 55 + 56 + ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, 57 + PCF50633_MBCC7_USB_MASK, bits); 58 + if (ret) 59 + dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma); 60 + else 61 + dev_info(pcf->dev, "usb curlim to %d mA\n", ma); 62 + 63 + power_supply_changed(&mbc->usb); 64 + 65 + return ret; 66 + } 67 + EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set); 68 + 69 + int pcf50633_mbc_get_status(struct pcf50633 *pcf) 70 + { 71 + struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); 72 + int status = 0; 73 + 74 + if (mbc->usb_online) 75 + status |= PCF50633_MBC_USB_ONLINE; 76 + if (mbc->usb_active) 77 + status |= PCF50633_MBC_USB_ACTIVE; 78 + if (mbc->adapter_online) 79 + status |= PCF50633_MBC_ADAPTER_ONLINE; 80 + if (mbc->adapter_active) 81 + status |= PCF50633_MBC_ADAPTER_ACTIVE; 82 + 83 + return status; 84 + } 85 + EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status); 86 + 87 + void pcf50633_mbc_set_status(struct pcf50633 *pcf, int what, int status) 88 + { 89 + struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); 90 + 91 + if (what & PCF50633_MBC_USB_ONLINE) 92 + mbc->usb_online = !!status; 93 + if (what & PCF50633_MBC_USB_ACTIVE) 94 + mbc->usb_active = !!status; 95 + if (what & PCF50633_MBC_ADAPTER_ONLINE) 96 + mbc->adapter_online = !!status; 97 + if (what & PCF50633_MBC_ADAPTER_ACTIVE) 98 + mbc->adapter_active = !!status; 99 + } 100 + EXPORT_SYMBOL_GPL(pcf50633_mbc_set_status); 101 + 102 + static ssize_t 103 + show_chgmode(struct device *dev, struct device_attribute *attr, char *buf) 104 + { 105 + struct pcf50633_mbc *mbc = dev_get_drvdata(dev); 106 + 107 + u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); 108 + u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); 109 + 110 + return sprintf(buf, "%d\n", chgmod); 111 + } 112 + static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL); 113 + 114 + static ssize_t 115 + show_usblim(struct device *dev, struct device_attribute *attr, char *buf) 116 + { 117 + struct pcf50633_mbc *mbc = dev_get_drvdata(dev); 118 + u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & 119 + PCF50633_MBCC7_USB_MASK; 120 + unsigned int ma; 121 + 122 + if (usblim == PCF50633_MBCC7_USB_1000mA) 123 + ma = 1000; 124 + else if (usblim == PCF50633_MBCC7_USB_500mA) 125 + ma = 500; 126 + else if (usblim == PCF50633_MBCC7_USB_100mA) 127 + ma = 100; 128 + else 129 + ma = 0; 130 + 131 + return sprintf(buf, "%u\n", ma); 132 + } 133 + 134 + static ssize_t set_usblim(struct device *dev, 135 + struct device_attribute *attr, const char *buf, size_t count) 136 + { 137 + struct pcf50633_mbc *mbc = dev_get_drvdata(dev); 138 + unsigned long ma; 139 + int ret; 140 + 141 + ret = strict_strtoul(buf, 10, &ma); 142 + if (ret) 143 + return -EINVAL; 144 + 145 + pcf50633_mbc_usb_curlim_set(mbc->pcf, ma); 146 + 147 + return count; 148 + } 149 + 150 + static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim); 151 + 152 + static struct attribute *pcf50633_mbc_sysfs_entries[] = { 153 + &dev_attr_chgmode.attr, 154 + &dev_attr_usb_curlim.attr, 155 + NULL, 156 + }; 157 + 158 + static struct attribute_group mbc_attr_group = { 159 + .name = NULL, /* put in device directory */ 160 + .attrs = pcf50633_mbc_sysfs_entries, 161 + }; 162 + 163 + static void 164 + pcf50633_mbc_irq_handler(int irq, void *data) 165 + { 166 + struct pcf50633_mbc *mbc = data; 167 + 168 + /* USB */ 169 + if (irq == PCF50633_IRQ_USBINS) { 170 + mbc->usb_online = 1; 171 + } else if (irq == PCF50633_IRQ_USBREM) { 172 + mbc->usb_online = 0; 173 + mbc->usb_active = 0; 174 + pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); 175 + } 176 + 177 + /* Adapter */ 178 + if (irq == PCF50633_IRQ_ADPINS) { 179 + mbc->adapter_online = 1; 180 + mbc->adapter_active = 1; 181 + } else if (irq == PCF50633_IRQ_ADPREM) { 182 + mbc->adapter_online = 0; 183 + mbc->adapter_active = 0; 184 + } 185 + 186 + if (irq == PCF50633_IRQ_BATFULL) { 187 + mbc->usb_active = 0; 188 + mbc->adapter_active = 0; 189 + } 190 + 191 + power_supply_changed(&mbc->usb); 192 + power_supply_changed(&mbc->adapter); 193 + 194 + if (mbc->pcf->pdata->mbc_event_callback) 195 + mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq); 196 + } 197 + 198 + static int adapter_get_property(struct power_supply *psy, 199 + enum power_supply_property psp, 200 + union power_supply_propval *val) 201 + { 202 + struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); 203 + int ret = 0; 204 + 205 + switch (psp) { 206 + case POWER_SUPPLY_PROP_ONLINE: 207 + val->intval = mbc->adapter_online; 208 + break; 209 + default: 210 + ret = -EINVAL; 211 + break; 212 + } 213 + return ret; 214 + } 215 + 216 + static int usb_get_property(struct power_supply *psy, 217 + enum power_supply_property psp, 218 + union power_supply_propval *val) 219 + { 220 + struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); 221 + int ret = 0; 222 + 223 + switch (psp) { 224 + case POWER_SUPPLY_PROP_ONLINE: 225 + val->intval = mbc->usb_online; 226 + break; 227 + default: 228 + ret = -EINVAL; 229 + break; 230 + } 231 + return ret; 232 + } 233 + 234 + static enum power_supply_property power_props[] = { 235 + POWER_SUPPLY_PROP_ONLINE, 236 + }; 237 + 238 + static const u8 mbc_irq_handlers[] = { 239 + PCF50633_IRQ_ADPINS, 240 + PCF50633_IRQ_ADPREM, 241 + PCF50633_IRQ_USBINS, 242 + PCF50633_IRQ_USBREM, 243 + PCF50633_IRQ_BATFULL, 244 + PCF50633_IRQ_CHGHALT, 245 + PCF50633_IRQ_THLIMON, 246 + PCF50633_IRQ_THLIMOFF, 247 + PCF50633_IRQ_USBLIMON, 248 + PCF50633_IRQ_USBLIMOFF, 249 + PCF50633_IRQ_LOWSYS, 250 + PCF50633_IRQ_LOWBAT, 251 + }; 252 + 253 + static int __devinit pcf50633_mbc_probe(struct platform_device *pdev) 254 + { 255 + struct pcf50633_mbc *mbc; 256 + struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data; 257 + int ret; 258 + int i; 259 + u8 mbcs1; 260 + 261 + mbc = kzalloc(sizeof(*mbc), GFP_KERNEL); 262 + if (!mbc) 263 + return -ENOMEM; 264 + 265 + platform_set_drvdata(pdev, mbc); 266 + mbc->pcf = pdata->pcf; 267 + 268 + /* Set up IRQ handlers */ 269 + for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) 270 + pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i], 271 + pcf50633_mbc_irq_handler, mbc); 272 + 273 + /* Create power supplies */ 274 + mbc->adapter.name = "adapter"; 275 + mbc->adapter.type = POWER_SUPPLY_TYPE_MAINS; 276 + mbc->adapter.properties = power_props; 277 + mbc->adapter.num_properties = ARRAY_SIZE(power_props); 278 + mbc->adapter.get_property = &adapter_get_property; 279 + mbc->adapter.supplied_to = mbc->pcf->pdata->batteries; 280 + mbc->adapter.num_supplicants = mbc->pcf->pdata->num_batteries; 281 + 282 + mbc->usb.name = "usb"; 283 + mbc->usb.type = POWER_SUPPLY_TYPE_USB; 284 + mbc->usb.properties = power_props; 285 + mbc->usb.num_properties = ARRAY_SIZE(power_props); 286 + mbc->usb.get_property = usb_get_property; 287 + mbc->usb.supplied_to = mbc->pcf->pdata->batteries; 288 + mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries; 289 + 290 + ret = power_supply_register(&pdev->dev, &mbc->adapter); 291 + if (ret) { 292 + dev_err(mbc->pcf->dev, "failed to register adapter\n"); 293 + kfree(mbc); 294 + return ret; 295 + } 296 + 297 + ret = power_supply_register(&pdev->dev, &mbc->usb); 298 + if (ret) { 299 + dev_err(mbc->pcf->dev, "failed to register usb\n"); 300 + power_supply_unregister(&mbc->adapter); 301 + kfree(mbc); 302 + return ret; 303 + } 304 + 305 + ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); 306 + if (ret) 307 + dev_err(mbc->pcf->dev, "failed to create sysfs entries\n"); 308 + 309 + mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1); 310 + if (mbcs1 & PCF50633_MBCS1_USBPRES) 311 + pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc); 312 + if (mbcs1 & PCF50633_MBCS1_ADAPTPRES) 313 + pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc); 314 + 315 + return 0; 316 + } 317 + 318 + static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) 319 + { 320 + struct pcf50633_mbc *mbc = platform_get_drvdata(pdev); 321 + int i; 322 + 323 + /* Remove IRQ handlers */ 324 + for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) 325 + pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); 326 + 327 + power_supply_unregister(&mbc->usb); 328 + power_supply_unregister(&mbc->adapter); 329 + 330 + kfree(mbc); 331 + 332 + return 0; 333 + } 334 + 335 + static struct platform_driver pcf50633_mbc_driver = { 336 + .driver = { 337 + .name = "pcf50633-mbc", 338 + }, 339 + .probe = pcf50633_mbc_probe, 340 + .remove = __devexit_p(pcf50633_mbc_remove), 341 + }; 342 + 343 + static int __init pcf50633_mbc_init(void) 344 + { 345 + return platform_driver_register(&pcf50633_mbc_driver); 346 + } 347 + module_init(pcf50633_mbc_init); 348 + 349 + static void __exit pcf50633_mbc_exit(void) 350 + { 351 + platform_driver_unregister(&pcf50633_mbc_driver); 352 + } 353 + module_exit(pcf50633_mbc_exit); 354 + 355 + MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); 356 + MODULE_DESCRIPTION("PCF50633 mbc driver"); 357 + MODULE_LICENSE("GPL"); 358 + MODULE_ALIAS("platform:pcf50633-mbc");
+134
include/linux/mfd/pcf50633/mbc.h
··· 1 + /* 2 + * mbc.h -- Driver for NXP PCF50633 Main Battery Charger 3 + * 4 + * (C) 2006-2008 by Openmoko, Inc. 5 + * All rights reserved. 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License as published by the 9 + * Free Software Foundation; either version 2 of the License, or (at your 10 + * option) any later version. 11 + */ 12 + 13 + #ifndef __LINUX_MFD_PCF50633_MBC_H 14 + #define __LINUX_MFD_PCF50633_MBC_H 15 + 16 + #include <linux/mfd/pcf50633/core.h> 17 + #include <linux/platform_device.h> 18 + 19 + #define PCF50633_REG_MBCC1 0x43 20 + #define PCF50633_REG_MBCC2 0x44 21 + #define PCF50633_REG_MBCC3 0x45 22 + #define PCF50633_REG_MBCC4 0x46 23 + #define PCF50633_REG_MBCC5 0x47 24 + #define PCF50633_REG_MBCC6 0x48 25 + #define PCF50633_REG_MBCC7 0x49 26 + #define PCF50633_REG_MBCC8 0x4a 27 + #define PCF50633_REG_MBCS1 0x4b 28 + #define PCF50633_REG_MBCS2 0x4c 29 + #define PCF50633_REG_MBCS3 0x4d 30 + 31 + enum pcf50633_reg_mbcc1 { 32 + PCF50633_MBCC1_CHGENA = 0x01, /* Charger enable */ 33 + PCF50633_MBCC1_AUTOSTOP = 0x02, 34 + PCF50633_MBCC1_AUTORES = 0x04, /* automatic resume */ 35 + PCF50633_MBCC1_RESUME = 0x08, /* explicit resume cmd */ 36 + PCF50633_MBCC1_RESTART = 0x10, /* restart charging */ 37 + PCF50633_MBCC1_PREWDTIME_60M = 0x20, /* max. precharging time */ 38 + PCF50633_MBCC1_WDTIME_1H = 0x00, 39 + PCF50633_MBCC1_WDTIME_2H = 0x40, 40 + PCF50633_MBCC1_WDTIME_4H = 0x80, 41 + PCF50633_MBCC1_WDTIME_6H = 0xc0, 42 + }; 43 + #define PCF50633_MBCC1_WDTIME_MASK 0xc0 44 + 45 + enum pcf50633_reg_mbcc2 { 46 + PCF50633_MBCC2_VBATCOND_2V7 = 0x00, 47 + PCF50633_MBCC2_VBATCOND_2V85 = 0x01, 48 + PCF50633_MBCC2_VBATCOND_3V0 = 0x02, 49 + PCF50633_MBCC2_VBATCOND_3V15 = 0x03, 50 + PCF50633_MBCC2_VMAX_4V = 0x00, 51 + PCF50633_MBCC2_VMAX_4V20 = 0x28, 52 + PCF50633_MBCC2_VRESDEBTIME_64S = 0x80, /* debounce time (32/64sec) */ 53 + }; 54 + 55 + enum pcf50633_reg_mbcc7 { 56 + PCF50633_MBCC7_USB_100mA = 0x00, 57 + PCF50633_MBCC7_USB_500mA = 0x01, 58 + PCF50633_MBCC7_USB_1000mA = 0x02, 59 + PCF50633_MBCC7_USB_SUSPEND = 0x03, 60 + PCF50633_MBCC7_BATTEMP_EN = 0x04, 61 + PCF50633_MBCC7_BATSYSIMAX_1A6 = 0x00, 62 + PCF50633_MBCC7_BATSYSIMAX_1A8 = 0x40, 63 + PCF50633_MBCC7_BATSYSIMAX_2A0 = 0x80, 64 + PCF50633_MBCC7_BATSYSIMAX_2A2 = 0xc0, 65 + }; 66 + #define PCF50633_MBCC7_USB_MASK 0x03 67 + 68 + enum pcf50633_reg_mbcc8 { 69 + PCF50633_MBCC8_USBENASUS = 0x10, 70 + }; 71 + 72 + enum pcf50633_reg_mbcs1 { 73 + PCF50633_MBCS1_USBPRES = 0x01, 74 + PCF50633_MBCS1_USBOK = 0x02, 75 + PCF50633_MBCS1_ADAPTPRES = 0x04, 76 + PCF50633_MBCS1_ADAPTOK = 0x08, 77 + PCF50633_MBCS1_TBAT_OK = 0x00, 78 + PCF50633_MBCS1_TBAT_ABOVE = 0x10, 79 + PCF50633_MBCS1_TBAT_BELOW = 0x20, 80 + PCF50633_MBCS1_TBAT_UNDEF = 0x30, 81 + PCF50633_MBCS1_PREWDTEXP = 0x40, 82 + PCF50633_MBCS1_WDTEXP = 0x80, 83 + }; 84 + 85 + enum pcf50633_reg_mbcs2_mbcmod { 86 + PCF50633_MBCS2_MBC_PLAY = 0x00, 87 + PCF50633_MBCS2_MBC_USB_PRE = 0x01, 88 + PCF50633_MBCS2_MBC_USB_PRE_WAIT = 0x02, 89 + PCF50633_MBCS2_MBC_USB_FAST = 0x03, 90 + PCF50633_MBCS2_MBC_USB_FAST_WAIT = 0x04, 91 + PCF50633_MBCS2_MBC_USB_SUSPEND = 0x05, 92 + PCF50633_MBCS2_MBC_ADP_PRE = 0x06, 93 + PCF50633_MBCS2_MBC_ADP_PRE_WAIT = 0x07, 94 + PCF50633_MBCS2_MBC_ADP_FAST = 0x08, 95 + PCF50633_MBCS2_MBC_ADP_FAST_WAIT = 0x09, 96 + PCF50633_MBCS2_MBC_BAT_FULL = 0x0a, 97 + PCF50633_MBCS2_MBC_HALT = 0x0b, 98 + }; 99 + #define PCF50633_MBCS2_MBC_MASK 0x0f 100 + enum pcf50633_reg_mbcs2_chgstat { 101 + PCF50633_MBCS2_CHGS_NONE = 0x00, 102 + PCF50633_MBCS2_CHGS_ADAPTER = 0x10, 103 + PCF50633_MBCS2_CHGS_USB = 0x20, 104 + PCF50633_MBCS2_CHGS_BOTH = 0x30, 105 + }; 106 + #define PCF50633_MBCS2_RESSTAT_AUTO 0x40 107 + 108 + enum pcf50633_reg_mbcs3 { 109 + PCF50633_MBCS3_USBLIM_PLAY = 0x01, 110 + PCF50633_MBCS3_USBLIM_CGH = 0x02, 111 + PCF50633_MBCS3_TLIM_PLAY = 0x04, 112 + PCF50633_MBCS3_TLIM_CHG = 0x08, 113 + PCF50633_MBCS3_ILIM = 0x10, /* 1: Ibat > Icutoff */ 114 + PCF50633_MBCS3_VLIM = 0x20, /* 1: Vbat == Vmax */ 115 + PCF50633_MBCS3_VBATSTAT = 0x40, /* 1: Vbat > Vbatcond */ 116 + PCF50633_MBCS3_VRES = 0x80, /* 1: Vbat > Vth(RES) */ 117 + }; 118 + 119 + #define PCF50633_MBCC2_VBATCOND_MASK 0x03 120 + #define PCF50633_MBCC2_VMAX_MASK 0x3c 121 + 122 + /* Charger status */ 123 + #define PCF50633_MBC_USB_ONLINE 0x01 124 + #define PCF50633_MBC_USB_ACTIVE 0x02 125 + #define PCF50633_MBC_ADAPTER_ONLINE 0x04 126 + #define PCF50633_MBC_ADAPTER_ACTIVE 0x08 127 + 128 + int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma); 129 + 130 + int pcf50633_mbc_get_status(struct pcf50633 *); 131 + void pcf50633_mbc_set_status(struct pcf50633 *, int what, int status); 132 + 133 + #endif 134 +