Attached is a patch to add:
Battery health
USB and AC power indicators
Change to threshold for detecting charge status
The last item is problematical for me. The battery tends to hover at 11
for BQ27000_AI_L and BQ27000_TTF_L is not zero. This is with 2 usb
connections - one direct, 1 via debug board - directly into laptop. My
blackberry complains it doesn't get enough juice under linux, so maybe
that is the issue. Capacity on the battery is at 100%, though.
Sean
diff --git a/drivers/power/bq27000_battery.c b/drivers/power/bq27000_battery.c
index d19186a..17a8dcf 100644
--- a/drivers/power/bq27000_battery.c
+++ b/drivers/power/bq27000_battery.c
@@ -32,6 +32,7 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/bq27000_battery.h>
+#include <linux/apm-emulation.h>
enum bq27000_regs {
/* RAM regs */
@@ -110,9 +111,13 @@ enum bq27000_status_flags {
#define NANOVOLTS_UNIT 3750
+#define BQ27000_ACTIVITY_THRESHOLD 12
+
struct bq27000_device_info {
struct device *dev;
struct power_supply bat;
+ struct power_supply ac;
+ struct power_supply usb;
int rsense_mohms; /* from platform */
@@ -152,13 +157,10 @@ static int hdq_read16(struct bq27000_device_info *di, int address)
return -ETIME;
}
-#define to_bq27000_device_info(x) container_of((x), \
- struct bq27000_device_info, \
- bat);
-
static void bq27000_battery_external_power_changed(struct power_supply *psy)
{
- struct bq27000_device_info *di = to_bq27000_device_info(psy);
+ struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, bat);
+
dev_dbg(di->dev, "%s\n", __FUNCTION__);
}
@@ -168,7 +170,7 @@ static int bq27000_battery_get_property(struct power_supply *psy,
union power_supply_propval *val)
{
int v, n;
- struct bq27000_device_info *di = to_bq27000_device_info(psy);
+ struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, bat);
if (!(di->hdq_initialized)())
return -EINVAL;
@@ -179,7 +181,7 @@ static int bq27000_battery_get_property(struct power_supply *psy,
v = hdq_read16(di, BQ27000_AI_L);
if (v < 0)
return v;
- if (v < 2) { /* no real activity on the battery */
+ if (v < BQ27000_ACTIVITY_THRESHOLD) { /* no real activity on the battery */
if (!hdq_read16(di, BQ27000_TTF_L))
val->intval = POWER_SUPPLY_STATUS_FULL;
else
@@ -195,6 +197,15 @@ static int bq27000_battery_get_property(struct power_supply *psy,
else
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+ /* Do we have accurate readings... */
+ v = (di->hdq_read)(BQ27000_FLAGS);
+ if (v < 0)
+ return v;
+ if (v & BQ27000_STATUS_VDQ)
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
v = hdq_read16(di, BQ27000_VOLT_L);
if (v < 0)
@@ -252,6 +263,9 @@ static int bq27000_battery_get_property(struct power_supply *psy,
return v;
val->intval = 60 * v;
break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = "gta02";
+ break;
default:
return -EINVAL;
}
@@ -259,8 +273,77 @@ static int bq27000_battery_get_property(struct power_supply *psy,
return 0;
}
+extern void (*apm_get_power_status)(struct apm_power_info *);
+
+static int ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, ac);
+ struct apm_power_info info;
+
+ if (!(di->hdq_initialized)())
+ return -EINVAL;
+
+ apm_get_power_status (&info);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ switch (info.ac_line_status) {
+ case APM_AC_ONLINE:
+ val->intval = 1;
+ break;
+ case APM_AC_OFFLINE:
+ val->intval = 0;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, usb);
+ struct apm_power_info info;
+
+ if (!(di->hdq_initialized)())
+ return -EINVAL;
+
+ apm_get_power_status (&info);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ switch (info.ac_line_status) {
+ case APM_AC_ONLINE:
+ val->intval = 1;
+ break;
+ case APM_AC_OFFLINE:
+ val->intval = 0;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static enum power_supply_property bq27000_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL,
@@ -273,6 +356,10 @@ static enum power_supply_property bq27000_battery_props[] = {
POWER_SUPPLY_PROP_CAPACITY
};
+static enum power_supply_property power_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
static int bq27000_battery_probe(struct platform_device *pdev)
{
int retval = 0;
@@ -292,17 +379,30 @@ static int bq27000_battery_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data;
di->dev = &pdev->dev;
/* di->w1_dev = pdev->dev.parent; */
- di->bat.name = pdata->name;
- di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->rsense_mohms = pdata->rsense_mohms;
+ di->hdq_read = pdata->hdq_read;
+ di->hdq_write = pdata->hdq_write;
+ di->hdq_initialized = pdata->hdq_initialized;
+
+ di->bat.name = "battery";
+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
di->bat.properties = bq27000_battery_props;
di->bat.num_properties = ARRAY_SIZE(bq27000_battery_props);
di->bat.get_property = bq27000_battery_get_property;
di->bat.external_power_changed =
bq27000_battery_external_power_changed;
- di->hdq_read = pdata->hdq_read;
- di->hdq_write = pdata->hdq_write;
- di->rsense_mohms = pdata->rsense_mohms;
- di->hdq_initialized = pdata->hdq_initialized;
+
+ di->ac.name = "ac";
+ di->ac.type = POWER_SUPPLY_TYPE_MAINS;
+ di->ac.properties = power_props;
+ di->ac.num_properties = ARRAY_SIZE(power_props);
+ di->ac.get_property = ac_get_property;
+
+ di->usb.name = "usb";
+ di->usb.type = POWER_SUPPLY_TYPE_USB;
+ di->usb.properties = power_props;
+ di->usb.num_properties = ARRAY_SIZE(power_props);
+ di->usb.get_property = usb_get_property;
retval = power_supply_register(&pdev->dev, &di->bat);
if (retval) {
@@ -310,8 +410,24 @@ static int bq27000_battery_probe(struct platform_device *pdev)
goto batt_failed;
}
+ retval = power_supply_register(&pdev->dev, &di->ac);
+ if (retval) {
+ dev_err(di->dev, "failed to register ac\n");
+ goto ac_failed;
+ }
+
+ retval = power_supply_register(&pdev->dev, &di->usb);
+ if (retval) {
+ dev_err(di->dev, "failed to register usb\n");
+ goto usb_failed;
+ }
+
return 0;
+usb_failed:
+ power_supply_unregister(&di->ac);
+ac_failed:
+ power_supply_unregister(&di->bat);
batt_failed:
kfree(di);
di_alloc_failed:
@@ -323,6 +439,8 @@ static int bq27000_battery_remove(struct platform_device *pdev)
struct bq27000_device_info *di = platform_get_drvdata(pdev);
power_supply_unregister(&di->bat);
+ power_supply_unregister(&di->ac);
+ power_supply_unregister(&di->usb);
return 0;
}