gta01_battery: generalise the driver, add capacity estimation
authorPaul Fertser <fercerpav@gmail.com>
Sun, 26 Jul 2009 14:00:29 +0000 (18:00 +0400)
committerPaul Fertser <fercerpav@gmail.com>
Sun, 2 Aug 2009 07:24:27 +0000 (11:24 +0400)
Check if machine-specific hooks exist before calling, this allows to use
this driver with gta02 or any other device that is able to provide battery
voltage readings.

Report an estimate of capacity percentage left, based on discharge curves.
Strictly speaking, this is a violation of power_supply class specs but
since this is the only driver for dumb batteries and userspace lacks any
library to do the estimation itself, we have to stuff it here.

Please keep in mind that this estimation is by no means accurate, usually
cell phones have only 5 bars to represent charge status.

Credit goes to Joerg Reisenweber and SpeedEvil for helping with gathering
and analysing the data.

Signed-off-by: Paul Fertser <fercerpav@gmail.com>
drivers/power/gta01_battery.c

index 5b1a6c9..3c7c497 100644 (file)
@@ -23,8 +23,35 @@ static enum power_supply_property gta01_bat_props[] = {
        POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
 };
 
+static int gta01_bat_voltscale(int volt)
+{
+       /* This table is suggested by SpeedEvil based on analysis of
+        * experimental data */
+       static const int lut[][2] = {
+               { 4120, 100 },
+               { 3900, 60 },
+               { 3740, 25 },
+               { 3600, 5 },
+               { 3000, 0 } };
+       int i, res = 0;
+
+       if (volt > lut[0][0])
+               res = lut[0][1];
+       else
+               for (i = 0; lut[i][1]; i++) {
+                       if (volt <= lut[i][0] && volt >= lut[i+1][0]) {
+                               res = lut[i][1] - (lut[i][0]-volt)*
+                                       (lut[i][1]-lut[i+1][1])/
+                                       (lut[i][0]-lut[i+1][0]);
+                               break;
+                       }
+               }
+       return res;
+}
+
 static int gta01_bat_get_property(struct power_supply *psy,
                                       enum power_supply_property psp,
                                       union power_supply_propval *val)
@@ -33,20 +60,37 @@ static int gta01_bat_get_property(struct power_supply *psy,
        
        switch(psp) {
        case POWER_SUPPLY_PROP_STATUS:
-               if (bat->pdata->get_charging_status())
-                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               if (bat->pdata->get_charging_status)
+                       if (bat->pdata->get_charging_status())
+                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+                       else
+                               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
                else
-                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = bat->pdata->get_voltage();
+               if (bat->pdata->get_voltage)
+                       val->intval = bat->pdata->get_voltage();
+               else
+                       val->intval = 0;
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
-               val->intval = bat->pdata->get_current();
+               if (bat->pdata->get_current)
+                       val->intval = bat->pdata->get_current();
+               else
+                       val->intval = 0;
                break;
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = 1; /* You must never run GTA01 without battery. */
                break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (bat->pdata->get_voltage)
+                       val->intval = gta01_bat_voltscale(
+                                       bat->pdata->get_voltage()/1000);
+               else
+                       val->intval = 0;
+               break;
+
        default:
                return -EINVAL;
        }