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