1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* NXP PCF50633 Main Battery Charger Driver
4 * (C) 2006-2008 by Openmoko, Inc.
5 * Author: Balaji Rao <balajirrao@openmoko.org>
8 * Broken down from monstrous PCF50633 driver mainly by
9 * Harald Welte, Andy Green and Werner Almesberger
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/init.h>
16 #include <linux/types.h>
17 #include <linux/device.h>
18 #include <linux/sysfs.h>
19 #include <linux/platform_device.h>
20 #include <linux/power_supply.h>
22 #include <linux/mfd/pcf50633/core.h>
23 #include <linux/mfd/pcf50633/mbc.h>
31 struct power_supply
*usb
;
32 struct power_supply
*adapter
;
33 struct power_supply
*ac
;
36 int pcf50633_mbc_usb_curlim_set(struct pcf50633
*pcf
, int ma
)
38 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
45 bits
= PCF50633_MBCC7_USB_1000mA
;
47 } else if (ma
>= 500) {
48 bits
= PCF50633_MBCC7_USB_500mA
;
50 } else if (ma
>= 100) {
51 bits
= PCF50633_MBCC7_USB_100mA
;
54 bits
= PCF50633_MBCC7_USB_SUSPEND
;
58 ret
= pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC7
,
59 PCF50633_MBCC7_USB_MASK
, bits
);
61 dev_err(pcf
->dev
, "error setting usb curlim to %d mA\n", ma
);
63 dev_info(pcf
->dev
, "usb curlim to %d mA\n", ma
);
66 * We limit the charging current to be the USB current limit.
67 * The reason is that on pcf50633, when it enters PMU Standby mode,
68 * which it does when the device goes "off", the USB current limit
69 * reverts to the variant default. In at least one common case, that
70 * default is 500mA. By setting the charging current to be the same
71 * as the USB limit we set here before PMU standby, we enforce it only
72 * using the correct amount of current even when the USB current limit
73 * gets reset to the wrong thing
76 if (mbc
->pcf
->pdata
->charger_reference_current_ma
) {
77 mbcc5
= (ma
<< 8) / mbc
->pcf
->pdata
->charger_reference_current_ma
;
80 pcf50633_reg_write(mbc
->pcf
, PCF50633_REG_MBCC5
, mbcc5
);
83 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
84 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
86 /* If chgmod == BATFULL, setting chgena has no effect.
87 * Datasheet says we need to set resume instead but when autoresume is
88 * used resume doesn't work. Clear and set chgena instead.
90 if (chgmod
!= PCF50633_MBCS2_MBC_BAT_FULL
)
91 pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC1
,
92 PCF50633_MBCC1_CHGENA
, PCF50633_MBCC1_CHGENA
);
94 pcf50633_reg_clear_bits(pcf
, PCF50633_REG_MBCC1
,
95 PCF50633_MBCC1_CHGENA
);
96 pcf50633_reg_set_bit_mask(pcf
, PCF50633_REG_MBCC1
,
97 PCF50633_MBCC1_CHGENA
, PCF50633_MBCC1_CHGENA
);
100 power_supply_changed(mbc
->usb
);
104 EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set
);
106 int pcf50633_mbc_get_status(struct pcf50633
*pcf
)
108 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
115 chgmod
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
)
116 & PCF50633_MBCS2_MBC_MASK
;
119 status
|= PCF50633_MBC_USB_ONLINE
;
120 if (chgmod
== PCF50633_MBCS2_MBC_USB_PRE
||
121 chgmod
== PCF50633_MBCS2_MBC_USB_PRE_WAIT
||
122 chgmod
== PCF50633_MBCS2_MBC_USB_FAST
||
123 chgmod
== PCF50633_MBCS2_MBC_USB_FAST_WAIT
)
124 status
|= PCF50633_MBC_USB_ACTIVE
;
125 if (mbc
->adapter_online
)
126 status
|= PCF50633_MBC_ADAPTER_ONLINE
;
127 if (chgmod
== PCF50633_MBCS2_MBC_ADP_PRE
||
128 chgmod
== PCF50633_MBCS2_MBC_ADP_PRE_WAIT
||
129 chgmod
== PCF50633_MBCS2_MBC_ADP_FAST
||
130 chgmod
== PCF50633_MBCS2_MBC_ADP_FAST_WAIT
)
131 status
|= PCF50633_MBC_ADAPTER_ACTIVE
;
135 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status
);
137 int pcf50633_mbc_get_usb_online_status(struct pcf50633
*pcf
)
139 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pcf
->mbc_pdev
);
144 return mbc
->usb_online
;
146 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status
);
149 show_chgmode(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
151 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
153 u8 mbcs2
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS2
);
154 u8 chgmod
= (mbcs2
& PCF50633_MBCS2_MBC_MASK
);
156 return sprintf(buf
, "%d\n", chgmod
);
158 static DEVICE_ATTR(chgmode
, S_IRUGO
, show_chgmode
, NULL
);
161 show_usblim(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
163 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
164 u8 usblim
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC7
) &
165 PCF50633_MBCC7_USB_MASK
;
168 if (usblim
== PCF50633_MBCC7_USB_1000mA
)
170 else if (usblim
== PCF50633_MBCC7_USB_500mA
)
172 else if (usblim
== PCF50633_MBCC7_USB_100mA
)
177 return sprintf(buf
, "%u\n", ma
);
180 static ssize_t
set_usblim(struct device
*dev
,
181 struct device_attribute
*attr
, const char *buf
, size_t count
)
183 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
187 ret
= kstrtoul(buf
, 10, &ma
);
191 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, ma
);
196 static DEVICE_ATTR(usb_curlim
, S_IRUGO
| S_IWUSR
, show_usblim
, set_usblim
);
199 show_chglim(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
201 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
202 u8 mbcc5
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC5
);
205 if (!mbc
->pcf
->pdata
->charger_reference_current_ma
)
208 ma
= (mbc
->pcf
->pdata
->charger_reference_current_ma
* mbcc5
) >> 8;
210 return sprintf(buf
, "%u\n", ma
);
213 static ssize_t
set_chglim(struct device
*dev
,
214 struct device_attribute
*attr
, const char *buf
, size_t count
)
216 struct pcf50633_mbc
*mbc
= dev_get_drvdata(dev
);
221 if (!mbc
->pcf
->pdata
->charger_reference_current_ma
)
224 ret
= kstrtoul(buf
, 10, &ma
);
228 mbcc5
= (ma
<< 8) / mbc
->pcf
->pdata
->charger_reference_current_ma
;
231 pcf50633_reg_write(mbc
->pcf
, PCF50633_REG_MBCC5
, mbcc5
);
237 * This attribute allows to change MBC charging limit on the fly
238 * independently of usb current limit. It also gets set automatically every
239 * time usb current limit is changed.
241 static DEVICE_ATTR(chg_curlim
, S_IRUGO
| S_IWUSR
, show_chglim
, set_chglim
);
243 static struct attribute
*pcf50633_mbc_sysfs_attrs
[] = {
244 &dev_attr_chgmode
.attr
,
245 &dev_attr_usb_curlim
.attr
,
246 &dev_attr_chg_curlim
.attr
,
250 ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs
);
253 pcf50633_mbc_irq_handler(int irq
, void *data
)
255 struct pcf50633_mbc
*mbc
= data
;
258 if (irq
== PCF50633_IRQ_USBINS
) {
260 } else if (irq
== PCF50633_IRQ_USBREM
) {
262 pcf50633_mbc_usb_curlim_set(mbc
->pcf
, 0);
266 if (irq
== PCF50633_IRQ_ADPINS
)
267 mbc
->adapter_online
= 1;
268 else if (irq
== PCF50633_IRQ_ADPREM
)
269 mbc
->adapter_online
= 0;
271 power_supply_changed(mbc
->ac
);
272 power_supply_changed(mbc
->usb
);
273 power_supply_changed(mbc
->adapter
);
275 if (mbc
->pcf
->pdata
->mbc_event_callback
)
276 mbc
->pcf
->pdata
->mbc_event_callback(mbc
->pcf
, irq
);
279 static int adapter_get_property(struct power_supply
*psy
,
280 enum power_supply_property psp
,
281 union power_supply_propval
*val
)
283 struct pcf50633_mbc
*mbc
= power_supply_get_drvdata(psy
);
287 case POWER_SUPPLY_PROP_ONLINE
:
288 val
->intval
= mbc
->adapter_online
;
297 static int usb_get_property(struct power_supply
*psy
,
298 enum power_supply_property psp
,
299 union power_supply_propval
*val
)
301 struct pcf50633_mbc
*mbc
= power_supply_get_drvdata(psy
);
303 u8 usblim
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC7
) &
304 PCF50633_MBCC7_USB_MASK
;
307 case POWER_SUPPLY_PROP_ONLINE
:
308 val
->intval
= mbc
->usb_online
&&
309 (usblim
<= PCF50633_MBCC7_USB_500mA
);
318 static int ac_get_property(struct power_supply
*psy
,
319 enum power_supply_property psp
,
320 union power_supply_propval
*val
)
322 struct pcf50633_mbc
*mbc
= power_supply_get_drvdata(psy
);
324 u8 usblim
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCC7
) &
325 PCF50633_MBCC7_USB_MASK
;
328 case POWER_SUPPLY_PROP_ONLINE
:
329 val
->intval
= mbc
->usb_online
&&
330 (usblim
== PCF50633_MBCC7_USB_1000mA
);
339 static enum power_supply_property power_props
[] = {
340 POWER_SUPPLY_PROP_ONLINE
,
343 static const u8 mbc_irq_handlers
[] = {
348 PCF50633_IRQ_BATFULL
,
349 PCF50633_IRQ_CHGHALT
,
350 PCF50633_IRQ_THLIMON
,
351 PCF50633_IRQ_THLIMOFF
,
352 PCF50633_IRQ_USBLIMON
,
353 PCF50633_IRQ_USBLIMOFF
,
358 static const struct power_supply_desc pcf50633_mbc_adapter_desc
= {
360 .type
= POWER_SUPPLY_TYPE_MAINS
,
361 .properties
= power_props
,
362 .num_properties
= ARRAY_SIZE(power_props
),
363 .get_property
= &adapter_get_property
,
366 static const struct power_supply_desc pcf50633_mbc_usb_desc
= {
368 .type
= POWER_SUPPLY_TYPE_USB
,
369 .properties
= power_props
,
370 .num_properties
= ARRAY_SIZE(power_props
),
371 .get_property
= usb_get_property
,
374 static const struct power_supply_desc pcf50633_mbc_ac_desc
= {
376 .type
= POWER_SUPPLY_TYPE_MAINS
,
377 .properties
= power_props
,
378 .num_properties
= ARRAY_SIZE(power_props
),
379 .get_property
= ac_get_property
,
382 static int pcf50633_mbc_probe(struct platform_device
*pdev
)
384 struct power_supply_config psy_cfg
= {};
385 struct power_supply_config usb_psy_cfg
;
386 struct pcf50633_mbc
*mbc
;
390 mbc
= devm_kzalloc(&pdev
->dev
, sizeof(*mbc
), GFP_KERNEL
);
394 platform_set_drvdata(pdev
, mbc
);
395 mbc
->pcf
= dev_to_pcf50633(pdev
->dev
.parent
);
397 /* Set up IRQ handlers */
398 for (i
= 0; i
< ARRAY_SIZE(mbc_irq_handlers
); i
++)
399 pcf50633_register_irq(mbc
->pcf
, mbc_irq_handlers
[i
],
400 pcf50633_mbc_irq_handler
, mbc
);
402 psy_cfg
.supplied_to
= mbc
->pcf
->pdata
->batteries
;
403 psy_cfg
.num_supplicants
= mbc
->pcf
->pdata
->num_batteries
;
404 psy_cfg
.drv_data
= mbc
;
406 /* Create power supplies */
407 mbc
->adapter
= power_supply_register(&pdev
->dev
,
408 &pcf50633_mbc_adapter_desc
,
410 if (IS_ERR(mbc
->adapter
)) {
411 dev_err(mbc
->pcf
->dev
, "failed to register adapter\n");
412 return PTR_ERR(mbc
->adapter
);
415 usb_psy_cfg
= psy_cfg
;
416 usb_psy_cfg
.attr_grp
= pcf50633_mbc_sysfs_groups
;
418 mbc
->usb
= power_supply_register(&pdev
->dev
, &pcf50633_mbc_usb_desc
,
420 if (IS_ERR(mbc
->usb
)) {
421 dev_err(mbc
->pcf
->dev
, "failed to register usb\n");
422 power_supply_unregister(mbc
->adapter
);
423 return PTR_ERR(mbc
->usb
);
426 mbc
->ac
= power_supply_register(&pdev
->dev
, &pcf50633_mbc_ac_desc
,
428 if (IS_ERR(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
);
432 return PTR_ERR(mbc
->ac
);
435 mbcs1
= pcf50633_reg_read(mbc
->pcf
, PCF50633_REG_MBCS1
);
436 if (mbcs1
& PCF50633_MBCS1_USBPRES
)
437 pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS
, mbc
);
438 if (mbcs1
& PCF50633_MBCS1_ADAPTPRES
)
439 pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS
, mbc
);
444 static int pcf50633_mbc_remove(struct platform_device
*pdev
)
446 struct pcf50633_mbc
*mbc
= platform_get_drvdata(pdev
);
449 /* Remove IRQ handlers */
450 for (i
= 0; i
< ARRAY_SIZE(mbc_irq_handlers
); i
++)
451 pcf50633_free_irq(mbc
->pcf
, mbc_irq_handlers
[i
]);
453 power_supply_unregister(mbc
->usb
);
454 power_supply_unregister(mbc
->adapter
);
455 power_supply_unregister(mbc
->ac
);
460 static struct platform_driver pcf50633_mbc_driver
= {
462 .name
= "pcf50633-mbc",
464 .probe
= pcf50633_mbc_probe
,
465 .remove
= pcf50633_mbc_remove
,
468 module_platform_driver(pcf50633_mbc_driver
);
470 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
471 MODULE_DESCRIPTION("PCF50633 mbc driver");
472 MODULE_LICENSE("GPL");
473 MODULE_ALIAS("platform:pcf50633-mbc");