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.29-rc5 359 lines 8.9 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 41int 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} 67EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set); 68 69int 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} 85EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status); 86 87void 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} 100EXPORT_SYMBOL_GPL(pcf50633_mbc_set_status); 101 102static ssize_t 103show_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} 112static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL); 113 114static ssize_t 115show_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 134static 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 150static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim); 151 152static struct attribute *pcf50633_mbc_sysfs_entries[] = { 153 &dev_attr_chgmode.attr, 154 &dev_attr_usb_curlim.attr, 155 NULL, 156}; 157 158static struct attribute_group mbc_attr_group = { 159 .name = NULL, /* put in device directory */ 160 .attrs = pcf50633_mbc_sysfs_entries, 161}; 162 163static void 164pcf50633_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 198static 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, 203 struct pcf50633_mbc, adapter); 204 int ret = 0; 205 206 switch (psp) { 207 case POWER_SUPPLY_PROP_ONLINE: 208 val->intval = mbc->adapter_online; 209 break; 210 default: 211 ret = -EINVAL; 212 break; 213 } 214 return ret; 215} 216 217static int usb_get_property(struct power_supply *psy, 218 enum power_supply_property psp, 219 union power_supply_propval *val) 220{ 221 struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); 222 int ret = 0; 223 224 switch (psp) { 225 case POWER_SUPPLY_PROP_ONLINE: 226 val->intval = mbc->usb_online; 227 break; 228 default: 229 ret = -EINVAL; 230 break; 231 } 232 return ret; 233} 234 235static enum power_supply_property power_props[] = { 236 POWER_SUPPLY_PROP_ONLINE, 237}; 238 239static const u8 mbc_irq_handlers[] = { 240 PCF50633_IRQ_ADPINS, 241 PCF50633_IRQ_ADPREM, 242 PCF50633_IRQ_USBINS, 243 PCF50633_IRQ_USBREM, 244 PCF50633_IRQ_BATFULL, 245 PCF50633_IRQ_CHGHALT, 246 PCF50633_IRQ_THLIMON, 247 PCF50633_IRQ_THLIMOFF, 248 PCF50633_IRQ_USBLIMON, 249 PCF50633_IRQ_USBLIMOFF, 250 PCF50633_IRQ_LOWSYS, 251 PCF50633_IRQ_LOWBAT, 252}; 253 254static int __devinit pcf50633_mbc_probe(struct platform_device *pdev) 255{ 256 struct pcf50633_mbc *mbc; 257 struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data; 258 int ret; 259 int i; 260 u8 mbcs1; 261 262 mbc = kzalloc(sizeof(*mbc), GFP_KERNEL); 263 if (!mbc) 264 return -ENOMEM; 265 266 platform_set_drvdata(pdev, mbc); 267 mbc->pcf = pdata->pcf; 268 269 /* Set up IRQ handlers */ 270 for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) 271 pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i], 272 pcf50633_mbc_irq_handler, mbc); 273 274 /* Create power supplies */ 275 mbc->adapter.name = "adapter"; 276 mbc->adapter.type = POWER_SUPPLY_TYPE_MAINS; 277 mbc->adapter.properties = power_props; 278 mbc->adapter.num_properties = ARRAY_SIZE(power_props); 279 mbc->adapter.get_property = &adapter_get_property; 280 mbc->adapter.supplied_to = mbc->pcf->pdata->batteries; 281 mbc->adapter.num_supplicants = mbc->pcf->pdata->num_batteries; 282 283 mbc->usb.name = "usb"; 284 mbc->usb.type = POWER_SUPPLY_TYPE_USB; 285 mbc->usb.properties = power_props; 286 mbc->usb.num_properties = ARRAY_SIZE(power_props); 287 mbc->usb.get_property = usb_get_property; 288 mbc->usb.supplied_to = mbc->pcf->pdata->batteries; 289 mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries; 290 291 ret = power_supply_register(&pdev->dev, &mbc->adapter); 292 if (ret) { 293 dev_err(mbc->pcf->dev, "failed to register adapter\n"); 294 kfree(mbc); 295 return ret; 296 } 297 298 ret = power_supply_register(&pdev->dev, &mbc->usb); 299 if (ret) { 300 dev_err(mbc->pcf->dev, "failed to register usb\n"); 301 power_supply_unregister(&mbc->adapter); 302 kfree(mbc); 303 return ret; 304 } 305 306 ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); 307 if (ret) 308 dev_err(mbc->pcf->dev, "failed to create sysfs entries\n"); 309 310 mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1); 311 if (mbcs1 & PCF50633_MBCS1_USBPRES) 312 pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc); 313 if (mbcs1 & PCF50633_MBCS1_ADAPTPRES) 314 pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc); 315 316 return 0; 317} 318 319static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) 320{ 321 struct pcf50633_mbc *mbc = platform_get_drvdata(pdev); 322 int i; 323 324 /* Remove IRQ handlers */ 325 for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++) 326 pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]); 327 328 power_supply_unregister(&mbc->usb); 329 power_supply_unregister(&mbc->adapter); 330 331 kfree(mbc); 332 333 return 0; 334} 335 336static struct platform_driver pcf50633_mbc_driver = { 337 .driver = { 338 .name = "pcf50633-mbc", 339 }, 340 .probe = pcf50633_mbc_probe, 341 .remove = __devexit_p(pcf50633_mbc_remove), 342}; 343 344static int __init pcf50633_mbc_init(void) 345{ 346 return platform_driver_register(&pcf50633_mbc_driver); 347} 348module_init(pcf50633_mbc_init); 349 350static void __exit pcf50633_mbc_exit(void) 351{ 352 platform_driver_unregister(&pcf50633_mbc_driver); 353} 354module_exit(pcf50633_mbc_exit); 355 356MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>"); 357MODULE_DESCRIPTION("PCF50633 mbc driver"); 358MODULE_LICENSE("GPL"); 359MODULE_ALIAS("platform:pcf50633-mbc");