public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/7] External power framework
@ 2007-04-20 11:57 Anton Vorontsov
  0 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-20 11:57 UTC (permalink / raw)
  To: linux-kernel; +Cc: kernel-discuss

External power framework - power supplies and power supplicants.

Supplicants (batteries so far) may ask to notify they when power supply
arrive/gone. This framework used by battery class (next patches).

It's permitted for supply to be bound to several supplicants (think main
and backup batteries).

It's also permitted for supplicants to consume power from several
external supplies (say AC and USB).

Here is how it look like from userspace:

        # pwd
        /sys/class/power_supply
        # ls
        ac  usb
        # cat ac/online usb/online
        1
        0


Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/Kconfig                |    2 +
 drivers/Makefile               |    1 +
 drivers/power/Kconfig          |   13 ++
 drivers/power/Makefile         |    1 +
 drivers/power/external_power.c |  354 ++++++++++++++++++++++++++++++++++++++++
 include/linux/external_power.h |   56 +++++++
 6 files changed, 427 insertions(+), 0 deletions(-)
 create mode 100644 drivers/power/Kconfig
 create mode 100644 drivers/power/Makefile
 create mode 100644 drivers/power/external_power.c
 create mode 100644 include/linux/external_power.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 050323f..c546de3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,6 +54,8 @@ source "drivers/spi/Kconfig"
 
 source "drivers/w1/Kconfig"
 
+source "drivers/power/Kconfig"
+
 source "drivers/hwmon/Kconfig"
 
 source "drivers/mfd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 3a718f5..2bdaae7 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_W1)		+= w1/
+obj-$(CONFIG_EXTERNAL_POWER)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
new file mode 100644
index 0000000..17349c1
--- /dev/null
+++ b/drivers/power/Kconfig
@@ -0,0 +1,13 @@
+
+menu "External power support"
+
+config EXTERNAL_POWER
+	tristate "External power kernel interface"
+	help
+	  Say Y here to enable kernel external power detection interface,
+	  like AC or USB. Information also will exported to userspace via
+	  /sys/class/external_power/ directory.
+
+	  This interface is mandatory for battery class support.
+
+endmenu
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
new file mode 100644
index 0000000..c303b45
--- /dev/null
+++ b/drivers/power/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_EXTERNAL_POWER)  += external_power.o
diff --git a/drivers/power/external_power.c b/drivers/power/external_power.c
new file mode 100644
index 0000000..8960681
--- /dev/null
+++ b/drivers/power/external_power.c
@@ -0,0 +1,354 @@
+/*
+ * Linux kernel interface for external power suppliers/supplicants
+ *
+ * Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *
+ * 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/device.h>
+#include <linux/rwsem.h>
+#include <linux/external_power.h>
+
+static struct class *power_supply_class;
+
+static LIST_HEAD(supplicants);
+static struct rw_semaphore supplicants_sem;
+
+struct bound_supply {
+	struct power_supply *psy;
+	struct list_head node;
+};
+
+struct bound_supplicant {
+	struct power_supplicant *pst;
+	struct list_head node;
+};
+
+int power_supplicant_am_i_supplied(struct power_supplicant *pst)
+{
+	int ret = 0;
+	struct bound_supply *bpsy;
+
+	pr_debug("%s\n", __FUNCTION__);
+	down(&power_supply_class->sem);
+	list_for_each_entry(bpsy, &pst->bound_supplies, node) {
+		if (bpsy->psy->is_online(bpsy->psy)) {
+			ret = 1;
+			goto out;
+		}
+	}
+out:
+	up(&power_supply_class->sem);
+
+	return ret;
+}
+
+static void unbind_pst_from_psys(struct power_supplicant *pst)
+{
+	struct bound_supply *bpsy, *bpsy_tmp;
+	struct bound_supplicant *bpst, *bpst_tmp;
+
+	list_for_each_entry_safe(bpsy, bpsy_tmp, &pst->bound_supplies, node) {
+		unsigned long flags;
+
+		write_lock_irqsave(&bpsy->psy->bpst_lock, flags);
+		list_for_each_entry_safe(bpst, bpst_tmp,
+		                &bpsy->psy->bound_supplicants, node) {
+			if (bpst->pst == pst) {
+				list_del(&bpst->node);
+				kfree(bpst);
+				break;
+			}
+		}
+		write_unlock_irqrestore(&bpsy->psy->bpst_lock, flags);
+
+		list_del(&bpsy->node);
+		kfree(bpsy);
+	}
+
+	return;
+}
+
+static void unbind_psy_from_psts(struct power_supply *psy)
+{
+	struct bound_supply *bpsy, *bpsy_tmp;
+	struct bound_supplicant *bpst, *bpst_tmp;
+	unsigned long flags;
+
+	write_lock_irqsave(&psy->bpst_lock, flags);
+	list_for_each_entry_safe(bpst, bpst_tmp, &psy->bound_supplicants,
+	                                                              node) {
+		list_for_each_entry_safe(bpsy, bpsy_tmp,
+		                &bpst->pst->bound_supplies, node) {
+			if (bpsy->psy == psy) {
+				list_del(&bpsy->node);
+				kfree(bpsy);
+				break;
+			}
+		}
+		list_del(&bpst->node);
+		kfree(bpst);
+	}
+	write_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	return;
+}
+
+static int bind_pst_to_psy(struct power_supplicant *pst,
+                           struct power_supply *psy)
+{
+	struct bound_supplicant *bpst = kmalloc(sizeof(*bpst), GFP_KERNEL);
+	unsigned long flags;
+
+	if (!bpst)
+		return -ENOMEM;
+
+	bpst->pst = pst;
+
+	write_lock_irqsave(&psy->bpst_lock, flags);
+	list_add_tail(&bpst->node, &psy->bound_supplicants);
+	write_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	pr_debug("power: bound pst %s to psy %s\n", pst->name, psy->name);
+
+	return 0;
+}
+
+static int bind_psy_to_pst(struct power_supply *psy,
+                           struct power_supplicant *pst)
+{
+	struct bound_supply *bpsy = kmalloc(sizeof(*bpsy), GFP_KERNEL);
+
+	if (!bpsy)
+		return -ENOMEM;
+
+	bpsy->psy = psy;
+	list_add_tail(&bpsy->node, &pst->bound_supplies);
+
+	pr_debug("power: bound psy %s to pst %s\n", psy->name, pst->name);
+
+	return 0;
+}
+
+int power_supplicant_register(struct power_supplicant *pst)
+{
+	int ret = 0;
+	size_t i;
+	struct device *dev;
+	struct power_supply *psy;
+
+	INIT_LIST_HEAD(&pst->bound_supplies);
+
+	down_write(&supplicants_sem);
+	down(&power_supply_class->sem);
+
+	list_for_each_entry(dev, &power_supply_class->devices, node) {
+		psy = dev_get_drvdata(dev);
+		for (i = 0; i < psy->num_supplicants; i++) {
+			if (!strcmp(pst->name, psy->supplied_to[i])) {
+				ret = bind_pst_to_psy(pst, psy);
+				if (ret)
+					goto binding_failed;
+				ret = bind_psy_to_pst(psy, pst);
+				if (ret)
+					goto binding_failed;
+				break;
+			}
+		}
+	}
+
+	list_add_tail(&pst->node, &supplicants);
+
+	goto succeed;
+
+binding_failed:
+	unbind_pst_from_psys(pst);
+succeed:
+	up(&power_supply_class->sem);
+	up_write(&supplicants_sem);
+
+	return ret;
+}
+
+void power_supplicant_unregister(struct power_supplicant *pst)
+{
+	down_write(&supplicants_sem);
+	list_del(&pst->node);
+	up_write(&supplicants_sem);
+
+	down(&power_supply_class->sem);
+	unbind_pst_from_psys(pst);
+	up(&power_supply_class->sem);
+	return;
+}
+
+void power_supply_changed(struct power_supply *psy)
+{
+	struct bound_supplicant *bpst;
+	unsigned long flags;
+
+	pr_debug("%s\n", __FUNCTION__);
+
+	read_lock_irqsave(&psy->bpst_lock, flags);
+	list_for_each_entry(bpst, &psy->bound_supplicants, node)
+		bpst->pst->power_supply_changed(bpst->pst, psy);
+	read_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	return;
+}
+
+static ssize_t power_supply_show_online(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", psy->is_online(psy));
+}
+
+static ssize_t power_supply_show_type(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	return sprintf(buf, "%s\n", psy->type ? psy->type : "unknown");
+}
+
+static ssize_t power_supply_show_nominal_voltage(struct device *dev,
+                                                 struct device_attribute *attr,
+                                                 char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	return sprintf(buf, "%d\n", psy->nominal_voltage);
+}
+
+static DEVICE_ATTR(online, 0444, power_supply_show_online, NULL);
+static DEVICE_ATTR(type, 0444, power_supply_show_type, NULL);
+static DEVICE_ATTR(nominal_voltage, 0444, power_supply_show_nominal_voltage,
+                   NULL);
+
+int power_supply_register(struct device *parent,
+                            struct power_supply *psy)
+{
+	int ret = 0;
+	struct power_supplicant *pst;
+	size_t i;
+
+	INIT_LIST_HEAD(&psy->bound_supplicants);
+	rwlock_init(&psy->bpst_lock);
+
+	psy->dev = device_create(power_supply_class, parent, 0, "%s",
+	                         psy->name);
+	if (IS_ERR(psy->dev)) {
+		ret = PTR_ERR(psy->dev);
+		goto dev_create_failed;
+	}
+
+	dev_set_drvdata(psy->dev, psy);
+
+	ret = device_create_file(psy->dev, &dev_attr_online);
+	if (ret)
+		goto create_online_failed;
+
+	ret = device_create_file(psy->dev, &dev_attr_type);
+	if (ret)
+		goto create_type_failed;
+
+	ret = device_create_file(psy->dev, &dev_attr_nominal_voltage);
+	if (ret)
+		goto create_nominal_voltage_failed;
+
+	down_write(&supplicants_sem);
+	list_for_each_entry(pst, &supplicants, node) {
+		for (i = 0; i < psy->num_supplicants; i++) {
+			if (!strcmp(pst->name, psy->supplied_to[i])) {
+				ret = bind_psy_to_pst(psy, pst);
+				if (ret)
+					goto binding_failed;
+				ret = bind_pst_to_psy(pst, psy);
+				if (ret)
+					goto binding_failed;
+				break;
+			}
+		}
+	}
+	up_write(&supplicants_sem);
+
+	/* notify supplicants that supply registred */
+	power_supply_changed(psy);
+
+	goto success;
+
+binding_failed:
+	unbind_psy_from_psts(psy);
+	device_remove_file(psy->dev, &dev_attr_nominal_voltage);
+create_nominal_voltage_failed:
+	device_remove_file(psy->dev, &dev_attr_type);
+create_type_failed:
+	device_remove_file(psy->dev, &dev_attr_online);
+create_online_failed:
+	device_unregister(psy->dev);
+dev_create_failed:
+success:
+	return ret;
+}
+
+void power_supply_unregister(struct power_supply *psy)
+{
+	down_write(&supplicants_sem);
+	unbind_psy_from_psts(psy);
+	up_write(&supplicants_sem);
+
+	device_remove_file(psy->dev, &dev_attr_nominal_voltage);
+	device_remove_file(psy->dev, &dev_attr_type);
+	device_remove_file(psy->dev, &dev_attr_online);
+
+	device_unregister(psy->dev);
+
+	return;
+}
+
+static int __init external_power_init(void)
+{
+	int ret = 0;
+
+	power_supply_class = class_create(THIS_MODULE, "power_supply");
+	if (IS_ERR(power_supply_class)) {
+		printk(KERN_ERR "external_power: failed to create "
+		       "power_supply class\n");
+		ret = PTR_ERR(power_supply_class);
+		goto class_create_failed;
+	}
+
+	init_rwsem(&supplicants_sem);
+
+	goto success;
+
+class_create_failed:
+success:
+	return ret;
+}
+
+static void __exit external_power_exit(void)
+{
+	class_destroy(power_supply_class);
+	return;
+}
+
+EXPORT_SYMBOL_GPL(power_supplicant_am_i_supplied);
+EXPORT_SYMBOL_GPL(power_supplicant_register);
+EXPORT_SYMBOL_GPL(power_supplicant_unregister);
+
+EXPORT_SYMBOL_GPL(power_supply_changed);
+EXPORT_SYMBOL_GPL(power_supply_register);
+EXPORT_SYMBOL_GPL(power_supply_unregister);
+
+subsys_initcall(external_power_init);
+module_exit(external_power_exit);
+
+MODULE_DESCRIPTION("Linux kernel interface for external power");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
diff --git a/include/linux/external_power.h b/include/linux/external_power.h
new file mode 100644
index 0000000..b635ff0
--- /dev/null
+++ b/include/linux/external_power.h
@@ -0,0 +1,56 @@
+/*
+ * Linux kernel interface for external power suppliers/supplicants
+ *
+ * Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *
+ * 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.
+ */
+
+#ifndef __EXTERNAL_POWER_H__
+#define __EXTERNAL_POWER_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+/* kernel interface for external power suppliers, like AC or USB */
+
+struct power_supply {
+	char *name;
+	char *type;
+	int nominal_voltage; /* units of uV */
+	int (*is_online)(struct power_supply *psy);
+	char **supplied_to;
+	size_t num_supplicants;
+
+	/* private */
+	struct list_head bound_supplicants;
+	rwlock_t bpst_lock;
+	struct device *dev;
+};
+
+extern void power_supply_changed(struct power_supply *psy);
+extern int power_supply_register(struct device *parent,
+                                 struct power_supply *psy);
+extern void power_supply_unregister(struct power_supply *psy);
+
+/* kernel interface for external power supplicants (batteries so far) */
+
+struct power_supplicant {
+	char *name;
+	/* used to notify supplicant about external power arrival/outage,
+	 * do not sleep there, this is called from irq, most probably */
+	void (*power_supply_changed)(struct power_supplicant *pst,
+	                             struct power_supply *psy);
+
+	/* private */
+	struct list_head bound_supplies;
+	struct list_head node;
+};
+
+extern int power_supplicant_am_i_supplied(struct power_supplicant *pst);
+extern int power_supplicant_register(struct power_supplicant *pst);
+extern void power_supplicant_unregister(struct power_supplicant *pst);
+
+#endif /* __EXTERNAL_POWER_H__ */
-- 
1.5.1.1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 0/7] Battery class, external power framework, ds2760 battery
@ 2007-04-25 15:48 Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 1/7] External power framework Anton Vorontsov
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:48 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

Hi all,

I believe most battery issues elaborated and settled, and battery
class has grown up for the -mm merge. Also, I hope that current
battery stuff will make everyone happy at some grade.

This patchset accumulated ideas and suggestions from reviews by
David Brownell, Evgeniy Polyakov, Greg KH, Henrique de Moraes Holschuh,
Ian Molton, Matt Reimer, Matthew Garrett, Ondrej Zajicek,
Paul Sokolovsky, Pavel Machek, Randy Dunlap, Russell King and
Shem Multinymous.

Thanks for the time you spent reviewing these patches!

On Tue, Apr 24, 2007 at 10:41:08PM +0200, Pavel Machek wrote:
> > > > Signed-off-by: Anton Vorontsov <cbou@mail.ru>
> > >
> > > Yes please. Generic battery support is badly needed.
> >
> > I'm glad you like it, thanks for the review!
> >
> > Also I've done some code split. It removes all #ifdefs from battery.c,
> > and also separates core from sysfs and leds.
> >
> > If there will no objections, I'll send this version when patch window
> > will opened.
>
> Actually, you probably want to add some changelogs, and send it to
> akpm _now_. He'll merge it to -mm tree, and hopefully send it upstream
> in the next window. Merging directly to linus is for bigger projects
> with git trees etc.

Andrew, would you please merge these patches into -mm? They're against
today's Linus git tree.

I hope it isn't too late for the merge. If it is, please tell me when I
should come back.

References:
http://lkml.org/lkml/2007/4/11/392
http://lkml.org/lkml/2007/4/11/393
http://lkml.org/lkml/2007/4/11/397
http://lkml.org/lkml/2007/4/11/398
http://lkml.org/lkml/2007/4/20/154

Thanks,

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.org/bd2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/7] External power framework
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
@ 2007-04-25 15:50 ` Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 2/7] pda power driver Anton Vorontsov
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:50 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

Power supplies and power supplicants.

Supplicants (batteries so far) may ask to notify they when power supply
arrive/gone. This framework used by battery class.

It's permitted for supply to be bound to several supplicants (think main
and backup batteries).

It's also permitted for supplicants to consume power from several
external supplies (say AC and USB).

Here is how it look like from userspace:

        # pwd
        /sys/class/power_supply
        # ls
        ac  usb
        # cat ac/online usb/online
        1
        0

Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/Kconfig                |    2 +
 drivers/Makefile               |    1 +
 drivers/power/Kconfig          |   13 ++
 drivers/power/Makefile         |    1 +
 drivers/power/external_power.c |  361 ++++++++++++++++++++++++++++++++++++++++
 include/linux/external_power.h |   56 ++++++
 6 files changed, 434 insertions(+), 0 deletions(-)
 create mode 100644 drivers/power/Kconfig
 create mode 100644 drivers/power/Makefile
 create mode 100644 drivers/power/external_power.c
 create mode 100644 include/linux/external_power.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 050323f..c546de3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,6 +54,8 @@ source "drivers/spi/Kconfig"
 
 source "drivers/w1/Kconfig"
 
+source "drivers/power/Kconfig"
+
 source "drivers/hwmon/Kconfig"
 
 source "drivers/mfd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 3a718f5..2bdaae7 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_W1)		+= w1/
+obj-$(CONFIG_EXTERNAL_POWER)	+= power/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
new file mode 100644
index 0000000..17349c1
--- /dev/null
+++ b/drivers/power/Kconfig
@@ -0,0 +1,13 @@
+
+menu "External power support"
+
+config EXTERNAL_POWER
+	tristate "External power kernel interface"
+	help
+	  Say Y here to enable kernel external power detection interface,
+	  like AC or USB. Information also will exported to userspace via
+	  /sys/class/external_power/ directory.
+
+	  This interface is mandatory for battery class support.
+
+endmenu
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
new file mode 100644
index 0000000..c303b45
--- /dev/null
+++ b/drivers/power/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_EXTERNAL_POWER)  += external_power.o
diff --git a/drivers/power/external_power.c b/drivers/power/external_power.c
new file mode 100644
index 0000000..94f919a
--- /dev/null
+++ b/drivers/power/external_power.c
@@ -0,0 +1,361 @@
+/*
+ * Linux kernel interface for external power suppliers/supplicants
+ *
+ * Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *
+ * 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/device.h>
+#include <linux/rwsem.h>
+#include <linux/external_power.h>
+
+static struct class *power_supply_class;
+
+static LIST_HEAD(supplicants);
+static struct rw_semaphore supplicants_sem;
+
+struct bound_supply {
+	struct power_supply *psy;
+	struct list_head node;
+};
+
+struct bound_supplicant {
+	struct power_supplicant *pst;
+	struct list_head node;
+};
+
+int power_supplicant_am_i_supplied(struct power_supplicant *pst)
+{
+	int ret = 0;
+	struct bound_supply *bpsy;
+
+	down(&power_supply_class->sem);
+	list_for_each_entry(bpsy, &pst->bound_supplies, node) {
+		if (bpsy->psy->is_online(bpsy->psy)) {
+			ret = 1;
+			goto out;
+		}
+	}
+out:
+	up(&power_supply_class->sem);
+
+	pr_debug("%s: %s %d\n", __FUNCTION__, pst->name, ret);
+
+	return ret;
+}
+
+static void unbind_pst_from_psys(struct power_supplicant *pst)
+{
+	struct bound_supply *bpsy, *bpsy_tmp;
+	struct bound_supplicant *bpst, *bpst_tmp;
+
+	list_for_each_entry_safe(bpsy, bpsy_tmp, &pst->bound_supplies, node) {
+		unsigned long flags;
+
+		write_lock_irqsave(&bpsy->psy->bpst_lock, flags);
+		list_for_each_entry_safe(bpst, bpst_tmp,
+		                &bpsy->psy->bound_supplicants, node) {
+			if (bpst->pst == pst) {
+				list_del(&bpst->node);
+				kfree(bpst);
+				break;
+			}
+		}
+		write_unlock_irqrestore(&bpsy->psy->bpst_lock, flags);
+
+		list_del(&bpsy->node);
+		kfree(bpsy);
+	}
+
+	return;
+}
+
+static void unbind_psy_from_psts(struct power_supply *psy)
+{
+	struct bound_supply *bpsy, *bpsy_tmp;
+	struct bound_supplicant *bpst, *bpst_tmp;
+	unsigned long flags;
+
+	write_lock_irqsave(&psy->bpst_lock, flags);
+	list_for_each_entry_safe(bpst, bpst_tmp, &psy->bound_supplicants,
+	                                                              node) {
+		list_for_each_entry_safe(bpsy, bpsy_tmp,
+		                &bpst->pst->bound_supplies, node) {
+			if (bpsy->psy == psy) {
+				list_del(&bpsy->node);
+				kfree(bpsy);
+				break;
+			}
+		}
+		list_del(&bpst->node);
+		kfree(bpst);
+	}
+	write_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	return;
+}
+
+static int bind_pst_to_psy(struct power_supplicant *pst,
+                           struct power_supply *psy)
+{
+	struct bound_supplicant *bpst = kmalloc(sizeof(*bpst), GFP_KERNEL);
+	unsigned long flags;
+
+	if (!bpst)
+		return -ENOMEM;
+
+	bpst->pst = pst;
+
+	write_lock_irqsave(&psy->bpst_lock, flags);
+	list_add_tail(&bpst->node, &psy->bound_supplicants);
+	write_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	pr_debug("external power: bound pst %s to psy %s\n",
+	         pst->name, psy->name);
+
+	return 0;
+}
+
+static int bind_psy_to_pst(struct power_supply *psy,
+                           struct power_supplicant *pst)
+{
+	struct bound_supply *bpsy = kmalloc(sizeof(*bpsy), GFP_KERNEL);
+
+	if (!bpsy)
+		return -ENOMEM;
+
+	bpsy->psy = psy;
+	list_add_tail(&bpsy->node, &pst->bound_supplies);
+
+	pr_debug("external power: bound psy %s to pst %s\n",
+	         psy->name, pst->name);
+
+	return 0;
+}
+
+int power_supplicant_register(struct power_supplicant *pst)
+{
+	int ret = 0;
+	size_t i;
+	struct device *dev;
+	struct power_supply *psy;
+
+	INIT_LIST_HEAD(&pst->bound_supplies);
+
+	down_write(&supplicants_sem);
+	down(&power_supply_class->sem);
+
+	list_for_each_entry(dev, &power_supply_class->devices, node) {
+		psy = dev_get_drvdata(dev);
+		for (i = 0; i < psy->num_supplicants; i++) {
+			if (!strcmp(pst->name, psy->supplied_to[i])) {
+				ret = bind_pst_to_psy(pst, psy);
+				if (ret)
+					goto binding_failed;
+				ret = bind_psy_to_pst(psy, pst);
+				if (ret)
+					goto binding_failed;
+				break;
+			}
+		}
+	}
+
+	list_add_tail(&pst->node, &supplicants);
+
+	goto succeed;
+
+binding_failed:
+	unbind_pst_from_psys(pst);
+succeed:
+	up(&power_supply_class->sem);
+	up_write(&supplicants_sem);
+
+	return ret;
+}
+
+void power_supplicant_unregister(struct power_supplicant *pst)
+{
+	down_write(&supplicants_sem);
+	list_del(&pst->node);
+	up_write(&supplicants_sem);
+
+	down(&power_supply_class->sem);
+	unbind_pst_from_psys(pst);
+	up(&power_supply_class->sem);
+
+	return;
+}
+
+void power_supply_changed(struct power_supply *psy)
+{
+	struct bound_supplicant *bpst;
+	unsigned long flags;
+
+	dev_dbg(psy->dev, "%s\n", __FUNCTION__);
+
+	read_lock_irqsave(&psy->bpst_lock, flags);
+	list_for_each_entry(bpst, &psy->bound_supplicants, node)
+		bpst->pst->power_supply_changed(bpst->pst, psy);
+	read_unlock_irqrestore(&psy->bpst_lock, flags);
+
+	return;
+}
+
+static ssize_t power_supply_show_online(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", psy->is_online(psy));
+}
+
+static ssize_t power_supply_show_type(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", psy->type ? psy->type : "unknown");
+}
+
+static ssize_t power_supply_show_nominal_voltage(struct device *dev,
+                                                 struct device_attribute *attr,
+                                                 char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", psy->nominal_voltage);
+}
+
+static DEVICE_ATTR(online, 0444, power_supply_show_online, NULL);
+static DEVICE_ATTR(type, 0444, power_supply_show_type, NULL);
+static DEVICE_ATTR(nominal_voltage, 0444, power_supply_show_nominal_voltage,
+                   NULL);
+
+int power_supply_register(struct device *parent,
+                            struct power_supply *psy)
+{
+	int ret = 0;
+	struct power_supplicant *pst;
+	size_t i;
+
+	INIT_LIST_HEAD(&psy->bound_supplicants);
+	rwlock_init(&psy->bpst_lock);
+
+	psy->dev = device_create(power_supply_class, parent, 0, "%s",
+	                         psy->name);
+	if (IS_ERR(psy->dev)) {
+		ret = PTR_ERR(psy->dev);
+		goto dev_create_failed;
+	}
+
+	dev_set_drvdata(psy->dev, psy);
+
+	ret = device_create_file(psy->dev, &dev_attr_online);
+	if (ret)
+		goto create_online_failed;
+
+	ret = device_create_file(psy->dev, &dev_attr_type);
+	if (ret)
+		goto create_type_failed;
+
+	ret = device_create_file(psy->dev, &dev_attr_nominal_voltage);
+	if (ret)
+		goto create_nominal_voltage_failed;
+
+	down_write(&supplicants_sem);
+	list_for_each_entry(pst, &supplicants, node) {
+		for (i = 0; i < psy->num_supplicants; i++) {
+			if (!strcmp(pst->name, psy->supplied_to[i])) {
+				ret = bind_psy_to_pst(psy, pst);
+				if (ret)
+					goto binding_failed;
+				ret = bind_pst_to_psy(pst, psy);
+				if (ret)
+					goto binding_failed;
+				break;
+			}
+		}
+	}
+	up_write(&supplicants_sem);
+
+	/* notify supplicants that supply registred */
+	power_supply_changed(psy);
+
+	goto success;
+
+binding_failed:
+	unbind_psy_from_psts(psy);
+	device_remove_file(psy->dev, &dev_attr_nominal_voltage);
+create_nominal_voltage_failed:
+	device_remove_file(psy->dev, &dev_attr_type);
+create_type_failed:
+	device_remove_file(psy->dev, &dev_attr_online);
+create_online_failed:
+	device_unregister(psy->dev);
+dev_create_failed:
+success:
+	return ret;
+}
+
+void power_supply_unregister(struct power_supply *psy)
+{
+	down_write(&supplicants_sem);
+	unbind_psy_from_psts(psy);
+	up_write(&supplicants_sem);
+
+	device_remove_file(psy->dev, &dev_attr_nominal_voltage);
+	device_remove_file(psy->dev, &dev_attr_type);
+	device_remove_file(psy->dev, &dev_attr_online);
+
+	device_unregister(psy->dev);
+
+	return;
+}
+
+static int __init external_power_init(void)
+{
+	int ret = 0;
+
+	power_supply_class = class_create(THIS_MODULE, "power_supply");
+	if (IS_ERR(power_supply_class)) {
+		printk(KERN_ERR "external_power: failed to create "
+		       "power_supply class\n");
+		ret = PTR_ERR(power_supply_class);
+		goto class_create_failed;
+	}
+
+	init_rwsem(&supplicants_sem);
+
+	goto success;
+
+class_create_failed:
+success:
+	return ret;
+}
+
+static void __exit external_power_exit(void)
+{
+	class_destroy(power_supply_class);
+	return;
+}
+
+EXPORT_SYMBOL_GPL(power_supplicant_am_i_supplied);
+EXPORT_SYMBOL_GPL(power_supplicant_register);
+EXPORT_SYMBOL_GPL(power_supplicant_unregister);
+
+EXPORT_SYMBOL_GPL(power_supply_changed);
+EXPORT_SYMBOL_GPL(power_supply_register);
+EXPORT_SYMBOL_GPL(power_supply_unregister);
+
+subsys_initcall(external_power_init);
+module_exit(external_power_exit);
+
+MODULE_DESCRIPTION("Linux kernel interface for external power");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
diff --git a/include/linux/external_power.h b/include/linux/external_power.h
new file mode 100644
index 0000000..b635ff0
--- /dev/null
+++ b/include/linux/external_power.h
@@ -0,0 +1,56 @@
+/*
+ * Linux kernel interface for external power suppliers/supplicants
+ *
+ * Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *
+ * 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.
+ */
+
+#ifndef __EXTERNAL_POWER_H__
+#define __EXTERNAL_POWER_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+/* kernel interface for external power suppliers, like AC or USB */
+
+struct power_supply {
+	char *name;
+	char *type;
+	int nominal_voltage; /* units of uV */
+	int (*is_online)(struct power_supply *psy);
+	char **supplied_to;
+	size_t num_supplicants;
+
+	/* private */
+	struct list_head bound_supplicants;
+	rwlock_t bpst_lock;
+	struct device *dev;
+};
+
+extern void power_supply_changed(struct power_supply *psy);
+extern int power_supply_register(struct device *parent,
+                                 struct power_supply *psy);
+extern void power_supply_unregister(struct power_supply *psy);
+
+/* kernel interface for external power supplicants (batteries so far) */
+
+struct power_supplicant {
+	char *name;
+	/* used to notify supplicant about external power arrival/outage,
+	 * do not sleep there, this is called from irq, most probably */
+	void (*power_supply_changed)(struct power_supplicant *pst,
+	                             struct power_supply *psy);
+
+	/* private */
+	struct list_head bound_supplies;
+	struct list_head node;
+};
+
+extern int power_supplicant_am_i_supplied(struct power_supplicant *pst);
+extern int power_supplicant_register(struct power_supplicant *pst);
+extern void power_supplicant_unregister(struct power_supplicant *pst);
+
+#endif /* __EXTERNAL_POWER_H__ */
-- 
1.5.1.1-dirty


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/7] pda power driver
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 1/7] External power framework Anton Vorontsov
@ 2007-04-25 15:50 ` Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 3/7] Universal battery class Anton Vorontsov
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:50 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

Common power driver for PDAs and phones with one or two external
power supplies (AC/USB) connected to main and backup batteries,
and optional builtin charger.

It's used to stop logic duplication through different embedded
devices. So, power supply *logic* is here. pda_power register
power supplies, and will take care about notifying batteries
about power changes through external power interface.

AC/USB power consumption issues should be handled by platform
code, inside set_charge function.

Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/power/Kconfig     |    8 ++
 drivers/power/Makefile    |    1 +
 drivers/power/pda_power.c |  239 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pda_power.h |   28 +++++
 4 files changed, 276 insertions(+), 0 deletions(-)
 create mode 100644 drivers/power/pda_power.c
 create mode 100644 include/linux/pda_power.h

diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 17349c1..b87779e 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -10,4 +10,12 @@ config EXTERNAL_POWER
 
 	  This interface is mandatory for battery class support.
 
+config PDA_POWER
+	tristate "Generic PDA/phone power driver"
+	depends on EXTERNAL_POWER
+	help
+	  Say Y here to enable generic power driver for PDAs and phones with
+	  one or two external power supplies (AC/USB) connected to main and
+	  backup batteries, and optional builtin charger.
+
 endmenu
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index c303b45..6f084e7 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_EXTERNAL_POWER)  += external_power.o
+obj-$(CONFIG_PDA_POWER)       += pda_power.o
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
new file mode 100644
index 0000000..2a22687
--- /dev/null
+++ b/drivers/power/pda_power.c
@@ -0,0 +1,239 @@
+/*
+ * Common power driver for PDAs and phones with one or two external
+ * power supplies (AC/USB) connected to main and backup batteries,
+ * and optional builtin charger.
+ *
+ * Copyright 2007 Anton Vorontsov <cbou@mail.ru>
+ *
+ * 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/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/external_power.h>
+#include <linux/pda_power.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+
+static inline unsigned int get_irq_flags(struct resource *res)
+{
+	unsigned int flags = IRQF_DISABLED | IRQF_SHARED;
+
+	flags |= res->flags & IRQF_TRIGGER_MASK;
+
+	return flags;
+}
+
+static struct device *dev;
+static struct pda_power_pdata *pdata;
+static struct resource *ac_irq, *usb_irq;
+static struct timer_list charger_timer;
+static struct timer_list supply_timer;
+
+static int pda_power_is_ac_online(struct power_supply *psy)
+{
+	return pdata->is_ac_online ? pdata->is_ac_online() : 0;
+}
+
+static int pda_power_is_usb_online(struct power_supply *psy)
+{
+	return pdata->is_usb_online ? pdata->is_usb_online() : 0;
+}
+
+static char *pda_power_supplied_to[] = {
+	"main-battery",
+	"backup-battery",
+};
+
+static struct power_supply pda_power_supplies[] = {
+	{
+		.name = "ac",
+		.type = "ac",
+		.supplied_to = pda_power_supplied_to,
+		.num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
+		.is_online = pda_power_is_ac_online,
+	},
+	{
+		.name = "usb",
+		.type = "dc",
+		.supplied_to = pda_power_supplied_to,
+		.num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
+		.is_online = pda_power_is_usb_online,
+	},
+};
+
+static void update_charger(void)
+{
+	if (!pdata->set_charge)
+		return;
+
+	if (pdata->is_ac_online && pdata->is_ac_online()) {
+		dev_dbg(dev, "charger on (AC)\n");
+		pdata->set_charge(PDA_POWER_CHARGE_AC);
+	}
+	else if (pdata->is_usb_online && pdata->is_usb_online()) {
+		dev_dbg(dev, "charger on (USB)\n");
+		pdata->set_charge(PDA_POWER_CHARGE_USB);
+	}
+	else {
+		dev_dbg(dev, "charger off\n");
+		pdata->set_charge(0);
+	}
+
+	return;
+}
+
+static void supply_timer_func(unsigned long irq)
+{
+	if (ac_irq && irq == ac_irq->start)
+		power_supply_changed(&pda_power_supplies[0]);
+	else if (usb_irq && irq == usb_irq->start)
+		power_supply_changed(&pda_power_supplies[1]);
+	return;
+}
+
+static void charger_timer_func(unsigned long irq)
+{
+	update_charger();
+
+	/* Okay, charger set. Now wait a bit before notifying supplicants,
+	 * charge power should stabilize. */
+	supply_timer.data = irq;
+	mod_timer(&supply_timer,
+	          jiffies + msecs_to_jiffies(pdata->wait_for_charger));
+	return;
+}
+
+static irqreturn_t power_changed_isr(int irq, void *unused)
+{
+	/* Wait a bit before reading ac/usb line status and setting charger,
+	 * because ac/usb status readings may lag from irq. */
+	charger_timer.data = irq;
+	mod_timer(&charger_timer,
+	          jiffies + msecs_to_jiffies(pdata->wait_for_status));
+	return IRQ_HANDLED;
+}
+
+static int pda_power_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	dev = &pdev->dev;
+
+	if (pdev->id != -1) {
+		dev_err(dev, "it's meaningless to register several "
+		        "pda_powers, use id = -1\n");
+		ret = -EINVAL;
+		goto wrongid;
+	}
+
+	pdata = pdev->dev.platform_data;
+
+	update_charger();
+
+	if (!pdata->wait_for_status)
+		pdata->wait_for_status = 500;
+
+	if (!pdata->wait_for_charger)
+		pdata->wait_for_status = 500;
+
+	setup_timer(&charger_timer, charger_timer_func, 0);
+	setup_timer(&supply_timer, supply_timer_func, 0);
+
+	ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
+	usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
+	if (!ac_irq && !usb_irq) {
+		dev_err(dev, "no ac/usb irq specified\n");
+		ret = -ENODEV;
+		goto noirqs;
+	}
+
+	ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
+	if (ret) {
+		dev_err(dev, "failed to register %s power supply\n",
+		        pda_power_supplies[0].name);
+		goto supply0_failed;
+	}
+
+	ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
+	if (ret) {
+		dev_err(dev, "failed to register %s power supply\n",
+		        pda_power_supplies[1].name);
+		goto supply1_failed;
+	}
+
+	if (ac_irq) {
+		ret = request_irq(ac_irq->start, power_changed_isr,
+		                  get_irq_flags(ac_irq), ac_irq->name,
+		                  &pda_power_supplies[0]);
+		if (ret) {
+			dev_err(dev, "request ac irq failed\n");
+			goto ac_irq_failed;
+		}
+	}
+
+	if (usb_irq) {
+		ret = request_irq(usb_irq->start, power_changed_isr,
+		                  get_irq_flags(usb_irq), usb_irq->name,
+		                  &pda_power_supplies[1]);
+		if (ret) {
+			dev_err(dev, "request usb irq failed\n");
+			goto usb_irq_failed;
+		}
+	}
+
+	goto success;
+
+usb_irq_failed:
+	if (ac_irq)
+		free_irq(ac_irq->start, &pda_power_supplies[0]);
+ac_irq_failed:
+	power_supply_unregister(&pda_power_supplies[1]);
+supply1_failed:
+	power_supply_unregister(&pda_power_supplies[0]);
+supply0_failed:
+noirqs:
+wrongid:
+success:
+	return ret;
+}
+
+static int pda_power_remove(struct platform_device *pdev)
+{
+	if (usb_irq)
+		free_irq(usb_irq->start, &pda_power_supplies[1]);
+	if (ac_irq)
+		free_irq(ac_irq->start, &pda_power_supplies[0]);
+	del_timer_sync(&charger_timer);
+	del_timer_sync(&supply_timer);
+	power_supply_unregister(&pda_power_supplies[1]);
+	power_supply_unregister(&pda_power_supplies[0]);
+	return 0;
+}
+
+static struct platform_driver pda_power_pdrv = {
+	.driver = {
+		.name = "pda-power",
+	},
+	.probe = pda_power_probe,
+	.remove = pda_power_remove,
+};
+
+static int __init pda_power_init(void)
+{
+	return platform_driver_register(&pda_power_pdrv);
+}
+
+static void __exit pda_power_exit(void)
+{
+	platform_driver_unregister(&pda_power_pdrv);
+	return;
+}
+
+module_init(pda_power_init);
+module_exit(pda_power_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
diff --git a/include/linux/pda_power.h b/include/linux/pda_power.h
new file mode 100644
index 0000000..77e673c
--- /dev/null
+++ b/include/linux/pda_power.h
@@ -0,0 +1,28 @@
+/*
+ * Common power driver for PDAs and phones with one or two external
+ * power supplies (AC/USB) connected to main and backup batteries,
+ * and optional builtin charger.
+ *
+ * Copyright 2007 Anton Vorontsov <cbou@mail.ru>
+ *
+ * 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.
+ */
+
+#ifndef __PDA_POWER_H__
+#define __PDA_POWER_H__
+
+#define PDA_POWER_CHARGE_AC  (1 << 0)
+#define PDA_POWER_CHARGE_USB (1 << 1)
+
+struct pda_power_pdata {
+	int (*is_ac_online)(void);
+	int (*is_usb_online)(void);
+	void (*set_charge)(int flags);
+
+	unsigned int wait_for_status; /* msecs, default is 500 */
+	unsigned int wait_for_charger; /* msecs, default is 500 */
+};
+
+#endif /* __PDA_POWER_H__ */
-- 
1.5.1.1-dirty


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/7] Universal battery class
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 1/7] External power framework Anton Vorontsov
  2007-04-25 15:50 ` [PATCH 2/7] pda power driver Anton Vorontsov
@ 2007-04-25 15:50 ` Anton Vorontsov
  2007-04-25 15:51 ` [PATCH 4/7] APM emulation driver for class batteries Anton Vorontsov
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:50 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

Battery class used to export battery properties to userspace in consistent
manner.

It defines core set of battery attributes, available via sysfs, which
should be applicable to (almost) every battery out there. Each attribute
has well defined meaning, up to unit of measure used. While the attributes
provided are believed to be universally applicable to any battery,
specific monitoring hardware may not be able to provide them all, so
any of them may be skipped.

Battery class is extensible, and allows to define drivers own attributes.
The core attribute set is subject to the standard Linux evolution (i.e.
if it will be found that some attribute is applicable to many batteries
or their drivers, it can be added to the core set).

Battery class integrates with External Power framework, for the purpose of
notification battery drivers when charging power is available. Note that
specific charge control is left to the battery drivers.

It also integrates with LED framework, for the purpose of providing
typically expected (at least for portable devices) feedback of battery
status (charging/fully charged) via LEDs. (Note that specific details of
the indication (including whether to use it at all) are fully controllable
by user and/or specific machine defaults, per design principles of LED
framework).

Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 Documentation/battery-class.txt |  150 +++++++++++++++++++++++++++++++++++++++
 drivers/Kconfig                 |    2 +
 drivers/Makefile                |    1 +
 drivers/battery/Kconfig         |   12 +++
 drivers/battery/Makefile        |    9 +++
 drivers/battery/battery.c       |  138 +++++++++++++++++++++++++++++++++++
 drivers/battery/battery.h       |   37 ++++++++++
 drivers/battery/battery_leds.c  |  106 +++++++++++++++++++++++++++
 drivers/battery/battery_sysfs.c |  122 +++++++++++++++++++++++++++++++
 include/linux/battery.h         |  127 +++++++++++++++++++++++++++++++++
 10 files changed, 704 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/battery-class.txt
 create mode 100644 drivers/battery/Kconfig
 create mode 100644 drivers/battery/Makefile
 create mode 100644 drivers/battery/battery.c
 create mode 100644 drivers/battery/battery.h
 create mode 100644 drivers/battery/battery_leds.c
 create mode 100644 drivers/battery/battery_sysfs.c
 create mode 100644 include/linux/battery.h

diff --git a/Documentation/battery-class.txt b/Documentation/battery-class.txt
new file mode 100644
index 0000000..6a7d591
--- /dev/null
+++ b/Documentation/battery-class.txt
@@ -0,0 +1,150 @@
+Linux battery class
+===================
+
+Synopsis
+~~~~~~~~
+Battery class used to export battery properties to userspace in consistent
+manner.
+
+It defines core set of battery attributes, available via sysfs, which
+should be applicable to (almost) every battery out there. Each attribute
+has well defined meaning, up to unit of measure used. While the attributes
+provided are believed to be universally applicable to any battery,
+specific monitoring hardware may not be able to provide them all, so
+any of them may be skipped.
+
+Battery class is extensible, and allows to define drivers own attributes.
+The core attribute set is subject to the standard Linux evolution (i.e.
+if it will be found that some attribute is applicable to many batteries
+or their drivers, it can be added to the core set).
+
+Battery class integrates with External Power framework, for the purpose of
+notification battery drivers when charging power is available. Note that
+specific charge control is left to the battery drivers.
+
+It also integrates with LED framework, for the purpose of providing
+typically expected (at least for portable devices) feedback of battery
+status (charging/fully charged) via LEDs. (Note that specific details of
+the indication (including whether to use it at all) are fully controllable
+by user and/or specific machine defaults, per design principles of LED
+framework).
+
+
+Attributes/properties
+~~~~~~~~~~~~~~~~~~~~~
+Battery class has predefined set of attributes, this eliminates code
+duplication across battery drivers. Battery class insist on reusing its
+predefined attributes *and* their units.
+
+So, userspace gets expected set of attributes and their units for
+any kind of battery, and can process/present them to a user in consistent
+manner. Results for different batteries and machines are also directly
+comparable.
+
+See drivers/battery/ds2760_battery.c for the example how to declare and
+handle attributes.
+
+
+Units
+~~~~~
+Quoting include/linux/battery.h:
+
+  All voltages, currents, charges, energies, time and temperatures in uV,
+  uA, uAh, uWh, seconds and tenths of degree Celsius unless otherwise
+  stated. It's driver's job to convert its raw values to units in which
+  this class operates.
+
+
+Attributes/properties detailed
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+~ ~ ~ ~ ~ ~ ~  Charge/Energy/Capacity - how to not confuse  ~ ~ ~ ~ ~ ~ ~
+~                                                                       ~
+~ Because both "charge" (uAh) and "energy" (uWh) represents "capacity"  ~
+~ of battery, battery class distinguish these terms. Don't mix them!    ~
+~                                                                       ~
+~ CHARGE_* attributes represents capacity in uAh only.                  ~
+~ ENERGY_* attributes represents capacity in uWh only.                  ~
+~ CAPACITY attribute represents capacity in *percents*, from 0 to 100.  ~
+~                                                                       ~
+~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+Postfixes:
+_AVG - *hardware* averaged value, use it if your hardware is really able to
+report averaged values.
+_NOW - momentary/instantaneous values.
+
+STATUS - this attribute represents operating status (charging, full,
+discharging (i.e. powering a load), etc.). This corresponds to
+BATTERY_STATUS_* values, as defined in battery.h.
+
+HEALTH - represents health of the battery, values corresponds to
+BATTERY_HEALTH_*, defined in battery.h.
+
+VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and
+minimal battery voltages. Maximal/minimal means values of voltages when
+battery considered "full"/"empty" at normal conditions. Yes, there is
+no direct relation between voltage and battery capacity, but some dumb
+batteries use voltage for very approximated calculation of capacity.
+Battery driver also can use this attribute just to inform userspace
+about maximal and minimal voltage thresholds of a given battery.
+
+CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
+battery considered full/empty.
+
+ENERGY_FULL_DESIGN, ENERGY_EMPTY_DESIGN - same as above but for energy.
+
+CHARGE_FULL, CHARGE_EMPTY - These attributes means "last remembered value
+of charge when battery became full/empty". It also could mean "value of
+charge when battery considered full/empty at given conditions (temperature,
+age)". I.e. these attributes represents real thresholds, not design values.
+
+ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
+
+CAPACITY - capacity in percents.
+
+TEMP - temperature of the battery.
+
+TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
+while battery powers a load)
+TIME_TO_FULL - seconds left for battery to be considered full (i.e.
+while battery is charging)
+
+
+QA
+~~
+Q: Where is BATTERY_PROP_XYZ attribute?
+A: If you cannot find attribute suitable for your battery needs, feel free
+   to add it and send patch along with your battery driver.
+
+   The attributes available currently are the ones currently provided by the
+   drivers written.
+
+   Good candidates to add in future: battery technology, model/part#,
+   cycle_time, manufacturer, etc.
+
+
+Q: I have some very specific attribute (e.g. battery color), should I add
+   this attribute to standard ones?
+A: Most likely, no. Such attribute can be placed in the driver itself, if
+   it is useful. Of course, if the attribute in question applicable to
+   large set of batteries, provided by many drivers, and/or comes from
+   some general battery specification/standard, it may be a candidate to
+   be added to the core attribute set.
+
+
+Q: Suppose, my battery monitoring chip/firmware does not provides capacity
+   in percents, but provides charge_{now,full,empty}. Should I calculate
+   percentage capacity manually, inside the driver, and register CAPACITY
+   attribute? The same question about time_to_empty/time_to_full.
+A: Most likely, no. Battery class is designed to export battery properties
+   which are directly measurable by the specific hardware available.
+   Inferring not available properties using some heuristics or mathematical
+   model is not subject of work for a battery driver. Such functionality
+   should be factored out, and in fact, apm_power, the driver to serve
+   legacy APM API on top of battery class, uses a simple heuristic of
+   approximating remaining battery capacity based on its charge, current,
+   voltage and so on. But full-fledged battery model is likely not subject
+   for kernel at all, as it would require floating point calculation to deal
+   with things like differential equations and Kalman filters. This is
+   better be handled by batteryd/libbattery, yet to be written.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c546de3..c3a0038 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -56,6 +56,8 @@ source "drivers/w1/Kconfig"
 
 source "drivers/power/Kconfig"
 
+source "drivers/battery/Kconfig"
+
 source "drivers/hwmon/Kconfig"
 
 source "drivers/mfd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 2bdaae7..7cbfd37 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-$(CONFIG_I2C)		+= i2c/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_EXTERNAL_POWER)	+= power/
+obj-$(CONFIG_BATTERY)		+= battery/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_PHONE)		+= telephony/
 obj-$(CONFIG_MD)		+= md/
diff --git a/drivers/battery/Kconfig b/drivers/battery/Kconfig
new file mode 100644
index 0000000..a66294d
--- /dev/null
+++ b/drivers/battery/Kconfig
@@ -0,0 +1,12 @@
+
+menu "Battery devices"
+
+config BATTERY
+	tristate "Battery class support"
+	select EXTERNAL_POWER
+	help
+	  Say Y here to enable battery class support. This allows battery
+	  monitoring by userspace via sysfs (if available) and/or APM
+	  kernel interface (if selected below).
+
+endmenu
diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile
new file mode 100644
index 0000000..1af291b
--- /dev/null
+++ b/drivers/battery/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_BATTERY)              += battery.o
+
+ifeq ($(CONFIG_SYSFS),y)
+obj-$(CONFIG_BATTERY)              += battery_sysfs.o
+endif
+
+ifeq ($(CONFIG_LEDS_TRIGGERS),y)
+obj-$(CONFIG_BATTERY)              += battery_leds.o
+endif
diff --git a/drivers/battery/battery.c b/drivers/battery/battery.c
new file mode 100644
index 0000000..265b4fe
--- /dev/null
+++ b/drivers/battery/battery.c
@@ -0,0 +1,138 @@
+/*
+ *  Universal battery monitor class
+ *
+ *  Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright (c) 2004  Szabolcs Gyurko
+ *  Copyright (c) 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/battery.h>
+#include "battery.h"
+
+struct class *battery_class;
+
+static void battery_external_power_changed(struct power_supplicant *pst,
+                                           struct power_supply *psy)
+{
+	struct battery *bat = container_of(pst, struct battery, pst);
+
+	dev_dbg(bat->dev, "%s by %s\n", __FUNCTION__, psy->name);
+
+	if (bat->external_power_changed)
+		bat->external_power_changed(bat);
+
+	return;
+}
+
+int battery_is_external_power_supplied(struct battery *bat)
+{
+	const int ret = power_supplicant_am_i_supplied(&bat->pst);
+
+	dev_dbg(bat->dev, "%s %d\n", __FUNCTION__, ret);
+
+	return ret;
+}
+
+void battery_status_changed(struct battery *bat)
+{
+	int *status = bat->get_property(bat, BATTERY_PROP_STATUS);
+
+	dev_dbg(bat->dev, "%s\n", __FUNCTION__);
+
+	if (!status)
+		return;
+
+	battery_update_leds(bat, *status);
+
+	return;
+}
+
+int battery_register(struct device *parent, struct battery *bat)
+{
+	int rc = 0;
+
+	bat->dev = device_create(battery_class, parent, 0, "%s", bat->name);
+	if (IS_ERR(bat->dev)) {
+		rc = PTR_ERR(bat->dev);
+		goto dev_create_failed;
+	}
+
+	dev_set_drvdata(bat->dev, bat);
+
+	rc = battery_create_attrs(bat);
+	if (rc)
+		goto create_attrs_failed;
+
+	rc = battery_create_triggers(bat);
+	if (rc)
+		goto create_triggers_failed;
+
+	bat->pst.name = bat->name;
+	bat->pst.power_supply_changed = battery_external_power_changed;
+	rc = power_supplicant_register(&bat->pst);
+	if (rc)
+		goto power_supplicant_failed;
+
+	goto success;
+
+power_supplicant_failed:
+	battery_remove_triggers(bat);
+create_triggers_failed:
+	battery_remove_attrs(bat);
+create_attrs_failed:
+	device_unregister(bat->dev);
+dev_create_failed:
+success:
+	return rc;
+}
+
+void battery_unregister(struct battery *bat)
+{
+	power_supplicant_unregister(&bat->pst);
+	battery_remove_triggers(bat);
+	battery_remove_attrs(bat);
+	device_unregister(bat->dev);
+	return;
+}
+
+static int __init battery_class_init(void)
+{
+	battery_class = class_create(THIS_MODULE, "battery");
+
+	if (IS_ERR(battery_class))
+		return PTR_ERR(battery_class);
+
+	return 0;
+}
+
+static void __exit battery_class_exit(void)
+{
+	class_destroy(battery_class);
+	return;
+}
+
+EXPORT_SYMBOL_GPL(battery_register);
+EXPORT_SYMBOL_GPL(battery_unregister);
+EXPORT_SYMBOL_GPL(battery_status_changed);
+EXPORT_SYMBOL_GPL(battery_is_external_power_supplied);
+
+/* exported for the APM Power driver, APM emulation */
+EXPORT_SYMBOL_GPL(battery_class);
+
+subsys_initcall(battery_class_init);
+module_exit(battery_class_exit);
+
+MODULE_DESCRIPTION("Universal battery monitor class");
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
+              "Szabolcs Gyurko, "
+              "Anton Vorontsov <cbou@mail.ru>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/battery/battery.h b/drivers/battery/battery.h
new file mode 100644
index 0000000..25a98c6
--- /dev/null
+++ b/drivers/battery/battery.h
@@ -0,0 +1,37 @@
+/*
+ *  Functions private to battery.c
+ *
+ *  Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright (c) 2004  Szabolcs Gyurko
+ *  Copyright (c) 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#ifdef CONFIG_SYSFS
+
+extern int battery_create_attrs(struct battery *bat);
+extern void battery_remove_attrs(struct battery *bat);
+
+#else
+
+static inline int battery_create_attrs(struct battery *bat) { return 0; }
+static inline void battery_remove_attrs(struct battery *bat) {}
+
+#endif /* CONFIG_SYSFS */
+
+#ifdef CONFIG_LEDS_TRIGGERS
+
+extern void battery_update_leds(struct battery *bat, int status);
+extern int battery_create_triggers(struct battery *bat);
+extern void battery_remove_triggers(struct battery *bat);
+
+#else
+
+static inline void battery_update_leds(struct battery *bat, int status) {}
+static inline int battery_create_triggers(struct battery *bat) { return 0; }
+static inline void battery_remove_triggers(struct battery *bat) {}
+
+#endif /* CONFIG_LEDS_TRIGGERS */
diff --git a/drivers/battery/battery_leds.c b/drivers/battery/battery_leds.c
new file mode 100644
index 0000000..4306b0e
--- /dev/null
+++ b/drivers/battery/battery_leds.c
@@ -0,0 +1,106 @@
+/*
+ *  LEDs triggers for class batteries
+ *
+ *  Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright (c) 2004  Szabolcs Gyurko
+ *  Copyright (c) 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#include <linux/battery.h>
+
+/* If we have hwtimer trigger, then use it to blink charging LED */
+#if defined(CONFIG_LEDS_TRIGGER_HWTIMER) ||                  \
+               (defined(CONFIG_BATTERY_MODULE) &&            \
+                defined(CONFIG_LEDS_TRIGGER_HWTIMER_MODULE))
+	#define led_trigger_register_charging led_trigger_register_hwtimer
+	#define led_trigger_unregister_charging led_trigger_unregister_hwtimer
+#else
+	#define led_trigger_register_charging led_trigger_register_simple
+	#define led_trigger_unregister_charging led_trigger_unregister_simple
+#endif
+
+void battery_update_leds(struct battery *bat, int status)
+{
+	dev_dbg(bat->dev, "%s %d\n", __FUNCTION__, status);
+
+	switch(status) {
+	case BATTERY_STATUS_FULL:
+		led_trigger_event(bat->charging_full_trig, LED_FULL);
+		led_trigger_event(bat->charging_trig, LED_OFF);
+		led_trigger_event(bat->full_trig, LED_FULL);
+		break;
+	case BATTERY_STATUS_CHARGING:
+		led_trigger_event(bat->charging_full_trig, LED_FULL);
+		led_trigger_event(bat->charging_trig, LED_FULL);
+		led_trigger_event(bat->full_trig, LED_OFF);
+		break;
+	default:
+		led_trigger_event(bat->charging_full_trig, LED_OFF);
+		led_trigger_event(bat->charging_trig, LED_OFF);
+		led_trigger_event(bat->full_trig, LED_OFF);
+		break;
+	}
+
+	return;
+}
+
+int battery_create_triggers(struct battery *bat)
+{
+	int rc = 0;
+
+	bat->charging_full_trig_name = kmalloc(strlen(bat->name) +
+	                          sizeof("-charging-or-full"), GFP_KERNEL);
+	if (!bat->charging_full_trig_name)
+		goto charging_full_failed;
+
+	bat->charging_trig_name = kmalloc(strlen(bat->name) +
+	                                  sizeof("-charging"), GFP_KERNEL);
+	if (!bat->charging_trig_name)
+		goto charging_failed;
+
+	bat->full_trig_name = kmalloc(strlen(bat->name) +
+	                              sizeof("-full"), GFP_KERNEL);
+	if (!bat->full_trig_name)
+		goto full_failed;
+
+	strcpy(bat->charging_full_trig_name, bat->name);
+	strcat(bat->charging_full_trig_name, "-charging-or-full");
+	strcpy(bat->charging_trig_name, bat->name);
+	strcat(bat->charging_trig_name, "-charging");
+	strcpy(bat->full_trig_name, bat->name);
+	strcat(bat->full_trig_name, "-full");
+
+	led_trigger_register_simple(bat->charging_full_trig_name,
+	                            &bat->charging_full_trig);
+	led_trigger_register_charging(bat->charging_trig_name,
+	                              &bat->charging_trig);
+	led_trigger_register_simple(bat->full_trig_name,
+	                            &bat->full_trig);
+
+	goto success;
+
+full_failed:
+	kfree(bat->charging_trig_name);
+charging_failed:
+	kfree(bat->charging_full_trig_name);
+charging_full_failed:
+	rc = -ENOMEM;
+success:
+	return rc;
+}
+
+void battery_remove_triggers(struct battery *bat)
+{
+	led_trigger_unregister_simple(bat->charging_full_trig);
+	led_trigger_unregister_charging(bat->charging_trig);
+	led_trigger_unregister_simple(bat->full_trig);
+	kfree(bat->full_trig_name);
+	kfree(bat->charging_trig_name);
+	kfree(bat->charging_full_trig_name);
+
+	return;
+}
diff --git a/drivers/battery/battery_sysfs.c b/drivers/battery/battery_sysfs.c
new file mode 100644
index 0000000..08a9f61
--- /dev/null
+++ b/drivers/battery/battery_sysfs.c
@@ -0,0 +1,122 @@
+/*
+ *  Sysfs interface for the universal battery monitor class
+ *
+ *  Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright (c) 2004  Szabolcs Gyurko
+ *  Copyright (c) 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#include <linux/battery.h>
+
+/*
+ * This is because the name "current" breaks the device attr macro.
+ * The "current" word resolvs to "(get_current())" so instead of
+ * "current" "(get_current())" appears in the sysfs.
+ *
+ * The source of this definition is the device.h which calls __ATTR
+ * macro in sysfs.h which calls the __stringify macro.
+ *
+ * Only modification that the name is not tried to be resolved
+ * (as a macro let's say).
+ */
+
+#define BATTERY_ATTR(_name)                                             \
+{                                                                       \
+	.attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \
+	.show = battery_show_property,                                  \
+	.store = NULL,                                                  \
+}
+
+static struct device_attribute battery_attrs[];
+
+static ssize_t battery_show_property(struct device *dev,
+                                     struct device_attribute *attr,
+                                     char *buf) {
+	static char *status_text[] = {
+		"Unknown", "Charging", "Discharging", "Not charging", "Full"
+	};
+	static char *health_text[] = {
+		"Unknown", "Good", "Overheat", "Dead"
+	};
+	struct battery *bat = dev_get_drvdata(dev);
+	const off_t off = attr - battery_attrs;
+	void *value = bat->get_property(bat, off);
+
+	if (!value)
+		return sprintf(buf, "Driver can't report this property, "
+		               "but claimed it can. Fix it.\n");
+
+	if (off == BATTERY_PROP_STATUS)
+		return sprintf(buf, "%s\n", status_text[*(int *)value]);
+	else if (off == BATTERY_PROP_HEALTH)
+		return sprintf(buf, "%s\n", health_text[*(int *)value]);
+
+	return sprintf(buf, "%d\n", *(int *)value);
+}
+
+/* Must be in the same order as BATTERY_PROP_*, defined in battery.h */
+static struct device_attribute battery_attrs[] = {
+	BATTERY_ATTR(status),
+	BATTERY_ATTR(health),
+	BATTERY_ATTR(voltage_max_design),
+	BATTERY_ATTR(voltage_min_design),
+	BATTERY_ATTR(voltage_now),
+	BATTERY_ATTR(voltage_avg),
+	BATTERY_ATTR(current_now),
+	BATTERY_ATTR(current_avg),
+	BATTERY_ATTR(charge_full_design),
+	BATTERY_ATTR(charge_empty_design),
+	BATTERY_ATTR(charge_full),
+	BATTERY_ATTR(charge_empty),
+	BATTERY_ATTR(charge_now),
+	BATTERY_ATTR(charge_avg),
+	BATTERY_ATTR(energy_full_design),
+	BATTERY_ATTR(energy_empty_design),
+	BATTERY_ATTR(energy_full),
+	BATTERY_ATTR(energy_empty),
+	BATTERY_ATTR(energy_now),
+	BATTERY_ATTR(energy_avg),
+	BATTERY_ATTR(capacity),
+	BATTERY_ATTR(temp),
+	BATTERY_ATTR(time_to_empty_now),
+	BATTERY_ATTR(time_to_empty_avg),
+	BATTERY_ATTR(time_to_full_now),
+	BATTERY_ATTR(time_to_full_avg),
+};
+
+int battery_create_attrs(struct battery *bat)
+{
+	int rc = 0;
+	int i;
+
+	for (i = 0; i < bat->num_properties; i++) {
+		rc = device_create_file(bat->dev,
+		            &battery_attrs[bat->properties[i]]);
+		if (rc)
+			goto failed;
+	}
+
+	goto succeed;
+
+failed:
+	while (i--)
+		device_remove_file(bat->dev,
+		           &battery_attrs[bat->properties[i]]);
+succeed:
+	return rc;
+}
+
+void battery_remove_attrs(struct battery *bat)
+{
+	int i;
+
+	for (i = 0; i < bat->num_properties; i++)
+		device_remove_file(bat->dev,
+		           &battery_attrs[bat->properties[i]]);
+
+	return;
+}
diff --git a/include/linux/battery.h b/include/linux/battery.h
new file mode 100644
index 0000000..482d747
--- /dev/null
+++ b/include/linux/battery.h
@@ -0,0 +1,127 @@
+/*
+ *  Universal battery monitor class
+ *
+ *  Copyright (c) 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright (c) 2004  Szabolcs Gyurko
+ *  Copyright (c) 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#ifndef __LINUX_BATTERY_H__
+#define __LINUX_BATTERY_H__
+
+#include <linux/device.h>
+#include <linux/external_power.h>
+#include <linux/leds.h>
+
+/*
+ * All voltages, currents, charges, energies, time and temperatures in uV,
+ * uA, uAh, uWh, seconds and tenths of degree Celsius unless otherwise
+ * stated. It's driver's job to convert its raw values to units in which
+ * this class operates.
+ */
+
+/*
+ * For systems where the charger determines the maximum battery capacity
+ * the min and max fields should be used to present these values to user
+ * space. Unused/unknown fields will not appear in sysfs.
+ */
+
+#define BATTERY_STATUS_UNKNOWN      0
+#define BATTERY_STATUS_CHARGING     1
+#define BATTERY_STATUS_DISCHARGING  2
+#define BATTERY_STATUS_NOT_CHARGING 3
+#define BATTERY_STATUS_FULL         4
+
+#define BATTERY_HEALTH_UNKNOWN  0
+#define BATTERY_HEALTH_GOOD     1
+#define BATTERY_HEALTH_OVERHEAT 2
+#define BATTERY_HEALTH_DEAD     3
+
+enum battery_property {
+	BATTERY_PROP_STATUS = 0,
+	BATTERY_PROP_HEALTH,
+	BATTERY_PROP_VOLTAGE_MAX_DESIGN,
+	BATTERY_PROP_VOLTAGE_MIN_DESIGN,
+	BATTERY_PROP_VOLTAGE_NOW,
+	BATTERY_PROP_VOLTAGE_AVG,
+	BATTERY_PROP_CURRENT_NOW,
+	BATTERY_PROP_CURRENT_AVG,
+	BATTERY_PROP_CHARGE_FULL_DESIGN,
+	BATTERY_PROP_CHARGE_EMPTY_DESIGN,
+	BATTERY_PROP_CHARGE_FULL,
+	BATTERY_PROP_CHARGE_EMPTY,
+	BATTERY_PROP_CHARGE_NOW,
+	BATTERY_PROP_CHARGE_AVG,
+	BATTERY_PROP_ENERGY_FULL_DESIGN,
+	BATTERY_PROP_ENERGY_EMPTY_DESIGN,
+	BATTERY_PROP_ENERGY_FULL,
+	BATTERY_PROP_ENERGY_EMPTY,
+	BATTERY_PROP_ENERGY_NOW,
+	BATTERY_PROP_ENERGY_AVG,
+	BATTERY_PROP_CAPACITY, /* in percents! */
+	BATTERY_PROP_TEMP,
+	BATTERY_PROP_TIME_TO_EMPTY_NOW,
+	BATTERY_PROP_TIME_TO_EMPTY_AVG,
+	BATTERY_PROP_TIME_TO_FULL_NOW,
+	BATTERY_PROP_TIME_TO_FULL_AVG,
+};
+
+struct battery {
+	char *name;
+	enum battery_property *properties;
+	size_t num_properties;
+
+	/* For APM emulation, think legacy userspace. */
+	int main_battery;
+
+	/* executed in userspace, feel free to sleep */
+	void *(*get_property)(struct battery *bat, enum battery_property);
+
+	/* drivers should not sleep inside it, you'll get there from ISRs */
+	void (*external_power_changed)(struct battery *bat);
+
+	/* private */
+	struct device *dev;
+	struct power_supplicant pst;
+
+#ifdef CONFIG_LEDS_TRIGGERS
+	struct led_trigger *charging_full_trig;
+	char *charging_full_trig_name;
+	struct led_trigger *charging_trig;
+	char *charging_trig_name;
+	struct led_trigger *full_trig;
+	char *full_trig_name;
+#endif
+};
+
+/*
+ * This is recommended structure to specify static battery parameters.
+ * Generic one, parametrizable for different batteries. Battery class
+ * itself does not use it, but that's what implementing most drivers,
+ * should try reuse for consistency.
+ */
+
+struct battery_info {
+	char *name;
+	int voltage_max_design;
+	int voltage_min_design;
+	int charge_full_design;
+	int charge_empty_design;
+	int energy_full_design;
+	int energy_empty_design;
+	int main_battery;
+};
+
+extern void battery_status_changed(struct battery *bat);
+extern int battery_is_external_power_supplied(struct battery *bat);
+extern int battery_register(struct device *parent, struct battery *bat);
+extern void battery_unregister(struct battery *bat);
+
+/* For APM emulation, think legacy userspace. */
+extern struct class *battery_class;
+
+#endif /* __LINUX_BATTERY_H__ */
-- 
1.5.1.1-dirty


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/7] APM emulation driver for class batteries
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
                   ` (2 preceding siblings ...)
  2007-04-25 15:50 ` [PATCH 3/7] Universal battery class Anton Vorontsov
@ 2007-04-25 15:51 ` Anton Vorontsov
  2007-04-25 15:51 ` [PATCH 5/7] 1-Wire ds2760 chip battery driver Anton Vorontsov
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:51 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss


Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/battery/Kconfig     |    7 ++
 drivers/battery/Makefile    |    2 +
 drivers/battery/apm_power.c |  228 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 237 insertions(+), 0 deletions(-)
 create mode 100644 drivers/battery/apm_power.c

diff --git a/drivers/battery/Kconfig b/drivers/battery/Kconfig
index a66294d..7e7f9cd 100644
--- a/drivers/battery/Kconfig
+++ b/drivers/battery/Kconfig
@@ -9,4 +9,11 @@ config BATTERY
 	  monitoring by userspace via sysfs (if available) and/or APM
 	  kernel interface (if selected below).
 
+config APM_POWER
+	tristate "APM emulation for class batteries"
+	depends on BATTERY && APM_EMULATION
+	help
+	  Say Y here to enable support APM status emulation using
+	  battery class devices.
+
 endmenu
diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile
index 1af291b..8c0d648 100644
--- a/drivers/battery/Makefile
+++ b/drivers/battery/Makefile
@@ -7,3 +7,5 @@ endif
 ifeq ($(CONFIG_LEDS_TRIGGERS),y)
 obj-$(CONFIG_BATTERY)              += battery_leds.o
 endif
+
+obj-$(CONFIG_APM_POWER)            += apm_power.o
diff --git a/drivers/battery/apm_power.c b/drivers/battery/apm_power.c
new file mode 100644
index 0000000..e3bb4bd
--- /dev/null
+++ b/drivers/battery/apm_power.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
+ * Copyright (c) 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
+ *
+ * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ */
+
+#include <linux/module.h>
+#include <linux/battery.h>
+#include <linux/apm-emulation.h>
+
+#ifdef current
+#undef current /* it expands to get_current() */
+#endif
+
+#define BATTERY_PROP(bat, prop) ({                                 \
+	void *value = bat->get_property(bat, BATTERY_PROP_##prop); \
+	value ? *(int*)value : 0;                                  \
+})
+
+#define MBATTERY_PROP(prop) main_battery->get_property(main_battery, \
+                                            BATTERY_PROP_##prop)
+
+static struct battery *main_battery;
+
+static void find_main_battery(void)
+{
+	struct device *dev;
+	struct battery *bat, *batm;
+	int max_charge = 0;
+
+	main_battery = NULL;
+	batm = NULL;
+	list_for_each_entry(dev, &battery_class->devices, node) {
+		bat = dev_get_drvdata(dev);
+		/* If none of battery devices cantains 'main_battery' flag,
+		   choice one with maximum design charge */
+		if (BATTERY_PROP(bat, CHARGE_FULL_DESIGN) > max_charge) {
+			batm = bat;
+			max_charge = BATTERY_PROP(bat, CHARGE_FULL_DESIGN);
+		}
+
+		if (bat->main_battery)
+			main_battery = bat;
+	}
+	if (!main_battery)
+		main_battery = batm;
+
+	return;
+}
+
+static int calculate_time(int status)
+{
+	int *charge_full;
+	int *charge_empty;
+	int *charge;
+	int *current;
+
+	charge_full = MBATTERY_PROP(CHARGE_FULL);
+	/* if battery can't report this property, use design value */
+	if (!charge_full)
+		charge_full = MBATTERY_PROP(CHARGE_FULL_DESIGN);
+
+	charge_empty = MBATTERY_PROP(CHARGE_EMPTY);
+	/* if battery can't report this property, use design value */
+	if (!charge_empty)
+		charge_empty = MBATTERY_PROP(CHARGE_EMPTY_DESIGN);
+
+	charge = MBATTERY_PROP(CHARGE_AVG);
+	/* if battery can't report average value, use momentary */
+	if (!charge)
+		charge = MBATTERY_PROP(CHARGE_NOW);
+
+	current = MBATTERY_PROP(CURRENT_AVG);
+	/* if battery can't report average value, use momentary */
+	if (!current)
+		current = MBATTERY_PROP(CURRENT_NOW);
+
+	/* no luck */
+	if (!current || !charge || !charge_empty)
+		return -1;
+
+	if (status == BATTERY_STATUS_CHARGING)
+		return ((*charge - *charge_full) * 60L) / *current;
+	else
+		return -((*charge - *charge_empty) * 60L) / *current;
+}
+
+/* TODO: teach this function to calculate battery life using energy */
+static int calculate_capacity(void)
+{
+	int ret;
+	int *charge_empty;
+	int *charge_full;
+	int *charge;
+
+	charge_full = MBATTERY_PROP(CHARGE_FULL);
+	/* if battery can't report this property, use design value */
+	if (!charge_full)
+		charge_full = MBATTERY_PROP(CHARGE_FULL_DESIGN);
+
+	charge_empty = MBATTERY_PROP(CHARGE_EMPTY);
+	/* if battery can't report this property, use design value */
+	if (!charge_empty)
+		charge_empty = MBATTERY_PROP(CHARGE_EMPTY_DESIGN);
+
+	charge = MBATTERY_PROP(CHARGE_AVG);
+	/* if battery can't report average value, use momentary */
+	if (!charge)
+		charge = MBATTERY_PROP(CHARGE_NOW);
+
+	/* no luck */
+	if (!charge_full || !charge_empty || !charge)
+		return -1;
+
+	if (*charge_full - *charge_empty)
+		ret =  ((*charge - *charge_empty) * 100L) /
+		       (*charge_full - *charge_empty);
+	else
+		return -1;
+
+	if (ret > 100)
+		return 100;
+	else if (ret < 0)
+		return 0;
+	else
+		return ret;
+}
+
+static void apm_battery_apm_get_power_status(struct apm_power_info *info)
+{
+	int status;
+	int *capacity;
+	int *time_to_full;
+	int *time_to_empty;
+
+	down(&battery_class->sem);
+	find_main_battery();
+	if (!main_battery) {
+		up(&battery_class->sem);
+		return;
+	}
+
+	/* status */
+
+	if (MBATTERY_PROP(STATUS))
+		status = *(int *)MBATTERY_PROP(STATUS);
+	else
+		status = BATTERY_STATUS_UNKNOWN;
+
+	/* ac line status */
+
+	if ((status == BATTERY_STATUS_CHARGING) ||
+	    (status == BATTERY_STATUS_NOT_CHARGING) ||
+	    (status == BATTERY_STATUS_FULL))
+		info->ac_line_status = APM_AC_ONLINE;
+	else
+		info->ac_line_status = APM_AC_OFFLINE;
+
+	/* battery life (i.e. capacity, in percents) */
+
+	capacity = MBATTERY_PROP(CAPACITY);
+	if (capacity)
+		info->battery_life = *capacity;
+	else
+		info->battery_life = calculate_capacity();
+
+	/* charging status */
+
+	if (status == BATTERY_STATUS_CHARGING)
+		info->battery_status = APM_BATTERY_STATUS_CHARGING;
+	else {
+		if (info->battery_life > 50)
+			info->battery_status = APM_BATTERY_STATUS_HIGH;
+		else if (info->battery_life > 5)
+			info->battery_status = APM_BATTERY_STATUS_LOW;
+		else
+			info->battery_status = APM_BATTERY_STATUS_CRITICAL;
+	}
+	info->battery_flag = info->battery_status;
+
+	/* time */
+
+	info->units = APM_UNITS_MINS;
+
+	time_to_empty = MBATTERY_PROP(TIME_TO_EMPTY_AVG);
+	if (!time_to_empty)
+		time_to_empty = MBATTERY_PROP(TIME_TO_EMPTY_NOW);
+
+	time_to_full = MBATTERY_PROP(TIME_TO_FULL_AVG);
+	if (!time_to_full)
+		time_to_full = MBATTERY_PROP(TIME_TO_FULL_NOW);
+
+	if (status == BATTERY_STATUS_CHARGING && time_to_full)
+		info->time = *time_to_full / 60;
+	else if (status != BATTERY_STATUS_CHARGING && time_to_empty)
+		info->time = *time_to_empty / 60;
+	else
+		info->time = calculate_time(status);
+
+	up(&battery_class->sem);
+	return;
+}
+
+static int __init apm_battery_init(void)
+{
+	printk(KERN_INFO "APM Battery Driver\n");
+
+	apm_get_power_status = apm_battery_apm_get_power_status;
+	return 0;
+}
+
+static void __exit apm_battery_exit(void)
+{
+	apm_get_power_status = NULL;
+	return;
+}
+
+module_init(apm_battery_init);
+module_exit(apm_battery_exit);
+
+MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
+MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
+MODULE_LICENSE("GPL");
-- 
1.5.1.1-dirty


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/7] 1-Wire ds2760 chip battery driver
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
                   ` (3 preceding siblings ...)
  2007-04-25 15:51 ` [PATCH 4/7] APM emulation driver for class batteries Anton Vorontsov
@ 2007-04-25 15:51 ` Anton Vorontsov
  2007-04-25 15:52 ` [PATCH 6/7] remove "#if 0" from find_bus function, export it Anton Vorontsov
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:51 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

This is driver for batteries with ds2760 chip inside, found inside
almost every HP iPaq and HTC PDAs/phones.

Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/battery/Kconfig          |    7 +
 drivers/battery/Makefile         |    2 +
 drivers/battery/ds2760_battery.c |  503 ++++++++++++++++++++++++++++++++++++++
 include/linux/ds2760_battery.h   |   32 +++
 4 files changed, 544 insertions(+), 0 deletions(-)
 create mode 100644 drivers/battery/ds2760_battery.c
 create mode 100644 include/linux/ds2760_battery.h

diff --git a/drivers/battery/Kconfig b/drivers/battery/Kconfig
index 7e7f9cd..7de72df 100644
--- a/drivers/battery/Kconfig
+++ b/drivers/battery/Kconfig
@@ -16,4 +16,11 @@ config APM_POWER
 	  Say Y here to enable support APM status emulation using
 	  battery class devices.
 
+config BATTERY_DS2760
+	tristate "DS2760 battery driver (HP iPAQ & others)"
+	depends on BATTERY && W1
+	select W1_SLAVE_DS2760
+	help
+	  Say Y here to enable support for batteries with ds2760 chip.
+
 endmenu
diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile
index 8c0d648..1b0c624 100644
--- a/drivers/battery/Makefile
+++ b/drivers/battery/Makefile
@@ -9,3 +9,5 @@ obj-$(CONFIG_BATTERY)              += battery_leds.o
 endif
 
 obj-$(CONFIG_APM_POWER)            += apm_power.o
+
+obj-$(CONFIG_BATTERY_DS2760)       += ds2760_battery.o
diff --git a/drivers/battery/ds2760_battery.c b/drivers/battery/ds2760_battery.c
new file mode 100644
index 0000000..1f2712b
--- /dev/null
+++ b/drivers/battery/ds2760_battery.c
@@ -0,0 +1,503 @@
+/*
+ * Driver for batteries with DS2760 chips inside.
+ *
+ * Copyright (c) 2007 Anton Vorontsov
+ *               2004-2007 Matt Reimer
+ *               2004 Szabolcs Gyurko
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * Author:  Anton Vorontsov <cbou@mail.ru>
+ *          February 2007
+ *
+ *          Matt Reimer <mreimer@vpop.net>
+ *          April 2004, 2005, 2007
+ *
+ *          Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *          September 2004
+ */
+
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/ds2760_battery.h>
+
+#include "../w1/w1.h"
+#include "../w1/slaves/w1_ds2760.h"
+
+struct ds2760_device_info {
+	struct battery_info *bi;
+	struct device *dev;
+
+	/* DS2760 data, valid after calling ds2760_battery_read_status() */
+	unsigned long update_time;      /* jiffies when data read */
+	char raw[DS2760_DATA_SIZE];     /* raw DS2760 data */
+	int voltage_raw;                /* units of 4.88 mV */
+	int voltage_uV;                 /* units of uV */
+	int current_raw;                /* units of 0.625 mA */
+	int current_uA;                 /* units of uA */
+	int accum_current_raw;          /* units of 0.25 mAh */
+	int accum_current_uAh;          /* units of uAh */
+	int temp_raw;                   /* units of 0.125 C */
+	int temp_C;                     /* units of 0.1 C */
+	int rated_capacity;             /* units of uAh */
+	int rem_capacity;               /* percentage */
+	int full_active_uAh;            /* units of uAh */
+	int empty_uAh;                  /* units of uAh */
+	int life_sec;                   /* units of seconds */
+	int charge_status;              /* BATTERY_STATUS_* */
+
+	int full_counter;
+	struct battery batt;
+	struct device *w1_dev;
+	struct workqueue_struct *monitor_wqueue;
+	struct delayed_work monitor_work;
+};
+
+static unsigned int cache_time = 1000;
+module_param(cache_time, uint, 0644);
+MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+
+/* Some batteries have their rated capacity stored a N * 10 mAh, while
+ * others use an index into this table. */
+static int rated_capacities[] = {
+	0,
+	920,        /* Samsung */
+	920,        /* BYD */
+	920,        /* Lishen */
+	920,        /* NEC */
+	1440,       /* Samsung */
+	1440,       /* BYD */
+	1440,       /* Lishen */
+	1440,       /* NEC */
+	2880,       /* Samsung */
+	2880,       /* BYD */
+	2880,       /* Lishen */
+	2880        /* NEC */
+};
+
+/* array is level at temps 0C, 10C, 20C, 30C, 40C
+ * temp is in Celsius */
+static int battery_interpolate(int array[], int temp)
+{
+	int index, dt;
+
+	if (temp <= 0)
+		return array[0];
+	if (temp >= 40)
+		return array[4];
+
+	index = temp / 10;
+	dt    = temp % 10;
+
+	return array[index] + (((array[index + 1] - array[index]) * dt) / 10);
+}
+
+static int ds2760_battery_read_status(struct ds2760_device_info *di)
+{
+	int ret, i, start, count, scale[5];
+
+	if (di->update_time && time_before(jiffies, di->update_time +
+	                                   msecs_to_jiffies(cache_time)))
+		return 0;
+
+	if (!di->w1_dev)
+		return 0;
+
+	/* The first time we read the entire contents of SRAM/EEPROM,
+	 * but after that we just read the interesting bits that change. */
+	if (di->update_time == 0) {
+		start = 0;
+		count = DS2760_DATA_SIZE;
+	}
+	else {
+		start = DS2760_VOLTAGE_MSB;
+		count = DS2760_TEMP_LSB - start + 1;
+	}
+
+	ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count);
+	if (ret != count) {
+		dev_warn(di->dev, "call to w1_ds2760_read failed (0x%08x)\n",
+		         (unsigned int)di->w1_dev);
+		return 1;
+	}
+
+	di->update_time = jiffies;
+
+	/* DS2760 reports voltage in units of 4.88mV, but the battery class
+	 * reports in units of uV, so convert by multiplying by 4880. */
+	di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) |
+	                  (di->raw[DS2760_VOLTAGE_LSB] >> 5);
+	di->voltage_uV = di->voltage_raw * 4880;
+
+	/* DS2760 reports current in signed units of 0.625mA, but the battery
+	 * class reports in units of uA, so convert by multiplying by 625. */
+	di->current_raw =
+	    (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) |
+	                  (di->raw[DS2760_CURRENT_LSB] >> 3);
+	di->current_uA = di->current_raw * 625;
+
+	/* DS2760 reports accumulated current in signed units of 0.25mAh. */
+	di->accum_current_raw =
+	    (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) |
+	                   di->raw[DS2760_CURRENT_ACCUM_LSB];
+	di->accum_current_uAh = di->accum_current_raw * 250;
+
+	/* DS2760 reports temperature in signed units of 0.125C, but the
+	 * battery class reports in units of 1/10 C, so we convert by
+	 * multiplying by .125 * 10 = 1.25. */
+	di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) |
+	                             (di->raw[DS2760_TEMP_LSB] >> 5);
+	di->temp_C = di->temp_raw + (di->temp_raw / 4);
+
+	/* At least some battery monitors (e.g. HP iPAQ) store the battery's
+	 * maximum rated capacity. */
+	if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities))
+		di->rated_capacity = rated_capacities[
+		        (unsigned int)di->raw[DS2760_RATED_CAPACITY]];
+	else
+		di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10;
+
+	di->rated_capacity *= 1000; /* convert to uAh */
+
+	/* Calculate the full level at the present temperature. */
+	di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 |
+	                      di->raw[DS2760_ACTIVE_FULL + 1];
+
+	scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 |
+	           di->raw[DS2760_ACTIVE_FULL + 1];
+	for (i = 1; i < 5; i++)
+		scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i];
+
+	di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10);
+	di->full_active_uAh *= 1000; /* convert to uAh */
+
+	/* Calculate the empty level at the present temperature. */
+	scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4];
+	for (i = 3; i >= 0; i--)
+		scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i];
+
+	di->empty_uAh = battery_interpolate(scale, di->temp_C / 10);
+	di->empty_uAh *= 1000; /* convert to uAh */
+
+	/* From Maxim Application Note 131: remaining capacity =
+	 * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */
+	di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) /
+	                    (di->full_active_uAh - di->empty_uAh);
+
+	if (di->rem_capacity < 0)
+		di->rem_capacity = 0;
+	if (di->rem_capacity > 100)
+		di->rem_capacity = 100;
+
+	if (di->current_uA)
+		di->life_sec = -((di->accum_current_uAh - di->empty_uAh) *
+		                 3600L) / di->current_uA;
+	else
+		di->life_sec = 0;
+
+	return 0;
+}
+
+static void ds2760_battery_update_status(struct ds2760_device_info *di)
+{
+	int old_charge_status = di->charge_status;
+
+	ds2760_battery_read_status(di);
+
+	if (di->charge_status == BATTERY_STATUS_UNKNOWN)
+		di->full_counter = 0;
+
+	if (battery_is_external_power_supplied(&di->batt)) {
+		if (di->current_uA > 10000) {
+			di->charge_status = BATTERY_STATUS_CHARGING;
+			di->full_counter = 0;
+		}
+		else if (di->current_uA < -5000) {
+			if (di->charge_status != BATTERY_STATUS_NOT_CHARGING)
+				dev_notice(di->dev, "not enough power to "
+				           "charge\n");
+			di->charge_status = BATTERY_STATUS_NOT_CHARGING;
+			di->full_counter = 0;
+		}
+		else if (di->current_uA < 10000 &&
+		            di->charge_status != BATTERY_STATUS_FULL) {
+
+			/* Don't consider the battery to be full unless
+			 * we've seen the current < 10 mA at least two
+			 * consecutive times. */
+
+			di->full_counter++;
+
+			if (di->full_counter < 2)
+				di->charge_status = BATTERY_STATUS_CHARGING;
+			else {
+				unsigned char acr[2];
+				int acr_val;
+
+				/* acr is in units of 0.25 mAh */
+				acr_val = di->full_active_uAh * 4L / 1000;
+
+				acr[0] = acr_val >> 8;
+				acr[1] = acr_val & 0xff;
+
+				if (w1_ds2760_write(di->w1_dev, acr,
+				    DS2760_CURRENT_ACCUM_MSB, 2) < 2)
+					dev_warn(di->dev,
+					         "ACR reset failed\n");
+
+				di->charge_status = BATTERY_STATUS_FULL;
+			}
+		}
+	}
+	else {
+		di->charge_status = BATTERY_STATUS_DISCHARGING;
+		di->full_counter = 0;
+	}
+
+	if (di->charge_status != old_charge_status)
+		battery_status_changed(&di->batt);
+
+	return;
+}
+
+static int ds2760_battery_match_callback(struct device *dev, void *data)
+{
+	struct w1_slave *sl;
+
+	if (!(dev->driver && dev->driver->name &&
+	     (strcmp(dev->driver->name, "w1_slave_driver") == 0)))
+		return 0;
+
+	sl = container_of(dev, struct w1_slave, dev);
+
+	/* DS2760 w1 slave device names begin with the family number 0x30. */
+	if (strncmp(sl->name, "30-", 3) != 0)
+		return 0;
+
+	return 1;
+}
+
+static void ds2760_battery_work(struct work_struct *work)
+{
+	struct ds2760_device_info *di = container_of(work,
+		struct ds2760_device_info, monitor_work.work);
+	struct bus_type *bus;
+	int interval = HZ * 60;
+
+	dev_dbg(di->dev, "%s\n", __FUNCTION__);
+
+	if (!di->w1_dev) {
+		/* Get the battery w1 slave device. */
+		bus = find_bus("w1");
+		if (bus)
+			di->w1_dev = bus_find_device(bus, NULL, NULL,
+			                   ds2760_battery_match_callback);
+
+		if (!di->w1_dev) {
+			dev_dbg(di->dev, "no w1 dev found\n");
+			interval = HZ * 10;
+			goto again_please;
+		}
+		dev_dbg(di->dev, "w1 dev found\n");
+	}
+
+	ds2760_battery_update_status(di);
+
+again_please:
+	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+	return;
+}
+
+#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \
+                                              batt);
+
+static void ds2760_battery_external_power_changed(struct battery *bat)
+{
+	struct ds2760_device_info *di = to_ds2760_device_info(bat);
+
+	dev_dbg(di->dev, "%s\n", __FUNCTION__);
+
+	cancel_delayed_work(&di->monitor_work);
+	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
+
+	return;
+}
+
+static void *ds2760_battery_get_property(struct battery *bat,
+                                         enum battery_property bp)
+{
+	struct ds2760_device_info *di = to_ds2760_device_info(bat);
+
+	switch (bp) {
+	case BATTERY_PROP_VOLTAGE_MAX_DESIGN:
+		return &di->bi->voltage_max_design;
+	case BATTERY_PROP_STATUS:
+		return &di->charge_status;
+	default:
+		break;
+	};
+
+	ds2760_battery_read_status(di);
+
+	switch (bp) {
+	case BATTERY_PROP_VOLTAGE_NOW:
+		return &di->voltage_uV;
+	case BATTERY_PROP_CURRENT_NOW:
+		return &di->current_uA;
+	case BATTERY_PROP_CHARGE_FULL_DESIGN:
+		return &di->rated_capacity;
+	case BATTERY_PROP_CHARGE_FULL:
+		return &di->full_active_uAh;
+	case BATTERY_PROP_CHARGE_EMPTY:
+		return &di->empty_uAh;
+	case BATTERY_PROP_CHARGE_NOW:
+		return &di->accum_current_uAh;
+	case BATTERY_PROP_TEMP:
+		return &di->temp_C;
+	default: break;
+	};
+
+	return NULL;
+}
+
+static enum battery_property ds2760_battery_properties[] = {
+	BATTERY_PROP_STATUS,
+	BATTERY_PROP_VOLTAGE_MAX_DESIGN,
+	BATTERY_PROP_VOLTAGE_NOW,
+	BATTERY_PROP_CURRENT_NOW,
+	BATTERY_PROP_CHARGE_FULL_DESIGN,
+	BATTERY_PROP_CHARGE_FULL,
+	BATTERY_PROP_CHARGE_EMPTY,
+	BATTERY_PROP_CHARGE_NOW,
+	BATTERY_PROP_TEMP,
+};
+
+static int ds2760_battery_probe(struct platform_device *pdev)
+{
+	int retval = 0;
+	struct ds2760_device_info *di;
+	struct ds2760_platform_data *pdata;
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		retval = -ENOMEM;
+		goto di_alloc_failed;
+	}
+
+	platform_set_drvdata(pdev, di);
+
+	pdata = pdev->dev.platform_data;
+	di->bi = &pdata->battery_info;
+	di->dev = &pdev->dev;
+
+	di->batt.name           = di->bi->name;
+	di->batt.properties     = ds2760_battery_properties;
+	di->batt.num_properties = ARRAY_SIZE(ds2760_battery_properties);
+	di->batt.get_property   = ds2760_battery_get_property;
+	di->batt.external_power_changed =
+	                          ds2760_battery_external_power_changed;
+
+	di->charge_status = BATTERY_STATUS_UNKNOWN;
+
+	retval = battery_register(&pdev->dev, &di->batt);
+	if (retval) {
+		dev_err(di->dev, "failed to register battery");
+		goto batt_failed;
+	}
+
+	INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
+	di->monitor_wqueue = create_singlethread_workqueue("ds2760_mon");
+	if (!di->monitor_wqueue) {
+		retval = -ESRCH;
+		goto workqueue_failed;
+	}
+	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
+
+	goto success;
+
+workqueue_failed:
+	battery_unregister(&di->batt);
+batt_failed:
+	kfree(di);
+di_alloc_failed:
+success:
+	return retval;
+}
+
+static int ds2760_battery_remove(struct platform_device *pdev)
+{
+	struct ds2760_device_info *di = platform_get_drvdata(pdev);
+
+	cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+	                                  &di->monitor_work);
+	destroy_workqueue(di->monitor_wqueue);
+	battery_unregister(&di->batt);
+	if (di->w1_dev)
+		put_device(di->w1_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ds2760_battery_suspend(struct platform_device *pdev,
+                                  pm_message_t state)
+{
+	struct ds2760_device_info *di = platform_get_drvdata(pdev);
+
+	di->charge_status = BATTERY_STATUS_UNKNOWN;
+	battery_status_changed(&di->batt);
+
+	return 0;
+}
+
+static int ds2760_battery_resume(struct platform_device *pdev)
+{
+	struct ds2760_device_info *di = platform_get_drvdata(pdev);
+
+	di->charge_status = BATTERY_STATUS_UNKNOWN;
+	battery_status_changed(&di->batt);
+
+	cancel_delayed_work(&di->monitor_work);
+	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
+
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver ds2760_battery_driver = {
+	.driver = {
+		.name = "ds2760-battery",
+	},
+	.probe	  = ds2760_battery_probe,
+	.remove   = ds2760_battery_remove,
+#ifdef CONFIG_PM
+	.suspend  = ds2760_battery_suspend,
+	.resume	  = ds2760_battery_resume,
+#endif
+};
+
+static int __init ds2760_battery_init(void)
+{
+	return platform_driver_register(&ds2760_battery_driver);
+}
+
+static void __exit ds2760_battery_exit(void)
+{
+	platform_driver_unregister(&ds2760_battery_driver);
+	return;
+}
+
+module_init(ds2760_battery_init);
+module_exit(ds2760_battery_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
+              "Matt Reimer <mreimer@vpop.net>, "
+              "Anton Vorontsov <cbou@mail.ru>");
+MODULE_DESCRIPTION("ds2760 battery driver");
diff --git a/include/linux/ds2760_battery.h b/include/linux/ds2760_battery.h
new file mode 100644
index 0000000..a614b6a
--- /dev/null
+++ b/include/linux/ds2760_battery.h
@@ -0,0 +1,32 @@
+/*
+ * Driver for batteries with DS2760 chips inside.
+ *
+ * Copyright (c) 2007 Anton Vorontsov
+ *               2004-2007 Matt Reimer
+ *               2004 Szabolcs Gyurko
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * Author:  Anton Vorontsov <cbou@mail.ru>
+ *          February 2007
+ *
+ *          Matt Reimer <mreimer@vpop.net>
+ *          April 2004, 2005, 2007
+ *
+ *          Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *          September 2004
+ */
+
+#ifndef __DS2760_BATTERY__
+#define __DS2760_BATTERY__
+
+#include <linux/battery.h>
+
+struct ds2760_platform_data {
+	/* Battery information and characteristics. */
+	struct battery_info battery_info;
+};
+
+#endif
-- 
1.5.1.1-dirty


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/7] remove "#if 0" from find_bus function, export it.
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
                   ` (4 preceding siblings ...)
  2007-04-25 15:51 ` [PATCH 5/7] 1-Wire ds2760 chip battery driver Anton Vorontsov
@ 2007-04-25 15:52 ` Anton Vorontsov
  2007-04-25 15:52 ` [PATCH 7/7] ds2760 W1 slave Anton Vorontsov
  2007-04-25 20:05 ` [PATCH 0/7] Battery class, external power framework, ds2760 battery Andrew Morton
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:52 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

This function were placed in "#if 0" because nobody was using it.
We using it now.

See http://lwn.net/Articles/210610/

Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/base/bus.c     |    5 ++---
 include/linux/device.h |    2 ++
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 253868e..971efa2 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -667,14 +667,13 @@ void put_bus(struct bus_type * bus)
  *
  *	Note that kset_find_obj increments bus' reference count.
  */
-#if 0
+
 struct bus_type * find_bus(char * name)
 {
 	struct kobject * k = kset_find_obj(&bus_subsys.kset, name);
 	return k ? to_bus(k) : NULL;
 }
-#endif  /*  0  */
-
+EXPORT_SYMBOL_GPL(find_bus);
 
 /**
  *	bus_add_attrs - Add default attributes for this bus.
diff --git a/include/linux/device.h b/include/linux/device.h
index 5cf30e9..4015b39 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -68,6 +68,8 @@ extern void bus_unregister(struct bus_type * bus);
 
 extern int __must_check bus_rescan_devices(struct bus_type * bus);
 
+extern struct bus_type *find_bus(char *name);
+
 /* iterator helpers for buses */
 
 int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
-- 
1.5.1.1-dirty


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 7/7] ds2760 W1 slave
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
                   ` (5 preceding siblings ...)
  2007-04-25 15:52 ` [PATCH 6/7] remove "#if 0" from find_bus function, export it Anton Vorontsov
@ 2007-04-25 15:52 ` Anton Vorontsov
  2007-04-25 20:05 ` [PATCH 0/7] Battery class, external power framework, ds2760 battery Andrew Morton
  7 siblings, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 15:52 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

This is W1 slave for ds2760 chip, found inside almost every HP iPaq and
HTC PDAs/phones.

Acked-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: Anton Vorontsov <cbou@mail.ru>
---
 drivers/w1/slaves/Kconfig     |   13 ++++
 drivers/w1/slaves/Makefile    |    1 +
 drivers/w1/slaves/w1_ds2760.c |  125 +++++++++++++++++++++++++++++++++++++++++
 drivers/w1/slaves/w1_ds2760.h |   50 ++++++++++++++++
 drivers/w1/w1_family.h        |    1 +
 5 files changed, 190 insertions(+), 0 deletions(-)
 create mode 100644 drivers/w1/slaves/w1_ds2760.c
 create mode 100644 drivers/w1/slaves/w1_ds2760.h

diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 904e5ae..df95d6c 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -35,4 +35,17 @@ config W1_SLAVE_DS2433_CRC
 	  Each block has 30 bytes of data and a two byte CRC16.
 	  Full block writes are only allowed if the CRC is valid.
 
+config W1_SLAVE_DS2760
+	tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)"
+	depends on W1
+	help
+	  If you enable this you will have the DS2760 battery monitor
+	  chip support.
+
+	  The battery monitor chip is used in many batteries/devices
+	  as the one who is responsible for charging/discharging/monitoring
+	  Li+ batteries.
+
+	  If you are unsure, say N.
+
 endmenu
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index 725dcfd..a8eb752 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -5,4 +5,5 @@
 obj-$(CONFIG_W1_SLAVE_THERM)	+= w1_therm.o
 obj-$(CONFIG_W1_SLAVE_SMEM)	+= w1_smem.o
 obj-$(CONFIG_W1_SLAVE_DS2433)	+= w1_ds2433.o
+obj-$(CONFIG_W1_SLAVE_DS2760)	+= w1_ds2760.o
 
diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c
new file mode 100644
index 0000000..5a340d1
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2760.c
@@ -0,0 +1,125 @@
+/*
+ * 1-Wire implementation for the ds2760 chip
+ *
+ * Copyright (c) 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+#include "w1_ds2760.h"
+
+static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,
+                        int io)
+{
+	struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+
+	if (!dev)
+		return 0;
+
+	mutex_lock(&sl->master->mutex);
+
+	if (addr > DS2760_DATA_SIZE || addr < 0) {
+		count = 0;
+		goto out;
+	}
+	if (addr + count > DS2760_DATA_SIZE)
+		count = DS2760_DATA_SIZE - addr;
+
+	if (!w1_reset_select_slave(sl)) {
+		if (!io) {
+			w1_write_8(sl->master, W1_DS2760_READ_DATA);
+			w1_write_8(sl->master, addr);
+			count = w1_read_block(sl->master, buf, count);
+		} else {
+			w1_write_8(sl->master, W1_DS2760_WRITE_DATA);
+			w1_write_8(sl->master, addr);
+			w1_write_block(sl->master, buf, count);
+			/* XXX w1_write_block returns void, not n_written */
+		}
+	}
+
+out:
+	mutex_unlock(&sl->master->mutex);
+
+	return count;
+}
+
+int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count)
+{
+	return w1_ds2760_io(dev, buf, addr, count, 0);
+}
+
+int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count)
+{
+	return w1_ds2760_io(dev, buf, addr, count, 1);
+}
+
+static ssize_t w1_ds2760_read_bin(struct kobject *kobj, char *buf, loff_t off,
+                                  size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	return w1_ds2760_read(dev, buf, off, count);
+}
+
+static struct bin_attribute w1_ds2760_bin_attr = {
+	.attr = {
+		.name = "w1_slave",
+		.mode = S_IRUGO,
+		.owner = THIS_MODULE,
+	},
+	.size = DS2760_DATA_SIZE,
+	.read = w1_ds2760_read_bin,
+};
+
+static int w1_ds2760_add_slave(struct w1_slave *sl)
+{
+	return sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr);
+}
+
+static void w1_ds2760_remove_slave(struct w1_slave *sl)
+{
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr);
+}
+
+static struct w1_family_ops w1_ds2760_fops = {
+	.add_slave    = w1_ds2760_add_slave,
+	.remove_slave = w1_ds2760_remove_slave,
+};
+
+static struct w1_family w1_ds2760_family = {
+	.fid = W1_FAMILY_DS2760,
+	.fops = &w1_ds2760_fops,
+};
+
+static int __init w1_ds2760_init(void)
+{
+	printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor "
+	       " chip  - (c) 2004-2005, Szabolcs Gyurko\n");
+	return w1_register_family(&w1_ds2760_family);
+}
+
+static void __exit w1_ds2760_exit(void)
+{
+	w1_unregister_family(&w1_ds2760_family);
+}
+
+EXPORT_SYMBOL(w1_ds2760_read);
+EXPORT_SYMBOL(w1_ds2760_write);
+
+module_init(w1_ds2760_init);
+module_exit(w1_ds2760_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>");
+MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip");
diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h
new file mode 100644
index 0000000..0ee92b9
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2760.h
@@ -0,0 +1,50 @@
+/*
+ * 1-Wire implementation for the ds2760 chip
+ *
+ * Copyright (c) 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ */
+
+#ifndef __w1_ds2760_h__
+#define __w1_ds2760_h__
+
+/* Known commands to the DS2760 chip */
+#define W1_DS2760_SWAP                      0xAA
+#define W1_DS2760_READ_DATA                 0x69
+#define W1_DS2760_WRITE_DATA                0x6C
+#define W1_DS2760_COPY_DATA                 0x48
+#define W1_DS2760_RECALL_DATA               0xB8
+#define W1_DS2760_LOCK                      0x6A
+
+/* Number of valid register addresses */
+#define DS2760_DATA_SIZE	    0x40
+
+#define DS2760_PROTECTION_REG	    0x00
+#define DS2760_STATUS_REG	    0x01
+#define DS2760_EEPROM_REG	    0x07
+#define DS2760_SPECIAL_FEATURE_REG  0x08
+#define DS2760_VOLTAGE_MSB	    0x0c
+#define DS2760_VOLTAGE_LSB	    0x0d
+#define DS2760_CURRENT_MSB	    0x0e
+#define DS2760_CURRENT_LSB	    0x0f
+#define DS2760_CURRENT_ACCUM_MSB    0x10
+#define DS2760_CURRENT_ACCUM_LSB    0x11
+#define DS2760_TEMP_MSB		    0x18
+#define DS2760_TEMP_LSB		    0x19
+#define DS2760_EEPROM_BLOCK0	    0x20
+#define DS2760_ACTIVE_FULL	    0x20
+#define DS2760_EEPROM_BLOCK1	    0x30
+#define DS2760_RATED_CAPACITY	    0x32
+#define DS2760_CURRENT_OFFSET_BIAS  0x33
+#define DS2760_ACTIVE_EMPTY	    0x3b
+
+extern int w1_ds2760_read(struct device *dev, char *buf, int addr,
+                          size_t count);
+extern int w1_ds2760_write(struct device *dev, char *buf, int addr,
+                           size_t count);
+
+#endif /* !__w1_ds2760_h__ */
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 1e2ac40..ef1e1da 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -33,6 +33,7 @@
 #define W1_THERM_DS1822  	0x22
 #define W1_EEPROM_DS2433  	0x23
 #define W1_THERM_DS18B20 	0x28
+#define W1_FAMILY_DS2760	0x30
 
 #define MAXNAMELEN		32
 
-- 
1.5.1.1-dirty

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] Battery class, external power framework, ds2760 battery
  2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
                   ` (6 preceding siblings ...)
  2007-04-25 15:52 ` [PATCH 7/7] ds2760 W1 slave Anton Vorontsov
@ 2007-04-25 20:05 ` Andrew Morton
  2007-04-25 21:05   ` Anton Vorontsov
  7 siblings, 1 reply; 13+ messages in thread
From: Andrew Morton @ 2007-04-25 20:05 UTC (permalink / raw)
  To: cbou; +Cc: linux-kernel, kernel-discuss

On Wed, 25 Apr 2007 19:48:20 +0400 Anton Vorontsov <cbou@mail.ru> wrote:

> Andrew, would you please merge these patches into -mm? They're against
> today's Linus git tree.

Not for a while, sorry.  We already have enough material queued for 2.6.22
and a lot of it is broken.  So I am concentrating on fixing the remaining
bugs in 2.6.21 and getting some of the new bugs we have prepared for 2.6.22
fixed up.

Meanwhile, I expect lots of new stuff is appearing in the subsystem trees,
but there isn't much I can do about that.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] Battery class, external power framework, ds2760 battery
  2007-04-25 20:05 ` [PATCH 0/7] Battery class, external power framework, ds2760 battery Andrew Morton
@ 2007-04-25 21:05   ` Anton Vorontsov
  2007-04-25 21:19     ` Anton Vorontsov
  2007-04-25 22:47     ` Andrew Morton
  0 siblings, 2 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 21:05 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

On Wed, Apr 25, 2007 at 01:05:01PM -0700, Andrew Morton wrote:
> On Wed, 25 Apr 2007 19:48:20 +0400 Anton Vorontsov <cbou@mail.ru> wrote:
> 
> > Andrew, would you please merge these patches into -mm? They're against
> > today's Linus git tree.
> 
> Not for a while, sorry.  We already have enough material queued for 2.6.22
> and a lot of it is broken.  So I am concentrating on fixing the remaining
> bugs in 2.6.21 and getting some of the new bugs we have prepared for 2.6.22
> fixed up.

Pity. Not trying to insist too hard, but this battery stuff can not break
anything pre-existing in kernel. Also it's proven to work at least on five
different embedded devices PXA2xx and S3C (HP iPaqs h2200, rx3000, h5550
and hx4700), with two battery types. Plus I believe that this battery
class more than suitable for OLPC guys (battery-2.6 tree).

Anyway, maybe it's even better if we'll boil battery stuff a *bit* more.

Also you didn't [directly] answered how long exactly we should wait,
should we come back after 2.6.21 or 2.6.22 release? In later case it's a
bit too long, isn't it?

> Meanwhile, I expect lots of new stuff is appearing in the subsystem trees,
> but there isn't much I can do about that.

:-) You can sum "ADC chips subsystem" to expectations, we're at
handhelds.org, going to cleanup and present it in few weeks. It will
include Analog-to-Digital chips subsystem, generic ADC battery driver,
generic ADC touchscreen driver with debouncing ability, along with few
ADC chip drivers.

Thanks,

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.org/bd2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] Battery class, external power framework, ds2760 battery
  2007-04-25 21:05   ` Anton Vorontsov
@ 2007-04-25 21:19     ` Anton Vorontsov
  2007-04-25 22:47     ` Andrew Morton
  1 sibling, 0 replies; 13+ messages in thread
From: Anton Vorontsov @ 2007-04-25 21:19 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, kernel-discuss

On Thu, Apr 26, 2007 at 01:05:16AM +0400, Anton Vorontsov wrote:
> On Wed, Apr 25, 2007 at 01:05:01PM -0700, Andrew Morton wrote:
> > On Wed, 25 Apr 2007 19:48:20 +0400 Anton Vorontsov <cbou@mail.ru> wrote:
> > 
> > > Andrew, would you please merge these patches into -mm? They're against
> > > today's Linus git tree.
> > 
> > Not for a while, sorry.  We already have enough material queued for 2.6.22
> > and a lot of it is broken.  So I am concentrating on fixing the remaining
> > bugs in 2.6.21 and getting some of the new bugs we have prepared for 2.6.22
> > fixed up.
> 
> Pity. Not trying to insist too hard, but this battery stuff can not break
> anything pre-existing in kernel. Also it's proven to work at least on five
> different embedded devices PXA2xx and S3C (HP iPaqs h2200, rx3000, h5550
> and hx4700), with two battery types. Plus I believe that this battery

Forgot h4000. Now there are five. ;-)

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.org/bd2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 0/7] Battery class, external power framework, ds2760 battery
  2007-04-25 21:05   ` Anton Vorontsov
  2007-04-25 21:19     ` Anton Vorontsov
@ 2007-04-25 22:47     ` Andrew Morton
  1 sibling, 0 replies; 13+ messages in thread
From: Andrew Morton @ 2007-04-25 22:47 UTC (permalink / raw)
  To: cbou; +Cc: linux-kernel, kernel-discuss

On Thu, 26 Apr 2007 01:05:16 +0400 Anton Vorontsov <cbou@mail.ru> wrote:

> On Wed, Apr 25, 2007 at 01:05:01PM -0700, Andrew Morton wrote:
> > On Wed, 25 Apr 2007 19:48:20 +0400 Anton Vorontsov <cbou@mail.ru> wrote:
> > 
> > > Andrew, would you please merge these patches into -mm? They're against
> > > today's Linus git tree.
> > 
> > Not for a while, sorry.  We already have enough material queued for 2.6.22
> > and a lot of it is broken.  So I am concentrating on fixing the remaining
> > bugs in 2.6.21 and getting some of the new bugs we have prepared for 2.6.22
> > fixed up.
> 
> Pity. Not trying to insist too hard, but this battery stuff can not break
> anything pre-existing in kernel.

That helps.

>  Also it's proven to work at least on five
> different embedded devices PXA2xx and S3C (HP iPaqs h2200, rx3000, h5550
> and hx4700), with two battery types. Plus I believe that this battery
> class more than suitable for OLPC guys (battery-2.6 tree).
> 
> Anyway, maybe it's even better if we'll boil battery stuff a *bit* more.
> 
> Also you didn't [directly] answered how long exactly we should wait,
> should we come back after 2.6.21 or 2.6.22 release? In later case it's a
> bit too long, isn't it?

I'm not sure yet.  I've saved away these patches.  They seemed to get a
decent review - let's aim for 2.6.22.


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2007-04-25 22:47 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-25 15:48 [PATCH 0/7] Battery class, external power framework, ds2760 battery Anton Vorontsov
2007-04-25 15:50 ` [PATCH 1/7] External power framework Anton Vorontsov
2007-04-25 15:50 ` [PATCH 2/7] pda power driver Anton Vorontsov
2007-04-25 15:50 ` [PATCH 3/7] Universal battery class Anton Vorontsov
2007-04-25 15:51 ` [PATCH 4/7] APM emulation driver for class batteries Anton Vorontsov
2007-04-25 15:51 ` [PATCH 5/7] 1-Wire ds2760 chip battery driver Anton Vorontsov
2007-04-25 15:52 ` [PATCH 6/7] remove "#if 0" from find_bus function, export it Anton Vorontsov
2007-04-25 15:52 ` [PATCH 7/7] ds2760 W1 slave Anton Vorontsov
2007-04-25 20:05 ` [PATCH 0/7] Battery class, external power framework, ds2760 battery Andrew Morton
2007-04-25 21:05   ` Anton Vorontsov
2007-04-25 21:19     ` Anton Vorontsov
2007-04-25 22:47     ` Andrew Morton
  -- strict thread matches above, loose matches on Subject: below --
2007-04-20 11:57 [PATCH 1/7] External power framework Anton Vorontsov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox