power_supply: bq27x00: add status and time properties
[pandora-kernel.git] / drivers / power / bq27x00_battery.c
index 62bb981..5d940fa 100644 (file)
 #include <linux/i2c.h>
 #include <asm/unaligned.h>
 
-#define DRIVER_VERSION                 "1.0.0"
+#define DRIVER_VERSION                 "1.1.0"
 
 #define BQ27x00_REG_TEMP               0x06
 #define BQ27x00_REG_VOLT               0x08
-#define BQ27x00_REG_RSOC               0x0B /* Relative State-of-Charge */
 #define BQ27x00_REG_AI                 0x14
 #define BQ27x00_REG_FLAGS              0x0A
+#define BQ27x00_REG_TTE                        0x16
+#define BQ27x00_REG_TTF                        0x18
+#define BQ27x00_REG_TTECP              0x26
+
+#define BQ27000_REG_RSOC               0x0B /* Relative State-of-Charge */
+#define BQ27000_FLAG_CHGS              BIT(7)
+
+#define BQ27500_REG_SOC                        0x2c
+#define BQ27500_FLAG_DSC               BIT(0)
+#define BQ27500_FLAG_FC                        BIT(9)
 
 /* If the system has several batteries we need a different name for each
  * of them...
@@ -46,25 +55,28 @@ struct bq27x00_access_methods {
                struct bq27x00_device_info *di);
 };
 
+enum bq27x00_chip { BQ27000, BQ27500 };
+
 struct bq27x00_device_info {
        struct device           *dev;
        int                     id;
-       int                     voltage_uV;
-       int                     current_uA;
-       int                     temp_C;
-       int                     charge_rsoc;
        struct bq27x00_access_methods   *bus;
        struct power_supply     bat;
+       enum bq27x00_chip       chip;
 
        struct i2c_client       *client;
 };
 
 static enum power_supply_property bq27x00_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_PRESENT,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CAPACITY,
        POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
 };
 
 /*
@@ -74,16 +86,11 @@ static enum power_supply_property bq27x00_battery_props[] = {
 static int bq27x00_read(u8 reg, int *rt_value, int b_single,
                        struct bq27x00_device_info *di)
 {
-       int ret;
-
-       ret = di->bus->read(reg, rt_value, b_single, di);
-       *rt_value = be16_to_cpu(*rt_value);
-
-       return ret;
+       return di->bus->read(reg, rt_value, b_single, di);
 }
 
 /*
- * Return the battery temperature in Celsius degrees
+ * Return the battery temperature in tenths of degree Celsius
  * Or < 0 if something fails.
  */
 static int bq27x00_battery_temperature(struct bq27x00_device_info *di)
@@ -97,7 +104,10 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di)
                return ret;
        }
 
-       return (temp >> 2) - 273;
+       if (di->chip == BQ27500)
+               return temp - 2731;
+       else
+               return ((temp >> 2) - 273) * 10;
 }
 
 /*
@@ -134,15 +144,22 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di)
                dev_err(di->dev, "error reading current\n");
                return 0;
        }
-       ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
-       if (ret < 0) {
-               dev_err(di->dev, "error reading flags\n");
-               return 0;
-       }
-       if ((flags & (1 << 7)) != 0) {
-               dev_dbg(di->dev, "negative current!\n");
-               return -curr;
+
+       if (di->chip == BQ27500) {
+               /* bq27500 returns signed value */
+               curr = (int)(s16)curr;
+       } else {
+               ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
+               if (ret < 0) {
+                       dev_err(di->dev, "error reading flags\n");
+                       return 0;
+               }
+               if (flags & BQ27000_FLAG_CHGS) {
+                       dev_dbg(di->dev, "negative current!\n");
+                       return -curr;
+               }
        }
+
        return curr;
 }
 
@@ -155,13 +172,70 @@ static int bq27x00_battery_rsoc(struct bq27x00_device_info *di)
        int ret;
        int rsoc = 0;
 
-       ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di);
+       if (di->chip == BQ27500)
+               ret = bq27x00_read(BQ27500_REG_SOC, &rsoc, 0, di);
+       else
+               ret = bq27x00_read(BQ27000_REG_RSOC, &rsoc, 1, di);
        if (ret) {
                dev_err(di->dev, "error reading relative State-of-Charge\n");
                return ret;
        }
 
-       return rsoc >> 8;
+       return rsoc;
+}
+
+static int bq27x00_battery_status(struct bq27x00_device_info *di,
+                                 union power_supply_propval *val)
+{
+       int flags = 0;
+       int status;
+       int ret;
+
+       ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di);
+       if (ret < 0) {
+               dev_err(di->dev, "error reading flags\n");
+               return ret;
+       }
+
+       if (di->chip == BQ27500) {
+               if (flags & BQ27500_FLAG_FC)
+                       status = POWER_SUPPLY_STATUS_FULL;
+               else if (flags & BQ27500_FLAG_DSC)
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+       } else {
+               if (flags & BQ27000_FLAG_CHGS)
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       val->intval = status;
+       return 0;
+}
+
+/*
+ * Read a time register.
+ * Return < 0 if something fails.
+ */
+static int bq27x00_battery_time(struct bq27x00_device_info *di, int reg,
+                               union power_supply_propval *val)
+{
+       int tval = 0;
+       int ret;
+
+       ret = bq27x00_read(reg, &tval, 0, di);
+       if (ret) {
+               dev_err(di->dev, "error reading register %02x\n", reg);
+               return ret;
+       }
+
+       if (tval == 65535)
+               return -ENODATA;
+
+       val->intval = tval * 60;
+       return 0;
 }
 
 #define to_bq27x00_device_info(x) container_of((x), \
@@ -171,9 +245,13 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
                                        enum power_supply_property psp,
                                        union power_supply_propval *val)
 {
+       int ret = 0;
        struct bq27x00_device_info *di = to_bq27x00_device_info(psy);
 
        switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = bq27x00_battery_status(di, val);
+               break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = bq27x00_battery_voltage(di);
@@ -189,11 +267,20 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_TEMP:
                val->intval = bq27x00_battery_temperature(di);
                break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+               ret = bq27x00_battery_time(di, BQ27x00_REG_TTE, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+               ret = bq27x00_battery_time(di, BQ27x00_REG_TTECP, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+               ret = bq27x00_battery_time(di, BQ27x00_REG_TTF, val);
+               break;
        default:
                return -EINVAL;
        }
 
-       return 0;
+       return ret;
 }
 
 static void bq27x00_powersupply_init(struct bq27x00_device_info *di)
@@ -206,10 +293,10 @@ static void bq27x00_powersupply_init(struct bq27x00_device_info *di)
 }
 
 /*
- * BQ27200 specific code
+ * i2c specific code
  */
 
-static int bq27200_read(u8 reg, int *rt_value, int b_single,
+static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single,
                        struct bq27x00_device_info *di)
 {
        struct i2c_client *client = di->client;
@@ -238,7 +325,7 @@ static int bq27200_read(u8 reg, int *rt_value, int b_single,
                err = i2c_transfer(client->adapter, msg, 1);
                if (err >= 0) {
                        if (!b_single)
-                               *rt_value = get_unaligned_be16(data);
+                               *rt_value = get_unaligned_le16(data);
                        else
                                *rt_value = data[0];
 
@@ -248,7 +335,7 @@ static int bq27200_read(u8 reg, int *rt_value, int b_single,
        return err;
 }
 
-static int bq27200_battery_probe(struct i2c_client *client,
+static int bq27x00_battery_probe(struct i2c_client *client,
                                 const struct i2c_device_id *id)
 {
        char *name;
@@ -267,7 +354,7 @@ static int bq27200_battery_probe(struct i2c_client *client,
        if (retval < 0)
                return retval;
 
-       name = kasprintf(GFP_KERNEL, "bq27200-%d", num);
+       name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
        if (!name) {
                dev_err(&client->dev, "failed to allocate device name\n");
                retval = -ENOMEM;
@@ -281,6 +368,7 @@ static int bq27200_battery_probe(struct i2c_client *client,
                goto batt_failed_2;
        }
        di->id = num;
+       di->chip = id->driver_data;
 
        bus = kzalloc(sizeof(*bus), GFP_KERNEL);
        if (!bus) {
@@ -293,7 +381,7 @@ static int bq27200_battery_probe(struct i2c_client *client,
        i2c_set_clientdata(client, di);
        di->dev = &client->dev;
        di->bat.name = name;
-       bus->read = &bq27200_read;
+       bus->read = &bq27x00_read_i2c;
        di->bus = bus;
        di->client = client;
 
@@ -323,7 +411,7 @@ batt_failed_1:
        return retval;
 }
 
-static int bq27200_battery_remove(struct i2c_client *client)
+static int bq27x00_battery_remove(struct i2c_client *client)
 {
        struct bq27x00_device_info *di = i2c_get_clientdata(client);
 
@@ -344,27 +432,28 @@ static int bq27200_battery_remove(struct i2c_client *client)
  * Module stuff
  */
 
-static const struct i2c_device_id bq27200_id[] = {
-       { "bq27200", 0 },
+static const struct i2c_device_id bq27x00_id[] = {
+       { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
+       { "bq27500", BQ27500 },
        {},
 };
 
-static struct i2c_driver bq27200_battery_driver = {
+static struct i2c_driver bq27x00_battery_driver = {
        .driver = {
-               .name = "bq27200-battery",
+               .name = "bq27x00-battery",
        },
-       .probe = bq27200_battery_probe,
-       .remove = bq27200_battery_remove,
-       .id_table = bq27200_id,
+       .probe = bq27x00_battery_probe,
+       .remove = bq27x00_battery_remove,
+       .id_table = bq27x00_id,
 };
 
 static int __init bq27x00_battery_init(void)
 {
        int ret;
 
-       ret = i2c_add_driver(&bq27200_battery_driver);
+       ret = i2c_add_driver(&bq27x00_battery_driver);
        if (ret)
-               printk(KERN_ERR "Unable to register BQ27200 driver\n");
+               printk(KERN_ERR "Unable to register BQ27x00 driver\n");
 
        return ret;
 }
@@ -372,7 +461,7 @@ module_init(bq27x00_battery_init);
 
 static void __exit bq27x00_battery_exit(void)
 {
-       i2c_del_driver(&bq27200_battery_driver);
+       i2c_del_driver(&bq27x00_battery_driver);
 }
 module_exit(bq27x00_battery_exit);