From: Grazvydas Ignotas Date: Sat, 14 May 2016 19:59:00 +0000 (+0300) Subject: bq27x00_battery: optimize i2c read X-Git-Tag: sz_175~26 X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=commitdiff_plain;h=098d5280b80966bd48659e09e8f5a251b584f3b3 bq27x00_battery: optimize i2c read One big instead of many small ones. Less irqs, wakeups and context switches. --- diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 17a7389802ad..590b79b6e91a 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -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) {