summary refs log tree commit diff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-05-24 08:00:13 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-05-24 08:00:13 -0700
commit15953654cc312429740fd58fb37a5a3d63a54376 (patch)
treebbb6bf8d4ea943545e29ce896848182c4f206298
parentc3ed9ea4ab460080dea9449c709be9316c670c72 (diff)
parent24af3202459d9a25643009b1ab8ab5ff04d94b49 (diff)
downloadlinux-15953654cc312429740fd58fb37a5a3d63a54376.tar.gz
Merge git://git.infradead.org/battery-2.6
* git://git.infradead.org/battery-2.6:
  ds2760_battery: Document ABI change
  ds2760_battery: Make charge_now and charge_full writeable
  power_supply: Add support for writeable properties
  power_supply: Use attribute groups
  power_supply: Add test_power driver
  tosa_battery: Fix build error due to direct driver_data usage
  wm97xx_battery: Quieten sparse warning (bat_set_pdata not declared)
  ds2782_battery: Get rid of magic numbers in driver_data
  ds2782_battery: Add support for ds2786 battery gas gauge
  pda_power: Add function callbacks for suspend and resume
  wm831x_power: Use genirq
  Driver for Zipit Z2 battery chip
  ds2782_battery: Fix clientdata on removal
-rw-r--r--Documentation/ABI/testing/sysfs-class-power20
-rw-r--r--drivers/power/Kconfig15
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/ds2760_battery.c64
-rw-r--r--drivers/power/ds2782_battery.c194
-rw-r--r--drivers/power/pda_power.c10
-rw-r--r--drivers/power/power_supply.h7
-rw-r--r--drivers/power/power_supply_core.c48
-rw-r--r--drivers/power/power_supply_sysfs.c145
-rw-r--r--drivers/power/test_power.c163
-rw-r--r--drivers/power/tosa_battery.c4
-rw-r--r--drivers/power/wm831x_power.c33
-rw-r--r--drivers/power/wm97xx_battery.c3
-rw-r--r--drivers/power/z2_battery.c328
-rw-r--r--include/linux/ds2782_battery.h8
-rw-r--r--include/linux/pda_power.h2
-rw-r--r--include/linux/power_supply.h6
-rw-r--r--include/linux/z2_battery.h17
18 files changed, 901 insertions, 168 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
new file mode 100644
index 000000000000..78c7baca3587
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power
@@ -0,0 +1,20 @@
+What:		/sys/class/power/ds2760-battery.*/charge_now
+Date:		May 2010
+KernelVersion:	2.6.35
+Contact:	Daniel Mack <daniel@caiaq.de>
+Description:
+		This file is writeable and can be used to set the current
+		coloumb counter value inside the battery monitor chip. This
+		is needed for unavoidable corrections of aging batteries.
+		A userspace daemon can monitor the battery charging logic
+		and once the counter drops out of considerable bounds, take
+		appropriate action.
+
+What:		/sys/class/power/ds2760-battery.*/charge_full
+Date:		May 2010
+KernelVersion:	2.6.35
+Contact:	Daniel Mack <daniel@caiaq.de>
+Description:
+		This file is writeable and can be used to set the assumed
+		battery 'full level'. As batteries age, this value has to be
+		amended over time.
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index faaa9b4d0d07..8e9ba177d817 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -57,6 +57,11 @@ config WM8350_POWER
           Say Y here to enable support for the power management unit
 	  provided by the Wolfson Microelectronics WM8350 PMIC.
 
+config TEST_POWER
+	tristate "Test power driver"
+	help
+	  This driver is used for testing. It's safe to say M here.
+
 config BATTERY_DS2760
 	tristate "DS2760 battery driver (HP iPAQ & others)"
 	select W1
@@ -65,10 +70,10 @@ config BATTERY_DS2760
 	  Say Y here to enable support for batteries with ds2760 chip.
 
 config BATTERY_DS2782
-	tristate "DS2782 standalone gas-gauge"
+	tristate "DS2782/DS2786 standalone gas-gauge"
 	depends on I2C
 	help
-	  Say Y here to enable support for the DS2782 standalone battery
+	  Say Y here to enable support for the DS2782/DS2786 standalone battery
 	  gas-gauge.
 
 config BATTERY_PMU
@@ -125,6 +130,12 @@ config BATTERY_MAX17040
 	  in handheld and portable equipment. The MAX17040 is configured
 	  to operate with a single lithium cell
 
+config BATTERY_Z2
+	tristate "Z2 battery driver"
+	depends on I2C && MACH_ZIPIT2
+	help
+	  Say Y to include support for the battery on the Zipit Z2.
+
 config CHARGER_PCF50633
 	tristate "NXP PCF50633 MBC"
 	depends on MFD_PCF50633
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index a2ba7c85c97a..00050809a6c7 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_MAX8925_POWER)	+= max8925_power.o
 obj-$(CONFIG_WM831X_BACKUP)	+= wm831x_backup.o
 obj-$(CONFIG_WM831X_POWER)	+= wm831x_power.o
 obj-$(CONFIG_WM8350_POWER)	+= wm8350_power.o
+obj-$(CONFIG_TEST_POWER)	+= test_power.o
 
 obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o
 obj-$(CONFIG_BATTERY_DS2782)	+= ds2782_battery.o
@@ -31,4 +32,5 @@ obj-$(CONFIG_BATTERY_WM97XX)	+= wm97xx_battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)	+= bq27x00_battery.o
 obj-$(CONFIG_BATTERY_DA9030)	+= da9030_battery.o
 obj-$(CONFIG_BATTERY_MAX17040)	+= max17040_battery.o
+obj-$(CONFIG_BATTERY_Z2)	+= z2_battery.o
 obj-$(CONFIG_CHARGER_PCF50633)	+= pcf50633-charger.o
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c
index 3bf8d1f622e3..4d3b27228a2e 100644
--- a/drivers/power/ds2760_battery.c
+++ b/drivers/power/ds2760_battery.c
@@ -304,6 +304,28 @@ static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
 	w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
 }
 
+static void ds2760_battery_write_active_full(struct ds2760_device_info *di,
+					     int active_full)
+{
+	unsigned char tmp[2] = {
+		active_full >> 8,
+		active_full & 0xff
+	};
+
+	if (tmp[0] == di->raw[DS2760_ACTIVE_FULL] &&
+	    tmp[1] == di->raw[DS2760_ACTIVE_FULL + 1])
+		return;
+
+	w1_ds2760_write(di->w1_dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp));
+	w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
+	w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
+
+	/* Write to the di->raw[] buffer directly - the DS2760_ACTIVE_FULL
+	 * values won't be read back by ds2760_battery_read_status() */
+	di->raw[DS2760_ACTIVE_FULL] = tmp[0];
+	di->raw[DS2760_ACTIVE_FULL + 1] = tmp[1];
+}
+
 static void ds2760_battery_work(struct work_struct *work)
 {
 	struct ds2760_device_info *di = container_of(work,
@@ -426,6 +448,45 @@ static int ds2760_battery_get_property(struct power_supply *psy,
 	return 0;
 }
 
+static int ds2760_battery_set_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       const union power_supply_propval *val)
+{
+	struct ds2760_device_info *di = to_ds2760_device_info(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		/* the interface counts in uAh, convert the value */
+		ds2760_battery_write_active_full(di, val->intval / 1000L);
+		break;
+
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		/* ds2760_battery_set_current_accum() does the conversion */
+		ds2760_battery_set_current_accum(di, val->intval);
+		break;
+
+	default:
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static int ds2760_battery_property_is_writeable(struct power_supply *psy,
+						enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+	case POWER_SUPPLY_PROP_CHARGE_NOW:
+		return 1;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static enum power_supply_property ds2760_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -460,6 +521,9 @@ static int ds2760_battery_probe(struct platform_device *pdev)
 	di->bat.properties	= ds2760_battery_props;
 	di->bat.num_properties	= ARRAY_SIZE(ds2760_battery_props);
 	di->bat.get_property	= ds2760_battery_get_property;
+	di->bat.set_property	= ds2760_battery_set_property;
+	di->bat.property_is_writeable =
+				  ds2760_battery_property_is_writeable;
 	di->bat.set_charged	= ds2760_battery_set_charged;
 	di->bat.external_power_changed =
 				  ds2760_battery_external_power_changed;
diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c
index 99c89976a902..d762a0cbc6af 100644
--- a/drivers/power/ds2782_battery.c
+++ b/drivers/power/ds2782_battery.c
@@ -5,6 +5,8 @@
  *
  * Author: Ryan Mallon <ryan@bluewatersys.com>
  *
+ * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
+ *
  * 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.
@@ -20,12 +22,13 @@
 #include <linux/idr.h>
 #include <linux/power_supply.h>
 #include <linux/slab.h>
+#include <linux/ds2782_battery.h>
 
 #define DS2782_REG_RARC		0x06	/* Remaining active relative capacity */
 
-#define DS2782_REG_VOLT_MSB	0x0c
-#define DS2782_REG_TEMP_MSB	0x0a
-#define DS2782_REG_CURRENT_MSB	0x0e
+#define DS278x_REG_VOLT_MSB	0x0c
+#define DS278x_REG_TEMP_MSB	0x0a
+#define DS278x_REG_CURRENT_MSB	0x0e
 
 /* EEPROM Block */
 #define DS2782_REG_RSNSP	0x69	/* Sense resistor value */
@@ -33,18 +36,33 @@
 /* Current unit measurement in uA for a 1 milli-ohm sense resistor */
 #define DS2782_CURRENT_UNITS	1563
 
-#define to_ds2782_info(x) container_of(x, struct ds2782_info, battery)
+#define DS2786_REG_RARC		0x02	/* Remaining active relative capacity */
+
+#define DS2786_CURRENT_UNITS	25
+
+struct ds278x_info;
+
+struct ds278x_battery_ops {
+	int	(*get_current)(struct ds278x_info *info, int *current_uA);
+	int	(*get_voltage)(struct ds278x_info *info, int *voltage_uA);
+	int	(*get_capacity)(struct ds278x_info *info, int *capacity_uA);
+
+};
+
+#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
 
-struct ds2782_info {
+struct ds278x_info {
 	struct i2c_client	*client;
 	struct power_supply	battery;
+	struct ds278x_battery_ops  *ops;
 	int			id;
+	int                     rsns;
 };
 
 static DEFINE_IDR(battery_id);
 static DEFINE_MUTEX(battery_lock);
 
-static inline int ds2782_read_reg(struct ds2782_info *info, int reg, u8 *val)
+static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
 {
 	int ret;
 
@@ -58,7 +76,7 @@ static inline int ds2782_read_reg(struct ds2782_info *info, int reg, u8 *val)
 	return 0;
 }
 
-static inline int ds2782_read_reg16(struct ds2782_info *info, int reg_msb,
+static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
 				    s16 *val)
 {
 	int ret;
@@ -73,7 +91,7 @@ static inline int ds2782_read_reg16(struct ds2782_info *info, int reg_msb,
 	return 0;
 }
 
-static int ds2782_get_temp(struct ds2782_info *info, int *temp)
+static int ds278x_get_temp(struct ds278x_info *info, int *temp)
 {
 	s16 raw;
 	int err;
@@ -84,14 +102,14 @@ static int ds2782_get_temp(struct ds2782_info *info, int *temp)
 	 * celsius. The temperature value is stored as a 10 bit number, plus
 	 * sign in the upper bits of a 16 bit register.
 	 */
-	err = ds2782_read_reg16(info, DS2782_REG_TEMP_MSB, &raw);
+	err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
 	if (err)
 		return err;
 	*temp = ((raw / 32) * 125) / 100;
 	return 0;
 }
 
-static int ds2782_get_current(struct ds2782_info *info, int *current_uA)
+static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
 {
 	int sense_res;
 	int err;
@@ -102,7 +120,7 @@ static int ds2782_get_current(struct ds2782_info *info, int *current_uA)
 	 * The units of measurement for current are dependent on the value of
 	 * the sense resistor.
 	 */
-	err = ds2782_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
+	err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
 	if (err)
 		return err;
 	if (sense_res_raw == 0) {
@@ -113,14 +131,14 @@ static int ds2782_get_current(struct ds2782_info *info, int *current_uA)
 
 	dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
 		sense_res);
-	err = ds2782_read_reg16(info, DS2782_REG_CURRENT_MSB, &raw);
+	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
 	if (err)
 		return err;
 	*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
 	return 0;
 }
 
-static int ds2782_get_voltage(struct ds2782_info *info, int *voltage_uA)
+static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA)
 {
 	s16 raw;
 	int err;
@@ -129,36 +147,77 @@ static int ds2782_get_voltage(struct ds2782_info *info, int *voltage_uA)
 	 * Voltage is measured in units of 4.88mV. The voltage is stored as
 	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
 	 */
-	err = ds2782_read_reg16(info, DS2782_REG_VOLT_MSB, &raw);
+	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
 	if (err)
 		return err;
 	*voltage_uA = (raw / 32) * 4800;
 	return 0;
 }
 
-static int ds2782_get_capacity(struct ds2782_info *info, int *capacity)
+static int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
 {
 	int err;
 	u8 raw;
 
-	err = ds2782_read_reg(info, DS2782_REG_RARC, &raw);
+	err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
 	if (err)
 		return err;
 	*capacity = raw;
 	return raw;
 }
 
-static int ds2782_get_status(struct ds2782_info *info, int *status)
+static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
+{
+	int err;
+	s16 raw;
+
+	err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
+	if (err)
+		return err;
+	*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
+	return 0;
+}
+
+static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
+{
+	s16 raw;
+	int err;
+
+	/*
+	 * Voltage is measured in units of 1.22mV. The voltage is stored as
+	 * a 10-bit number plus sign, in the upper bits of a 16-bit register
+	 */
+	err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
+	if (err)
+		return err;
+	*voltage_uA = (raw / 8) * 1220;
+	return 0;
+}
+
+static int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
+{
+	int err;
+	u8 raw;
+
+	err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
+	if (err)
+		return err;
+	/* Relative capacity is displayed with resolution 0.5 % */
+	*capacity = raw/2 ;
+	return 0;
+}
+
+static int ds278x_get_status(struct ds278x_info *info, int *status)
 {
 	int err;
 	int current_uA;
 	int capacity;
 
-	err = ds2782_get_current(info, &current_uA);
+	err = info->ops->get_current(info, &current_uA);
 	if (err)
 		return err;
 
-	err = ds2782_get_capacity(info, &capacity);
+	err = info->ops->get_capacity(info, &capacity);
 	if (err)
 		return err;
 
@@ -174,32 +233,32 @@ static int ds2782_get_status(struct ds2782_info *info, int *status)
 	return 0;
 }
 
-static int ds2782_battery_get_property(struct power_supply *psy,
+static int ds278x_battery_get_property(struct power_supply *psy,
 				       enum power_supply_property prop,
 				       union power_supply_propval *val)
 {
-	struct ds2782_info *info = to_ds2782_info(psy);
+	struct ds278x_info *info = to_ds278x_info(psy);
 	int ret;
 
 	switch (prop) {
 	case POWER_SUPPLY_PROP_STATUS:
-		ret = ds2782_get_status(info, &val->intval);
+		ret = ds278x_get_status(info, &val->intval);
 		break;
 
 	case POWER_SUPPLY_PROP_CAPACITY:
-		ret = ds2782_get_capacity(info, &val->intval);
+		ret = info->ops->get_capacity(info, &val->intval);
 		break;
 
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-		ret = ds2782_get_voltage(info, &val->intval);
+		ret = info->ops->get_voltage(info, &val->intval);
 		break;
 
 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		ret = ds2782_get_current(info, &val->intval);
+		ret = info->ops->get_current(info, &val->intval);
 		break;
 
 	case POWER_SUPPLY_PROP_TEMP:
-		ret = ds2782_get_temp(info, &val->intval);
+		ret = ds278x_get_temp(info, &val->intval);
 		break;
 
 	default:
@@ -209,7 +268,7 @@ static int ds2782_battery_get_property(struct power_supply *psy,
 	return ret;
 }
 
-static enum power_supply_property ds2782_battery_props[] = {
+static enum power_supply_property ds278x_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_CAPACITY,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -217,18 +276,18 @@ static enum power_supply_property ds2782_battery_props[] = {
 	POWER_SUPPLY_PROP_TEMP,
 };
 
-static void ds2782_power_supply_init(struct power_supply *battery)
+static void ds278x_power_supply_init(struct power_supply *battery)
 {
 	battery->type			= POWER_SUPPLY_TYPE_BATTERY;
-	battery->properties		= ds2782_battery_props;
-	battery->num_properties		= ARRAY_SIZE(ds2782_battery_props);
-	battery->get_property		= ds2782_battery_get_property;
+	battery->properties		= ds278x_battery_props;
+	battery->num_properties		= ARRAY_SIZE(ds278x_battery_props);
+	battery->get_property		= ds278x_battery_get_property;
 	battery->external_power_changed	= NULL;
 }
 
-static int ds2782_battery_remove(struct i2c_client *client)
+static int ds278x_battery_remove(struct i2c_client *client)
 {
-	struct ds2782_info *info = i2c_get_clientdata(client);
+	struct ds278x_info *info = i2c_get_clientdata(client);
 
 	power_supply_unregister(&info->battery);
 	kfree(info->battery.name);
@@ -237,19 +296,45 @@ static int ds2782_battery_remove(struct i2c_client *client)
 	idr_remove(&battery_id, info->id);
 	mutex_unlock(&battery_lock);
 
-	i2c_set_clientdata(client, info);
-
 	kfree(info);
 	return 0;
 }
 
-static int ds2782_battery_probe(struct i2c_client *client,
+enum ds278x_num_id {
+	DS2782 = 0,
+	DS2786,
+};
+
+static struct ds278x_battery_ops ds278x_ops[] = {
+	[DS2782] = {
+		.get_current  = ds2782_get_current,
+		.get_voltage  = ds2782_get_voltage,
+		.get_capacity = ds2782_get_capacity,
+	},
+	[DS2786] = {
+		.get_current  = ds2786_get_current,
+		.get_voltage  = ds2786_get_voltage,
+		.get_capacity = ds2786_get_capacity,
+	}
+};
+
+static int ds278x_battery_probe(struct i2c_client *client,
 				const struct i2c_device_id *id)
 {
-	struct ds2782_info *info;
+	struct ds278x_platform_data *pdata = client->dev.platform_data;
+	struct ds278x_info *info;
 	int ret;
 	int num;
 
+	/*
+	 * ds2786 should have the sense resistor value set
+	 * in the platform data
+	 */
+	if (id->driver_data == DS2786 && !pdata) {
+		dev_err(&client->dev, "missing platform data for ds2786\n");
+		return -EINVAL;
+	}
+
 	/* Get an ID for this battery */
 	ret = idr_pre_get(&battery_id, GFP_KERNEL);
 	if (ret == 0) {
@@ -269,15 +354,20 @@ static int ds2782_battery_probe(struct i2c_client *client,
 		goto fail_info;
 	}
 
-	info->battery.name = kasprintf(GFP_KERNEL, "ds2782-%d", num);
+	info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
 	if (!info->battery.name) {
 		ret = -ENOMEM;
 		goto fail_name;
 	}
 
+	if (id->driver_data == DS2786)
+		info->rsns = pdata->rsns;
+
 	i2c_set_clientdata(client, info);
 	info->client = client;
-	ds2782_power_supply_init(&info->battery);
+	info->id = num;
+	info->ops  = &ds278x_ops[id->driver_data];
+	ds278x_power_supply_init(&info->battery);
 
 	ret = power_supply_register(&client->dev, &info->battery);
 	if (ret) {
@@ -290,7 +380,6 @@ static int ds2782_battery_probe(struct i2c_client *client,
 fail_register:
 	kfree(info->battery.name);
 fail_name:
-	i2c_set_clientdata(client, info);
 	kfree(info);
 fail_info:
 	mutex_lock(&battery_lock);
@@ -300,31 +389,32 @@ fail_id:
 	return ret;
 }
 
-static const struct i2c_device_id ds2782_id[] = {
-	{"ds2782", 0},
+static const struct i2c_device_id ds278x_id[] = {
+	{"ds2782", DS2782},
+	{"ds2786", DS2786},
 	{},
 };
 
-static struct i2c_driver ds2782_battery_driver = {
+static struct i2c_driver ds278x_battery_driver = {
 	.driver 	= {
 		.name	= "ds2782-battery",
 	},
-	.probe		= ds2782_battery_probe,
-	.remove		= ds2782_battery_remove,
-	.id_table	= ds2782_id,
+	.probe		= ds278x_battery_probe,
+	.remove		= ds278x_battery_remove,
+	.id_table	= ds278x_id,
 };
 
-static int __init ds2782_init(void)
+static int __init ds278x_init(void)
 {
-	return i2c_add_driver(&ds2782_battery_driver);
+	return i2c_add_driver(&ds278x_battery_driver);
 }
-module_init(ds2782_init);
+module_init(ds278x_init);
 
-static void __exit ds2782_exit(void)
+static void __exit ds278x_exit(void)
 {
-	i2c_del_driver(&ds2782_battery_driver);
+	i2c_del_driver(&ds278x_battery_driver);
 }
-module_exit(ds2782_exit);
+module_exit(ds278x_exit);
 
 MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
 MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
index a232de6a5703..69f8aa3a6a4b 100644
--- a/drivers/power/pda_power.c
+++ b/drivers/power/pda_power.c
@@ -404,6 +404,13 @@ static int usb_wakeup_enabled;
 
 static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
 {
+	if (pdata->suspend) {
+		int ret = pdata->suspend(state);
+
+		if (ret)
+			return ret;
+	}
+
 	if (device_may_wakeup(&pdev->dev)) {
 		if (ac_irq)
 			ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
@@ -423,6 +430,9 @@ static int pda_power_resume(struct platform_device *pdev)
 			disable_irq_wake(ac_irq->start);
 	}
 
+	if (pdata->resume)
+		return pdata->resume();
+
 	return 0;
 }
 #else
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
index f38ba482be75..018de2b26998 100644
--- a/drivers/power/power_supply.h
+++ b/drivers/power/power_supply.h
@@ -12,15 +12,12 @@
 
 #ifdef CONFIG_SYSFS
 
-extern int power_supply_create_attrs(struct power_supply *psy);
-extern void power_supply_remove_attrs(struct power_supply *psy);
+extern void power_supply_init_attrs(struct device_type *dev_type);
 extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
 
 #else
 
-static inline int power_supply_create_attrs(struct power_supply *psy)
-{ return 0; }
-static inline void power_supply_remove_attrs(struct power_supply *psy) {}
+static inline void power_supply_init_attrs(struct device_type *dev_type) {}
 #define power_supply_uevent NULL
 
 #endif /* CONFIG_SYSFS */
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index cce75b40b435..91606bb55318 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/init.h>
+#include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/power_supply.h>
@@ -22,6 +23,8 @@
 struct class *power_supply_class;
 EXPORT_SYMBOL_GPL(power_supply_class);
 
+static struct device_type power_supply_dev_type;
+
 static int __power_supply_changed_work(struct device *dev, void *data)
 {
 	struct power_supply *psy = (struct power_supply *)data;
@@ -144,22 +147,39 @@ struct power_supply *power_supply_get_by_name(char *name)
 }
 EXPORT_SYMBOL_GPL(power_supply_get_by_name);
 
+static void power_supply_dev_release(struct device *dev)
+{
+	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
+	kfree(dev);
+}
+
 int power_supply_register(struct device *parent, struct power_supply *psy)
 {
-	int rc = 0;
+	struct device *dev;
+	int rc;
 
-	psy->dev = device_create(power_supply_class, parent, 0, psy,
-				 "%s", psy->name);
-	if (IS_ERR(psy->dev)) {
-		rc = PTR_ERR(psy->dev);
-		goto dev_create_failed;
-	}
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
 
-	INIT_WORK(&psy->changed_work, power_supply_changed_work);
+	device_initialize(dev);
 
-	rc = power_supply_create_attrs(psy);
+	dev->class = power_supply_class;
+	dev->type = &power_supply_dev_type;
+	dev->parent = parent;
+	dev->release = power_supply_dev_release;
+	dev_set_drvdata(dev, psy);
+	psy->dev = dev;
+
+	rc = kobject_set_name(&dev->kobj, "%s", psy->name);
+	if (rc)
+		goto kobject_set_name_failed;
+
+	rc = device_add(dev);
 	if (rc)
-		goto create_attrs_failed;
+		goto device_add_failed;
+
+	INIT_WORK(&psy->changed_work, power_supply_changed_work);
 
 	rc = power_supply_create_triggers(psy);
 	if (rc)
@@ -170,10 +190,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
 	goto success;
 
 create_triggers_failed:
-	power_supply_remove_attrs(psy);
-create_attrs_failed:
 	device_unregister(psy->dev);
-dev_create_failed:
+kobject_set_name_failed:
+device_add_failed:
+	kfree(dev);
 success:
 	return rc;
 }
@@ -183,7 +203,6 @@ void power_supply_unregister(struct power_supply *psy)
 {
 	flush_scheduled_work();
 	power_supply_remove_triggers(psy);
-	power_supply_remove_attrs(psy);
 	device_unregister(psy->dev);
 }
 EXPORT_SYMBOL_GPL(power_supply_unregister);
@@ -196,6 +215,7 @@ static int __init power_supply_class_init(void)
 		return PTR_ERR(power_supply_class);
 
 	power_supply_class->dev_uevent = power_supply_uevent;
+	power_supply_init_attrs(&power_supply_dev_type);
 
 	return 0;
 }
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 5b6e352ac7c1..6a86cdfd79fa 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -31,9 +31,9 @@
 
 #define POWER_SUPPLY_ATTR(_name)					\
 {									\
-	.attr = { .name = #_name, .mode = 0444 },	\
+	.attr = { .name = #_name },					\
 	.show = power_supply_show_property,				\
-	.store = NULL,							\
+	.store = power_supply_store_property,				\
 }
 
 static struct device_attribute power_supply_attrs[];
@@ -41,6 +41,9 @@ static struct device_attribute power_supply_attrs[];
 static ssize_t power_supply_show_property(struct device *dev,
 					  struct device_attribute *attr,
 					  char *buf) {
+	static char *type_text[] = {
+		"Battery", "UPS", "Mains", "USB"
+	};
 	static char *status_text[] = {
 		"Unknown", "Charging", "Discharging", "Not charging", "Full"
 	};
@@ -58,12 +61,15 @@ static ssize_t power_supply_show_property(struct device *dev,
 	static char *capacity_level_text[] = {
 		"Unknown", "Critical", "Low", "Normal", "High", "Full"
 	};
-	ssize_t ret;
+	ssize_t ret = 0;
 	struct power_supply *psy = dev_get_drvdata(dev);
 	const ptrdiff_t off = attr - power_supply_attrs;
 	union power_supply_propval value;
 
-	ret = psy->get_property(psy, off, &value);
+	if (off == POWER_SUPPLY_PROP_TYPE)
+		value.intval = psy->type;
+	else
+		ret = psy->get_property(psy, off, &value);
 
 	if (ret < 0) {
 		if (ret == -ENODATA)
@@ -85,12 +91,37 @@ static ssize_t power_supply_show_property(struct device *dev,
 		return sprintf(buf, "%s\n", technology_text[value.intval]);
 	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
 		return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
+	else if (off == POWER_SUPPLY_PROP_TYPE)
+		return sprintf(buf, "%s\n", type_text[value.intval]);
 	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
 		return sprintf(buf, "%s\n", value.strval);
 
 	return sprintf(buf, "%d\n", value.intval);
 }
 
+static ssize_t power_supply_store_property(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count) {
+	ssize_t ret;
+	struct power_supply *psy = dev_get_drvdata(dev);
+	const ptrdiff_t off = attr - power_supply_attrs;
+	union power_supply_propval value;
+	long long_val;
+
+	/* TODO: support other types than int */
+	ret = strict_strtol(buf, 10, &long_val);
+	if (ret < 0)
+		return ret;
+
+	value.intval = long_val;
+
+	ret = psy->set_property(psy, off, &value);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
 /* Must be in the same order as POWER_SUPPLY_PROP_* */
 static struct device_attribute power_supply_attrs[] = {
 	/* Properties of type `int' */
@@ -132,67 +163,59 @@ static struct device_attribute power_supply_attrs[] = {
 	POWER_SUPPLY_ATTR(time_to_empty_avg),
 	POWER_SUPPLY_ATTR(time_to_full_now),
 	POWER_SUPPLY_ATTR(time_to_full_avg),
+	POWER_SUPPLY_ATTR(type),
 	/* Properties of type `const char *' */
 	POWER_SUPPLY_ATTR(model_name),
 	POWER_SUPPLY_ATTR(manufacturer),
 	POWER_SUPPLY_ATTR(serial_number),
 };
 
-static ssize_t power_supply_show_static_attrs(struct device *dev,
-					      struct device_attribute *attr,
-					      char *buf) {
-	static char *type_text[] = { "Battery", "UPS", "Mains", "USB" };
+static struct attribute *
+__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
+
+static mode_t power_supply_attr_is_visible(struct kobject *kobj,
+					   struct attribute *attr,
+					   int attrno)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
 	struct power_supply *psy = dev_get_drvdata(dev);
+	int i;
 
-	return sprintf(buf, "%s\n", type_text[psy->type]);
-}
+	for (i = 0; i < psy->num_properties; i++) {
+		int property = psy->properties[i];
 
-static struct device_attribute power_supply_static_attrs[] = {
-	__ATTR(type, 0444, power_supply_show_static_attrs, NULL),
-};
+		if (property == attrno) {
+			mode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
 
-int power_supply_create_attrs(struct power_supply *psy)
-{
-	int rc = 0;
-	int i, j;
-
-	for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) {
-		rc = device_create_file(psy->dev,
-			    &power_supply_static_attrs[i]);
-		if (rc)
-			goto statics_failed;
-	}
+			if (psy->property_is_writeable &&
+			    psy->property_is_writeable(psy, property) > 0)
+				mode |= S_IWUSR;
 
-	for (j = 0; j < psy->num_properties; j++) {
-		rc = device_create_file(psy->dev,
-			    &power_supply_attrs[psy->properties[j]]);
-		if (rc)
-			goto dynamics_failed;
+			return mode;
+		}
 	}
 
-	goto succeed;
-
-dynamics_failed:
-	while (j--)
-		device_remove_file(psy->dev,
-			   &power_supply_attrs[psy->properties[j]]);
-statics_failed:
-	while (i--)
-		device_remove_file(psy->dev, &power_supply_static_attrs[i]);
-succeed:
-	return rc;
+	return 0;
 }
 
-void power_supply_remove_attrs(struct power_supply *psy)
+static struct attribute_group power_supply_attr_group = {
+	.attrs = __power_supply_attrs,
+	.is_visible = power_supply_attr_is_visible,
+};
+
+static const struct attribute_group *power_supply_attr_groups[] = {
+	&power_supply_attr_group,
+	NULL,
+};
+
+void power_supply_init_attrs(struct device_type *dev_type)
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++)
-		device_remove_file(psy->dev, &power_supply_static_attrs[i]);
+	dev_type->groups = power_supply_attr_groups;
 
-	for (i = 0; i < psy->num_properties; i++)
-		device_remove_file(psy->dev,
-			    &power_supply_attrs[psy->properties[i]]);
+	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
+		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
 }
 
 static char *kstruprdup(const char *str, gfp_t gfp)
@@ -236,36 +259,6 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
 	if (!prop_buf)
 		return -ENOMEM;
 
-	for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) {
-		struct device_attribute *attr;
-		char *line;
-
-		attr = &power_supply_static_attrs[j];
-
-		ret = power_supply_show_static_attrs(dev, attr, prop_buf);
-		if (ret < 0)
-			goto out;
-
-		line = strchr(prop_buf, '\n');
-		if (line)
-			*line = 0;
-
-		attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
-		if (!attrname) {
-			ret = -ENOMEM;
-			goto out;
-		}
-
-		dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
-
-		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
-		kfree(attrname);
-		if (ret)
-			goto out;
-	}
-
-	dev_dbg(dev, "%zd dynamic props\n", psy->num_properties);
-
 	for (j = 0; j < psy->num_properties; j++) {
 		struct device_attribute *attr;
 		char *line;
diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c
new file mode 100644
index 000000000000..0cd9f67d33e5
--- /dev/null
+++ b/drivers/power/test_power.c
@@ -0,0 +1,163 @@
+/*
+ * Power supply driver for testing.
+ *
+ * Copyright 2010  Anton Vorontsov <cbouatmailru@gmail.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/vermagic.h>
+
+static int test_power_ac_online = 1;
+static int test_power_battery_status = POWER_SUPPLY_STATUS_CHARGING;
+
+static int test_power_get_ac_property(struct power_supply *psy,
+				      enum power_supply_property psp,
+				      union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = test_power_ac_online;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int test_power_get_battery_property(struct power_supply *psy,
+					   enum power_supply_property psp,
+					   union power_supply_propval *val)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = "Test battery";
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "Linux";
+		break;
+	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+		val->strval = UTS_RELEASE;
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = test_power_battery_status;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+		val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = 50;
+		break;
+	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+		val->intval = 3600;
+		break;
+	default:
+		pr_info("%s: some properties deliberately report errors.\n",
+			__func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static enum power_supply_property test_power_ac_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property test_power_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_EMPTY,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_SERIAL_NUMBER,
+};
+
+static char *test_power_ac_supplied_to[] = {
+	"test_battery",
+};
+
+static struct power_supply test_power_supplies[] = {
+	{
+		.name = "test_ac",
+		.type = POWER_SUPPLY_TYPE_MAINS,
+		.supplied_to = test_power_ac_supplied_to,
+		.num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
+		.properties = test_power_ac_props,
+		.num_properties = ARRAY_SIZE(test_power_ac_props),
+		.get_property = test_power_get_ac_property,
+	}, {
+		.name = "test_battery",
+		.type = POWER_SUPPLY_TYPE_BATTERY,
+		.properties = test_power_battery_props,
+		.num_properties = ARRAY_SIZE(test_power_battery_props),
+		.get_property = test_power_get_battery_property,
+	},
+};
+
+static int __init test_power_init(void)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
+		ret = power_supply_register(NULL, &test_power_supplies[i]);
+		if (ret) {
+			pr_err("%s: failed to register %s\n", __func__,
+				test_power_supplies[i].name);
+			goto failed;
+		}
+	}
+
+	return 0;
+failed:
+	while (--i >= 0)
+		power_supply_unregister(&test_power_supplies[i]);
+	return ret;
+}
+module_init(test_power_init);
+
+static void __exit test_power_exit(void)
+{
+	int i;
+
+	/* Let's see how we handle changes... */
+	test_power_ac_online = 0;
+	test_power_battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+	for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
+		power_supply_changed(&test_power_supplies[i]);
+	pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n",
+		__func__);
+	ssleep(10);
+
+	for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
+		power_supply_unregister(&test_power_supplies[i]);
+}
+module_exit(test_power_exit);
+
+MODULE_DESCRIPTION("Power supply driver for testing");
+MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c
index 2eab35aab311..ee04936b2db5 100644
--- a/drivers/power/tosa_battery.c
+++ b/drivers/power/tosa_battery.c
@@ -61,7 +61,7 @@ static unsigned long tosa_read_bat(struct tosa_bat *bat)
 	mutex_lock(&bat_lock);
 	gpio_set_value(bat->gpio_bat, 1);
 	msleep(5);
-	value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data,
+	value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent),
 			bat->adc_bat);
 	gpio_set_value(bat->gpio_bat, 0);
 	mutex_unlock(&bat_lock);
@@ -81,7 +81,7 @@ static unsigned long tosa_read_temp(struct tosa_bat *bat)
 	mutex_lock(&bat_lock);
 	gpio_set_value(bat->gpio_temp, 1);
 	msleep(5);
-	value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data,
+	value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent),
 			bat->adc_temp);
 	gpio_set_value(bat->gpio_temp, 0);
 	mutex_unlock(&bat_lock);
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
index 875c4d0f776b..fbcc36dae470 100644
--- a/drivers/power/wm831x_power.c
+++ b/drivers/power/wm831x_power.c
@@ -537,9 +537,9 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 		goto err_battery;
 
 	irq = platform_get_irq_byname(pdev, "SYSLO");
-	ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
-				 IRQF_TRIGGER_RISING, "SYSLO",
-				 power);
+	ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
+				   IRQF_TRIGGER_RISING, "System power low",
+				   power);
 	if (ret != 0) {
 		dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
 			irq, ret);
@@ -547,9 +547,9 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 	}
 
 	irq = platform_get_irq_byname(pdev, "PWR SRC");
-	ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq,
-				 IRQF_TRIGGER_RISING, "Power source",
-				 power);
+	ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
+				   IRQF_TRIGGER_RISING, "Power source",
+				   power);
 	if (ret != 0) {
 		dev_err(&pdev->dev, "Failed to request PWR SRC IRQ %d: %d\n",
 			irq, ret);
@@ -558,10 +558,10 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 
 	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
 		irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
-		ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq,
-					 IRQF_TRIGGER_RISING,
-					 wm831x_bat_irqs[i],
-					 power);
+		ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
+					   IRQF_TRIGGER_RISING,
+					   wm831x_bat_irqs[i],
+					   power);
 		if (ret != 0) {
 			dev_err(&pdev->dev,
 				"Failed to request %s IRQ %d: %d\n",
@@ -575,13 +575,13 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
 err_bat_irq:
 	for (; i >= 0; i--) {
 		irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
-		wm831x_free_irq(wm831x, irq, power);
+		free_irq(irq, power);
 	}
 	irq = platform_get_irq_byname(pdev, "PWR SRC");
-	wm831x_free_irq(wm831x, irq, power);
+	free_irq(irq, power);
 err_syslo:
 	irq = platform_get_irq_byname(pdev, "SYSLO");
-	wm831x_free_irq(wm831x, irq, power);
+	free_irq(irq, power);
 err_usb:
 	power_supply_unregister(usb);
 err_battery:
@@ -596,19 +596,18 @@ err_kmalloc:
 static __devexit int wm831x_power_remove(struct platform_device *pdev)
 {
 	struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
-	struct wm831x *wm831x = wm831x_power->wm831x;
 	int irq, i;
 
 	for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
 		irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
-		wm831x_free_irq(wm831x, irq, wm831x_power);
+		free_irq(irq, wm831x_power);
 	}
 
 	irq = platform_get_irq_byname(pdev, "PWR SRC");
-	wm831x_free_irq(wm831x, irq, wm831x_power);
+	free_irq(irq, wm831x_power);
 
 	irq = platform_get_irq_byname(pdev, "SYSLO");
-	wm831x_free_irq(wm831x, irq, wm831x_power);
+	free_irq(irq, wm831x_power);
 
 	power_supply_unregister(&wm831x_power->battery);
 	power_supply_unregister(&wm831x_power->wall);
diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c
index 94c70650aafc..4e8afce0c818 100644
--- a/drivers/power/wm97xx_battery.c
+++ b/drivers/power/wm97xx_battery.c
@@ -308,6 +308,9 @@ static void __exit wm97xx_bat_exit(void)
 	platform_driver_unregister(&wm97xx_bat_driver);
 }
 
+/* The interface is deprecated, as well as linux/wm97xx_batt.h */
+void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data);
+
 void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data)
 {
 	gpdata = data;
diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c
new file mode 100644
index 000000000000..9cca465436e3
--- /dev/null
+++ b/drivers/power/z2_battery.c
@@ -0,0 +1,328 @@
+/*
+ * Battery measurement code for Zipit Z2
+ *
+ * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com>
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <linux/z2_battery.h>
+
+#define	Z2_DEFAULT_NAME	"Z2"
+
+struct z2_charger {
+	struct z2_battery_info	*info;
+	int			bat_status;
+	struct i2c_client	*client;
+	struct power_supply	batt_ps;
+	struct mutex		work_lock;
+	struct work_struct	bat_work;
+};
+
+static unsigned long z2_read_bat(struct z2_charger *charger)
+{
+	int data;
+	data = i2c_smbus_read_byte_data(charger->client,
+					charger->info->batt_I2C_reg);
+	if (data < 0)
+		return 0;
+
+	return data * charger->info->batt_mult / charger->info->batt_div;
+}
+
+static int z2_batt_get_property(struct power_supply *batt_ps,
+			    enum power_supply_property psp,
+			    union power_supply_propval *val)
+{
+	struct z2_charger *charger = container_of(batt_ps, struct z2_charger,
+						batt_ps);
+	struct z2_battery_info *info = charger->info;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = charger->bat_status;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = info->batt_tech;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		if (info->batt_I2C_reg >= 0)
+			val->intval = z2_read_bat(charger);
+		else
+			return -EINVAL;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		if (info->max_voltage >= 0)
+			val->intval = info->max_voltage;
+		else
+			return -EINVAL;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+		if (info->min_voltage >= 0)
+			val->intval = info->min_voltage;
+		else
+			return -EINVAL;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
+{
+	struct z2_charger *charger = container_of(batt_ps, struct z2_charger,
+						batt_ps);
+	schedule_work(&charger->bat_work);
+}
+
+static void z2_batt_update(struct z2_charger *charger)
+{
+	int old_status = charger->bat_status;
+	struct z2_battery_info *info;
+
+	info = charger->info;
+
+	mutex_lock(&charger->work_lock);
+
+	charger->bat_status = (info->charge_gpio >= 0) ?
+		(gpio_get_value(info->charge_gpio) ?
+		POWER_SUPPLY_STATUS_CHARGING :
+		POWER_SUPPLY_STATUS_DISCHARGING) :
+		POWER_SUPPLY_STATUS_UNKNOWN;
+
+	if (old_status != charger->bat_status) {
+		pr_debug("%s: %i -> %i\n", charger->batt_ps.name, old_status,
+			charger->bat_status);
+		power_supply_changed(&charger->batt_ps);
+	}
+
+	mutex_unlock(&charger->work_lock);
+}
+
+static void z2_batt_work(struct work_struct *work)
+{
+	struct z2_charger *charger;
+	charger = container_of(work, struct z2_charger, bat_work);
+	z2_batt_update(charger);
+}
+
+static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
+{
+	struct z2_charger *charger = devid;
+	schedule_work(&charger->bat_work);
+	return IRQ_HANDLED;
+}
+
+static int z2_batt_ps_init(struct z2_charger *charger, int props)
+{
+	int i = 0;
+	enum power_supply_property *prop;
+	struct z2_battery_info *info = charger->info;
+
+	if (info->batt_tech >= 0)
+		props++;	/* POWER_SUPPLY_PROP_TECHNOLOGY */
+	if (info->batt_I2C_reg >= 0)
+		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_NOW */
+	if (info->max_voltage >= 0)
+		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MAX */
+	if (info->min_voltage >= 0)
+		props++;	/* POWER_SUPPLY_PROP_VOLTAGE_MIN */
+
+	prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
+	if (!prop)
+		return -ENOMEM;
+
+	prop[i++] = POWER_SUPPLY_PROP_PRESENT;
+	if (info->charge_gpio >= 0)
+		prop[i++] = POWER_SUPPLY_PROP_STATUS;
+	if (info->batt_tech >= 0)
+		prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
+	if (info->batt_I2C_reg >= 0)
+		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+	if (info->max_voltage >= 0)
+		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
+	if (info->min_voltage >= 0)
+		prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
+
+	if (!info->batt_name) {
+		dev_info(&charger->client->dev,
+				"Please consider setting proper battery "
+				"name in platform definition file, falling "
+				"back to name \" Z2_DEFAULT_NAME \"\n");
+		charger->batt_ps.name = Z2_DEFAULT_NAME;
+	} else
+		charger->batt_ps.name = info->batt_name;
+
+	charger->batt_ps.properties		= prop;
+	charger->batt_ps.num_properties		= props;
+	charger->batt_ps.type			= POWER_SUPPLY_TYPE_BATTERY;
+	charger->batt_ps.get_property		= z2_batt_get_property;
+	charger->batt_ps.external_power_changed	= z2_batt_ext_power_changed;
+	charger->batt_ps.use_for_apm		= 1;
+
+	return 0;
+}
+
+static int __devinit z2_batt_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	int ret = 0;
+	int props = 1;	/* POWER_SUPPLY_PROP_PRESENT */
+	struct z2_charger *charger;
+	struct z2_battery_info *info = client->dev.platform_data;
+
+	if (info == NULL) {
+		dev_err(&client->dev,
+			"Please set platform device platform_data"
+			" to a valid z2_battery_info pointer!\n");
+		return -EINVAL;
+	}
+
+	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
+	if (charger == NULL)
+		return -ENOMEM;
+
+	charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
+	charger->info = info;
+	charger->client = client;
+	i2c_set_clientdata(client, charger);
+
+	mutex_init(&charger->work_lock);
+
+	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
+		ret = gpio_request(info->charge_gpio, "BATT CHRG");
+		if (ret)
+			goto err;
+
+		ret = gpio_direction_input(info->charge_gpio);
+		if (ret)
+			goto err2;
+
+		set_irq_type(gpio_to_irq(info->charge_gpio),
+				IRQ_TYPE_EDGE_BOTH);
+		ret = request_irq(gpio_to_irq(info->charge_gpio),
+				z2_charge_switch_irq, IRQF_DISABLED,
+				"AC Detect", charger);
+		if (ret)
+			goto err3;
+	}
+
+	ret = z2_batt_ps_init(charger, props);
+	if (ret)
+		goto err3;
+
+	INIT_WORK(&charger->bat_work, z2_batt_work);
+
+	ret = power_supply_register(&client->dev, &charger->batt_ps);
+	if (ret)
+		goto err4;
+
+	schedule_work(&charger->bat_work);
+
+	return 0;
+
+err4:
+	kfree(charger->batt_ps.properties);
+err3:
+	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
+		free_irq(gpio_to_irq(info->charge_gpio), charger);
+err2:
+	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
+		gpio_free(info->charge_gpio);
+err:
+	kfree(charger);
+	return ret;
+}
+
+static int __devexit z2_batt_remove(struct i2c_client *client)
+{
+	struct z2_charger *charger = i2c_get_clientdata(client);
+	struct z2_battery_info *info = charger->info;
+
+	flush_scheduled_work();
+	power_supply_unregister(&charger->batt_ps);
+
+	kfree(charger->batt_ps.properties);
+	if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
+		free_irq(gpio_to_irq(info->charge_gpio), charger);
+		gpio_free(info->charge_gpio);
+	}
+
+	kfree(charger);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int z2_batt_suspend(struct i2c_client *client, pm_message_t state)
+{
+	flush_scheduled_work();
+	return 0;
+}
+
+static int z2_batt_resume(struct i2c_client *client)
+{
+	struct z2_charger *charger = i2c_get_clientdata(client);
+
+	schedule_work(&charger->bat_work);
+	return 0;
+}
+#else
+#define z2_batt_suspend NULL
+#define z2_batt_resume NULL
+#endif
+
+static const struct i2c_device_id z2_batt_id[] = {
+	{ "aer915", 0 },
+	{ }
+};
+
+static struct i2c_driver z2_batt_driver = {
+	.driver	= {
+		.name	= "z2-battery",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= z2_batt_probe,
+	.remove		= z2_batt_remove,
+	.suspend	= z2_batt_suspend,
+	.resume		= z2_batt_resume,
+	.id_table	= z2_batt_id,
+};
+
+static int __init z2_batt_init(void)
+{
+	return i2c_add_driver(&z2_batt_driver);
+}
+
+static void __exit z2_batt_exit(void)
+{
+	i2c_del_driver(&z2_batt_driver);
+}
+
+module_init(z2_batt_init);
+module_exit(z2_batt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
+MODULE_DESCRIPTION("Zipit Z2 battery driver");
diff --git a/include/linux/ds2782_battery.h b/include/linux/ds2782_battery.h
new file mode 100644
index 000000000000..b4e281f65c15
--- /dev/null
+++ b/include/linux/ds2782_battery.h
@@ -0,0 +1,8 @@
+#ifndef __LINUX_DS2782_BATTERY_H
+#define __LINUX_DS2782_BATTERY_H
+
+struct ds278x_platform_data {
+	int rsns;
+};
+
+#endif
diff --git a/include/linux/pda_power.h b/include/linux/pda_power.h
index d4cf7a2ceb3e..c9e4d814ff77 100644
--- a/include/linux/pda_power.h
+++ b/include/linux/pda_power.h
@@ -24,6 +24,8 @@ struct pda_power_pdata {
 	int (*is_usb_online)(void);
 	void (*set_charge)(int flags);
 	void (*exit)(struct device *dev);
+	int (*suspend)(pm_message_t state);
+	int (*resume)(void);
 
 	char **supplied_to;
 	size_t num_supplicants;
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index ebd2b8fb00d0..30083a896f36 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -114,6 +114,7 @@ enum power_supply_property {
 	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
 	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
 	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+	POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
 	/* Properties of type `const char *' */
 	POWER_SUPPLY_PROP_MODEL_NAME,
 	POWER_SUPPLY_PROP_MANUFACTURER,
@@ -144,6 +145,11 @@ struct power_supply {
 	int (*get_property)(struct power_supply *psy,
 			    enum power_supply_property psp,
 			    union power_supply_propval *val);
+	int (*set_property)(struct power_supply *psy,
+			    enum power_supply_property psp,
+			    const union power_supply_propval *val);
+	int (*property_is_writeable)(struct power_supply *psy,
+				     enum power_supply_property psp);
 	void (*external_power_changed)(struct power_supply *psy);
 	void (*set_charged)(struct power_supply *psy);
 
diff --git a/include/linux/z2_battery.h b/include/linux/z2_battery.h
new file mode 100644
index 000000000000..7b9750404d22
--- /dev/null
+++ b/include/linux/z2_battery.h
@@ -0,0 +1,17 @@
+#ifndef _LINUX_Z2_BATTERY_H
+#define _LINUX_Z2_BATTERY_H
+
+struct z2_battery_info {
+	int	 batt_I2C_bus;
+	int	 batt_I2C_addr;
+	int	 batt_I2C_reg;
+	int	 charge_gpio;
+	int	 min_voltage;
+	int	 max_voltage;
+	int	 batt_div;
+	int	 batt_mult;
+	int	 batt_tech;
+	char	*batt_name;
+};
+
+#endif