[BATTERY] Add support for PMU battery information

David Woodhouse dwmw2 at infradead.org
Tue Nov 7 22:38:28 EST 2006


Commit:     55410d878c5e034be97ce2ae3b0d7e16fb0f985f
Parent:     0195ef745141d018b842b43dc4c33e6adb710073
commit 55410d878c5e034be97ce2ae3b0d7e16fb0f985f
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Sat Oct 28 15:10:21 2006 +0300
Commit:     David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sat Oct 28 15:10:21 2006 +0300

    [BATTERY] Add support for PMU battery information
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/battery/pmu-battery.c |  265 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 265 insertions(+), 0 deletions(-)

diff --git a/drivers/battery/pmu-battery.c b/drivers/battery/pmu-battery.c
new file mode 100644
index 0000000..7b3aa6b
--- /dev/null
+++ b/drivers/battery/pmu-battery.c
@@ -0,0 +1,265 @@
+/*
+ * Battery class driver for Apple PMU
+ *
+ *	© 2006 David Woodhouse <dwmw2 at infradead.org>
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <linux/battery.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+static struct pmu_battery_dev {
+	struct battery_dev bdev;
+	struct pmu_battery_info *pmu_bat;
+	char name[16];
+} *pbats[PMU_MAX_BATTERIES];
+
+/*********************************************************************
+ *		Status flags
+ *********************************************************************/
+static ssize_t pmu_ac_status_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	unsigned long status = 
+                ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0) || 
+		pmu_battery_count == 0;
+
+	return battery_attribute_show_ac_status(buf, status);
+}
+
+static struct device_attribute dev_attr_ac_status =
+__ATTR(status, 0444, pmu_ac_status_show, NULL);
+
+static ssize_t pmu_bat_status_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	/* You are lost in a maze of twisty structures, all alike */
+	struct pmu_battery_dev *pbat = dev_get_drvdata(dev);
+	struct pmu_battery_info *pmu_bat = pbat->pmu_bat;
+	unsigned long status = 0;
+
+	if (pmu_bat->flags & PMU_BATT_PRESENT)
+		status |= BAT_STAT_PRESENT;
+	if (pmu_bat->flags & PMU_BATT_CHARGING)
+		status |= BAT_STAT_CHARGING;
+
+	return battery_attribute_show_status(buf, status);
+}
+static struct device_attribute dev_attr_bat_status =
+__ATTR(status, 0444, pmu_bat_status_show, NULL);
+
+/*********************************************************************
+ *		Integer attributes
+ *********************************************************************/
+
+#define ATTR_INT(_name) {				\
+	.attr.name = _name,				\
+	.attr.mode = 0444,				\
+	.show = pmu_bat_attr_int_show,			\
+}
+
+static int pmu_bat_attr_int_show(struct device *dev,
+				 struct device_attribute *attr, char *buf);
+
+static struct device_attribute attrs_int[] = {
+	ATTR_INT(BAT_INFO_CAP_LEFT),
+	ATTR_INT(BAT_INFO_CAP_LAST_FULL),
+	ATTR_INT(BAT_INFO_CURRENT),
+	ATTR_INT(BAT_INFO_VOLTAGE),
+	ATTR_INT(BAT_INFO_TIME_EMPTY),
+	ATTR_INT(BAT_INFO_CAP_PCT),
+};
+
+static int pmu_bat_attr_int_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct pmu_battery_dev *pbat = dev_get_drvdata(dev);
+	struct pmu_battery_info *pmu_bat = pbat->pmu_bat;
+
+	int field = attr - attrs_int;
+	long value;
+
+	switch (field) {
+	case 0:
+		value = pmu_bat->charge;
+		break;
+		
+	case 1:
+		value = pmu_bat->max_charge;
+		break;
+
+	case 2:
+		value = pmu_bat->amperage;
+		break;
+
+	case 3:
+		value = pmu_bat->voltage;
+		break;
+
+	case 4:
+		value = pmu_bat->time_remaining;
+		break;
+
+	case 5:
+		value = 100 * pmu_bat->charge / pmu_bat->max_charge;
+		break;
+
+	default:
+		BUG();
+	}
+	return 1 + sprintf(buf, "%ld\n", value);
+}
+
+
+/*********************************************************************
+ *		String attributes
+ *********************************************************************/
+
+#define ATTR_STR(_name) {				\
+	.attr.name = _name,				\
+	.attr.mode = 0444,				\
+	.show = pmu_bat_attr_str_show,			\
+}
+
+static int pmu_bat_attr_str_show(struct device *dev,
+				 struct device_attribute *attr, char *buf);
+
+static struct device_attribute attrs_str[] = {
+	ATTR_STR(BAT_INFO_CAP_UNITS),
+	ATTR_STR(BAT_INFO_MODEL),
+};
+
+static int pmu_bat_attr_str_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct pmu_battery_dev *pbat = dev_get_drvdata(dev);
+	struct pmu_battery_info *pmu_bat = pbat->pmu_bat;
+	int field = attr - attrs_str;
+	const char *str;
+
+
+	switch (field) {
+	case 0:
+		str = "mW";
+		break;
+
+	case 1:
+		switch (pmu_bat->flags & PMU_BATT_TYPE_MASK) {
+		case PMU_BATT_TYPE_SMART:
+			str = "Smart";
+			break;
+		case PMU_BATT_TYPE_COMET:
+			str = "Comet";
+			break;
+		case PMU_BATT_TYPE_HOOPER:
+			str = "Hooper";
+			break;
+		default:
+			str = "unknown";
+			break;
+		}
+		break;
+	default:
+		BUG();
+	}
+	return 1 + sprintf(buf, "%s\n", str);
+}
+
+/*********************************************************************
+ *		Initialisation
+ *********************************************************************/
+
+static struct battery_dev pmu_ac = {
+	.name = "PMU AC",
+	.type = PWRDEV_TYPE_AC,
+	.status_cap = BAT_STAT_PRESENT
+};
+
+static struct platform_device *bat_plat_dev;
+
+int __init pmu_bat_init(void)
+{
+	int ret;
+	int i, j;
+
+	bat_plat_dev =
+	    platform_device_register_simple("pmu-battery", 0, NULL, 0);
+	if (IS_ERR(bat_plat_dev))
+		return PTR_ERR(bat_plat_dev);
+
+	ret = battery_device_register(&bat_plat_dev->dev, &pmu_ac);
+	if (ret) {
+		platform_device_unregister(bat_plat_dev);
+		return ret;
+	}
+
+	device_create_file(pmu_ac.dev, &dev_attr_ac_status);
+
+	for (i=0; i < pmu_battery_count; i++) {
+		struct pmu_battery_dev *pbat = kmalloc(sizeof(*pbat), GFP_KERNEL);
+		if (!pbat)
+			break;
+		memset(pbat, 0, sizeof(*pbat));
+
+		sprintf(pbat->name, "PMU battery %d\n", i);
+		pbat->bdev.name = pbat->name;
+		pbat->bdev.type = PWRDEV_TYPE_BATTERY;
+		pbat->bdev.status_cap = BAT_STAT_PRESENT|BAT_STAT_CHARGING;
+
+		ret = battery_device_register(&bat_plat_dev->dev, &pbat->bdev);
+		if (ret) {
+			kfree(pbat);
+			break;
+		}
+		pbats[i] = pbat;
+		pbat->pmu_bat = &pmu_batteries[i];
+
+		device_create_file(pbat->bdev.dev, &dev_attr_bat_status);
+
+		for (j = 0; j < ARRAY_SIZE(attrs_int); j++)
+			device_create_file(pbat->bdev.dev, &attrs_int[j]);
+		for (j = 0; j < ARRAY_SIZE(attrs_str); j++)
+			device_create_file(pbat->bdev.dev, &attrs_str[j]);
+	}
+
+	return 0;
+}
+
+void __exit pmu_bat_exit(void)
+{
+	int i, j;
+	device_remove_file(pmu_ac.dev, &dev_attr_ac_status);
+	battery_device_unregister(&pmu_ac);
+
+	for (i=0; i < PMU_MAX_BATTERIES; i++) {
+		if (!pbats[i])
+			continue;
+
+		device_remove_file(pbats[i]->bdev.dev, &dev_attr_bat_status);
+
+		for (j = 0; j < ARRAY_SIZE(attrs_int); j++)
+			device_remove_file(pbats[i]->bdev.dev, &attrs_int[j]);
+		for (j = 0; j < ARRAY_SIZE(attrs_str); j++)
+			device_remove_file(pbats[i]->bdev.dev, &attrs_str[j]);
+		battery_device_unregister(&pbats[i]->bdev);
+		kfree(pbats[i]);
+	}
+	platform_device_unregister(bat_plat_dev);
+}
+
+module_init(pmu_bat_init);
+module_exit(pmu_bat_exit);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2 at infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PMU battery class driver");


More information about the Commits-kernel mailing list