xref: /OK3568_Linux_fs/kernel/drivers/power/supply/pcf50633-charger.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* NXP PCF50633 Main Battery Charger Driver
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * (C) 2006-2008 by Openmoko, Inc.
5*4882a593Smuzhiyun  * Author: Balaji Rao <balajirrao@openmoko.org>
6*4882a593Smuzhiyun  * All rights reserved.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Broken down from monstrous PCF50633 driver mainly by
9*4882a593Smuzhiyun  * Harald Welte, Andy Green and Werner Almesberger
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/types.h>
17*4882a593Smuzhiyun #include <linux/device.h>
18*4882a593Smuzhiyun #include <linux/sysfs.h>
19*4882a593Smuzhiyun #include <linux/platform_device.h>
20*4882a593Smuzhiyun #include <linux/power_supply.h>
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include <linux/mfd/pcf50633/core.h>
23*4882a593Smuzhiyun #include <linux/mfd/pcf50633/mbc.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun struct pcf50633_mbc {
26*4882a593Smuzhiyun 	struct pcf50633 *pcf;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	int adapter_online;
29*4882a593Smuzhiyun 	int usb_online;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	struct power_supply *usb;
32*4882a593Smuzhiyun 	struct power_supply *adapter;
33*4882a593Smuzhiyun 	struct power_supply *ac;
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun 
pcf50633_mbc_usb_curlim_set(struct pcf50633 * pcf,int ma)36*4882a593Smuzhiyun int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
39*4882a593Smuzhiyun 	int ret = 0;
40*4882a593Smuzhiyun 	u8 bits;
41*4882a593Smuzhiyun 	u8 mbcs2, chgmod;
42*4882a593Smuzhiyun 	unsigned int mbcc5;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	if (ma >= 1000) {
45*4882a593Smuzhiyun 		bits = PCF50633_MBCC7_USB_1000mA;
46*4882a593Smuzhiyun 		ma = 1000;
47*4882a593Smuzhiyun 	} else if (ma >= 500) {
48*4882a593Smuzhiyun 		bits = PCF50633_MBCC7_USB_500mA;
49*4882a593Smuzhiyun 		ma = 500;
50*4882a593Smuzhiyun 	} else if (ma >= 100) {
51*4882a593Smuzhiyun 		bits = PCF50633_MBCC7_USB_100mA;
52*4882a593Smuzhiyun 		ma = 100;
53*4882a593Smuzhiyun 	} else {
54*4882a593Smuzhiyun 		bits = PCF50633_MBCC7_USB_SUSPEND;
55*4882a593Smuzhiyun 		ma = 0;
56*4882a593Smuzhiyun 	}
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
59*4882a593Smuzhiyun 					PCF50633_MBCC7_USB_MASK, bits);
60*4882a593Smuzhiyun 	if (ret)
61*4882a593Smuzhiyun 		dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
62*4882a593Smuzhiyun 	else
63*4882a593Smuzhiyun 		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	/*
66*4882a593Smuzhiyun 	 * We limit the charging current to be the USB current limit.
67*4882a593Smuzhiyun 	 * The reason is that on pcf50633, when it enters PMU Standby mode,
68*4882a593Smuzhiyun 	 * which it does when the device goes "off", the USB current limit
69*4882a593Smuzhiyun 	 * reverts to the variant default.  In at least one common case, that
70*4882a593Smuzhiyun 	 * default is 500mA.  By setting the charging current to be the same
71*4882a593Smuzhiyun 	 * as the USB limit we set here before PMU standby, we enforce it only
72*4882a593Smuzhiyun 	 * using the correct amount of current even when the USB current limit
73*4882a593Smuzhiyun 	 * gets reset to the wrong thing
74*4882a593Smuzhiyun 	 */
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	if (mbc->pcf->pdata->charger_reference_current_ma) {
77*4882a593Smuzhiyun 		mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
78*4882a593Smuzhiyun 		if (mbcc5 > 255)
79*4882a593Smuzhiyun 			mbcc5 = 255;
80*4882a593Smuzhiyun 		pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
81*4882a593Smuzhiyun 	}
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
84*4882a593Smuzhiyun 	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	/* If chgmod == BATFULL, setting chgena has no effect.
87*4882a593Smuzhiyun 	 * Datasheet says we need to set resume instead but when autoresume is
88*4882a593Smuzhiyun 	 * used resume doesn't work. Clear and set chgena instead.
89*4882a593Smuzhiyun 	 */
90*4882a593Smuzhiyun 	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
91*4882a593Smuzhiyun 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
92*4882a593Smuzhiyun 				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
93*4882a593Smuzhiyun 	else {
94*4882a593Smuzhiyun 		pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
95*4882a593Smuzhiyun 				PCF50633_MBCC1_CHGENA);
96*4882a593Smuzhiyun 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
97*4882a593Smuzhiyun 				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	power_supply_changed(mbc->usb);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	return ret;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
105*4882a593Smuzhiyun 
pcf50633_mbc_get_status(struct pcf50633 * pcf)106*4882a593Smuzhiyun int pcf50633_mbc_get_status(struct pcf50633 *pcf)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
109*4882a593Smuzhiyun 	int status = 0;
110*4882a593Smuzhiyun 	u8 chgmod;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	if (!mbc)
113*4882a593Smuzhiyun 		return 0;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
116*4882a593Smuzhiyun 		& PCF50633_MBCS2_MBC_MASK;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	if (mbc->usb_online)
119*4882a593Smuzhiyun 		status |= PCF50633_MBC_USB_ONLINE;
120*4882a593Smuzhiyun 	if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
121*4882a593Smuzhiyun 	    chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
122*4882a593Smuzhiyun 	    chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
123*4882a593Smuzhiyun 	    chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
124*4882a593Smuzhiyun 		status |= PCF50633_MBC_USB_ACTIVE;
125*4882a593Smuzhiyun 	if (mbc->adapter_online)
126*4882a593Smuzhiyun 		status |= PCF50633_MBC_ADAPTER_ONLINE;
127*4882a593Smuzhiyun 	if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
128*4882a593Smuzhiyun 	    chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
129*4882a593Smuzhiyun 	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
130*4882a593Smuzhiyun 	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
131*4882a593Smuzhiyun 		status |= PCF50633_MBC_ADAPTER_ACTIVE;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	return status;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
136*4882a593Smuzhiyun 
pcf50633_mbc_get_usb_online_status(struct pcf50633 * pcf)137*4882a593Smuzhiyun int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (!mbc)
142*4882a593Smuzhiyun 		return 0;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	return mbc->usb_online;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun static ssize_t
show_chgmode(struct device * dev,struct device_attribute * attr,char * buf)149*4882a593Smuzhiyun show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
154*4882a593Smuzhiyun 	u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	return sprintf(buf, "%d\n", chgmod);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun static ssize_t
show_usblim(struct device * dev,struct device_attribute * attr,char * buf)161*4882a593Smuzhiyun show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
162*4882a593Smuzhiyun {
163*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
164*4882a593Smuzhiyun 	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
165*4882a593Smuzhiyun 						PCF50633_MBCC7_USB_MASK;
166*4882a593Smuzhiyun 	unsigned int ma;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	if (usblim == PCF50633_MBCC7_USB_1000mA)
169*4882a593Smuzhiyun 		ma = 1000;
170*4882a593Smuzhiyun 	else if (usblim == PCF50633_MBCC7_USB_500mA)
171*4882a593Smuzhiyun 		ma = 500;
172*4882a593Smuzhiyun 	else if (usblim == PCF50633_MBCC7_USB_100mA)
173*4882a593Smuzhiyun 		ma = 100;
174*4882a593Smuzhiyun 	else
175*4882a593Smuzhiyun 		ma = 0;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	return sprintf(buf, "%u\n", ma);
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun 
set_usblim(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)180*4882a593Smuzhiyun static ssize_t set_usblim(struct device *dev,
181*4882a593Smuzhiyun 		struct device_attribute *attr, const char *buf, size_t count)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
184*4882a593Smuzhiyun 	unsigned long ma;
185*4882a593Smuzhiyun 	int ret;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	ret = kstrtoul(buf, 10, &ma);
188*4882a593Smuzhiyun 	if (ret)
189*4882a593Smuzhiyun 		return ret;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	return count;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun static ssize_t
show_chglim(struct device * dev,struct device_attribute * attr,char * buf)199*4882a593Smuzhiyun show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
202*4882a593Smuzhiyun 	u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
203*4882a593Smuzhiyun 	unsigned int ma;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if (!mbc->pcf->pdata->charger_reference_current_ma)
206*4882a593Smuzhiyun 		return -ENODEV;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	return sprintf(buf, "%u\n", ma);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun 
set_chglim(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)213*4882a593Smuzhiyun static ssize_t set_chglim(struct device *dev,
214*4882a593Smuzhiyun 		struct device_attribute *attr, const char *buf, size_t count)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
217*4882a593Smuzhiyun 	unsigned long ma;
218*4882a593Smuzhiyun 	unsigned int mbcc5;
219*4882a593Smuzhiyun 	int ret;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	if (!mbc->pcf->pdata->charger_reference_current_ma)
222*4882a593Smuzhiyun 		return -ENODEV;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	ret = kstrtoul(buf, 10, &ma);
225*4882a593Smuzhiyun 	if (ret)
226*4882a593Smuzhiyun 		return ret;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
229*4882a593Smuzhiyun 	if (mbcc5 > 255)
230*4882a593Smuzhiyun 		mbcc5 = 255;
231*4882a593Smuzhiyun 	pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	return count;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun /*
237*4882a593Smuzhiyun  * This attribute allows to change MBC charging limit on the fly
238*4882a593Smuzhiyun  * independently of usb current limit. It also gets set automatically every
239*4882a593Smuzhiyun  * time usb current limit is changed.
240*4882a593Smuzhiyun  */
241*4882a593Smuzhiyun static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun static struct attribute *pcf50633_mbc_sysfs_attrs[] = {
244*4882a593Smuzhiyun 	&dev_attr_chgmode.attr,
245*4882a593Smuzhiyun 	&dev_attr_usb_curlim.attr,
246*4882a593Smuzhiyun 	&dev_attr_chg_curlim.attr,
247*4882a593Smuzhiyun 	NULL,
248*4882a593Smuzhiyun };
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs);
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun static void
pcf50633_mbc_irq_handler(int irq,void * data)253*4882a593Smuzhiyun pcf50633_mbc_irq_handler(int irq, void *data)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = data;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	/* USB */
258*4882a593Smuzhiyun 	if (irq == PCF50633_IRQ_USBINS) {
259*4882a593Smuzhiyun 		mbc->usb_online = 1;
260*4882a593Smuzhiyun 	} else if (irq == PCF50633_IRQ_USBREM) {
261*4882a593Smuzhiyun 		mbc->usb_online = 0;
262*4882a593Smuzhiyun 		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
263*4882a593Smuzhiyun 	}
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	/* Adapter */
266*4882a593Smuzhiyun 	if (irq == PCF50633_IRQ_ADPINS)
267*4882a593Smuzhiyun 		mbc->adapter_online = 1;
268*4882a593Smuzhiyun 	else if (irq == PCF50633_IRQ_ADPREM)
269*4882a593Smuzhiyun 		mbc->adapter_online = 0;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	power_supply_changed(mbc->ac);
272*4882a593Smuzhiyun 	power_supply_changed(mbc->usb);
273*4882a593Smuzhiyun 	power_supply_changed(mbc->adapter);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	if (mbc->pcf->pdata->mbc_event_callback)
276*4882a593Smuzhiyun 		mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
adapter_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)279*4882a593Smuzhiyun static int adapter_get_property(struct power_supply *psy,
280*4882a593Smuzhiyun 			enum power_supply_property psp,
281*4882a593Smuzhiyun 			union power_supply_propval *val)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
284*4882a593Smuzhiyun 	int ret = 0;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	switch (psp) {
287*4882a593Smuzhiyun 	case POWER_SUPPLY_PROP_ONLINE:
288*4882a593Smuzhiyun 		val->intval =  mbc->adapter_online;
289*4882a593Smuzhiyun 		break;
290*4882a593Smuzhiyun 	default:
291*4882a593Smuzhiyun 		ret = -EINVAL;
292*4882a593Smuzhiyun 		break;
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 	return ret;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun 
usb_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)297*4882a593Smuzhiyun static int usb_get_property(struct power_supply *psy,
298*4882a593Smuzhiyun 			enum power_supply_property psp,
299*4882a593Smuzhiyun 			union power_supply_propval *val)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
302*4882a593Smuzhiyun 	int ret = 0;
303*4882a593Smuzhiyun 	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
304*4882a593Smuzhiyun 						PCF50633_MBCC7_USB_MASK;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	switch (psp) {
307*4882a593Smuzhiyun 	case POWER_SUPPLY_PROP_ONLINE:
308*4882a593Smuzhiyun 		val->intval = mbc->usb_online &&
309*4882a593Smuzhiyun 				(usblim <= PCF50633_MBCC7_USB_500mA);
310*4882a593Smuzhiyun 		break;
311*4882a593Smuzhiyun 	default:
312*4882a593Smuzhiyun 		ret = -EINVAL;
313*4882a593Smuzhiyun 		break;
314*4882a593Smuzhiyun 	}
315*4882a593Smuzhiyun 	return ret;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun 
ac_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)318*4882a593Smuzhiyun static int ac_get_property(struct power_supply *psy,
319*4882a593Smuzhiyun 			enum power_supply_property psp,
320*4882a593Smuzhiyun 			union power_supply_propval *val)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
323*4882a593Smuzhiyun 	int ret = 0;
324*4882a593Smuzhiyun 	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
325*4882a593Smuzhiyun 						PCF50633_MBCC7_USB_MASK;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	switch (psp) {
328*4882a593Smuzhiyun 	case POWER_SUPPLY_PROP_ONLINE:
329*4882a593Smuzhiyun 		val->intval = mbc->usb_online &&
330*4882a593Smuzhiyun 				(usblim == PCF50633_MBCC7_USB_1000mA);
331*4882a593Smuzhiyun 		break;
332*4882a593Smuzhiyun 	default:
333*4882a593Smuzhiyun 		ret = -EINVAL;
334*4882a593Smuzhiyun 		break;
335*4882a593Smuzhiyun 	}
336*4882a593Smuzhiyun 	return ret;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun static enum power_supply_property power_props[] = {
340*4882a593Smuzhiyun 	POWER_SUPPLY_PROP_ONLINE,
341*4882a593Smuzhiyun };
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun static const u8 mbc_irq_handlers[] = {
344*4882a593Smuzhiyun 	PCF50633_IRQ_ADPINS,
345*4882a593Smuzhiyun 	PCF50633_IRQ_ADPREM,
346*4882a593Smuzhiyun 	PCF50633_IRQ_USBINS,
347*4882a593Smuzhiyun 	PCF50633_IRQ_USBREM,
348*4882a593Smuzhiyun 	PCF50633_IRQ_BATFULL,
349*4882a593Smuzhiyun 	PCF50633_IRQ_CHGHALT,
350*4882a593Smuzhiyun 	PCF50633_IRQ_THLIMON,
351*4882a593Smuzhiyun 	PCF50633_IRQ_THLIMOFF,
352*4882a593Smuzhiyun 	PCF50633_IRQ_USBLIMON,
353*4882a593Smuzhiyun 	PCF50633_IRQ_USBLIMOFF,
354*4882a593Smuzhiyun 	PCF50633_IRQ_LOWSYS,
355*4882a593Smuzhiyun 	PCF50633_IRQ_LOWBAT,
356*4882a593Smuzhiyun };
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun static const struct power_supply_desc pcf50633_mbc_adapter_desc = {
359*4882a593Smuzhiyun 	.name		= "adapter",
360*4882a593Smuzhiyun 	.type		= POWER_SUPPLY_TYPE_MAINS,
361*4882a593Smuzhiyun 	.properties	= power_props,
362*4882a593Smuzhiyun 	.num_properties	= ARRAY_SIZE(power_props),
363*4882a593Smuzhiyun 	.get_property	= &adapter_get_property,
364*4882a593Smuzhiyun };
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun static const struct power_supply_desc pcf50633_mbc_usb_desc = {
367*4882a593Smuzhiyun 	.name		= "usb",
368*4882a593Smuzhiyun 	.type		= POWER_SUPPLY_TYPE_USB,
369*4882a593Smuzhiyun 	.properties	= power_props,
370*4882a593Smuzhiyun 	.num_properties	= ARRAY_SIZE(power_props),
371*4882a593Smuzhiyun 	.get_property	= usb_get_property,
372*4882a593Smuzhiyun };
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun static const struct power_supply_desc pcf50633_mbc_ac_desc = {
375*4882a593Smuzhiyun 	.name		= "ac",
376*4882a593Smuzhiyun 	.type		= POWER_SUPPLY_TYPE_MAINS,
377*4882a593Smuzhiyun 	.properties	= power_props,
378*4882a593Smuzhiyun 	.num_properties	= ARRAY_SIZE(power_props),
379*4882a593Smuzhiyun 	.get_property	= ac_get_property,
380*4882a593Smuzhiyun };
381*4882a593Smuzhiyun 
pcf50633_mbc_probe(struct platform_device * pdev)382*4882a593Smuzhiyun static int pcf50633_mbc_probe(struct platform_device *pdev)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	struct power_supply_config psy_cfg = {};
385*4882a593Smuzhiyun 	struct power_supply_config usb_psy_cfg;
386*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc;
387*4882a593Smuzhiyun 	int i;
388*4882a593Smuzhiyun 	u8 mbcs1;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
391*4882a593Smuzhiyun 	if (!mbc)
392*4882a593Smuzhiyun 		return -ENOMEM;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	platform_set_drvdata(pdev, mbc);
395*4882a593Smuzhiyun 	mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	/* Set up IRQ handlers */
398*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
399*4882a593Smuzhiyun 		pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
400*4882a593Smuzhiyun 					pcf50633_mbc_irq_handler, mbc);
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	psy_cfg.supplied_to		= mbc->pcf->pdata->batteries;
403*4882a593Smuzhiyun 	psy_cfg.num_supplicants		= mbc->pcf->pdata->num_batteries;
404*4882a593Smuzhiyun 	psy_cfg.drv_data		= mbc;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	/* Create power supplies */
407*4882a593Smuzhiyun 	mbc->adapter = power_supply_register(&pdev->dev,
408*4882a593Smuzhiyun 					     &pcf50633_mbc_adapter_desc,
409*4882a593Smuzhiyun 					     &psy_cfg);
410*4882a593Smuzhiyun 	if (IS_ERR(mbc->adapter)) {
411*4882a593Smuzhiyun 		dev_err(mbc->pcf->dev, "failed to register adapter\n");
412*4882a593Smuzhiyun 		return PTR_ERR(mbc->adapter);
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	usb_psy_cfg = psy_cfg;
416*4882a593Smuzhiyun 	usb_psy_cfg.attr_grp = pcf50633_mbc_sysfs_groups;
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
419*4882a593Smuzhiyun 					 &usb_psy_cfg);
420*4882a593Smuzhiyun 	if (IS_ERR(mbc->usb)) {
421*4882a593Smuzhiyun 		dev_err(mbc->pcf->dev, "failed to register usb\n");
422*4882a593Smuzhiyun 		power_supply_unregister(mbc->adapter);
423*4882a593Smuzhiyun 		return PTR_ERR(mbc->usb);
424*4882a593Smuzhiyun 	}
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
427*4882a593Smuzhiyun 					&psy_cfg);
428*4882a593Smuzhiyun 	if (IS_ERR(mbc->ac)) {
429*4882a593Smuzhiyun 		dev_err(mbc->pcf->dev, "failed to register ac\n");
430*4882a593Smuzhiyun 		power_supply_unregister(mbc->adapter);
431*4882a593Smuzhiyun 		power_supply_unregister(mbc->usb);
432*4882a593Smuzhiyun 		return PTR_ERR(mbc->ac);
433*4882a593Smuzhiyun 	}
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
436*4882a593Smuzhiyun 	if (mbcs1 & PCF50633_MBCS1_USBPRES)
437*4882a593Smuzhiyun 		pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
438*4882a593Smuzhiyun 	if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
439*4882a593Smuzhiyun 		pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	return 0;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun 
pcf50633_mbc_remove(struct platform_device * pdev)444*4882a593Smuzhiyun static int pcf50633_mbc_remove(struct platform_device *pdev)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun 	struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
447*4882a593Smuzhiyun 	int i;
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	/* Remove IRQ handlers */
450*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
451*4882a593Smuzhiyun 		pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	power_supply_unregister(mbc->usb);
454*4882a593Smuzhiyun 	power_supply_unregister(mbc->adapter);
455*4882a593Smuzhiyun 	power_supply_unregister(mbc->ac);
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	return 0;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun static struct platform_driver pcf50633_mbc_driver = {
461*4882a593Smuzhiyun 	.driver = {
462*4882a593Smuzhiyun 		.name = "pcf50633-mbc",
463*4882a593Smuzhiyun 	},
464*4882a593Smuzhiyun 	.probe = pcf50633_mbc_probe,
465*4882a593Smuzhiyun 	.remove = pcf50633_mbc_remove,
466*4882a593Smuzhiyun };
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun module_platform_driver(pcf50633_mbc_driver);
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
471*4882a593Smuzhiyun MODULE_DESCRIPTION("PCF50633 mbc driver");
472*4882a593Smuzhiyun MODULE_LICENSE("GPL");
473*4882a593Smuzhiyun MODULE_ALIAS("platform:pcf50633-mbc");
474