From patchwork Wed Jan 11 06:20:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matt Ranostay X-Patchwork-Id: 9509317 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id DBFF360710 for ; Wed, 11 Jan 2017 06:20:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id CE5B028570 for ; Wed, 11 Jan 2017 06:20:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C3383285E2; Wed, 11 Jan 2017 06:20:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1438828570 for ; Wed, 11 Jan 2017 06:20:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1761935AbdAKGUS (ORCPT ); Wed, 11 Jan 2017 01:20:18 -0500 Received: from mail-pg0-f41.google.com ([74.125.83.41]:36285 "EHLO mail-pg0-f41.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755662AbdAKGUO (ORCPT ); Wed, 11 Jan 2017 01:20:14 -0500 Received: by mail-pg0-f41.google.com with SMTP id f188so278519713pgc.3 for ; Tue, 10 Jan 2017 22:20:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ranostay-consulting.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AA5MpUYHkVQ84w04af3uZgM3V1n9vVzlJJAsI74fpDY=; b=tz1q4h2Na3goFxzrCuYfmQdwgJfQPk6lEBABA1p8dHVTsQZaVj8gmEUJevR6TF2Qn5 HdzsptRjJNVLkP4IcBRa2UsUlgAVU2RzEq9r3DuA9cDLCgU0uivfmxTvbKkKxHKpM8Yn OV0ORvFqN6oV2dpmm0UJhOvtz5MySD1XXSuFFP9g+AL05n+UCPNu6YahBJbywgDMvJtV ju7+lVucEoadtZPTQWsbIPuLHq4OeoVqa+XlL0dGgWMScmyo1CU3pPBHt9YtkLiVDZfF zgD5/PWFzRRTs7FTlob4R0A99v5cNbpDtoXyjuxm8uzUpuO599GSOIaLeFnTXQuAJ+vm nucQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AA5MpUYHkVQ84w04af3uZgM3V1n9vVzlJJAsI74fpDY=; b=Y3Qs7jax4uAX1eG3po0KDyMcCIF+tbbNpG1go6E/nGkPg4mCTRvad08U2471WYXyej OsK71VJvf3c2rac6eENJ4XNNTRCTH/uBT866VP0st2IHNtlnFb6foPTtWqF2wEpLVv/O ke6/hSaEa2jrqY7biW+74jgzE2W4jmZmhtgln7Jh/9R6caCq4QVq5G9IqCfHCZpbIyCm l8vuyyp4Lz1vPrnNmDod3t5IbWoE2hL4l/Kn34mn2Nsxl80bATogWG+qAGGVmCWFL3Mi K1YPZ+89OJOr+HLObpufGVTkVaCtgXzMDiwZish+FB4XjoHLQ8gp92sFNZ9Rkwx5oL+W WWOQ== X-Gm-Message-State: AIkVDXKsV26sSTWWsou6VC1EaKhLwwEj2IRS/FYKlZ1hejz/4kClTgCChSmDMcsk+hMkqA== X-Received: by 10.98.149.218 with SMTP id c87mr490469pfk.88.1484115614090; Tue, 10 Jan 2017 22:20:14 -0800 (PST) Received: from niteshade.hsd1.or.comcast.net (c-73-25-156-150.hsd1.or.comcast.net. [73.25.156.150]) by smtp.gmail.com with ESMTPSA id x81sm10193009pff.69.2017.01.10.22.20.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Jan 2017 22:20:13 -0800 (PST) From: Matt Ranostay To: linux-pm@vger.kernel.org, devicetree@vger.kernel.org Cc: sre@kernel.org, tony@atomide.com, Matt Ranostay Subject: [PATCH v3 7/7] power: bq27xxx_battery: add initial state machine support Date: Tue, 10 Jan 2017 22:20:03 -0800 Message-Id: <20170111062003.10110-8-matt@ranostay.consulting> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20170111062003.10110-1-matt@ranostay.consulting> References: <20170111062003.10110-1-matt@ranostay.consulting> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Matt Ranostay --- drivers/power/supply/bq27xxx_battery.c | 230 ++++++++++++++++++++++++++++++++- 1 file changed, 229 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 7475a5f31c53..edb4bc78ae4d 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -37,6 +37,7 @@ * http://www.ti.com/product/bq27621-g1 */ +#include #include #include #include @@ -452,6 +453,46 @@ static struct { static DEFINE_MUTEX(bq27xxx_list_lock); static LIST_HEAD(bq27xxx_battery_devices); +#define BQ27XXX_TERM_V_MIN 2800 +#define BQ27XXX_TERM_V_MAX 3700 + +#define BQ27XXX_BLOCK_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +enum bq27xxx_dm_subclass_index { + BQ27XXX_DM_DESIGN_CAP = 0, + BQ27XXX_DM_DESIGN_ENERGY, + BQ27XXX_DM_TERMINATE_VOLTAGE, + BQ27XXX_NUM_IDX, +}; + +struct bq27xxx_dm_regs { + unsigned int subclass_id; + unsigned int offset; + char *name; +}; + +#define BQ27XXX_GAS_GAUGING_STATE_SUBCLASS 82 + +static struct bq27xxx_dm_regs bq27425_dm_subclass_regs[] = { + { BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 12, "design-capacity" }, + { BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 14, "design-energy" }, + { BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 18, "terminate-voltage" }, +}; + +static struct bq27xxx_dm_regs *bq27xxx_dm_subclass_regs[] = { + [BQ27425] = bq27425_dm_subclass_regs, +}; + +static unsigned int bq27xxx_unseal_keys[] = { + [BQ27425] = 0x04143672, +}; + static int poll_interval_param_set(const char *val, const struct kernel_param *kp) { struct bq27xxx_device_info *di; @@ -496,6 +537,165 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, return di->bus.read(di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state(struct bq27xxx_device_info *di, + bool state) +{ + unsigned int key = bq27xxx_unseal_keys[di->chip]; + int ret; + + if (state) + return di->bus.write(di, BQ27XXX_REG_CTRL, 0x20, false); + + ret = di->bus.write(di, BQ27XXX_REG_CTRL, (key >> 16) & 0xffff, false); + if (ret < 0) + return ret; + + return di->bus.write(di, BQ27XXX_REG_CTRL, key & 0xffff, false); +} + +static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di, + int subclass) +{ + int ret = di->bus.write(di, BQ27XXX_REG_CTRL, 0, false); + + if (ret < 0) + return ret; + + ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true); + if (ret < 0) + return ret; + + ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CLASS, subclass, true); + if (ret < 0) + return ret; + + ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, 0, true); + if (ret < 0) + return ret; + + usleep_range(1000, 1500); + + return di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, + (u8 *) &di->buffer, sizeof(di->buffer)); +} + +static int bq27xxx_battery_print_config(struct bq27xxx_device_info *di) +{ + struct bq27xxx_dm_regs *reg = bq27xxx_dm_subclass_regs[di->chip]; + int ret, i; + + ret = bq27xxx_battery_read_dm_block(di, + BQ27XXX_GAS_GAUGING_STATE_SUBCLASS); + if (ret < 0) + return ret; + + for (i = 0; i < BQ27XXX_NUM_IDX; i++) { + int val; + + if (reg->subclass_id != BQ27XXX_GAS_GAUGING_STATE_SUBCLASS) + continue; + + val = be16_to_cpup((u16 *) &di->buffer[reg->offset]); + + dev_info(di->dev, "settings for %s set at %d\n", reg->name, val); + + reg++; + } + + return 0; +} + +static bool bq27xxx_battery_update_dm_setting(struct bq27xxx_device_info *di, + unsigned int reg, unsigned int val) +{ + struct bq27xxx_dm_regs *dm_reg = &bq27xxx_dm_subclass_regs[di->chip][reg]; + u16 *prev = (u16 *) &di->buffer[dm_reg->offset]; + + if (be16_to_cpup(prev) == val) + return false; + + *prev = cpu_to_be16(val); + + return true; +} + +static u8 bq27xxx_battery_checksum(struct bq27xxx_device_info *di) +{ + u8 *data = (u8 *) &di->buffer; + u16 sum = 0; + int i; + + for (i = 0; i < sizeof(di->buffer); i++) { + sum += data[i]; + sum &= 0xff; + } + + return 0xff - sum; +} + +static int bq27xxx_battery_write_nvram(struct bq27xxx_device_info *di, + unsigned int subclass) +{ + int ret; + + ret = di->bus.write(di, BQ27XXX_REG_CTRL, BQ27XXX_SET_CFGUPDATE, false); + if (ret) + return ret; + + ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true); + if (ret) + return ret; + + ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CLASS, subclass, true); + if (ret) + return ret; + + ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, 0, true); + if (ret) + return ret; + + ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, + (u8 *) &di->buffer, sizeof(di->buffer)); + if (ret < 0) + return ret; + + usleep_range(1000, 1500); + + di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM, + bq27xxx_battery_checksum(di), true); + + usleep_range(1000, 1500); + + di->bus.write(di, BQ27XXX_REG_CTRL, BQ27XXX_SOFT_RESET, false); + + return 0; +} + +static int bq27xxx_battery_set_config(struct bq27xxx_device_info *di, + struct power_supply_battery_info *info) +{ + int ret = bq27xxx_battery_read_dm_block(di, + BQ27XXX_GAS_GAUGING_STATE_SUBCLASS); + + if (ret < 0) + return ret; + + ret = bq27xxx_battery_update_dm_setting(di, BQ27XXX_DM_DESIGN_CAP, + info->power / 1000); + ret |= bq27xxx_battery_update_dm_setting(di, BQ27XXX_DM_DESIGN_ENERGY, + info->energy / 1000); + ret |= bq27xxx_battery_update_dm_setting(di, BQ27XXX_DM_TERMINATE_VOLTAGE, + info->nominal_voltage / 1000); + + if (ret) { + dev_info(di->dev, "updating NVM settings\n"); + return bq27xxx_battery_write_nvram(di, + BQ27XXX_GAS_GAUGING_STATE_SUBCLASS); + } + + return 0; +} + /* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -757,6 +957,30 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) return POWER_SUPPLY_HEALTH_GOOD; } +void bq27xxx_battery_settings(struct bq27xxx_device_info *di) +{ + struct power_supply_battery_info info = {}; + + /* functions don't exist for writing data so abort */ + if (!di->bus.write || !di->bus.write_bulk) + return; + + /* no settings to be set for this chipset so abort */ + if (!bq27xxx_dm_subclass_regs[di->chip]) + return; + + bq27xxx_battery_set_seal_state(di, false); + + if (power_supply_get_battery_info(di->bat, &info, "monitored-battery") < 0) + goto out; + + bq27xxx_battery_set_config(di, &info); + +out: + bq27xxx_battery_print_config(di); + bq27xxx_battery_set_seal_state(di, true); +} + void bq27xxx_battery_update(struct bq27xxx_device_info *di) { struct bq27xxx_reg_cache cache = {0, }; @@ -1039,7 +1263,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy) int bq27xxx_battery_setup(struct bq27xxx_device_info *di) { struct power_supply_desc *psy_desc; - struct power_supply_config psy_cfg = { .drv_data = di, }; + struct power_supply_config psy_cfg; + + psy_cfg.of_node = di->dev->of_node; + psy_cfg.drv_data = di; INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll); mutex_init(&di->lock); @@ -1064,6 +1291,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); + bq27xxx_battery_settings(di); bq27xxx_battery_update(di); mutex_lock(&bq27xxx_list_lock);