1 /* NXP PCF50633 Main Battery Charger Driver
3 * (C) 2006-2008 by Openmoko, Inc.
4 * Author: Balaji Rao <balajirrao@openmoko.org>
7 * Broken down from monstrous PCF50633 driver mainly by
8 * Harald Welte, Andy Green and Werner Almesberger
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.
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>
26 #include <linux/mfd/pcf50633/core.h>
27 #include <linux/mfd/pcf50633/mbc.h>
35 struct power_supply usb
;
36 struct power_supply adapter
;
37 struct power_supply ac
;
40 int pcf50633_mbc_usb_curlim_set(struct pcf50633
*pcf
, int ma
)
42 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
45 int charging_start
= 1;
50 bits
= PCF50633_MBCC7_USB_1000mA
;
52 } else if (ma
>= 500) {
53 bits
= PCF50633_MBCC7_USB_500mA
;
55 } else if (ma
>= 100) {
56 bits
= PCF50633_MBCC7_USB_100mA
;
59 bits
= PCF50633_MBCC7_USB_SUSPEND
;
64 ret
= pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC7
,
65 PCF50633_MBCC7_USB_MASK
, bits
);
67 dev_err(pcf
->dev
, "error setting usb curlim to %d mA\n", ma
);
69 dev_info(pcf
->dev
, "usb curlim to %d mA\n", ma
);
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
82 if (mbc
->pcf
->pdata
->charger_reference_current_ma
) {
83 mbcc5
= (ma
<< 8) / mbc
->pcf
->pdata
->charger_reference_current_ma
;
86 pcf50633_reg_write(mbc
->pcf
, PCF50633_REG_MBCC5
, mbcc5
);
89 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
90 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
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.
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
);
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
);
106 power_supply_changed(&mbc
->usb
);
110 EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set
);
112 int pcf50633_mbc_get_status(struct pcf50633
*pcf
)
114 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
121 chgmod
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
)
122 & PCF50633_MBCS2_MBC_MASK
;
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
;
141 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status
);
143 int pcf50633_mbc_get_usb_online_status(struct pcf50633
*pcf
)
145 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
150 return mbc
->usb_online
;
152 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status
);
155 show_chgmode(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
157 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
159 u8 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
160 u8 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
162 return sprintf(buf
, "%d\n", chgmod
);
164 static DEVICE_ATTR(chgmode
, S_IRUGO
, show_chgmode
, NULL
);
167 show_usblim(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
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
;
174 if (usblim
== PCF50633_MBCC7_USB_1000mA
)
176 else if (usblim
== PCF50633_MBCC7_USB_500mA
)
178 else if (usblim
== PCF50633_MBCC7_USB_100mA
)
183 return sprintf(buf
, "%u\n", ma
);
186 static ssize_t
set_usblim(struct device
*dev
,
187 struct device_attribute
*attr
, const char *buf
, size_t count
)
189 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
193 ret
= strict_strtoul(buf
, 10, &ma
);
197 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, ma
);
202 static DEVICE_ATTR(usb_curlim
, S_IRUGO
| S_IWUSR
, show_usblim
, set_usblim
);
205 show_chglim(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
207 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
208 u8 mbcc5
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC5
);
211 if (!mbc
->pcf
->pdata
->charger_reference_current_ma
)
214 ma
= (mbc
->pcf
->pdata
->charger_reference_current_ma
* mbcc5
) >> 8;
216 return sprintf(buf
, "%u\n", ma
);
219 static ssize_t
set_chglim(struct device
*dev
,
220 struct device_attribute
*attr
, const char *buf
, size_t count
)
222 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
227 if (!mbc
->pcf
->pdata
->charger_reference_current_ma
)
230 ret
= strict_strtoul(buf
, 10, &ma
);
234 mbcc5
= (ma
<< 8) / mbc
->pcf
->pdata
->charger_reference_current_ma
;
237 pcf50633_reg_write(mbc
->pcf
, PCF50633_REG_MBCC5
, mbcc5
);
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.
247 static DEVICE_ATTR(chg_curlim
, S_IRUGO
| S_IWUSR
, show_chglim
, set_chglim
);
249 static struct attribute
*pcf50633_mbc_sysfs_entries
[] = {
250 &dev_attr_chgmode
.attr
,
251 &dev_attr_usb_curlim
.attr
,
252 &dev_attr_chg_curlim
.attr
,
256 static struct attribute_group mbc_attr_group
= {
257 .name
= NULL
, /* put in device directory */
258 .attrs
= pcf50633_mbc_sysfs_entries
,
262 pcf50633_mbc_irq_handler(int irq
, void *data
)
264 struct pcf50633_mbc
*mbc
= data
;
267 if (irq
== PCF50633_IRQ_USBINS
) {
269 } else if (irq
== PCF50633_IRQ_USBREM
) {
271 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, 0);
275 if (irq
== PCF50633_IRQ_ADPINS
)
276 mbc
->adapter_online
= 1;
277 else if (irq
== PCF50633_IRQ_ADPREM
)
278 mbc
->adapter_online
= 0;
280 power_supply_changed(&mbc
->ac
);
281 power_supply_changed(&mbc
->usb
);
282 power_supply_changed(&mbc
->adapter
);
284 if (mbc
->pcf
->pdata
->mbc_event_callback
)
285 mbc
->pcf
->pdata
->mbc_event_callback(mbc
->pcf
, irq
);
288 static int adapter_get_property(struct power_supply
*psy
,
289 enum power_supply_property psp
,
290 union power_supply_propval
*val
)
292 struct pcf50633_mbc
*mbc
= container_of(psy
,
293 struct pcf50633_mbc
, adapter
);
297 case POWER_SUPPLY_PROP_ONLINE
:
298 val
->intval
= mbc
->adapter_online
;
307 static int usb_get_property(struct power_supply
*psy
,
308 enum power_supply_property psp
,
309 union power_supply_propval
*val
)
311 struct pcf50633_mbc
*mbc
= container_of(psy
, struct pcf50633_mbc
, usb
);
313 u8 usblim
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC7
) &
314 PCF50633_MBCC7_USB_MASK
;
317 case POWER_SUPPLY_PROP_ONLINE
:
318 val
->intval
= mbc
->usb_online
&&
319 (usblim
<= PCF50633_MBCC7_USB_500mA
);
328 static int ac_get_property(struct power_supply
*psy
,
329 enum power_supply_property psp
,
330 union power_supply_propval
*val
)
332 struct pcf50633_mbc
*mbc
= container_of(psy
, struct pcf50633_mbc
, ac
);
334 u8 usblim
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC7
) &
335 PCF50633_MBCC7_USB_MASK
;
338 case POWER_SUPPLY_PROP_ONLINE
:
339 val
->intval
= mbc
->usb_online
&&
340 (usblim
== PCF50633_MBCC7_USB_1000mA
);
349 static enum power_supply_property power_props
[] = {
350 POWER_SUPPLY_PROP_ONLINE
,
353 static const u8 mbc_irq_handlers
[] = {
358 PCF50633_IRQ_BATFULL
,
359 PCF50633_IRQ_CHGHALT
,
360 PCF50633_IRQ_THLIMON
,
361 PCF50633_IRQ_THLIMOFF
,
362 PCF50633_IRQ_USBLIMON
,
363 PCF50633_IRQ_USBLIMOFF
,
368 static int __devinit
pcf50633_mbc_probe(struct platform_device
*pdev
)
370 struct pcf50633_mbc
*mbc
;
375 mbc
= kzalloc(sizeof(*mbc
), GFP_KERNEL
);
379 platform_set_drvdata(pdev
, mbc
);
380 mbc
->pcf
= dev_to_pcf50633(pdev
->dev
.parent
);
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
);
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
;
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
;
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
;
412 ret
= power_supply_register(&pdev
->dev
, &mbc
->adapter
);
414 dev_err(mbc
->pcf
->dev
, "failed to register adapter\n");
419 ret
= power_supply_register(&pdev
->dev
, &mbc
->usb
);
421 dev_err(mbc
->pcf
->dev
, "failed to register usb\n");
422 power_supply_unregister(&mbc
->adapter
);
427 ret
= power_supply_register(&pdev
->dev
, &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
);
436 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &mbc_attr_group
);
438 dev_err(mbc
->pcf
->dev
, "failed to create sysfs entries\n");
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
);
449 static int __devexit
pcf50633_mbc_remove(struct platform_device
*pdev
)
451 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pdev
);
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
]);
458 power_supply_unregister(&mbc
->usb
);
459 power_supply_unregister(&mbc
->adapter
);
460 power_supply_unregister(&mbc
->ac
);
467 static struct platform_driver pcf50633_mbc_driver
= {
469 .name
= "pcf50633-mbc",
471 .probe
= pcf50633_mbc_probe
,
472 .remove
= __devexit_p(pcf50633_mbc_remove
),
475 static int __init
pcf50633_mbc_init(void)
477 return platform_driver_register(&pcf50633_mbc_driver
);
479 module_init(pcf50633_mbc_init
);
481 static void __exit
pcf50633_mbc_exit(void)
483 platform_driver_unregister(&pcf50633_mbc_driver
);
485 module_exit(pcf50633_mbc_exit
);
487 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
488 MODULE_DESCRIPTION("PCF50633 mbc driver");
489 MODULE_LICENSE("GPL");
490 MODULE_ALIAS("platform:pcf50633-mbc");