/*************************************************************************** * can't able to update the design capacity in bq27441-G1 * 声明: * 本文主要是记录分析bq27441-G1芯片无法修改一些参数的原因,主要是因为 * bq27x00_powersupply_init中绑定了bq27x00_battery_poll作为定时任务,之后 * 直接调用bq27x00_update获取电池信息,这个时候bq27x00_battery_poll还没有 * 执行,所以第一次bq27x00_update获取的是默认的bq27441-G1信息,而在此之后 * bq27x00_update中会对有些信息进行选择更新,所以造成了design capacity不 * 准确。 * * 2016-2-22 深圳 南山平山村 曾剑锋 **************************************************************************/ /** * 参考文章: * Application of bq27441-g1 with 3.X Linux kernel * http://e2e.ti.com/support/power_management/battery_management/f/180/p/471744/1701660 */ static int __init bq27x00_powersupply_init(struct bq27x00_device_info *di) { int ret; di->bat.type = POWER_SUPPLY_TYPE_BATTERY; if (di->chip == BQ274XX) { set_properties_array(di, bq274xx_battery_props, ARRAY_SIZE(bq274xx_battery_props)); } else if (di->chip == BQ276XX) { set_properties_array(di, bq276xx_battery_props, ARRAY_SIZE(bq276xx_battery_props)); } else if (di->chip == BQ27520) { set_properties_array(di, bq27520_battery_props, ARRAY_SIZE(bq27520_battery_props)); } else if (di->chip == BQ2753X) { set_properties_array(di, bq2753x_battery_props, ARRAY_SIZE(bq2753x_battery_props)); } else { set_properties_array(di, bq27x00_battery_props, ARRAY_SIZE(bq27x00_battery_props)); } di->bat.get_property = bq27x00_battery_get_property; di->bat.external_power_changed = bq27x00_external_power_changed; INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); ------------------+ mutex_init(&di->lock); | | ret = power_supply_register(di->dev, &di->bat); | if (ret) { | dev_err(di->dev, "failed to register battery: %d\n", ret); | return ret; | } | | dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); | | bq27x00_update(di); ------------------*------+ | | return 0; | | } | | | | static void bq27x00_battery_poll(struct work_struct *work) <------------+ | { | struct bq27x00_device_info *di = | container_of(work, struct bq27x00_device_info, work.work); | | if (((di->chip == BQ274XX) || (di->chip == BQ276XX)) && | !rom_mode_gauge_dm_initialized(di)) { | rom_mode_gauge_dm_init(di); -------------------+ | } | | | | bq27x00_update(di); -------------------*-------+ | | if (poll_interval > 0) { | | /* The timer does not have to be accurate. */ | | set_timer_slack(&di->work.timer, poll_interval * HZ / 4); | | schedule_delayed_work(&di->work, poll_interval * HZ); | | } | | } | | | | #define INITCOMP_TIMEOUT_MS 10000 | | static void rom_mode_gauge_dm_init(struct bq27x00_device_info *di) <----+ | { | int i; | int timeout = INITCOMP_TIMEOUT_MS; | u8 subclass, offset; | u32 blk_number; | u32 blk_number_prev = 0; | u8 buf[32]; | bool buf_valid = false; | struct dm_reg *dm_reg; | | dev_dbg(di->dev, "%s:\n", __func__); | | while (!rom_mode_gauge_init_completed(di) && timeout > 0) { | msleep(100); | timeout -= 100; | } | | if (timeout <= 0) { | dev_err(di->dev, "%s: INITCOMP not set after %d seconds\n", | __func__, INITCOMP_TIMEOUT_MS/100); | return; | } | | if (!di->dm_regs || !di->dm_regs_count) { | dev_err(di->dev, "%s: Data not available for DM initialization\n", | __func__); | return; | } | | enter_cfg_update_mode(di); | for (i = 0; i < di->dm_regs_count; i++) { | dm_reg = &di->dm_regs[i]; | subclass = dm_reg->subclass; | offset = dm_reg->offset; | | /* | * Create a composite block number to see if the subsequent | * register also belongs to the same 32 btye block in the DM | */ | blk_number = subclass << 8; | blk_number |= offset >> 5; | | if (blk_number == blk_number_prev) { | copy_to_dm_buf_big_endian(di, buf, offset, | dm_reg->len, dm_reg->data); | } else { | | if (buf_valid) | update_dm_block(di, blk_number_prev >> 8, | (blk_number_prev << 5) & 0xFF , buf); | else | buf_valid = true; | | read_dm_block(di, dm_reg->subclass, dm_reg->offset, | buf); | copy_to_dm_buf_big_endian(di, buf, offset, | dm_reg->len, dm_reg->data); | } | blk_number_prev = blk_number; | } | | /* Last buffer to be written */ | if (buf_valid) | update_dm_block(di, subclass, offset, buf); | | exit_cfg_update_mode(di); | } | | static void bq27x00_update(struct bq27x00_device_info *di) <----------------+ { struct bq27x00_reg_cache cache = {0, }; bool is_bq27200 = (di->chip == BQ27200); bool is_bq27500 = (di->chip == BQ27500); bool is_bq274xx = (di->chip == BQ274XX); bool is_bq276xx = (di->chip == BQ276XX); cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, !is_bq27500); if (cache.flags >= 0) { if (is_bq27200 && (cache.flags & BQ27200_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; cache.health = -ENODATA; } else { cache.capacity = bq27x00_battery_read_soc(di); if (!(is_bq274xx || is_bq276xx)) { cache.energy = bq27x00_battery_read_energy(di); cache.time_to_empty = bq27x00_battery_read_time(di, BQ27XXX_REG_TTE); cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27XXX_REG_TTECP); cache.time_to_full = bq27x00_battery_read_time(di, BQ27XXX_REG_TTF); } cache.charge_full = bq27x00_battery_read_fcc(di); cache.health = bq27x00_battery_read_health(di); } cache.temperature = bq27x00_battery_read_temperature(di); if (!is_bq274xx) cache.cycle_count = bq27x00_battery_read_cyct(di); cache.power_avg = bq27x00_battery_read_pwr_avg(di, BQ27XXX_POWER_AVG); /* We only have to read charge design full once */ if (di->charge_design_full <= 0) // pay attention at here di->charge_design_full = bq27x00_battery_read_dcap(di); printk("---------------------------------------\n"); printk("cache.capacity : %d\n", cache.capacity); printk("cache.energy : %d\n", cache.energy); printk("cache.time_to_empty : %d\n", cache.time_to_empty); printk("cache.charge_full : %d\n", cache.charge_full); printk("cache.health : %d\n", cache.health); printk("cache.tmperature : %d\n", cache.temperature); printk("cache.power_avg : %d\n", cache.power_avg); printk("di->charge_design_full : %d\n", di->charge_design_full); printk("---------------------------------------\n"); } if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { di->cache = cache; power_supply_changed(&di->bat); } di->last_update = jiffies; }