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>
37 struct power_supply usb
;
38 struct power_supply adapter
;
40 struct delayed_work charging_restart_work
;
43 int pcf50633_mbc_usb_curlim_set(struct pcf50633
*pcf
, int ma
)
45 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
48 int charging_start
= 1;
52 bits
= PCF50633_MBCC7_USB_1000mA
;
54 bits
= PCF50633_MBCC7_USB_500mA
;
56 bits
= PCF50633_MBCC7_USB_100mA
;
58 bits
= PCF50633_MBCC7_USB_SUSPEND
;
62 ret
= pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC7
,
63 PCF50633_MBCC7_USB_MASK
, bits
);
65 dev_err(pcf
->dev
, "error setting usb curlim to %d mA\n", ma
);
67 dev_info(pcf
->dev
, "usb curlim to %d mA\n", ma
);
69 /* Manual charging start */
70 mbcs2
= pcf50633_reg_read(pcf
, PCF50633_REG_MBCS2
);
71 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
73 /* If chgmod == BATFULL, setting chgena has no effect.
74 * We need to set resume instead.
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
);
80 pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC1
,
81 PCF50633_MBCC1_RESUME
, PCF50633_MBCC1_RESUME
);
83 mbc
->usb_active
= charging_start
;
85 power_supply_changed(&mbc
->usb
);
89 EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set
);
91 int pcf50633_mbc_get_status(struct pcf50633
*pcf
)
93 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
97 status
|= PCF50633_MBC_USB_ONLINE
;
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
;
107 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status
);
110 show_chgmode(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
112 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
114 u8 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
115 u8 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
117 return sprintf(buf
, "%d\n", chgmod
);
119 static DEVICE_ATTR(chgmode
, S_IRUGO
, show_chgmode
, NULL
);
122 show_usblim(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
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
;
129 if (usblim
== PCF50633_MBCC7_USB_1000mA
)
131 else if (usblim
== PCF50633_MBCC7_USB_500mA
)
133 else if (usblim
== PCF50633_MBCC7_USB_100mA
)
138 return sprintf(buf
, "%u\n", ma
);
141 static ssize_t
set_usblim(struct device
*dev
,
142 struct device_attribute
*attr
, const char *buf
, size_t count
)
144 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
148 ret
= strict_strtoul(buf
, 10, &ma
);
152 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, ma
);
157 static DEVICE_ATTR(usb_curlim
, S_IRUGO
| S_IWUSR
, show_usblim
, set_usblim
);
159 static struct attribute
*pcf50633_mbc_sysfs_entries
[] = {
160 &dev_attr_chgmode
.attr
,
161 &dev_attr_usb_curlim
.attr
,
165 static struct attribute_group mbc_attr_group
= {
166 .name
= NULL
, /* put in device directory */
167 .attrs
= pcf50633_mbc_sysfs_entries
,
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%.
175 * This work_struct function restarts charging at regular intervals to make
176 * sure we don't discharge too much
179 static void pcf50633_mbc_charging_restart(struct work_struct
*work
)
181 struct pcf50633_mbc
*mbc
;
184 mbc
= container_of(work
, struct pcf50633_mbc
,
185 charging_restart_work
.work
);
187 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
188 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
190 if (chgmod
!= PCF50633_MBCS2_MBC_BAT_FULL
)
193 /* Restart charging */
194 pcf50633_reg_set_bit_mask(mbc
->pcf
, PCF50633_REG_MBCC1
,
195 PCF50633_MBCC1_RESUME
, PCF50633_MBCC1_RESUME
);
197 power_supply_changed(&mbc
->usb
);
199 dev_info(mbc
->pcf
->dev
, "Charging restarted\n");
203 pcf50633_mbc_irq_handler(int irq
, void *data
)
205 struct pcf50633_mbc
*mbc
= data
;
206 int chg_restart_interval
=
207 mbc
->pcf
->pdata
->charging_restart_interval
;
210 if (irq
== PCF50633_IRQ_USBINS
) {
212 } else if (irq
== PCF50633_IRQ_USBREM
) {
215 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, 0);
216 cancel_delayed_work_sync(&mbc
->charging_restart_work
);
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;
228 if (irq
== PCF50633_IRQ_BATFULL
) {
230 mbc
->adapter_active
= 0;
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
)
237 else if (irq
== PCF50633_IRQ_USBLIMOFF
)
240 power_supply_changed(&mbc
->usb
);
241 power_supply_changed(&mbc
->adapter
);
243 if (mbc
->pcf
->pdata
->mbc_event_callback
)
244 mbc
->pcf
->pdata
->mbc_event_callback(mbc
->pcf
, irq
);
247 static int adapter_get_property(struct power_supply
*psy
,
248 enum power_supply_property psp
,
249 union power_supply_propval
*val
)
251 struct pcf50633_mbc
*mbc
= container_of(psy
,
252 struct pcf50633_mbc
, adapter
);
256 case POWER_SUPPLY_PROP_ONLINE
:
257 val
->intval
= mbc
->adapter_online
;
266 static int usb_get_property(struct power_supply
*psy
,
267 enum power_supply_property psp
,
268 union power_supply_propval
*val
)
270 struct pcf50633_mbc
*mbc
= container_of(psy
, struct pcf50633_mbc
, usb
);
274 case POWER_SUPPLY_PROP_ONLINE
:
275 val
->intval
= mbc
->usb_online
;
284 static enum power_supply_property power_props
[] = {
285 POWER_SUPPLY_PROP_ONLINE
,
288 static const u8 mbc_irq_handlers
[] = {
293 PCF50633_IRQ_BATFULL
,
294 PCF50633_IRQ_CHGHALT
,
295 PCF50633_IRQ_THLIMON
,
296 PCF50633_IRQ_THLIMOFF
,
297 PCF50633_IRQ_USBLIMON
,
298 PCF50633_IRQ_USBLIMOFF
,
303 static int __devinit
pcf50633_mbc_probe(struct platform_device
*pdev
)
305 struct pcf50633_mbc
*mbc
;
306 struct pcf50633_subdev_pdata
*pdata
= pdev
->dev
.platform_data
;
311 mbc
= kzalloc(sizeof(*mbc
), GFP_KERNEL
);
315 platform_set_drvdata(pdev
, mbc
);
316 mbc
->pcf
= pdata
->pcf
;
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
);
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
;
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
;
340 ret
= power_supply_register(&pdev
->dev
, &mbc
->adapter
);
342 dev_err(mbc
->pcf
->dev
, "failed to register adapter\n");
347 ret
= power_supply_register(&pdev
->dev
, &mbc
->usb
);
349 dev_err(mbc
->pcf
->dev
, "failed to register usb\n");
350 power_supply_unregister(&mbc
->adapter
);
355 INIT_DELAYED_WORK(&mbc
->charging_restart_work
,
356 pcf50633_mbc_charging_restart
);
358 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &mbc_attr_group
);
360 dev_err(mbc
->pcf
->dev
, "failed to create sysfs entries\n");
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
);
371 static int __devexit
pcf50633_mbc_remove(struct platform_device
*pdev
)
373 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pdev
);
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
]);
380 power_supply_unregister(&mbc
->usb
);
381 power_supply_unregister(&mbc
->adapter
);
383 cancel_delayed_work_sync(&mbc
->charging_restart_work
);
390 static struct platform_driver pcf50633_mbc_driver
= {
392 .name
= "pcf50633-mbc",
394 .probe
= pcf50633_mbc_probe
,
395 .remove
= __devexit_p(pcf50633_mbc_remove
),
398 static int __init
pcf50633_mbc_init(void)
400 return platform_driver_register(&pcf50633_mbc_driver
);
402 module_init(pcf50633_mbc_init
);
404 static void __exit
pcf50633_mbc_exit(void)
406 platform_driver_unregister(&pcf50633_mbc_driver
);
408 module_exit(pcf50633_mbc_exit
);
410 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
411 MODULE_DESCRIPTION("PCF50633 mbc driver");
412 MODULE_LICENSE("GPL");
413 MODULE_ALIAS("platform:pcf50633-mbc");