bq27x00_battery: optimize i2c read
authorGrazvydas Ignotas <notasas@gmail.com>
Sat, 14 May 2016 19:59:00 +0000 (22:59 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 15 May 2016 12:43:21 +0000 (15:43 +0300)
One big instead of many small ones.
Less irqs, wakeups and context switches.

drivers/power/bq27x00_battery.c

index 17a7389..590b79b 100644 (file)
@@ -86,10 +86,12 @@ struct bq27x00_reg_cache {
        int time_to_empty_avg;
        int time_to_full;
        int charge_full;
-       int cycle_count;
-       int capacity;
+       int charge_now;
        int energy;
        int flags;
+
+       int voltage;
+       int curr;
 };
 
 struct bq27x00_device_info {
@@ -308,45 +310,63 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)
        return tval * 60;
 }
 
+static int bq27x00_time(int tval)
+{
+       if (tval == 65535)
+               return -ENODATA;
+
+       return tval * 60;
+}
+
+static int bq27x00_read_i2c_n(struct bq27x00_device_info *di, u8 *data,
+       size_t len, u8 start);
+
 static void bq27x00_update(struct bq27x00_device_info *di)
 {
        struct bq27x00_reg_cache cache = {0, };
        bool is_bq27500 = di->chip == BQ27500;
+       u8 state[0x28];
        int flags_changed;
+       int ret;
 
-       cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, !is_bq27500);
-       if (cache.flags >= 0) {
-               if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) {
-                       dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
-                       cache.capacity = -ENODATA;
-                       cache.energy = -ENODATA;
-                       cache.time_to_empty = -ENODATA;
-                       cache.time_to_empty_avg = -ENODATA;
-                       cache.time_to_full = -ENODATA;
-                       cache.charge_full = -ENODATA;
-               } else {
-                       cache.capacity = bq27x00_battery_read_rsoc(di);
-                       cache.energy = bq27x00_battery_read_energy(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.temperature = bq27x00_battery_read_temperature(di);
-               cache.cycle_count = bq27x00_battery_read_cyct(di);
-
-               /* We only have to read charge design full once */
-               if (di->charge_design_full <= 0)
-                       di->charge_design_full = bq27x00_battery_read_ilmd(di);
+       /* pandora hack */
+       WARN_ON_ONCE(!is_bq27500);
+       (void)bq27x00_battery_read_energy;
+       (void)bq27x00_battery_read_temperature;
+       (void)bq27x00_battery_read_time;
+
+       /* reading reserved field breaks subsequent reads,
+        * so can't read everything in one go :( */
+       ret = bq27x00_read_i2c_n(di, state + 6, sizeof(state) - 6, 6);
+       if (ret < 0) {
+               dev_err(di->dev, "error reading state: %d\n", ret);
+               return;
        }
 
+       cache.flags         = get_unaligned_le16(&state[BQ27x00_REG_FLAGS]);
+       cache.energy        = get_unaligned_le16(&state[BQ27x00_REG_AE]) * 1000;
+       cache.time_to_empty = get_unaligned_le16(&state[BQ27x00_REG_TTE]);
+       cache.time_to_empty = bq27x00_time(cache.time_to_empty);
+       cache.time_to_empty_avg = get_unaligned_le16(&state[BQ27x00_REG_TTECP]);
+       cache.time_to_empty_avg = bq27x00_time(cache.time_to_empty_avg);
+       cache.time_to_full  = get_unaligned_le16(&state[BQ27x00_REG_TTF]);
+       cache.time_to_full  = bq27x00_time(cache.time_to_full);
+       cache.charge_full   = get_unaligned_le16(&state[BQ27x00_REG_LMD]) * 1000;
+       cache.charge_now    = get_unaligned_le16(&state[BQ27x00_REG_NAC]) * 1000;
+       cache.temperature   = get_unaligned_le16(&state[BQ27x00_REG_TEMP]);
+       cache.voltage       = get_unaligned_le16(&state[BQ27x00_REG_VOLT]) * 1000;
+       cache.curr          = (s16)get_unaligned_le16(&state[BQ27x00_REG_AI]) * 1000;
+
+       /* We only have to read charge design full once */
+       if (di->charge_design_full <= 0)
+               di->charge_design_full = bq27x00_battery_read_ilmd(di);
+
        /*
         * On bq27500, DSG is not set on discharge with very low currents,
         * so check AI to not misreport that we are charging in status query
         */
        if (is_bq27500 && !(cache.flags & BQ27500_FLAG_DSC)) {
-               int curr = bq27x00_read(di, BQ27x00_REG_AI, false);
-               if ((s16)curr <= 0)
+               if (cache.curr <= 0)
                        cache.flags |= BQ27500_FLAG_DSC;
        }
 
@@ -523,16 +543,23 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
                ret = bq27x00_battery_status(di, val);
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = bq27x00_battery_voltage(di, val);
+               if (time_is_before_jiffies(di->last_update + HZ))
+                       ret = bq27x00_battery_voltage(di, val);
+               else
+                       ret = bq27x00_simple_value(di->cache.voltage, val);
                break;
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = di->cache.flags < 0 ? 0 : 1;
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = bq27x00_battery_current(di, val);
+               ret = 0;
+               if (time_is_before_jiffies(di->last_update + HZ))
+                       ret = bq27x00_battery_current(di, val);
+               else
+                       val->intval = di->cache.curr;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
-               ret = bq27x00_simple_value(di->cache.capacity, val);
+               ret = bq27x00_simple_value(bq27x00_battery_read_rsoc(di), val);
                break;
        case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
                ret = bq27x00_battery_capacity_level(di, val);
@@ -555,7 +582,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
                break;
        case POWER_SUPPLY_PROP_CHARGE_NOW:
-               ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val);
+               ret = bq27x00_simple_value(di->cache.charge_now, val);
                break;
        case POWER_SUPPLY_PROP_CHARGE_FULL:
                ret = bq27x00_simple_value(di->cache.charge_full, val);
@@ -564,7 +591,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
                ret = bq27x00_simple_value(di->charge_design_full, val);
                break;
        case POWER_SUPPLY_PROP_CYCLE_COUNT:
-               ret = bq27x00_simple_value(di->cache.cycle_count, val);
+               ret = bq27x00_simple_value(bq27x00_battery_read_cyct(di), val);
                break;
        case POWER_SUPPLY_PROP_ENERGY_NOW:
                ret = bq27x00_simple_value(di->cache.energy, val);
@@ -676,6 +703,27 @@ static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)
        return ret;
 }
 
+static int bq27x00_read_i2c_n(struct bq27x00_device_info *di, u8 *data,
+       size_t len, u8 start)
+{
+       struct i2c_client *client = to_i2c_client(di->dev);
+       struct i2c_msg msg[2];
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = &start;
+       msg[0].len = sizeof(start);
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = data;
+       msg[1].len = len;
+
+       return i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+}
+
 static int bq27x00_battery_probe(struct i2c_client *client,
                                 const struct i2c_device_id *id)
 {