162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* NXP PCF50633 Main Battery Charger Driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * (C) 2006-2008 by Openmoko, Inc.
562306a36Sopenharmony_ci * Author: Balaji Rao <balajirrao@openmoko.org>
662306a36Sopenharmony_ci * All rights reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Broken down from monstrous PCF50633 driver mainly by
962306a36Sopenharmony_ci * Harald Welte, Andy Green and Werner Almesberger
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/device.h>
1862306a36Sopenharmony_ci#include <linux/sysfs.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/power_supply.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/mfd/pcf50633/core.h>
2362306a36Sopenharmony_ci#include <linux/mfd/pcf50633/mbc.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct pcf50633_mbc {
2662306a36Sopenharmony_ci	struct pcf50633 *pcf;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	int adapter_online;
2962306a36Sopenharmony_ci	int usb_online;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	struct power_supply *usb;
3262306a36Sopenharmony_ci	struct power_supply *adapter;
3362306a36Sopenharmony_ci	struct power_supply *ac;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciint pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev);
3962306a36Sopenharmony_ci	int ret = 0;
4062306a36Sopenharmony_ci	u8 bits;
4162306a36Sopenharmony_ci	u8 mbcs2, chgmod;
4262306a36Sopenharmony_ci	unsigned int mbcc5;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	if (ma >= 1000) {
4562306a36Sopenharmony_ci		bits = PCF50633_MBCC7_USB_1000mA;
4662306a36Sopenharmony_ci		ma = 1000;
4762306a36Sopenharmony_ci	} else if (ma >= 500) {
4862306a36Sopenharmony_ci		bits = PCF50633_MBCC7_USB_500mA;
4962306a36Sopenharmony_ci		ma = 500;
5062306a36Sopenharmony_ci	} else if (ma >= 100) {
5162306a36Sopenharmony_ci		bits = PCF50633_MBCC7_USB_100mA;
5262306a36Sopenharmony_ci		ma = 100;
5362306a36Sopenharmony_ci	} else {
5462306a36Sopenharmony_ci		bits = PCF50633_MBCC7_USB_SUSPEND;
5562306a36Sopenharmony_ci		ma = 0;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
5962306a36Sopenharmony_ci					PCF50633_MBCC7_USB_MASK, bits);
6062306a36Sopenharmony_ci	if (ret)
6162306a36Sopenharmony_ci		dev_err(pcf->dev, "error setting usb curlim to %d mA\n", ma);
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/*
6662306a36Sopenharmony_ci	 * We limit the charging current to be the USB current limit.
6762306a36Sopenharmony_ci	 * The reason is that on pcf50633, when it enters PMU Standby mode,
6862306a36Sopenharmony_ci	 * which it does when the device goes "off", the USB current limit
6962306a36Sopenharmony_ci	 * reverts to the variant default.  In at least one common case, that
7062306a36Sopenharmony_ci	 * default is 500mA.  By setting the charging current to be the same
7162306a36Sopenharmony_ci	 * as the USB limit we set here before PMU standby, we enforce it only
7262306a36Sopenharmony_ci	 * using the correct amount of current even when the USB current limit
7362306a36Sopenharmony_ci	 * gets reset to the wrong thing
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (mbc->pcf->pdata->charger_reference_current_ma) {
7762306a36Sopenharmony_ci		mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
7862306a36Sopenharmony_ci		if (mbcc5 > 255)
7962306a36Sopenharmony_ci			mbcc5 = 255;
8062306a36Sopenharmony_ci		pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
8462306a36Sopenharmony_ci	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* If chgmod == BATFULL, setting chgena has no effect.
8762306a36Sopenharmony_ci	 * Datasheet says we need to set resume instead but when autoresume is
8862306a36Sopenharmony_ci	 * used resume doesn't work. Clear and set chgena instead.
8962306a36Sopenharmony_ci	 */
9062306a36Sopenharmony_ci	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
9162306a36Sopenharmony_ci		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
9262306a36Sopenharmony_ci				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
9362306a36Sopenharmony_ci	else {
9462306a36Sopenharmony_ci		pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
9562306a36Sopenharmony_ci				PCF50633_MBCC1_CHGENA);
9662306a36Sopenharmony_ci		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
9762306a36Sopenharmony_ci				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	power_supply_changed(mbc->usb);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return ret;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_mbc_usb_curlim_set);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint pcf50633_mbc_get_status(struct pcf50633 *pcf)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
10962306a36Sopenharmony_ci	int status = 0;
11062306a36Sopenharmony_ci	u8 chgmod;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!mbc)
11362306a36Sopenharmony_ci		return 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
11662306a36Sopenharmony_ci		& PCF50633_MBCS2_MBC_MASK;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (mbc->usb_online)
11962306a36Sopenharmony_ci		status |= PCF50633_MBC_USB_ONLINE;
12062306a36Sopenharmony_ci	if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
12162306a36Sopenharmony_ci	    chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
12262306a36Sopenharmony_ci	    chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
12362306a36Sopenharmony_ci	    chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
12462306a36Sopenharmony_ci		status |= PCF50633_MBC_USB_ACTIVE;
12562306a36Sopenharmony_ci	if (mbc->adapter_online)
12662306a36Sopenharmony_ci		status |= PCF50633_MBC_ADAPTER_ONLINE;
12762306a36Sopenharmony_ci	if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
12862306a36Sopenharmony_ci	    chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
12962306a36Sopenharmony_ci	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
13062306a36Sopenharmony_ci	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
13162306a36Sopenharmony_ci		status |= PCF50633_MBC_ADAPTER_ACTIVE;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return status;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciint pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (!mbc)
14262306a36Sopenharmony_ci		return 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return mbc->usb_online;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic ssize_t
14962306a36Sopenharmony_cishow_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	u8 mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
15462306a36Sopenharmony_ci	u8 chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", chgmod);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_cistatic DEVICE_ATTR(chgmode, S_IRUGO, show_chgmode, NULL);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic ssize_t
16162306a36Sopenharmony_cishow_usblim(struct device *dev, struct device_attribute *attr, char *buf)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
16462306a36Sopenharmony_ci	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
16562306a36Sopenharmony_ci						PCF50633_MBCC7_USB_MASK;
16662306a36Sopenharmony_ci	unsigned int ma;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (usblim == PCF50633_MBCC7_USB_1000mA)
16962306a36Sopenharmony_ci		ma = 1000;
17062306a36Sopenharmony_ci	else if (usblim == PCF50633_MBCC7_USB_500mA)
17162306a36Sopenharmony_ci		ma = 500;
17262306a36Sopenharmony_ci	else if (usblim == PCF50633_MBCC7_USB_100mA)
17362306a36Sopenharmony_ci		ma = 100;
17462306a36Sopenharmony_ci	else
17562306a36Sopenharmony_ci		ma = 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", ma);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic ssize_t set_usblim(struct device *dev,
18162306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
18462306a36Sopenharmony_ci	unsigned long ma;
18562306a36Sopenharmony_ci	int ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ret = kstrtoul(buf, 10, &ma);
18862306a36Sopenharmony_ci	if (ret)
18962306a36Sopenharmony_ci		return ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	pcf50633_mbc_usb_curlim_set(mbc->pcf, ma);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return count;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic ssize_t
19962306a36Sopenharmony_cishow_chglim(struct device *dev, struct device_attribute *attr, char *buf)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
20262306a36Sopenharmony_ci	u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
20362306a36Sopenharmony_ci	unsigned int ma;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if (!mbc->pcf->pdata->charger_reference_current_ma)
20662306a36Sopenharmony_ci		return -ENODEV;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", ma);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic ssize_t set_chglim(struct device *dev,
21462306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
21762306a36Sopenharmony_ci	unsigned long ma;
21862306a36Sopenharmony_ci	unsigned int mbcc5;
21962306a36Sopenharmony_ci	int ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (!mbc->pcf->pdata->charger_reference_current_ma)
22262306a36Sopenharmony_ci		return -ENODEV;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ret = kstrtoul(buf, 10, &ma);
22562306a36Sopenharmony_ci	if (ret)
22662306a36Sopenharmony_ci		return ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
22962306a36Sopenharmony_ci	if (mbcc5 > 255)
23062306a36Sopenharmony_ci		mbcc5 = 255;
23162306a36Sopenharmony_ci	pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return count;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * This attribute allows to change MBC charging limit on the fly
23862306a36Sopenharmony_ci * independently of usb current limit. It also gets set automatically every
23962306a36Sopenharmony_ci * time usb current limit is changed.
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_cistatic DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct attribute *pcf50633_mbc_sysfs_attrs[] = {
24462306a36Sopenharmony_ci	&dev_attr_chgmode.attr,
24562306a36Sopenharmony_ci	&dev_attr_usb_curlim.attr,
24662306a36Sopenharmony_ci	&dev_attr_chg_curlim.attr,
24762306a36Sopenharmony_ci	NULL,
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciATTRIBUTE_GROUPS(pcf50633_mbc_sysfs);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void
25362306a36Sopenharmony_cipcf50633_mbc_irq_handler(int irq, void *data)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = data;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* USB */
25862306a36Sopenharmony_ci	if (irq == PCF50633_IRQ_USBINS) {
25962306a36Sopenharmony_ci		mbc->usb_online = 1;
26062306a36Sopenharmony_ci	} else if (irq == PCF50633_IRQ_USBREM) {
26162306a36Sopenharmony_ci		mbc->usb_online = 0;
26262306a36Sopenharmony_ci		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Adapter */
26662306a36Sopenharmony_ci	if (irq == PCF50633_IRQ_ADPINS)
26762306a36Sopenharmony_ci		mbc->adapter_online = 1;
26862306a36Sopenharmony_ci	else if (irq == PCF50633_IRQ_ADPREM)
26962306a36Sopenharmony_ci		mbc->adapter_online = 0;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	power_supply_changed(mbc->ac);
27262306a36Sopenharmony_ci	power_supply_changed(mbc->usb);
27362306a36Sopenharmony_ci	power_supply_changed(mbc->adapter);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (mbc->pcf->pdata->mbc_event_callback)
27662306a36Sopenharmony_ci		mbc->pcf->pdata->mbc_event_callback(mbc->pcf, irq);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int adapter_get_property(struct power_supply *psy,
28062306a36Sopenharmony_ci			enum power_supply_property psp,
28162306a36Sopenharmony_ci			union power_supply_propval *val)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
28462306a36Sopenharmony_ci	int ret = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	switch (psp) {
28762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
28862306a36Sopenharmony_ci		val->intval =  mbc->adapter_online;
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci	default:
29162306a36Sopenharmony_ci		ret = -EINVAL;
29262306a36Sopenharmony_ci		break;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	return ret;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int usb_get_property(struct power_supply *psy,
29862306a36Sopenharmony_ci			enum power_supply_property psp,
29962306a36Sopenharmony_ci			union power_supply_propval *val)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
30262306a36Sopenharmony_ci	int ret = 0;
30362306a36Sopenharmony_ci	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
30462306a36Sopenharmony_ci						PCF50633_MBCC7_USB_MASK;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	switch (psp) {
30762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
30862306a36Sopenharmony_ci		val->intval = mbc->usb_online &&
30962306a36Sopenharmony_ci				(usblim <= PCF50633_MBCC7_USB_500mA);
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci	default:
31262306a36Sopenharmony_ci		ret = -EINVAL;
31362306a36Sopenharmony_ci		break;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	return ret;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int ac_get_property(struct power_supply *psy,
31962306a36Sopenharmony_ci			enum power_supply_property psp,
32062306a36Sopenharmony_ci			union power_supply_propval *val)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = power_supply_get_drvdata(psy);
32362306a36Sopenharmony_ci	int ret = 0;
32462306a36Sopenharmony_ci	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
32562306a36Sopenharmony_ci						PCF50633_MBCC7_USB_MASK;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	switch (psp) {
32862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
32962306a36Sopenharmony_ci		val->intval = mbc->usb_online &&
33062306a36Sopenharmony_ci				(usblim == PCF50633_MBCC7_USB_1000mA);
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	default:
33362306a36Sopenharmony_ci		ret = -EINVAL;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	return ret;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic enum power_supply_property power_props[] = {
34062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic const u8 mbc_irq_handlers[] = {
34462306a36Sopenharmony_ci	PCF50633_IRQ_ADPINS,
34562306a36Sopenharmony_ci	PCF50633_IRQ_ADPREM,
34662306a36Sopenharmony_ci	PCF50633_IRQ_USBINS,
34762306a36Sopenharmony_ci	PCF50633_IRQ_USBREM,
34862306a36Sopenharmony_ci	PCF50633_IRQ_BATFULL,
34962306a36Sopenharmony_ci	PCF50633_IRQ_CHGHALT,
35062306a36Sopenharmony_ci	PCF50633_IRQ_THLIMON,
35162306a36Sopenharmony_ci	PCF50633_IRQ_THLIMOFF,
35262306a36Sopenharmony_ci	PCF50633_IRQ_USBLIMON,
35362306a36Sopenharmony_ci	PCF50633_IRQ_USBLIMOFF,
35462306a36Sopenharmony_ci	PCF50633_IRQ_LOWSYS,
35562306a36Sopenharmony_ci	PCF50633_IRQ_LOWBAT,
35662306a36Sopenharmony_ci};
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic const struct power_supply_desc pcf50633_mbc_adapter_desc = {
35962306a36Sopenharmony_ci	.name		= "adapter",
36062306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_MAINS,
36162306a36Sopenharmony_ci	.properties	= power_props,
36262306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(power_props),
36362306a36Sopenharmony_ci	.get_property	= &adapter_get_property,
36462306a36Sopenharmony_ci};
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic const struct power_supply_desc pcf50633_mbc_usb_desc = {
36762306a36Sopenharmony_ci	.name		= "usb",
36862306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_USB,
36962306a36Sopenharmony_ci	.properties	= power_props,
37062306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(power_props),
37162306a36Sopenharmony_ci	.get_property	= usb_get_property,
37262306a36Sopenharmony_ci};
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic const struct power_supply_desc pcf50633_mbc_ac_desc = {
37562306a36Sopenharmony_ci	.name		= "ac",
37662306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_MAINS,
37762306a36Sopenharmony_ci	.properties	= power_props,
37862306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(power_props),
37962306a36Sopenharmony_ci	.get_property	= ac_get_property,
38062306a36Sopenharmony_ci};
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic int pcf50633_mbc_probe(struct platform_device *pdev)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
38562306a36Sopenharmony_ci	struct power_supply_config usb_psy_cfg;
38662306a36Sopenharmony_ci	struct pcf50633_mbc *mbc;
38762306a36Sopenharmony_ci	int i;
38862306a36Sopenharmony_ci	u8 mbcs1;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	mbc = devm_kzalloc(&pdev->dev, sizeof(*mbc), GFP_KERNEL);
39162306a36Sopenharmony_ci	if (!mbc)
39262306a36Sopenharmony_ci		return -ENOMEM;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	platform_set_drvdata(pdev, mbc);
39562306a36Sopenharmony_ci	mbc->pcf = dev_to_pcf50633(pdev->dev.parent);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* Set up IRQ handlers */
39862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
39962306a36Sopenharmony_ci		pcf50633_register_irq(mbc->pcf, mbc_irq_handlers[i],
40062306a36Sopenharmony_ci					pcf50633_mbc_irq_handler, mbc);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	psy_cfg.supplied_to		= mbc->pcf->pdata->batteries;
40362306a36Sopenharmony_ci	psy_cfg.num_supplicants		= mbc->pcf->pdata->num_batteries;
40462306a36Sopenharmony_ci	psy_cfg.drv_data		= mbc;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* Create power supplies */
40762306a36Sopenharmony_ci	mbc->adapter = power_supply_register(&pdev->dev,
40862306a36Sopenharmony_ci					     &pcf50633_mbc_adapter_desc,
40962306a36Sopenharmony_ci					     &psy_cfg);
41062306a36Sopenharmony_ci	if (IS_ERR(mbc->adapter)) {
41162306a36Sopenharmony_ci		dev_err(mbc->pcf->dev, "failed to register adapter\n");
41262306a36Sopenharmony_ci		return PTR_ERR(mbc->adapter);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	usb_psy_cfg = psy_cfg;
41662306a36Sopenharmony_ci	usb_psy_cfg.attr_grp = pcf50633_mbc_sysfs_groups;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	mbc->usb = power_supply_register(&pdev->dev, &pcf50633_mbc_usb_desc,
41962306a36Sopenharmony_ci					 &usb_psy_cfg);
42062306a36Sopenharmony_ci	if (IS_ERR(mbc->usb)) {
42162306a36Sopenharmony_ci		dev_err(mbc->pcf->dev, "failed to register usb\n");
42262306a36Sopenharmony_ci		power_supply_unregister(mbc->adapter);
42362306a36Sopenharmony_ci		return PTR_ERR(mbc->usb);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	mbc->ac = power_supply_register(&pdev->dev, &pcf50633_mbc_ac_desc,
42762306a36Sopenharmony_ci					&psy_cfg);
42862306a36Sopenharmony_ci	if (IS_ERR(mbc->ac)) {
42962306a36Sopenharmony_ci		dev_err(mbc->pcf->dev, "failed to register ac\n");
43062306a36Sopenharmony_ci		power_supply_unregister(mbc->adapter);
43162306a36Sopenharmony_ci		power_supply_unregister(mbc->usb);
43262306a36Sopenharmony_ci		return PTR_ERR(mbc->ac);
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	mbcs1 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS1);
43662306a36Sopenharmony_ci	if (mbcs1 & PCF50633_MBCS1_USBPRES)
43762306a36Sopenharmony_ci		pcf50633_mbc_irq_handler(PCF50633_IRQ_USBINS, mbc);
43862306a36Sopenharmony_ci	if (mbcs1 & PCF50633_MBCS1_ADAPTPRES)
43962306a36Sopenharmony_ci		pcf50633_mbc_irq_handler(PCF50633_IRQ_ADPINS, mbc);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int pcf50633_mbc_remove(struct platform_device *pdev)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct pcf50633_mbc *mbc = platform_get_drvdata(pdev);
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* Remove IRQ handlers */
45062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mbc_irq_handlers); i++)
45162306a36Sopenharmony_ci		pcf50633_free_irq(mbc->pcf, mbc_irq_handlers[i]);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	power_supply_unregister(mbc->usb);
45462306a36Sopenharmony_ci	power_supply_unregister(mbc->adapter);
45562306a36Sopenharmony_ci	power_supply_unregister(mbc->ac);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return 0;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic struct platform_driver pcf50633_mbc_driver = {
46162306a36Sopenharmony_ci	.driver = {
46262306a36Sopenharmony_ci		.name = "pcf50633-mbc",
46362306a36Sopenharmony_ci	},
46462306a36Sopenharmony_ci	.probe = pcf50633_mbc_probe,
46562306a36Sopenharmony_ci	.remove = pcf50633_mbc_remove,
46662306a36Sopenharmony_ci};
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cimodule_platform_driver(pcf50633_mbc_driver);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ciMODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
47162306a36Sopenharmony_ciMODULE_DESCRIPTION("PCF50633 mbc driver");
47262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
47362306a36Sopenharmony_ciMODULE_ALIAS("platform:pcf50633-mbc");
474