[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