Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
[pandora-kernel.git] / drivers / power / bq27x00_battery.c
index 25350fc..59e68db 100644 (file)
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  */
+
+/*
+ * Datasheets:
+ * http://focus.ti.com/docs/prod/folders/print/bq27000.html
+ * http://focus.ti.com/docs/prod/folders/print/bq27500.html
+ */
+
 #include <linux/module.h>
 #include <linux/param.h>
 #include <linux/jiffies.h>
@@ -30,7 +37,7 @@
 
 #include <linux/power/bq27x00_battery.h>
 
-#define DRIVER_VERSION                 "1.1.0"
+#define DRIVER_VERSION                 "1.2.0"
 
 #define BQ27x00_REG_TEMP               0x06
 #define BQ27x00_REG_VOLT               0x08
 #define BQ27x00_REG_TTE                        0x16
 #define BQ27x00_REG_TTF                        0x18
 #define BQ27x00_REG_TTECP              0x26
+#define BQ27x00_REG_NAC                        0x0C /* Nominal available capaciy */
+#define BQ27x00_REG_LMD                        0x12 /* Last measured discharge */
+#define BQ27x00_REG_CYCT               0x2A /* Cycle count total */
+#define BQ27x00_REG_AE                 0x22 /* Available enery */
 
 #define BQ27000_REG_RSOC               0x0B /* Relative State-of-Charge */
+#define BQ27000_REG_ILMD               0x76 /* Initial last measured discharge */
 #define BQ27000_FLAG_CHGS              BIT(7)
+#define BQ27000_FLAG_FC                        BIT(5)
 
-#define BQ27500_REG_SOC                        0x2c
+#define BQ27500_REG_SOC                        0x2C
+#define BQ27500_REG_DCAP               0x3C /* Design capacity */
 #define BQ27500_FLAG_DSC               BIT(0)
 #define BQ27500_FLAG_FC                        BIT(9)
 
@@ -61,6 +75,8 @@ struct bq27x00_reg_cache {
        int time_to_empty;
        int time_to_empty_avg;
        int time_to_full;
+       int charge_full;
+       int charge_counter;
        int capacity;
        int flags;
 
@@ -73,6 +89,8 @@ struct bq27x00_device_info {
        enum bq27x00_chip       chip;
 
        struct bq27x00_reg_cache cache;
+       int charge_design_full;
+
        unsigned long last_update;
        struct delayed_work work;
 
@@ -94,6 +112,11 @@ static enum power_supply_property bq27x00_battery_props[] = {
        POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
        POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
        POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_ENERGY_NOW,
 };
 
 static unsigned int poll_interval = 360;
@@ -130,6 +153,87 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
        return rsoc;
 }
 
+/*
+ * Return a battery charge value in µAh
+ * Or < 0 if something fails.
+ */
+static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
+{
+       int charge;
+
+       charge = bq27x00_read(di, reg, false);
+       if (charge < 0) {
+               dev_err(di->dev, "error reading nominal available capacity\n");
+               return charge;
+       }
+
+       if (di->chip == BQ27500)
+               charge *= 1000;
+       else
+               charge = charge * 3570 / BQ27000_RS;
+
+       return charge;
+}
+
+/*
+ * Return the battery Nominal available capaciy in µAh
+ * Or < 0 if something fails.
+ */
+static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)
+{
+       return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC);
+}
+
+/*
+ * Return the battery Last measured discharge in µAh
+ * Or < 0 if something fails.
+ */
+static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di)
+{
+       return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD);
+}
+
+/*
+ * Return the battery Initial last measured discharge in µAh
+ * Or < 0 if something fails.
+ */
+static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
+{
+       int ilmd;
+
+       if (di->chip == BQ27500)
+               ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
+       else
+               ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
+
+       if (ilmd < 0) {
+               dev_err(di->dev, "error reading initial last measured discharge\n");
+               return ilmd;
+       }
+
+       if (di->chip == BQ27500)
+               ilmd *= 1000;
+       else
+               ilmd = ilmd * 256 * 3570 / BQ27000_RS;
+
+       return ilmd;
+}
+
+/*
+ * Return the battery Cycle count total
+ * Or < 0 if something fails.
+ */
+static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di)
+{
+       int cyct;
+
+       cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false);
+       if (cyct < 0)
+               dev_err(di->dev, "error reading cycle count total\n");
+
+       return cyct;
+}
+
 /*
  * Read a time register.
  * Return < 0 if something fails.
@@ -162,9 +266,15 @@ static void bq27x00_update(struct bq27x00_device_info *di)
                cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE);
                cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);
                cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);
+               cache.charge_full = bq27x00_battery_read_lmd(di);
+               cache.charge_counter = bq27x00_battery_read_cyct(di);
 
                if (!is_bq27500)
                        cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false);
+
+               /* We only have to read charge design full once */
+               if (di->charge_design_full <= 0)
+                       di->charge_design_full = bq27x00_battery_read_ilmd(di);
        }
 
        /* Ignore current_now which is a snapshot of the current battery state
@@ -211,7 +321,7 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di,
 }
 
 /*
- * Return the battery average current
+ * Return the battery average current in µA
  * Note that current can be negative signed as well
  * Or 0 if something fails.
  */
@@ -256,8 +366,12 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
                else
                        status = POWER_SUPPLY_STATUS_CHARGING;
        } else {
-               if (di->cache.flags & BQ27000_FLAG_CHGS)
+               if (di->cache.flags & BQ27000_FLAG_FC)
+                       status = POWER_SUPPLY_STATUS_FULL;
+               else if (di->cache.flags & BQ27000_FLAG_CHGS)
                        status = POWER_SUPPLY_STATUS_CHARGING;
+               else if (power_supply_am_i_supplied(&di->bat))
+                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
                else
                        status = POWER_SUPPLY_STATUS_DISCHARGING;
        }
@@ -285,6 +399,32 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
        return 0;
 }
 
+/*
+ * Return the battery Available energy in µWh
+ * Or < 0 if something fails.
+ */
+static int bq27x00_battery_energy(struct bq27x00_device_info *di,
+       union power_supply_propval *val)
+{
+       int ae;
+
+       ae = bq27x00_read(di, BQ27x00_REG_AE, false);
+       if (ae < 0) {
+               dev_err(di->dev, "error reading available energy\n");
+               return ae;
+       }
+
+       if (di->chip == BQ27500)
+               ae *= 1000;
+       else
+               ae = ae * 29200 / BQ27000_RS;
+
+       val->intval = ae;
+
+       return 0;
+}
+
+
 static int bq27x00_simple_value(int value,
        union power_supply_propval *val)
 {
@@ -347,6 +487,21 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_TECHNOLOGY:
                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
                break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               ret = bq27x00_simple_value(di->cache.charge_full, val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               ret = bq27x00_simple_value(di->charge_design_full, val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+               ret = bq27x00_simple_value(di->cache.charge_counter, val);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+               ret = bq27x00_battery_energy(di, val);
+               break;
        default:
                return -EINVAL;
        }
@@ -410,36 +565,34 @@ static DEFINE_MUTEX(battery_mutex);
 static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)
 {
        struct i2c_client *client = to_i2c_client(di->dev);
-       struct i2c_msg msg[1];
+       struct i2c_msg msg[2];
        unsigned char data[2];
        int ret;
 
        if (!client->adapter)
                return -ENODEV;
 
-       msg->addr = client->addr;
-       msg->flags = 0;
-       msg->len = 1;
-       msg->buf = data;
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = &reg;
+       msg[0].len = sizeof(reg);
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = data;
+       if (single)
+               msg[1].len = 1;
+       else
+               msg[1].len = 2;
+
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       if (ret < 0)
+               return ret;
 
-       data[0] = reg;
-       ret = i2c_transfer(client->adapter, msg, 1);
+       if (!single)
+               ret = get_unaligned_le16(data);
+       else
+               ret = data[0];
 
-       if (ret >= 0) {
-               if (!single)
-                       msg->len = 2;
-               else
-                       msg->len = 1;
-
-               msg->flags = I2C_M_RD;
-               ret = i2c_transfer(client->adapter, msg, 1);
-               if (ret >= 0) {
-                       if (!single)
-                               ret = get_unaligned_le16(data);
-                       else
-                               ret = data[0];
-               }
-       }
        return ret;
 }
 
@@ -522,6 +675,7 @@ static const struct i2c_device_id bq27x00_id[] = {
        { "bq27500", BQ27500 },
        {},
 };
+MODULE_DEVICE_TABLE(i2c, bq27x00_id);
 
 static struct i2c_driver bq27x00_battery_driver = {
        .driver = {