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