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
;
41 int pcf50633_mbc_usb_curlim_set(struct pcf50633
*pcf
, int ma
)
43 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
48 bits
= PCF50633_MBCC7_USB_1000mA
;
50 bits
= PCF50633_MBCC7_USB_500mA
;
52 bits
= PCF50633_MBCC7_USB_100mA
;
54 bits
= PCF50633_MBCC7_USB_SUSPEND
;
56 ret
= pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC7
,
57 PCF50633_MBCC7_USB_MASK
, bits
);
59 dev_err(pcf
->dev
, "error setting usb curlim to %d mA\n", ma
);
61 dev_info(pcf
->dev
, "usb curlim to %d mA\n", ma
);
63 power_supply_changed(&mbc
->usb
);
67 EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set
);
69 int pcf50633_mbc_get_status(struct pcf50633
*pcf
)
71 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
75 status
|= PCF50633_MBC_USB_ONLINE
;
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
;
85 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status
);
87 void pcf50633_mbc_set_status(struct pcf50633
*pcf
, int what
, int status
)
89 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
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
;
100 EXPORT_SYMBOL_GPL(pcf50633_mbc_set_status
);
103 show_chgmode(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
105 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
107 u8 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
108 u8 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
110 return sprintf(buf
, "%d\n", chgmod
);
112 static DEVICE_ATTR(chgmode
, S_IRUGO
, show_chgmode
, NULL
);
115 show_usblim(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
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
;
122 if (usblim
== PCF50633_MBCC7_USB_1000mA
)
124 else if (usblim
== PCF50633_MBCC7_USB_500mA
)
126 else if (usblim
== PCF50633_MBCC7_USB_100mA
)
131 return sprintf(buf
, "%u\n", ma
);
134 static ssize_t
set_usblim(struct device
*dev
,
135 struct device_attribute
*attr
, const char *buf
, size_t count
)
137 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
141 ret
= strict_strtoul(buf
, 10, &ma
);
145 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, ma
);
150 static DEVICE_ATTR(usb_curlim
, S_IRUGO
| S_IWUSR
, show_usblim
, set_usblim
);
152 static struct attribute
*pcf50633_mbc_sysfs_entries
[] = {
153 &dev_attr_chgmode
.attr
,
154 &dev_attr_usb_curlim
.attr
,
158 static struct attribute_group mbc_attr_group
= {
159 .name
= NULL
, /* put in device directory */
160 .attrs
= pcf50633_mbc_sysfs_entries
,
164 pcf50633_mbc_irq_handler(int irq
, void *data
)
166 struct pcf50633_mbc
*mbc
= data
;
169 if (irq
== PCF50633_IRQ_USBINS
) {
171 } else if (irq
== PCF50633_IRQ_USBREM
) {
174 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, 0);
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;
186 if (irq
== PCF50633_IRQ_BATFULL
) {
188 mbc
->adapter_active
= 0;
191 power_supply_changed(&mbc
->usb
);
192 power_supply_changed(&mbc
->adapter
);
194 if (mbc
->pcf
->pdata
->mbc_event_callback
)
195 mbc
->pcf
->pdata
->mbc_event_callback(mbc
->pcf
, irq
);
198 static int adapter_get_property(struct power_supply
*psy
,
199 enum power_supply_property psp
,
200 union power_supply_propval
*val
)
202 struct pcf50633_mbc
*mbc
= container_of(psy
, struct pcf50633_mbc
, usb
);
206 case POWER_SUPPLY_PROP_ONLINE
:
207 val
->intval
= mbc
->adapter_online
;
216 static int usb_get_property(struct power_supply
*psy
,
217 enum power_supply_property psp
,
218 union power_supply_propval
*val
)
220 struct pcf50633_mbc
*mbc
= container_of(psy
, struct pcf50633_mbc
, usb
);
224 case POWER_SUPPLY_PROP_ONLINE
:
225 val
->intval
= mbc
->usb_online
;
234 static enum power_supply_property power_props
[] = {
235 POWER_SUPPLY_PROP_ONLINE
,
238 static const u8 mbc_irq_handlers
[] = {
243 PCF50633_IRQ_BATFULL
,
244 PCF50633_IRQ_CHGHALT
,
245 PCF50633_IRQ_THLIMON
,
246 PCF50633_IRQ_THLIMOFF
,
247 PCF50633_IRQ_USBLIMON
,
248 PCF50633_IRQ_USBLIMOFF
,
253 static int __devinit
pcf50633_mbc_probe(struct platform_device
*pdev
)
255 struct pcf50633_mbc
*mbc
;
256 struct pcf50633_subdev_pdata
*pdata
= pdev
->dev
.platform_data
;
261 mbc
= kzalloc(sizeof(*mbc
), GFP_KERNEL
);
265 platform_set_drvdata(pdev
, mbc
);
266 mbc
->pcf
= pdata
->pcf
;
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
);
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
;
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
;
290 ret
= power_supply_register(&pdev
->dev
, &mbc
->adapter
);
292 dev_err(mbc
->pcf
->dev
, "failed to register adapter\n");
297 ret
= power_supply_register(&pdev
->dev
, &mbc
->usb
);
299 dev_err(mbc
->pcf
->dev
, "failed to register usb\n");
300 power_supply_unregister(&mbc
->adapter
);
305 ret
= sysfs_create_group(&pdev
->dev
.kobj
, &mbc_attr_group
);
307 dev_err(mbc
->pcf
->dev
, "failed to create sysfs entries\n");
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
);
318 static int __devexit
pcf50633_mbc_remove(struct platform_device
*pdev
)
320 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pdev
);
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
]);
327 power_supply_unregister(&mbc
->usb
);
328 power_supply_unregister(&mbc
->adapter
);
335 static struct platform_driver pcf50633_mbc_driver
= {
337 .name
= "pcf50633-mbc",
339 .probe
= pcf50633_mbc_probe
,
340 .remove
= __devexit_p(pcf50633_mbc_remove
),
343 static int __init
pcf50633_mbc_init(void)
345 return platform_driver_register(&pcf50633_mbc_driver
);
347 module_init(pcf50633_mbc_init
);
349 static void __exit
pcf50633_mbc_exit(void)
351 platform_driver_unregister(&pcf50633_mbc_driver
);
353 module_exit(pcf50633_mbc_exit
);
355 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
356 MODULE_DESCRIPTION("PCF50633 mbc driver");
357 MODULE_LICENSE("GPL");
358 MODULE_ALIAS("platform:pcf50633-mbc");