xref: /linux/drivers/power/supply/pcf50633-charger.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c0984e5SSebastian Reichel /* NXP PCF50633 Main Battery Charger Driver
38c0984e5SSebastian Reichel  *
48c0984e5SSebastian Reichel  * (C) 2006-2008 by Openmoko, Inc.
58c0984e5SSebastian Reichel  * Author: Balaji Rao <balajirrao@openmoko.org>
68c0984e5SSebastian Reichel  * All rights reserved.
78c0984e5SSebastian Reichel  *
88c0984e5SSebastian Reichel  * Broken down from monstrous PCF50633 driver mainly by
98c0984e5SSebastian Reichel  * Harald Welte, Andy Green and Werner Almesberger
108c0984e5SSebastian Reichel  */
118c0984e5SSebastian Reichel 
128c0984e5SSebastian Reichel #include <linux/kernel.h>
138c0984e5SSebastian Reichel #include <linux/module.h>
148c0984e5SSebastian Reichel #include <linux/slab.h>
158c0984e5SSebastian Reichel #include <linux/init.h>
168c0984e5SSebastian Reichel #include <linux/types.h>
178c0984e5SSebastian Reichel #include <linux/device.h>
188c0984e5SSebastian Reichel #include <linux/sysfs.h>
198c0984e5SSebastian Reichel #include <linux/platform_device.h>
208c0984e5SSebastian Reichel #include <linux/power_supply.h>
218c0984e5SSebastian Reichel 
228c0984e5SSebastian Reichel #include <linux/mfd/pcf50633/core.h>
238c0984e5SSebastian Reichel #include <linux/mfd/pcf50633/mbc.h>
248c0984e5SSebastian Reichel 
258c0984e5SSebastian Reichel struct pcf50633_mbc {
268c0984e5SSebastian Reichel 	struct pcf50633 *pcf;
278c0984e5SSebastian Reichel 
288c0984e5SSebastian Reichel 	int adapter_online;
298c0984e5SSebastian Reichel 	int usb_online;
308c0984e5SSebastian Reichel 
318c0984e5SSebastian Reichel 	struct power_supply *usb;
328c0984e5SSebastian Reichel 	struct power_supply *adapter;
338c0984e5SSebastian Reichel 	struct power_supply *ac;
348c0984e5SSebastian Reichel };
358c0984e5SSebastian Reichel 
pcf50633_mbc_usb_curlim_set(struct pcf50633 * pcf,int ma)368c0984e5SSebastian Reichel int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
378c0984e5SSebastian Reichel {
388c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
398c0984e5SSebastian Reichel 	int ret = 0;
408c0984e5SSebastian Reichel 	u8 bits;
418c0984e5SSebastian Reichel 	u8 mbcs2, chgmod;
428c0984e5SSebastian Reichel 	unsigned int mbcc5;
438c0984e5SSebastian Reichel 
448c0984e5SSebastian Reichel 	if (ma >= 1000) {
458c0984e5SSebastian Reichel 		bits = PCF50633_MBCC7_USB_1000mA;
468c0984e5SSebastian Reichel 		ma = 1000;
478c0984e5SSebastian Reichel 	} else if (ma >= 500) {
488c0984e5SSebastian Reichel 		bits = PCF50633_MBCC7_USB_500mA;
498c0984e5SSebastian Reichel 		ma = 500;
508c0984e5SSebastian Reichel 	} else if (ma >= 100) {
518c0984e5SSebastian Reichel 		bits = PCF50633_MBCC7_USB_100mA;
528c0984e5SSebastian Reichel 		ma = 100;
538c0984e5SSebastian Reichel 	} else {
548c0984e5SSebastian Reichel 		bits = PCF50633_MBCC7_USB_SUSPEND;
558c0984e5SSebastian Reichel 		ma = 0;
568c0984e5SSebastian Reichel 	}
578c0984e5SSebastian Reichel 
588c0984e5SSebastian Reichel 	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
598c0984e5SSebastian Reichel 					PCF50633_MBCC7_USB_MASK, bits);
608c0984e5SSebastian Reichel 	if (ret)
618c0984e5SSebastian Reichel 		dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
628c0984e5SSebastian Reichel 	else
638c0984e5SSebastian Reichel 		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
648c0984e5SSebastian Reichel 
658c0984e5SSebastian Reichel 	/*
668c0984e5SSebastian Reichel 	 * We limit the charging current to be the USB current limit.
678c0984e5SSebastian Reichel 	 * The reason is that on pcf50633, when it enters PMU Standby mode,
688c0984e5SSebastian Reichel 	 * which it does when the device goes "off", the USB current limit
698c0984e5SSebastian Reichel 	 * reverts to the variant default.  In at least one common case, that
708c0984e5SSebastian Reichel 	 * default is 500mA.  By setting the charging current to be the same
718c0984e5SSebastian Reichel 	 * as the USB limit we set here before PMU standby, we enforce it only
728c0984e5SSebastian Reichel 	 * using the correct amount of current even when the USB current limit
738c0984e5SSebastian Reichel 	 * gets reset to the wrong thing
748c0984e5SSebastian Reichel 	 */
758c0984e5SSebastian Reichel 
768c0984e5SSebastian Reichel 	if (mbc->pcf->pdata->charger_reference_current_ma) {
778c0984e5SSebastian Reichel 		mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
788c0984e5SSebastian Reichel 		if (mbcc5 > 255)
798c0984e5SSebastian Reichel 			mbcc5 = 255;
808c0984e5SSebastian Reichel 		pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
818c0984e5SSebastian Reichel 	}
828c0984e5SSebastian Reichel 
838c0984e5SSebastian Reichel 	mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
848c0984e5SSebastian Reichel 	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
858c0984e5SSebastian Reichel 
868c0984e5SSebastian Reichel 	/* If chgmod == BATFULL, setting chgena has no effect.
878c0984e5SSebastian Reichel 	 * Datasheet says we need to set resume instead but when autoresume is
888c0984e5SSebastian Reichel 	 * used resume doesn't work. Clear and set chgena instead.
898c0984e5SSebastian Reichel 	 */
908c0984e5SSebastian Reichel 	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
918c0984e5SSebastian Reichel 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
928c0984e5SSebastian Reichel 				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
938c0984e5SSebastian Reichel 	else {
948c0984e5SSebastian Reichel 		pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
958c0984e5SSebastian Reichel 				PCF50633_MBCC1_CHGENA);
968c0984e5SSebastian Reichel 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
978c0984e5SSebastian Reichel 				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
988c0984e5SSebastian Reichel 	}
998c0984e5SSebastian Reichel 
1008c0984e5SSebastian Reichel 	power_supply_changed(mbc->usb);
1018c0984e5SSebastian Reichel 
1028c0984e5SSebastian Reichel 	return ret;
1038c0984e5SSebastian Reichel }
1048c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
1058c0984e5SSebastian Reichel 
pcf50633_mbc_get_status(struct pcf50633 * pcf)1068c0984e5SSebastian Reichel int pcf50633_mbc_get_status(struct pcf50633 *pcf)
1078c0984e5SSebastian Reichel {
1088c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
1098c0984e5SSebastian Reichel 	int status = 0;
1108c0984e5SSebastian Reichel 	u8 chgmod;
1118c0984e5SSebastian Reichel 
1128c0984e5SSebastian Reichel 	if (!mbc)
1138c0984e5SSebastian Reichel 		return 0;
1148c0984e5SSebastian Reichel 
1158c0984e5SSebastian Reichel 	chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
1168c0984e5SSebastian Reichel 		& PCF50633_MBCS2_MBC_MASK;
1178c0984e5SSebastian Reichel 
1188c0984e5SSebastian Reichel 	if (mbc->usb_online)
1198c0984e5SSebastian Reichel 		status |= PCF50633_MBC_USB_ONLINE;
1208c0984e5SSebastian Reichel 	if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
1218c0984e5SSebastian Reichel 	    chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
1228c0984e5SSebastian Reichel 	    chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
1238c0984e5SSebastian Reichel 	    chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
1248c0984e5SSebastian Reichel 		status |= PCF50633_MBC_USB_ACTIVE;
1258c0984e5SSebastian Reichel 	if (mbc->adapter_online)
1268c0984e5SSebastian Reichel 		status |= PCF50633_MBC_ADAPTER_ONLINE;
1278c0984e5SSebastian Reichel 	if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
1288c0984e5SSebastian Reichel 	    chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
1298c0984e5SSebastian Reichel 	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
1308c0984e5SSebastian Reichel 	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
1318c0984e5SSebastian Reichel 		status |= PCF50633_MBC_ADAPTER_ACTIVE;
1328c0984e5SSebastian Reichel 
1338c0984e5SSebastian Reichel 	return status;
1348c0984e5SSebastian Reichel }
1358c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
1368c0984e5SSebastian Reichel 
pcf50633_mbc_get_usb_online_status(struct pcf50633 * pcf)1378c0984e5SSebastian Reichel int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
1388c0984e5SSebastian Reichel {
1398c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
1408c0984e5SSebastian Reichel 
1418c0984e5SSebastian Reichel 	if (!mbc)
1428c0984e5SSebastian Reichel 		return 0;
1438c0984e5SSebastian Reichel 
1448c0984e5SSebastian Reichel 	return mbc->usb_online;
1458c0984e5SSebastian Reichel }
1468c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
1478c0984e5SSebastian Reichel 
1488c0984e5SSebastian Reichel static ssize_t
show_chgmode(struct device * dev,struct device_attribute * attr,char * buf)1498c0984e5SSebastian Reichel show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
1508c0984e5SSebastian Reichel {
1518c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
1528c0984e5SSebastian Reichel 
1538c0984e5SSebastian Reichel 	u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
1548c0984e5SSebastian Reichel 	u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
1558c0984e5SSebastian Reichel 
156a441f3b9Sye xingchen 	return sysfs_emit(buf, "%d\n", chgmod);
1578c0984e5SSebastian Reichel }
1588c0984e5SSebastian Reichel static DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
1598c0984e5SSebastian Reichel 
1608c0984e5SSebastian Reichel static ssize_t
show_usblim(struct device * dev,struct device_attribute * attr,char * buf)1618c0984e5SSebastian Reichel show_usblim(struct device *dev, struct device_attribute *attr, char *buf)
1628c0984e5SSebastian Reichel {
1638c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
1648c0984e5SSebastian Reichel 	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
1658c0984e5SSebastian Reichel 						PCF50633_MBCC7_USB_MASK;
1668c0984e5SSebastian Reichel 	unsigned int ma;
1678c0984e5SSebastian Reichel 
1688c0984e5SSebastian Reichel 	if (usblim == PCF50633_MBCC7_USB_1000mA)
1698c0984e5SSebastian Reichel 		ma = 1000;
1708c0984e5SSebastian Reichel 	else if (usblim == PCF50633_MBCC7_USB_500mA)
1718c0984e5SSebastian Reichel 		ma = 500;
1728c0984e5SSebastian Reichel 	else if (usblim == PCF50633_MBCC7_USB_100mA)
1738c0984e5SSebastian Reichel 		ma = 100;
1748c0984e5SSebastian Reichel 	else
1758c0984e5SSebastian Reichel 		ma = 0;
1768c0984e5SSebastian Reichel 
177a441f3b9Sye xingchen 	return sysfs_emit(buf, "%u\n", ma);
1788c0984e5SSebastian Reichel }
1798c0984e5SSebastian Reichel 
set_usblim(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1808c0984e5SSebastian Reichel static ssize_t set_usblim(struct device *dev,
1818c0984e5SSebastian Reichel 		struct device_attribute *attr, const char *buf, size_t count)
1828c0984e5SSebastian Reichel {
1838c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
1848c0984e5SSebastian Reichel 	unsigned long ma;
1858c0984e5SSebastian Reichel 	int ret;
1868c0984e5SSebastian Reichel 
1878c0984e5SSebastian Reichel 	ret = kstrtoul(buf, 10, &ma);
1888c0984e5SSebastian Reichel 	if (ret)
1898c0984e5SSebastian Reichel 		return ret;
1908c0984e5SSebastian Reichel 
1918c0984e5SSebastian Reichel 	pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
1928c0984e5SSebastian Reichel 
1938c0984e5SSebastian Reichel 	return count;
1948c0984e5SSebastian Reichel }
1958c0984e5SSebastian Reichel 
1968c0984e5SSebastian Reichel static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
1978c0984e5SSebastian Reichel 
1988c0984e5SSebastian Reichel static ssize_t
show_chglim(struct device * dev,struct device_attribute * attr,char * buf)1998c0984e5SSebastian Reichel show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
2008c0984e5SSebastian Reichel {
2018c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
2028c0984e5SSebastian Reichel 	u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
2038c0984e5SSebastian Reichel 	unsigned int ma;
2048c0984e5SSebastian Reichel 
2058c0984e5SSebastian Reichel 	if (!mbc->pcf->pdata->charger_reference_current_ma)
2068c0984e5SSebastian Reichel 		return -ENODEV;
2078c0984e5SSebastian Reichel 
2088c0984e5SSebastian Reichel 	ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
2098c0984e5SSebastian Reichel 
210a441f3b9Sye xingchen 	return sysfs_emit(buf, "%u\n", ma);
2118c0984e5SSebastian Reichel }
2128c0984e5SSebastian Reichel 
set_chglim(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2138c0984e5SSebastian Reichel static ssize_t set_chglim(struct device *dev,
2148c0984e5SSebastian Reichel 		struct device_attribute *attr, const char *buf, size_t count)
2158c0984e5SSebastian Reichel {
2168c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
2178c0984e5SSebastian Reichel 	unsigned long ma;
2188c0984e5SSebastian Reichel 	unsigned int mbcc5;
2198c0984e5SSebastian Reichel 	int ret;
2208c0984e5SSebastian Reichel 
2218c0984e5SSebastian Reichel 	if (!mbc->pcf->pdata->charger_reference_current_ma)
2228c0984e5SSebastian Reichel 		return -ENODEV;
2238c0984e5SSebastian Reichel 
2248c0984e5SSebastian Reichel 	ret = kstrtoul(buf, 10, &ma);
2258c0984e5SSebastian Reichel 	if (ret)
2268c0984e5SSebastian Reichel 		return ret;
2278c0984e5SSebastian Reichel 
2288c0984e5SSebastian Reichel 	mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
2298c0984e5SSebastian Reichel 	if (mbcc5 > 255)
2308c0984e5SSebastian Reichel 		mbcc5 = 255;
2318c0984e5SSebastian Reichel 	pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
2328c0984e5SSebastian Reichel 
2338c0984e5SSebastian Reichel 	return count;
2348c0984e5SSebastian Reichel }
2358c0984e5SSebastian Reichel 
2368c0984e5SSebastian Reichel /*
2378c0984e5SSebastian Reichel  * This attribute allows to change MBC charging limit on the fly
2388c0984e5SSebastian Reichel  * independently of usb current limit. It also gets set automatically every
2398c0984e5SSebastian Reichel  * time usb current limit is changed.
2408c0984e5SSebastian Reichel  */
2418c0984e5SSebastian Reichel static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
2428c0984e5SSebastian Reichel 
243451ba0e4SSebastian Reichel static struct attribute *pcf50633_mbc_sysfs_attrs[] = {
2448c0984e5SSebastian Reichel 	&dev_attr_chgmode.attr,
2458c0984e5SSebastian Reichel 	&dev_attr_usb_curlim.attr,
2468c0984e5SSebastian Reichel 	&dev_attr_chg_curlim.attr,
2478c0984e5SSebastian Reichel 	NULL,
2488c0984e5SSebastian Reichel };
2498c0984e5SSebastian Reichel 
250451ba0e4SSebastian Reichel ATTRIBUTE_GROUPS(pcf50633_mbc_sysfs);
2518c0984e5SSebastian Reichel 
2528c0984e5SSebastian Reichel static void
pcf50633_mbc_irq_handler(int irq,void * data)2538c0984e5SSebastian Reichel pcf50633_mbc_irq_handler(int irq, void *data)
2548c0984e5SSebastian Reichel {
2558c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = data;
2568c0984e5SSebastian Reichel 
2578c0984e5SSebastian Reichel 	/* USB */
2588c0984e5SSebastian Reichel 	if (irq == PCF50633_IRQ_USBINS) {
2598c0984e5SSebastian Reichel 		mbc->usb_online = 1;
2608c0984e5SSebastian Reichel 	} else if (irq == PCF50633_IRQ_USBREM) {
2618c0984e5SSebastian Reichel 		mbc->usb_online = 0;
2628c0984e5SSebastian Reichel 		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
2638c0984e5SSebastian Reichel 	}
2648c0984e5SSebastian Reichel 
2658c0984e5SSebastian Reichel 	/* Adapter */
2668c0984e5SSebastian Reichel 	if (irq == PCF50633_IRQ_ADPINS)
2678c0984e5SSebastian Reichel 		mbc->adapter_online = 1;
2688c0984e5SSebastian Reichel 	else if (irq == PCF50633_IRQ_ADPREM)
2698c0984e5SSebastian Reichel 		mbc->adapter_online = 0;
2708c0984e5SSebastian Reichel 
2718c0984e5SSebastian Reichel 	power_supply_changed(mbc->ac);
2728c0984e5SSebastian Reichel 	power_supply_changed(mbc->usb);
2738c0984e5SSebastian Reichel 	power_supply_changed(mbc->adapter);
2748c0984e5SSebastian Reichel 
2758c0984e5SSebastian Reichel 	if (mbc->pcf->pdata->mbc_event_callback)
2768c0984e5SSebastian Reichel 		mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
2778c0984e5SSebastian Reichel }
2788c0984e5SSebastian Reichel 
adapter_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)2798c0984e5SSebastian Reichel static int adapter_get_property(struct power_supply *psy,
2808c0984e5SSebastian Reichel 			enum power_supply_property psp,
2818c0984e5SSebastian Reichel 			union power_supply_propval *val)
2828c0984e5SSebastian Reichel {
2838c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
2848c0984e5SSebastian Reichel 	int ret = 0;
2858c0984e5SSebastian Reichel 
2868c0984e5SSebastian Reichel 	switch (psp) {
2878c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
2888c0984e5SSebastian Reichel 		val->intval =  mbc->adapter_online;
2898c0984e5SSebastian Reichel 		break;
2908c0984e5SSebastian Reichel 	default:
2918c0984e5SSebastian Reichel 		ret = -EINVAL;
2928c0984e5SSebastian Reichel 		break;
2938c0984e5SSebastian Reichel 	}
2948c0984e5SSebastian Reichel 	return ret;
2958c0984e5SSebastian Reichel }
2968c0984e5SSebastian Reichel 
usb_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)2978c0984e5SSebastian Reichel static int usb_get_property(struct power_supply *psy,
2988c0984e5SSebastian Reichel 			enum power_supply_property psp,
2998c0984e5SSebastian Reichel 			union power_supply_propval *val)
3008c0984e5SSebastian Reichel {
3018c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
3028c0984e5SSebastian Reichel 	int ret = 0;
3038c0984e5SSebastian Reichel 	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
3048c0984e5SSebastian Reichel 						PCF50633_MBCC7_USB_MASK;
3058c0984e5SSebastian Reichel 
3068c0984e5SSebastian Reichel 	switch (psp) {
3078c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
3088c0984e5SSebastian Reichel 		val->intval = mbc->usb_online &&
3098c0984e5SSebastian Reichel 				(usblim <= PCF50633_MBCC7_USB_500mA);
3108c0984e5SSebastian Reichel 		break;
3118c0984e5SSebastian Reichel 	default:
3128c0984e5SSebastian Reichel 		ret = -EINVAL;
3138c0984e5SSebastian Reichel 		break;
3148c0984e5SSebastian Reichel 	}
3158c0984e5SSebastian Reichel 	return ret;
3168c0984e5SSebastian Reichel }
3178c0984e5SSebastian Reichel 
ac_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3188c0984e5SSebastian Reichel static int ac_get_property(struct power_supply *psy,
3198c0984e5SSebastian Reichel 			enum power_supply_property psp,
3208c0984e5SSebastian Reichel 			union power_supply_propval *val)
3218c0984e5SSebastian Reichel {
3228c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
3238c0984e5SSebastian Reichel 	int ret = 0;
3248c0984e5SSebastian Reichel 	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
3258c0984e5SSebastian Reichel 						PCF50633_MBCC7_USB_MASK;
3268c0984e5SSebastian Reichel 
3278c0984e5SSebastian Reichel 	switch (psp) {
3288c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
3298c0984e5SSebastian Reichel 		val->intval = mbc->usb_online &&
3308c0984e5SSebastian Reichel 				(usblim == PCF50633_MBCC7_USB_1000mA);
3318c0984e5SSebastian Reichel 		break;
3328c0984e5SSebastian Reichel 	default:
3338c0984e5SSebastian Reichel 		ret = -EINVAL;
3348c0984e5SSebastian Reichel 		break;
3358c0984e5SSebastian Reichel 	}
3368c0984e5SSebastian Reichel 	return ret;
3378c0984e5SSebastian Reichel }
3388c0984e5SSebastian Reichel 
3398c0984e5SSebastian Reichel static enum power_supply_property power_props[] = {
3408c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
3418c0984e5SSebastian Reichel };
3428c0984e5SSebastian Reichel 
3438c0984e5SSebastian Reichel static const u8 mbc_irq_handlers[] = {
3448c0984e5SSebastian Reichel 	PCF50633_IRQ_ADPINS,
3458c0984e5SSebastian Reichel 	PCF50633_IRQ_ADPREM,
3468c0984e5SSebastian Reichel 	PCF50633_IRQ_USBINS,
3478c0984e5SSebastian Reichel 	PCF50633_IRQ_USBREM,
3488c0984e5SSebastian Reichel 	PCF50633_IRQ_BATFULL,
3498c0984e5SSebastian Reichel 	PCF50633_IRQ_CHGHALT,
3508c0984e5SSebastian Reichel 	PCF50633_IRQ_THLIMON,
3518c0984e5SSebastian Reichel 	PCF50633_IRQ_THLIMOFF,
3528c0984e5SSebastian Reichel 	PCF50633_IRQ_USBLIMON,
3538c0984e5SSebastian Reichel 	PCF50633_IRQ_USBLIMOFF,
3548c0984e5SSebastian Reichel 	PCF50633_IRQ_LOWSYS,
3558c0984e5SSebastian Reichel 	PCF50633_IRQ_LOWBAT,
3568c0984e5SSebastian Reichel };
3578c0984e5SSebastian Reichel 
3588c0984e5SSebastian Reichel static const struct power_supply_desc pcf50633_mbc_adapter_desc = {
3598c0984e5SSebastian Reichel 	.name		= "adapter",
3608c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_MAINS,
3618c0984e5SSebastian Reichel 	.properties	= power_props,
3628c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(power_props),
3638c0984e5SSebastian Reichel 	.get_property	= &adapter_get_property,
3648c0984e5SSebastian Reichel };
3658c0984e5SSebastian Reichel 
3668c0984e5SSebastian Reichel static const struct power_supply_desc pcf50633_mbc_usb_desc = {
3678c0984e5SSebastian Reichel 	.name		= "usb",
3688c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_USB,
3698c0984e5SSebastian Reichel 	.properties	= power_props,
3708c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(power_props),
3718c0984e5SSebastian Reichel 	.get_property	= usb_get_property,
3728c0984e5SSebastian Reichel };
3738c0984e5SSebastian Reichel 
3748c0984e5SSebastian Reichel static const struct power_supply_desc pcf50633_mbc_ac_desc = {
3758c0984e5SSebastian Reichel 	.name		= "ac",
3768c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_MAINS,
3778c0984e5SSebastian Reichel 	.properties	= power_props,
3788c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(power_props),
3798c0984e5SSebastian Reichel 	.get_property	= ac_get_property,
3808c0984e5SSebastian Reichel };
3818c0984e5SSebastian Reichel 
pcf50633_mbc_probe(struct platform_device * pdev)3828c0984e5SSebastian Reichel static int pcf50633_mbc_probe(struct platform_device *pdev)
3838c0984e5SSebastian Reichel {
3848c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = {};
385451ba0e4SSebastian Reichel 	struct power_supply_config usb_psy_cfg;
3868c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc;
3878c0984e5SSebastian Reichel 	int i;
3888c0984e5SSebastian Reichel 	u8 mbcs1;
3898c0984e5SSebastian Reichel 
3908c0984e5SSebastian Reichel 	mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
3918c0984e5SSebastian Reichel 	if (!mbc)
3928c0984e5SSebastian Reichel 		return -ENOMEM;
3938c0984e5SSebastian Reichel 
3948c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, mbc);
3958c0984e5SSebastian Reichel 	mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
3968c0984e5SSebastian Reichel 
3978c0984e5SSebastian Reichel 	/* Set up IRQ handlers */
3988c0984e5SSebastian Reichel 	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
3998c0984e5SSebastian Reichel 		pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
4008c0984e5SSebastian Reichel 					pcf50633_mbc_irq_handler, mbc);
4018c0984e5SSebastian Reichel 
4028c0984e5SSebastian Reichel 	psy_cfg.supplied_to		= mbc->pcf->pdata->batteries;
4038c0984e5SSebastian Reichel 	psy_cfg.num_supplicants		= mbc->pcf->pdata->num_batteries;
4048c0984e5SSebastian Reichel 	psy_cfg.drv_data		= mbc;
4058c0984e5SSebastian Reichel 
4068c0984e5SSebastian Reichel 	/* Create power supplies */
407*e90a67f6SAndrew Davis 	mbc->adapter = devm_power_supply_register(&pdev->dev,
4088c0984e5SSebastian Reichel 						  &pcf50633_mbc_adapter_desc,
4098c0984e5SSebastian Reichel 						  &psy_cfg);
4108c0984e5SSebastian Reichel 	if (IS_ERR(mbc->adapter)) {
4118c0984e5SSebastian Reichel 		dev_err(mbc->pcf->dev, "failed to register adapter\n");
412e448e2d1SGustavo A. R. Silva 		return PTR_ERR(mbc->adapter);
4138c0984e5SSebastian Reichel 	}
4148c0984e5SSebastian Reichel 
415451ba0e4SSebastian Reichel 	usb_psy_cfg = psy_cfg;
416451ba0e4SSebastian Reichel 	usb_psy_cfg.attr_grp = pcf50633_mbc_sysfs_groups;
417451ba0e4SSebastian Reichel 
418*e90a67f6SAndrew Davis 	mbc->usb = devm_power_supply_register(&pdev->dev,
419*e90a67f6SAndrew Davis 					      &pcf50633_mbc_usb_desc,
420451ba0e4SSebastian Reichel 					      &usb_psy_cfg);
4218c0984e5SSebastian Reichel 	if (IS_ERR(mbc->usb)) {
4228c0984e5SSebastian Reichel 		dev_err(mbc->pcf->dev, "failed to register usb\n");
423e448e2d1SGustavo A. R. Silva 		return PTR_ERR(mbc->usb);
4248c0984e5SSebastian Reichel 	}
4258c0984e5SSebastian Reichel 
426*e90a67f6SAndrew Davis 	mbc->ac = devm_power_supply_register(&pdev->dev,
427*e90a67f6SAndrew Davis 					     &pcf50633_mbc_ac_desc,
4288c0984e5SSebastian Reichel 					     &psy_cfg);
4298c0984e5SSebastian Reichel 	if (IS_ERR(mbc->ac)) {
4308c0984e5SSebastian Reichel 		dev_err(mbc->pcf->dev, "failed to register ac\n");
431e448e2d1SGustavo A. R. Silva 		return PTR_ERR(mbc->ac);
4328c0984e5SSebastian Reichel 	}
4338c0984e5SSebastian Reichel 
4348c0984e5SSebastian Reichel 	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
4358c0984e5SSebastian Reichel 	if (mbcs1 & PCF50633_MBCS1_USBPRES)
4368c0984e5SSebastian Reichel 		pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
4378c0984e5SSebastian Reichel 	if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
4388c0984e5SSebastian Reichel 		pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
4398c0984e5SSebastian Reichel 
4408c0984e5SSebastian Reichel 	return 0;
4418c0984e5SSebastian Reichel }
4428c0984e5SSebastian Reichel 
pcf50633_mbc_remove(struct platform_device * pdev)4436e3ed20eSUwe Kleine-König static void pcf50633_mbc_remove(struct platform_device *pdev)
4448c0984e5SSebastian Reichel {
4458c0984e5SSebastian Reichel 	struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
4468c0984e5SSebastian Reichel 	int i;
4478c0984e5SSebastian Reichel 
4488c0984e5SSebastian Reichel 	/* Remove IRQ handlers */
4498c0984e5SSebastian Reichel 	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
4508c0984e5SSebastian Reichel 		pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
4518c0984e5SSebastian Reichel }
4528c0984e5SSebastian Reichel 
4538c0984e5SSebastian Reichel static struct platform_driver pcf50633_mbc_driver = {
4548c0984e5SSebastian Reichel 	.driver = {
4558c0984e5SSebastian Reichel 		.name = "pcf50633-mbc",
4568c0984e5SSebastian Reichel 	},
4578c0984e5SSebastian Reichel 	.probe = pcf50633_mbc_probe,
4586e3ed20eSUwe Kleine-König 	.remove_new = pcf50633_mbc_remove,
4598c0984e5SSebastian Reichel };
4608c0984e5SSebastian Reichel 
4618c0984e5SSebastian Reichel module_platform_driver(pcf50633_mbc_driver);
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
4648c0984e5SSebastian Reichel MODULE_DESCRIPTION("PCF50633 mbc driver");
4658c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
4668c0984e5SSebastian Reichel MODULE_ALIAS("platform:pcf50633-mbc");
467