[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