[BATTERY] Update OLPC battery driver
David Woodhouse
dwmw2 at infradead.org
Tue Nov 7 22:38:26 EST 2006
Commit: 0aa9acc1c47dbb3c285d61bb55a1a363433c129f
Parent: 0513b2f5473f1e8bb8880c9dee8e1d63af86336e
commit 0aa9acc1c47dbb3c285d61bb55a1a363433c129f
Author: David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Tue Oct 24 16:14:59 2006 +0100
Commit: David Woodhouse <dwmw2 at infradead.org>
CommitDate: Tue Oct 24 16:14:59 2006 +0100
[BATTERY] Update OLPC battery driver
- Use platform_device
- Implement all available status fields
Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
drivers/battery/olpc-battery.c | 278 ++++++++++++++++++++++++++--------------
include/linux/battery.h | 7 +
2 files changed, 183 insertions(+), 102 deletions(-)
diff --git a/drivers/battery/olpc-battery.c b/drivers/battery/olpc-battery.c
index 09ea57a..2057b7c 100644
--- a/drivers/battery/olpc-battery.c
+++ b/drivers/battery/olpc-battery.c
@@ -14,6 +14,7 @@ #include <linux/device.h>
#include <linux/battery.h>
#include <linux/spinlock.h>
#include <linux/err.h>
+#include <linux/platform_device.h>
#include <asm/io.h>
#define wBAT_VOLTAGE 0xf900 /* *9.76/32, mV */
@@ -34,27 +35,9 @@ #define sBAT_NiMH 8
#define sPOWER_FLAG 0xfa40
#define ADAPTER_IN 1
-struct olpc_bat_attr_int {
- struct device_attribute dev_attr;
- unsigned short adr1, adr2;
- int mul, div;
-};
-
-#define ATTR_INT(_name, _adr1, _adr2, _mul, _div) { \
- .dev_attr.attr.name = _name, \
- .dev_attr.attr.mode = 0444, \
- .dev_attr.show = olpc_bat_attr_int_show, \
- .adr1 = _adr1, \
- .adr2 = _adr2, \
- .mul = _mul, \
- .div = _div, \
-}
-
-#define ATTR_BYTE(_name, _adr, _mul, _div) \
- ATTR_INT(_name, _adr, _adr+1, _mul, _div)
-#define ATTR_WORD(_name, _adr, _mul, _div) \
- ATTR_INT(_name, _adr, _adr+1, _mul, _div)
-
+/*********************************************************************
+ * EC locking and access
+ *********************************************************************/
static int lock_ec(void)
{
@@ -64,68 +47,112 @@ static int lock_ec(void)
unsigned char lock = inb(0x6c) & 0x80;
if (!lock)
return 0;
- if (time_after(jiffies, timeo))
+ if (time_after(jiffies, timeo)) {
+ printk(KERN_ERR "Failed to lock EC for battery access\n");
return 1;
+ }
yield();
- }
+ }
}
static void unlock_ec(void)
{
- outb(0xff, 0x6c);
+ outb(0xff, 0x6c);
}
-unsigned char read_ec_byte(unsigned short adr)
+static unsigned char read_ec_byte(unsigned short adr)
{
- outb(adr >> 8, 0x381);
- outb(adr, 0x382);
- return inb(0x383);
+ outb(adr >> 8, 0x381);
+ outb(adr, 0x382);
+ return inb(0x383);
}
unsigned short read_ec_word(unsigned short adr)
{
- return (read_ec_byte(adr) << 8) | read_ec_byte(adr+1);
+ return (read_ec_byte(adr) << 8) | read_ec_byte(adr+1);
}
-#if 0
-unsigned long olpc_bat_status(struct battery_dev *cdev, unsigned long mask)
+
+/*********************************************************************
+ * Status flags
+ *********************************************************************/
+static ssize_t olpc_ac_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- unsigned long result = 0;
- unsigned short tmp;
+ unsigned long status;
- if (lock_ec()) {
- printk(KERN_ERR "Failed to lock EC for battery access\n");
- return BAT_STAT_ERROR;
- }
+ if (lock_ec())
+ return -EIO;
- if (mask & BAT_STAT_AC) {
- if (read_ec_byte(sPOWER_FLAG) & ADAPTER_IN)
- result |= BAT_STAT_AC;
+ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) {
+ unlock_ec();
+ return -ENODEV;
}
- if (mask & (BAT_STAT_PRESENT|BAT_STAT_FULL|BAT_STAT_FIRE|BAT_STAT_LOW|BAT_STAT_DISCHARGING)) {
- tmp = read_ec_byte(sMBAT_STATUS);
+
+ status = read_ec_byte(sPOWER_FLAG) & ADAPTER_IN;
+
+ unlock_ec();
+
+ return battery_attribute_show_ac_status(buf, status);
+}
+
+static struct device_attribute dev_attr_ac_status =
+ __ATTR(status, 0444, olpc_ac_status_show, NULL);
+
+static ssize_t olpc_bat_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long status = 0;
+ unsigned short tmp;
+
+ if (lock_ec())
+ return -EIO;
+
+ tmp = read_ec_byte(sMBAT_STATUS);
- if (tmp & sBAT_PRESENT)
- result |= BAT_STAT_PRESENT;
- if (tmp & sBAT_FULL)
- result |= BAT_STAT_FULL;
- if (tmp & sBAT_DESTROY)
- result |= BAT_STAT_FIRE;
- if (tmp & sBAT_LOW)
- result |= BAT_STAT_LOW;
- if (tmp & sBAT_DISCHG)
- result |= BAT_STAT_DISCHARGING;
- }
- if (mask & (BAT_STAT_CHARGING|BAT_STAT_OVERTEMP)) {
- tmp = read_ec_byte(sMCHARGE_STATUS);
- if (tmp & sBAT_CHARGE)
- result |= BAT_STAT_CHARGING;
- if (tmp & sBAT_OVERTEMP)
- result |= BAT_STAT_OVERTEMP;
- }
+ if (tmp & sBAT_PRESENT)
+ status |= BAT_STAT_PRESENT;
+ if (tmp & sBAT_FULL)
+ status |= BAT_STAT_FULL;
+ if (tmp & sBAT_DESTROY)
+ status |= BAT_STAT_CRITICAL;
+ if (tmp & sBAT_LOW)
+ status |= BAT_STAT_LOW;
+ if (tmp & sBAT_DISCHG)
+ status |= BAT_STAT_DISCHARGING;
+
+ tmp = read_ec_byte(sMCHARGE_STATUS);
+ if (tmp & sBAT_CHARGE)
+ status |= BAT_STAT_CHARGING;
+ if (tmp & sBAT_OVERTEMP)
+ status |= BAT_STAT_OVERTEMP;
+
unlock_ec();
- return result;
+ return battery_attribute_show_status(buf, status);
}
-#endif
+static struct device_attribute dev_attr_bat_status =
+ __ATTR(status, 0444, olpc_bat_status_show, NULL);
+
+/*********************************************************************
+ * Integer attributes
+ *********************************************************************/
+
+struct olpc_bat_attr_int {
+ struct device_attribute dev_attr;
+ unsigned short adr;
+ int mul, div;
+};
+
+#define ATTR_INT(_name, _adr, _mul, _div) { \
+ .dev_attr.attr.name = _name, \
+ .dev_attr.attr.mode = 0444, \
+ .dev_attr.show = olpc_bat_attr_int_show, \
+ .adr = _adr, \
+ .mul = _mul, \
+ .div = _div, \
+}
+
static int olpc_bat_attr_int_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -136,59 +163,92 @@ static int olpc_bat_attr_int_show(struct
if (lock_ec())
return -EIO;
- value = read_ec_byte(battr->adr1);
- if (battr->adr2)
- value |= read_ec_byte(battr->adr2) << 8;
+ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) {
+ unlock_ec();
+ return -ENODEV;
+ }
+
+ if (battr->adr == SOC) {
+ value = read_ec_byte(battr->adr);
+ } else {
+ value = (signed short)read_ec_word(battr->adr);
+ value *= battr->mul;
+ value /= battr->div;
+ }
unlock_ec();
- value *= battr->mul;
- value /= battr->div;
-
return 1 + sprintf(buf, "%ld\n", value);
}
static struct olpc_bat_attr_int attrs_int[] = {
- ATTR_WORD(BAT_INFO_VOLTAGE, wBAT_VOLTAGE, 9760, 32000),
- ATTR_WORD(BAT_INFO_CURRENT, wBAT_CURRENT, 15625, 120000),
- ATTR_WORD(BAT_INFO_TEMP1, wBAT_TEMP, 1000, 256),
- ATTR_WORD(BAT_INFO_TEMP2, wAMB_TEMP, 1000, 256),
- ATTR_BYTE(BAT_INFO_CHARGE_PCT, SOC, 1, 1)
+ ATTR_INT(BAT_INFO_VOLTAGE, wBAT_VOLTAGE, 9760, 32000),
+ ATTR_INT(BAT_INFO_CURRENT, wBAT_CURRENT, 15625, 120000),
+ ATTR_INT(BAT_INFO_TEMP1, wBAT_TEMP, 1000, 256),
+ ATTR_INT(BAT_INFO_TEMP2, wAMB_TEMP, 1000, 256),
+ ATTR_INT(BAT_INFO_CHARGE_PCT, SOC, 1, 1)
};
-#if 0
-static int olpc_bat_attr_str_show(struct battery_dev *dev,
+
+
+/*********************************************************************
+ * String attributes
+ *********************************************************************/
+
+struct olpc_bat_attr_str {
+ struct device_attribute dev_attr;
+ char *str;
+};
+
+#define ATTR_STR(_name, _str) { \
+ .dev_attr.attr.name = _name, \
+ .dev_attr.attr.mode = 0444, \
+ .dev_attr.show = olpc_bat_attr_str_show, \
+ .str = (char *)_str, \
+}
+
+static int olpc_bat_attr_str_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct olpc_bat_attr_int *battr = (void *)attr;
+ struct olpc_bat_attr_str *sattr = (void *)attr;
+ unsigned short tmp;
int ret = 0;
- if (attr == BAT_INFO_TYPE) {
- snprintf(str, len, "OLPC");
- } else if (attr == BAT_INFO_TEMP1_NAME) {
- snprintf(str, len, "battery");
- } else if (attr == BAT_INFO_TEMP2_NAME) {
- snprintf(str, len, "ambient");
- } else if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) {
+ /* Static strings are simple */
+ if ((unsigned long)sattr->str > PAGE_SIZE) {
+ return 1 + sprintf(buf, "%s\n", sattr->str);
+ }
+
+ if (lock_ec())
+ ret = -EIO;
+
+ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT))
ret = -ENODEV;
- } else if (attr == BAT_INFO_TECHNOLOGY) {
- if (lock_ec())
- ret = -EIO;
- else {
- unsigned short tmp = read_ec_byte(sMCHARGE_STATUS);
- if (tmp & sBAT_NiMH)
- snprintf(str, len, "NiMH");
- else
- snprintf(str, len, "unknown");
- }
- unlock_ec();
- } else {
- ret = -EINVAL;
+ else switch((unsigned long)sattr->str) {
+ case 1:
+ tmp = read_ec_byte(sMCHARGE_STATUS);
+ if (tmp & sBAT_NiMH)
+ ret = 1 + sprintf(buf, "NiMH\n");
+ else
+ ret = 1 + sprintf(buf, "unknown\n");
+ break;
+ default:
+ printk(KERN_ERR "Unknown string type %p\n", sattr->str);
}
+ unlock_ec();
return ret;
}
-#endif
+
+static struct olpc_bat_attr_str attrs_str[] = {
+ ATTR_STR(BAT_INFO_TEMP1_NAME, "battery"),
+ ATTR_STR(BAT_INFO_TEMP2_NAME, "ambient"),
+ ATTR_STR(BAT_INFO_TECHNOLOGY, 1),
+};
+
+/*********************************************************************
+ * Initialisation
+ *********************************************************************/
static struct battery_dev olpc_bat = {
.name = "OLPC battery",
@@ -199,6 +259,7 @@ static struct battery_dev olpc_ac = {
.name = "OLPC AC",
.type = PWRDEV_TYPE_AC,
};
+static struct platform_device *bat_plat_dev;
int __init olpc_bat_init(void)
{
@@ -220,19 +281,30 @@ int __init olpc_bat_init(void)
return -ENODEV;
}
- ret = battery_device_register(NULL, &olpc_bat);
- if (ret)
+ bat_plat_dev = platform_device_register_simple("olpc-battery", 0, NULL, 0);
+ if (IS_ERR(bat_plat_dev))
goto out_rel;
- ret = battery_device_register(NULL, &olpc_ac);
+ ret = battery_device_register(&bat_plat_dev->dev, &olpc_bat);
+ if (ret)
+ goto out_plat;
+
+ ret = battery_device_register(&bat_plat_dev->dev, &olpc_ac);
if (ret) {
battery_device_unregister(&olpc_bat);
+ out_plat:
+ platform_device_unregister(bat_plat_dev);
out_rel:
release_region(0x380, 4);
return ret;
}
for (i=0; i < ARRAY_SIZE(attrs_int); i++)
device_create_file(olpc_bat.dev, &attrs_int[i].dev_attr);
+ for (i=0; i < ARRAY_SIZE(attrs_str); i++)
+ device_create_file(olpc_bat.dev, &attrs_str[i].dev_attr);
+
+ device_create_file(olpc_bat.dev, &dev_attr_bat_status);
+ device_create_file(olpc_ac.dev, &dev_attr_ac_status);
return ret;
}
@@ -241,10 +313,16 @@ void __exit olpc_bat_exit(void)
{
int i;
+ device_remove_file(olpc_bat.dev, &dev_attr_bat_status);
+ device_remove_file(olpc_ac.dev, &dev_attr_ac_status);
+
for (i=0; i < ARRAY_SIZE(attrs_int); i++)
device_remove_file(olpc_bat.dev, &attrs_int[i].dev_attr);
+ for (i=0; i < ARRAY_SIZE(attrs_str); i++)
+ device_remove_file(olpc_bat.dev, &attrs_str[i].dev_attr);
battery_device_unregister(&olpc_ac);
battery_device_unregister(&olpc_bat);
+ platform_device_unregister(bat_plat_dev);
release_region(0x380, 4);
}
diff --git a/include/linux/battery.h b/include/linux/battery.h
index 1688d07..6148d5f 100644
--- a/include/linux/battery.h
+++ b/include/linux/battery.h
@@ -22,8 +22,8 @@ struct class_device;
/*
* Battery Core
*/
-#define PWRDEV_TYPE_BATTERY 1
-#define PWRDEV_TYPE_AC 2
+#define PWRDEV_TYPE_BATTERY 0
+#define PWRDEV_TYPE_AC 1
#define BAT_STAT_PRESENT (1<<0)
#define BAT_STAT_LOW (1<<1)
@@ -37,6 +37,9 @@ #define BAT_STAT_CHARGE_DONE (1<<8)
/* Thou shalt not export any attributes in sysfs except these, and
with these units: */
+#define BAT_INFO_STATUS "status" /* For AC: "on-line" or "off-line" */
+ /* For battery: ... */
+
#define BAT_INFO_TEMP1 "temp1" /* °C/1000 */
#define BAT_INFO_TEMP1_NAME "temp1_name" /* string */
More information about the Commits-kernel
mailing list