Browse Source

Merge git://git.infradead.org/battery-2.6

* git://git.infradead.org/battery-2.6:
  power_supply_sysfs: Handle -ENODATA in a special way
  wm831x_backup: Remove unused variables
  gta02: Set pcf50633 charger_reference_current_ma
  pcf50633: Query charger status directly
  pcf50633: Properly reenable charging when the supply conditions change
  pcf50633: Get rid of charging restart software auto-triggering
  pcf50633: introduces battery charging current control
  pcf50633: Add ac power supply class to the charger
  wm831x: Factor out WM831x backup battery charger
Linus Torvalds 15 years ago
parent
commit
48e902f0a3

+ 3 - 0
arch/arm/mach-s3c2442/mach-gta02.c

@@ -268,6 +268,9 @@ struct pcf50633_platform_data gta02_pcf_pdata = {
 
 
 	.batteries = gta02_batteries,
 	.batteries = gta02_batteries,
 	.num_batteries = ARRAY_SIZE(gta02_batteries),
 	.num_batteries = ARRAY_SIZE(gta02_batteries),
+
+	.charger_reference_current_ma = 1000,
+
 	.reg_init_data = {
 	.reg_init_data = {
 		[PCF50633_REGULATOR_AUTO] = {
 		[PCF50633_REGULATOR_AUTO] = {
 			.constraints = {
 			.constraints = {

+ 9 - 0
drivers/mfd/wm831x-core.c

@@ -808,6 +808,9 @@ static struct resource wm831x_wdt_resources[] = {
 };
 };
 
 
 static struct mfd_cell wm8310_devs[] = {
 static struct mfd_cell wm8310_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
 	{
 	{
 		.name = "wm831x-buckv",
 		.name = "wm831x-buckv",
 		.id = 1,
 		.id = 1,
@@ -961,6 +964,9 @@ static struct mfd_cell wm8310_devs[] = {
 };
 };
 
 
 static struct mfd_cell wm8311_devs[] = {
 static struct mfd_cell wm8311_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
 	{
 	{
 		.name = "wm831x-buckv",
 		.name = "wm831x-buckv",
 		.id = 1,
 		.id = 1,
@@ -1095,6 +1101,9 @@ static struct mfd_cell wm8311_devs[] = {
 };
 };
 
 
 static struct mfd_cell wm8312_devs[] = {
 static struct mfd_cell wm8312_devs[] = {
+	{
+		.name = "wm831x-backup",
+	},
 	{
 	{
 		.name = "wm831x-buckv",
 		.name = "wm831x-buckv",
 		.id = 1,
 		.id = 1,

+ 7 - 0
drivers/power/Kconfig

@@ -29,6 +29,13 @@ config APM_POWER
 	  Say Y here to enable support APM status emulation using
 	  Say Y here to enable support APM status emulation using
 	  battery class devices.
 	  battery class devices.
 
 
+config WM831X_BACKUP
+	tristate "WM831X backup battery charger support"
+	depends on MFD_WM831X
+	help
+	  Say Y here to enable support for the backup battery charger
+	  in the Wolfson Microelectronics WM831x PMICs.
+
 config WM831X_POWER
 config WM831X_POWER
 	tristate "WM831X PMU support"
 	tristate "WM831X PMU support"
 	depends on MFD_WM831X
 	depends on MFD_WM831X

+ 1 - 0
drivers/power/Makefile

@@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY)	+= power_supply.o
 
 
 obj-$(CONFIG_PDA_POWER)		+= pda_power.o
 obj-$(CONFIG_PDA_POWER)		+= pda_power.o
 obj-$(CONFIG_APM_POWER)		+= apm_power.o
 obj-$(CONFIG_APM_POWER)		+= apm_power.o
+obj-$(CONFIG_WM831X_BACKUP)	+= wm831x_backup.o
 obj-$(CONFIG_WM831X_POWER)	+= wm831x_power.o
 obj-$(CONFIG_WM831X_POWER)	+= wm831x_power.o
 obj-$(CONFIG_WM8350_POWER)	+= wm8350_power.o
 obj-$(CONFIG_WM8350_POWER)	+= wm8350_power.o
 
 

+ 153 - 75
drivers/power/pcf50633-charger.c

@@ -29,15 +29,12 @@
 struct pcf50633_mbc {
 struct pcf50633_mbc {
 	struct pcf50633 *pcf;
 	struct pcf50633 *pcf;
 
 
-	int adapter_active;
 	int adapter_online;
 	int adapter_online;
-	int usb_active;
 	int usb_online;
 	int usb_online;
 
 
 	struct power_supply usb;
 	struct power_supply usb;
 	struct power_supply adapter;
 	struct power_supply adapter;
-
-	struct delayed_work charging_restart_work;
+	struct power_supply ac;
 };
 };
 
 
 int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
 int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
@@ -47,16 +44,21 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
 	u8 bits;
 	u8 bits;
 	int charging_start = 1;
 	int charging_start = 1;
 	u8 mbcs2, chgmod;
 	u8 mbcs2, chgmod;
+	unsigned int mbcc5;
 
 
-	if (ma >= 1000)
+	if (ma >= 1000) {
 		bits = PCF50633_MBCC7_USB_1000mA;
 		bits = PCF50633_MBCC7_USB_1000mA;
-	else if (ma >= 500)
+		ma = 1000;
+	} else if (ma >= 500) {
 		bits = PCF50633_MBCC7_USB_500mA;
 		bits = PCF50633_MBCC7_USB_500mA;
-	else if (ma >= 100)
+		ma = 500;
+	} else if (ma >= 100) {
 		bits = PCF50633_MBCC7_USB_100mA;
 		bits = PCF50633_MBCC7_USB_100mA;
-	else {
+		ma = 100;
+	} else {
 		bits = PCF50633_MBCC7_USB_SUSPEND;
 		bits = PCF50633_MBCC7_USB_SUSPEND;
 		charging_start = 0;
 		charging_start = 0;
+		ma = 0;
 	}
 	}
 
 
 	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
 	ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7,
@@ -66,21 +68,40 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma)
 	else
 	else
 		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
 		dev_info(pcf->dev, "usb curlim to %d mA\n", ma);
 
 
-	/* Manual charging start */
-	mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2);
+	/*
+	 * We limit the charging current to be the USB current limit.
+	 * The reason is that on pcf50633, when it enters PMU Standby mode,
+	 * which it does when the device goes "off", the USB current limit
+	 * reverts to the variant default.  In at least one common case, that
+	 * default is 500mA.  By setting the charging current to be the same
+	 * as the USB limit we set here before PMU standby, we enforce it only
+	 * using the correct amount of current even when the USB current limit
+	 * gets reset to the wrong thing
+	 */
+
+	if (mbc->pcf->pdata->charger_reference_current_ma) {
+		mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
+		if (mbcc5 > 255)
+			mbcc5 = 255;
+		pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
+	}
+
+	mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
 	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
 	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
 
 
 	/* If chgmod == BATFULL, setting chgena has no effect.
 	/* If chgmod == BATFULL, setting chgena has no effect.
-	 * We need to set resume instead.
+	 * Datasheet says we need to set resume instead but when autoresume is
+	 * used resume doesn't work. Clear and set chgena instead.
 	 */
 	 */
 	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
 	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
 				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
 				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
-	else
+	else {
+		pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1,
+				PCF50633_MBCC1_CHGENA);
 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
 		pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1,
-				PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
-
-	mbc->usb_active = charging_start;
+				PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA);
+	}
 
 
 	power_supply_changed(&mbc->usb);
 	power_supply_changed(&mbc->usb);
 
 
@@ -92,20 +113,44 @@ int pcf50633_mbc_get_status(struct pcf50633 *pcf)
 {
 {
 	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
 	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
 	int status = 0;
 	int status = 0;
+	u8 chgmod;
+
+	if (!mbc)
+		return 0;
+
+	chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2)
+		& PCF50633_MBCS2_MBC_MASK;
 
 
 	if (mbc->usb_online)
 	if (mbc->usb_online)
 		status |= PCF50633_MBC_USB_ONLINE;
 		status |= PCF50633_MBC_USB_ONLINE;
-	if (mbc->usb_active)
+	if (chgmod == PCF50633_MBCS2_MBC_USB_PRE ||
+	    chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT ||
+	    chgmod == PCF50633_MBCS2_MBC_USB_FAST ||
+	    chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT)
 		status |= PCF50633_MBC_USB_ACTIVE;
 		status |= PCF50633_MBC_USB_ACTIVE;
 	if (mbc->adapter_online)
 	if (mbc->adapter_online)
 		status |= PCF50633_MBC_ADAPTER_ONLINE;
 		status |= PCF50633_MBC_ADAPTER_ONLINE;
-	if (mbc->adapter_active)
+	if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE ||
+	    chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT ||
+	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST ||
+	    chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT)
 		status |= PCF50633_MBC_ADAPTER_ACTIVE;
 		status |= PCF50633_MBC_ADAPTER_ACTIVE;
 
 
 	return status;
 	return status;
 }
 }
 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
 EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status);
 
 
+int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf)
+{
+	struct pcf50633_mbc *mbc  = platform_get_drvdata(pcf->mbc_pdev);
+
+	if (!mbc)
+		return 0;
+
+	return mbc->usb_online;
+}
+EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status);
+
 static ssize_t
 static ssize_t
 show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
 show_chgmode(struct device *dev, struct device_attribute *attr, char *buf)
 {
 {
@@ -156,9 +201,55 @@ static ssize_t set_usblim(struct device *dev,
 
 
 static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
 static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim);
 
 
+static ssize_t
+show_chglim(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+	u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5);
+	unsigned int ma;
+
+	if (!mbc->pcf->pdata->charger_reference_current_ma)
+		return -ENODEV;
+
+	ma = (mbc->pcf->pdata->charger_reference_current_ma *  mbcc5) >> 8;
+
+	return sprintf(buf, "%u\n", ma);
+}
+
+static ssize_t set_chglim(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct pcf50633_mbc *mbc = dev_get_drvdata(dev);
+	unsigned long ma;
+	unsigned int mbcc5;
+	int ret;
+
+	if (!mbc->pcf->pdata->charger_reference_current_ma)
+		return -ENODEV;
+
+	ret = strict_strtoul(buf, 10, &ma);
+	if (ret)
+		return -EINVAL;
+
+	mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma;
+	if (mbcc5 > 255)
+		mbcc5 = 255;
+	pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5);
+
+	return count;
+}
+
+/*
+ * This attribute allows to change MBC charging limit on the fly
+ * independently of usb current limit. It also gets set automatically every
+ * time usb current limit is changed.
+ */
+static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim);
+
 static struct attribute *pcf50633_mbc_sysfs_entries[] = {
 static struct attribute *pcf50633_mbc_sysfs_entries[] = {
 	&dev_attr_chgmode.attr,
 	&dev_attr_chgmode.attr,
 	&dev_attr_usb_curlim.attr,
 	&dev_attr_usb_curlim.attr,
+	&dev_attr_chg_curlim.attr,
 	NULL,
 	NULL,
 };
 };
 
 
@@ -167,76 +258,26 @@ static struct attribute_group mbc_attr_group = {
 	.attrs	= pcf50633_mbc_sysfs_entries,
 	.attrs	= pcf50633_mbc_sysfs_entries,
 };
 };
 
 
-/* MBC state machine switches into charging mode when the battery voltage
- * falls below 96% of a battery float voltage. But the voltage drop in Li-ion
- * batteries is marginal(1~2 %) till about 80% of its capacity - which means,
- * after a BATFULL, charging won't be restarted until 80%.
- *
- * This work_struct function restarts charging at regular intervals to make
- * sure we don't discharge too much
- */
-
-static void pcf50633_mbc_charging_restart(struct work_struct *work)
-{
-	struct pcf50633_mbc *mbc;
-	u8 mbcs2, chgmod;
-
-	mbc = container_of(work, struct pcf50633_mbc,
-				charging_restart_work.work);
-
-	mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2);
-	chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
-
-	if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL)
-		return;
-
-	/* Restart charging */
-	pcf50633_reg_set_bit_mask(mbc->pcf, PCF50633_REG_MBCC1,
-				PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME);
-	mbc->usb_active = 1;
-	power_supply_changed(&mbc->usb);
-
-	dev_info(mbc->pcf->dev, "Charging restarted\n");
-}
-
 static void
 static void
 pcf50633_mbc_irq_handler(int irq, void *data)
 pcf50633_mbc_irq_handler(int irq, void *data)
 {
 {
 	struct pcf50633_mbc *mbc = data;
 	struct pcf50633_mbc *mbc = data;
-	int chg_restart_interval =
-			mbc->pcf->pdata->charging_restart_interval;
 
 
 	/* USB */
 	/* USB */
 	if (irq == PCF50633_IRQ_USBINS) {
 	if (irq == PCF50633_IRQ_USBINS) {
 		mbc->usb_online = 1;
 		mbc->usb_online = 1;
 	} else if (irq == PCF50633_IRQ_USBREM) {
 	} else if (irq == PCF50633_IRQ_USBREM) {
 		mbc->usb_online = 0;
 		mbc->usb_online = 0;
-		mbc->usb_active = 0;
 		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
 		pcf50633_mbc_usb_curlim_set(mbc->pcf, 0);
-		cancel_delayed_work_sync(&mbc->charging_restart_work);
 	}
 	}
 
 
 	/* Adapter */
 	/* Adapter */
-	if (irq == PCF50633_IRQ_ADPINS) {
+	if (irq == PCF50633_IRQ_ADPINS)
 		mbc->adapter_online = 1;
 		mbc->adapter_online = 1;
-		mbc->adapter_active = 1;
-	} else if (irq == PCF50633_IRQ_ADPREM) {
+	else if (irq == PCF50633_IRQ_ADPREM)
 		mbc->adapter_online = 0;
 		mbc->adapter_online = 0;
-		mbc->adapter_active = 0;
-	}
-
-	if (irq == PCF50633_IRQ_BATFULL) {
-		mbc->usb_active = 0;
-		mbc->adapter_active = 0;
-
-		if (chg_restart_interval > 0)
-			schedule_delayed_work(&mbc->charging_restart_work,
-							chg_restart_interval);
-	} else if (irq == PCF50633_IRQ_USBLIMON)
-		mbc->usb_active = 0;
-	else if (irq == PCF50633_IRQ_USBLIMOFF)
-		mbc->usb_active = 1;
 
 
+	power_supply_changed(&mbc->ac);
 	power_supply_changed(&mbc->usb);
 	power_supply_changed(&mbc->usb);
 	power_supply_changed(&mbc->adapter);
 	power_supply_changed(&mbc->adapter);
 
 
@@ -269,10 +310,34 @@ static int usb_get_property(struct power_supply *psy,
 {
 {
 	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
 	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb);
 	int ret = 0;
 	int ret = 0;
+	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+						PCF50633_MBCC7_USB_MASK;
 
 
 	switch (psp) {
 	switch (psp) {
 	case POWER_SUPPLY_PROP_ONLINE:
 	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = mbc->usb_online;
+		val->intval = mbc->usb_online &&
+				(usblim <= PCF50633_MBCC7_USB_500mA);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int ac_get_property(struct power_supply *psy,
+			enum power_supply_property psp,
+			union power_supply_propval *val)
+{
+	struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, ac);
+	int ret = 0;
+	u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) &
+						PCF50633_MBCC7_USB_MASK;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = mbc->usb_online &&
+				(usblim == PCF50633_MBCC7_USB_1000mA);
 		break;
 		break;
 	default:
 	default:
 		ret = -EINVAL;
 		ret = -EINVAL;
@@ -336,6 +401,14 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
 	mbc->usb.supplied_to		= mbc->pcf->pdata->batteries;
 	mbc->usb.supplied_to		= mbc->pcf->pdata->batteries;
 	mbc->usb.num_supplicants	= mbc->pcf->pdata->num_batteries;
 	mbc->usb.num_supplicants	= mbc->pcf->pdata->num_batteries;
 
 
+	mbc->ac.name			= "ac";
+	mbc->ac.type			= POWER_SUPPLY_TYPE_MAINS;
+	mbc->ac.properties		= power_props;
+	mbc->ac.num_properties		= ARRAY_SIZE(power_props);
+	mbc->ac.get_property		= ac_get_property;
+	mbc->ac.supplied_to		= mbc->pcf->pdata->batteries;
+	mbc->ac.num_supplicants		= mbc->pcf->pdata->num_batteries;
+
 	ret = power_supply_register(&pdev->dev, &mbc->adapter);
 	ret = power_supply_register(&pdev->dev, &mbc->adapter);
 	if (ret) {
 	if (ret) {
 		dev_err(mbc->pcf->dev, "failed to register adapter\n");
 		dev_err(mbc->pcf->dev, "failed to register adapter\n");
@@ -351,8 +424,14 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev)
 		return ret;
 		return ret;
 	}
 	}
 
 
-	INIT_DELAYED_WORK(&mbc->charging_restart_work,
-				pcf50633_mbc_charging_restart);
+	ret = power_supply_register(&pdev->dev, &mbc->ac);
+	if (ret) {
+		dev_err(mbc->pcf->dev, "failed to register ac\n");
+		power_supply_unregister(&mbc->adapter);
+		power_supply_unregister(&mbc->usb);
+		kfree(mbc);
+		return ret;
+	}
 
 
 	ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
 	ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group);
 	if (ret)
 	if (ret)
@@ -378,8 +457,7 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev)
 
 
 	power_supply_unregister(&mbc->usb);
 	power_supply_unregister(&mbc->usb);
 	power_supply_unregister(&mbc->adapter);
 	power_supply_unregister(&mbc->adapter);
-
-	cancel_delayed_work_sync(&mbc->charging_restart_work);
+	power_supply_unregister(&mbc->ac);
 
 
 	kfree(mbc);
 	kfree(mbc);
 
 

+ 4 - 1
drivers/power/power_supply_sysfs.c

@@ -65,7 +65,10 @@ static ssize_t power_supply_show_property(struct device *dev,
 	ret = psy->get_property(psy, off, &value);
 	ret = psy->get_property(psy, off, &value);
 
 
 	if (ret < 0) {
 	if (ret < 0) {
-		if (ret != -ENODEV)
+		if (ret == -ENODATA)
+			dev_dbg(dev, "driver has no data for `%s' property\n",
+				attr->attr.name);
+		else if (ret != -ENODEV)
 			dev_err(dev, "driver failed to report `%s' property\n",
 			dev_err(dev, "driver failed to report `%s' property\n",
 				attr->attr.name);
 				attr->attr.name);
 		return ret;
 		return ret;

+ 233 - 0
drivers/power/wm831x_backup.c

@@ -0,0 +1,233 @@
+/*
+ * Backup battery driver for Wolfson Microelectronics wm831x PMICs
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/wm831x/core.h>
+#include <linux/mfd/wm831x/auxadc.h>
+#include <linux/mfd/wm831x/pmu.h>
+#include <linux/mfd/wm831x/pdata.h>
+
+struct wm831x_backup {
+	struct wm831x *wm831x;
+	struct power_supply backup;
+};
+
+static int wm831x_backup_read_voltage(struct wm831x *wm831x,
+				     enum wm831x_auxadc src,
+				     union power_supply_propval *val)
+{
+	int ret;
+
+	ret = wm831x_auxadc_read_uv(wm831x, src);
+	if (ret >= 0)
+		val->intval = ret;
+
+	return ret;
+}
+
+/*********************************************************************
+ *		Backup supply properties
+ *********************************************************************/
+
+static void wm831x_config_backup(struct wm831x *wm831x)
+{
+	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
+	struct wm831x_backup_pdata *pdata;
+	int ret, reg;
+
+	if (!wm831x_pdata || !wm831x_pdata->backup) {
+		dev_warn(wm831x->dev,
+			 "No backup battery charger configuration\n");
+		return;
+	}
+
+	pdata = wm831x_pdata->backup;
+
+	reg = 0;
+
+	if (pdata->charger_enable)
+		reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
+	if (pdata->no_constant_voltage)
+		reg |= WM831X_BKUP_CHG_MODE;
+
+	switch (pdata->vlim) {
+	case 2500:
+		break;
+	case 3100:
+		reg |= WM831X_BKUP_CHG_VLIM;
+		break;
+	default:
+		dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
+			pdata->vlim);
+	}
+
+	switch (pdata->ilim) {
+	case 100:
+		break;
+	case 200:
+		reg |= 1;
+		break;
+	case 300:
+		reg |= 2;
+		break;
+	case 400:
+		reg |= 3;
+		break;
+	default:
+		dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
+			pdata->ilim);
+	}
+
+	ret = wm831x_reg_unlock(wm831x);
+	if (ret != 0) {
+		dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
+		return;
+	}
+
+	ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
+			      WM831X_BKUP_CHG_ENA_MASK |
+			      WM831X_BKUP_CHG_MODE_MASK |
+			      WM831X_BKUP_BATT_DET_ENA_MASK |
+			      WM831X_BKUP_CHG_VLIM_MASK |
+			      WM831X_BKUP_CHG_ILIM_MASK,
+			      reg);
+	if (ret != 0)
+		dev_err(wm831x->dev,
+			"Failed to set backup charger config: %d\n", ret);
+
+	wm831x_reg_lock(wm831x);
+}
+
+static int wm831x_backup_get_prop(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct wm831x_backup *devdata = dev_get_drvdata(psy->dev->parent);
+	struct wm831x *wm831x = devdata->wm831x;
+	int ret = 0;
+
+	ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
+	if (ret < 0)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		if (ret & WM831X_BKUP_CHG_STS)
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+		else
+			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		break;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = wm831x_backup_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
+						val);
+		break;
+
+	case POWER_SUPPLY_PROP_PRESENT:
+		if (ret & WM831X_BKUP_CHG_STS)
+			val->intval = 1;
+		else
+			val->intval = 0;
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property wm831x_backup_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_PRESENT,
+};
+
+/*********************************************************************
+ *		Initialisation
+ *********************************************************************/
+
+static __devinit int wm831x_backup_probe(struct platform_device *pdev)
+{
+	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+	struct wm831x_backup *devdata;
+	struct power_supply *backup;
+	int ret;
+
+	devdata = kzalloc(sizeof(struct wm831x_backup), GFP_KERNEL);
+	if (devdata == NULL)
+		return -ENOMEM;
+
+	devdata->wm831x = wm831x;
+	platform_set_drvdata(pdev, devdata);
+
+	backup = &devdata->backup;
+
+	/* We ignore configuration failures since we can still read
+	 * back the status without enabling the charger (which may
+	 * already be enabled anyway).
+	 */
+	wm831x_config_backup(wm831x);
+
+	backup->name = "wm831x-backup";
+	backup->type = POWER_SUPPLY_TYPE_BATTERY;
+	backup->properties = wm831x_backup_props;
+	backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
+	backup->get_property = wm831x_backup_get_prop;
+	ret = power_supply_register(&pdev->dev, backup);
+	if (ret)
+		goto err_kmalloc;
+
+	return ret;
+
+err_kmalloc:
+	kfree(devdata);
+	return ret;
+}
+
+static __devexit int wm831x_backup_remove(struct platform_device *pdev)
+{
+	struct wm831x_backup *devdata = platform_get_drvdata(pdev);
+
+	power_supply_unregister(&devdata->backup);
+	kfree(devdata);
+
+	return 0;
+}
+
+static struct platform_driver wm831x_backup_driver = {
+	.probe = wm831x_backup_probe,
+	.remove = __devexit_p(wm831x_backup_remove),
+	.driver = {
+		.name = "wm831x-backup",
+	},
+};
+
+static int __init wm831x_backup_init(void)
+{
+	return platform_driver_register(&wm831x_backup_driver);
+}
+module_init(wm831x_backup_init);
+
+static void __exit wm831x_backup_exit(void)
+{
+	platform_driver_unregister(&wm831x_backup_driver);
+}
+module_exit(wm831x_backup_exit);
+
+MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-backup");

+ 3 - 141
drivers/power/wm831x_power.c

@@ -21,7 +21,6 @@
 struct wm831x_power {
 struct wm831x_power {
 	struct wm831x *wm831x;
 	struct wm831x *wm831x;
 	struct power_supply wall;
 	struct power_supply wall;
-	struct power_supply backup;
 	struct power_supply usb;
 	struct power_supply usb;
 	struct power_supply battery;
 	struct power_supply battery;
 };
 };
@@ -453,125 +452,6 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
 }
 }
 
 
 
 
-/*********************************************************************
- *		Backup supply properties
- *********************************************************************/
-
-static void wm831x_config_backup(struct wm831x *wm831x)
-{
-	struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
-	struct wm831x_backup_pdata *pdata;
-	int ret, reg;
-
-	if (!wm831x_pdata || !wm831x_pdata->backup) {
-		dev_warn(wm831x->dev,
-			 "No backup battery charger configuration\n");
-		return;
-	}
-
-	pdata = wm831x_pdata->backup;
-
-	reg = 0;
-
-	if (pdata->charger_enable)
-		reg |= WM831X_BKUP_CHG_ENA | WM831X_BKUP_BATT_DET_ENA;
-	if (pdata->no_constant_voltage)
-		reg |= WM831X_BKUP_CHG_MODE;
-
-	switch (pdata->vlim) {
-	case 2500:
-		break;
-	case 3100:
-		reg |= WM831X_BKUP_CHG_VLIM;
-		break;
-	default:
-		dev_err(wm831x->dev, "Invalid backup voltage limit %dmV\n",
-			pdata->vlim);
-	}
-
-	switch (pdata->ilim) {
-	case 100:
-		break;
-	case 200:
-		reg |= 1;
-		break;
-	case 300:
-		reg |= 2;
-		break;
-	case 400:
-		reg |= 3;
-		break;
-	default:
-		dev_err(wm831x->dev, "Invalid backup current limit %duA\n",
-			pdata->ilim);
-	}
-
-	ret = wm831x_reg_unlock(wm831x);
-	if (ret != 0) {
-		dev_err(wm831x->dev, "Failed to unlock registers: %d\n", ret);
-		return;
-	}
-
-	ret = wm831x_set_bits(wm831x, WM831X_BACKUP_CHARGER_CONTROL,
-			      WM831X_BKUP_CHG_ENA_MASK |
-			      WM831X_BKUP_CHG_MODE_MASK |
-			      WM831X_BKUP_BATT_DET_ENA_MASK |
-			      WM831X_BKUP_CHG_VLIM_MASK |
-			      WM831X_BKUP_CHG_ILIM_MASK,
-			      reg);
-	if (ret != 0)
-		dev_err(wm831x->dev,
-			"Failed to set backup charger config: %d\n", ret);
-
-	wm831x_reg_lock(wm831x);
-}
-
-static int wm831x_backup_get_prop(struct power_supply *psy,
-				  enum power_supply_property psp,
-				  union power_supply_propval *val)
-{
-	struct wm831x_power *wm831x_power = dev_get_drvdata(psy->dev->parent);
-	struct wm831x *wm831x = wm831x_power->wm831x;
-	int ret = 0;
-
-	ret = wm831x_reg_read(wm831x, WM831X_BACKUP_CHARGER_CONTROL);
-	if (ret < 0)
-		return ret;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_STATUS:
-		if (ret & WM831X_BKUP_CHG_STS)
-			val->intval = POWER_SUPPLY_STATUS_CHARGING;
-		else
-			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
-		break;
-
-	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		ret = wm831x_power_read_voltage(wm831x, WM831X_AUX_BKUP_BATT,
-						val);
-		break;
-
-	case POWER_SUPPLY_PROP_PRESENT:
-		if (ret & WM831X_BKUP_CHG_STS)
-			val->intval = 1;
-		else
-			val->intval = 0;
-		break;
-
-	default:
-		ret = -EINVAL;
-		break;
-	}
-
-	return ret;
-}
-
-static enum power_supply_property wm831x_backup_props[] = {
-	POWER_SUPPLY_PROP_STATUS,
-	POWER_SUPPLY_PROP_VOLTAGE_NOW,
-	POWER_SUPPLY_PROP_PRESENT,
-};
-
 /*********************************************************************
 /*********************************************************************
  *		Initialisation
  *		Initialisation
  *********************************************************************/
  *********************************************************************/
@@ -595,10 +475,7 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
 
 
 	dev_dbg(wm831x->dev, "Power source changed\n");
 	dev_dbg(wm831x->dev, "Power source changed\n");
 
 
-	/* Just notify for everything - little harm in overnotifying.
-	 * The backup battery is not a power source while the system
-	 * is running so skip that.
-	 */
+	/* Just notify for everything - little harm in overnotifying. */
 	power_supply_changed(&wm831x_power->battery);
 	power_supply_changed(&wm831x_power->battery);
 	power_supply_changed(&wm831x_power->usb);
 	power_supply_changed(&wm831x_power->usb);
 	power_supply_changed(&wm831x_power->wall);
 	power_supply_changed(&wm831x_power->wall);
@@ -613,7 +490,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 	struct power_supply *usb;
 	struct power_supply *usb;
 	struct power_supply *battery;
 	struct power_supply *battery;
 	struct power_supply *wall;
 	struct power_supply *wall;
-	struct power_supply *backup;
 	int ret, irq, i;
 	int ret, irq, i;
 
 
 	power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
 	power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
@@ -626,13 +502,11 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 	usb = &power->usb;
 	usb = &power->usb;
 	battery = &power->battery;
 	battery = &power->battery;
 	wall = &power->wall;
 	wall = &power->wall;
-	backup = &power->backup;
 
 
 	/* We ignore configuration failures since we can still read back
 	/* We ignore configuration failures since we can still read back
-	 * the status without enabling either of the chargers.
+	 * the status without enabling the charger.
 	 */
 	 */
 	wm831x_config_battery(wm831x);
 	wm831x_config_battery(wm831x);
-	wm831x_config_backup(wm831x);
 
 
 	wall->name = "wm831x-wall";
 	wall->name = "wm831x-wall";
 	wall->type = POWER_SUPPLY_TYPE_MAINS;
 	wall->type = POWER_SUPPLY_TYPE_MAINS;
@@ -661,15 +535,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 	if (ret)
 	if (ret)
 		goto err_battery;
 		goto err_battery;
 
 
-	backup->name = "wm831x-backup";
-	backup->type = POWER_SUPPLY_TYPE_BATTERY;
-	backup->properties = wm831x_backup_props;
-	backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
-	backup->get_property = wm831x_backup_get_prop;
-	ret = power_supply_register(&pdev->dev, backup);
-	if (ret)
-		goto err_usb;
-
 	irq = platform_get_irq_byname(pdev, "SYSLO");
 	irq = platform_get_irq_byname(pdev, "SYSLO");
 	ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
 	ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
 				 IRQF_TRIGGER_RISING, "SYSLO",
 				 IRQF_TRIGGER_RISING, "SYSLO",
@@ -677,7 +542,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
 		dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
 			irq, ret);
 			irq, ret);
-		goto err_backup;
+		goto err_usb;
 	}
 	}
 
 
 	irq = platform_get_irq_byname(pdev, "PWR SRC");
 	irq = platform_get_irq_byname(pdev, "PWR SRC");
@@ -716,8 +581,6 @@ err_bat_irq:
 err_syslo:
 err_syslo:
 	irq = platform_get_irq_byname(pdev, "SYSLO");
 	irq = platform_get_irq_byname(pdev, "SYSLO");
 	wm831x_free_irq(wm831x, irq, power);
 	wm831x_free_irq(wm831x, irq, power);
-err_backup:
-	power_supply_unregister(backup);
 err_usb:
 err_usb:
 	power_supply_unregister(usb);
 	power_supply_unregister(usb);
 err_battery:
 err_battery:
@@ -746,7 +609,6 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev)
 	irq = platform_get_irq_byname(pdev, "SYSLO");
 	irq = platform_get_irq_byname(pdev, "SYSLO");
 	wm831x_free_irq(wm831x, irq, wm831x_power);
 	wm831x_free_irq(wm831x, irq, wm831x_power);
 
 
-	power_supply_unregister(&wm831x_power->backup);
 	power_supply_unregister(&wm831x_power->battery);
 	power_supply_unregister(&wm831x_power->battery);
 	power_supply_unregister(&wm831x_power->wall);
 	power_supply_unregister(&wm831x_power->wall);
 	power_supply_unregister(&wm831x_power->usb);
 	power_supply_unregister(&wm831x_power->usb);

+ 6 - 1
include/linux/mfd/pcf50633/core.h

@@ -29,7 +29,12 @@ struct pcf50633_platform_data {
 	char **batteries;
 	char **batteries;
 	int num_batteries;
 	int num_batteries;
 
 
-	int charging_restart_interval;
+	/*
+	 * Should be set accordingly to the reference resistor used, see
+	 * I_{ch(ref)} charger reference current in the pcf50633 User
+	 * Manual.
+	 */
+	int charger_reference_current_ma;
 
 
 	/* Callbacks */
 	/* Callbacks */
 	void (*probe_done)(struct pcf50633 *);
 	void (*probe_done)(struct pcf50633 *);

+ 1 - 0
include/linux/mfd/pcf50633/mbc.h

@@ -128,6 +128,7 @@ enum pcf50633_reg_mbcs3 {
 int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma);
 int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma);
 
 
 int pcf50633_mbc_get_status(struct pcf50633 *);
 int pcf50633_mbc_get_status(struct pcf50633 *);
+int pcf50633_mbc_get_usb_online_status(struct pcf50633 *);
 
 
 #endif
 #endif