diff mbox

[v5,5/8] power: bq27xxx_battery: Add power_supply_battery_info support

Message ID 20170204091603.32242-6-liam@networkimprov.net (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Liam Breck Feb. 4, 2017, 9:16 a.m. UTC
From: Matt Ranostay <matt@ranostay.consulting>

From: Matt Ranostay <matt@ranostay.consulting>

Previously there was no way to set chip registers in the event that the
defaults didn't match the battery in question.

BQ27xxx driver now calls power_supply_get_battery_info, checks the inputs,
and writes battery data to NVRAM.

Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
 drivers/power/supply/bq27xxx_battery.c | 303 ++++++++++++++++++++++++++++++++-
 include/linux/power/bq27xxx_battery.h  |   1 +
 2 files changed, 303 insertions(+), 1 deletion(-)

Comments

kernel test robot Feb. 4, 2017, 9:42 a.m. UTC | #1
Hi Matt,

[auto build test ERROR on next-20170203]
[cannot apply to battery/master linus/master linux/master v4.9-rc8 v4.9-rc7 v4.9-rc6 v4.10-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Liam-Breck/Devicetree-battery-support-and-client-bq27xxx_battery/20170204-172332
config: i386-randconfig-x004-201705 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

Note: the linux-review/Liam-Breck/Devicetree-battery-support-and-client-bq27xxx_battery/20170204-172332 HEAD 26caaa850a9e06e4d96ba05e9bff37ca03531973 builds fine.
      It only hurts bisectibility.

All error/warnings (new ones prefixed by >>):

>> drivers/power/supply/bq27xxx_battery.c:802:3: error: 'BQ27425' undeclared here (not in a function)
     [BQ27425] = bq27425_dm_subclass_regs,
      ^~~~~~~
>> drivers/power/supply/bq27xxx_battery.c:802:3: error: array index in initializer not of integer type
   drivers/power/supply/bq27xxx_battery.c:802:3: note: (near initialization for 'bq27xxx_dm_subclass_regs')
   drivers/power/supply/bq27xxx_battery.c:806:3: error: array index in initializer not of integer type
     [BQ27425] = 0x04143672,
      ^~~~~~~
   drivers/power/supply/bq27xxx_battery.c:806:3: note: (near initialization for 'bq27xxx_unseal_keys')
   drivers/power/supply/bq27xxx_battery.c: In function 'bq27xxx_battery_set_seal_state':
>> drivers/power/supply/bq27xxx_battery.c:860:17: error: 'struct bq27xxx_access_methods' has no member named 'write'
      return di->bus.write(di, BQ27XXX_REG_CTRL, 0x20, false);
                    ^
   drivers/power/supply/bq27xxx_battery.c:862:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_REG_CTRL, (key >> 16) & 0xffff, false);
                  ^
   drivers/power/supply/bq27xxx_battery.c:866:16: error: 'struct bq27xxx_access_methods' has no member named 'write'
     return di->bus.write(di, BQ27XXX_REG_CTRL, key & 0xffff, false);
                   ^
   drivers/power/supply/bq27xxx_battery.c: In function 'bq27xxx_battery_read_dm_block':
   drivers/power/supply/bq27xxx_battery.c:872:19: error: 'struct bq27xxx_access_methods' has no member named 'write'
     int ret = di->bus.write(di, BQ27XXX_REG_CTRL, 0, false);
                      ^
   drivers/power/supply/bq27xxx_battery.c:877:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
                  ^
   drivers/power/supply/bq27xxx_battery.c:881:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CLASS, subclass, true);
                  ^
   drivers/power/supply/bq27xxx_battery.c:885:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, 0, true);
                  ^
>> drivers/power/supply/bq27xxx_battery.c:891:16: error: 'struct bq27xxx_access_methods' has no member named 'read_bulk'
     return di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA,
                   ^
   drivers/power/supply/bq27xxx_battery.c: In function 'bq27xxx_battery_write_nvram':
   drivers/power/supply/bq27xxx_battery.c:954:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_REG_CTRL, BQ27XXX_SET_CFGUPDATE, false);
                  ^
   drivers/power/supply/bq27xxx_battery.c:958:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
                  ^
   drivers/power/supply/bq27xxx_battery.c:962:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CLASS, subclass, true);
                  ^
   drivers/power/supply/bq27xxx_battery.c:966:15: error: 'struct bq27xxx_access_methods' has no member named 'write'
     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, 0, true);
                  ^
>> drivers/power/supply/bq27xxx_battery.c:970:15: error: 'struct bq27xxx_access_methods' has no member named 'write_bulk'
     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA,
                  ^
   drivers/power/supply/bq27xxx_battery.c:977:9: error: 'struct bq27xxx_access_methods' has no member named 'write'
     di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
            ^
   drivers/power/supply/bq27xxx_battery.c:982:9: error: 'struct bq27xxx_access_methods' has no member named 'write'
     di->bus.write(di, BQ27XXX_REG_CTRL, BQ27XXX_SOFT_RESET, false);
            ^
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from include/linux/delay.h:21,
                    from drivers/power/supply/bq27xxx_battery.c:46:
   drivers/power/supply/bq27xxx_battery.c: In function 'bq27xxx_battery_settings':
   drivers/power/supply/bq27xxx_battery.c:1299:14: error: 'struct bq27xxx_access_methods' has no member named 'write'
     if (!di->bus.write || !di->bus.write_bulk)
                 ^
   include/linux/compiler.h:160:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> drivers/power/supply/bq27xxx_battery.c:1299:2: note: in expansion of macro 'if'
     if (!di->bus.write || !di->bus.write_bulk)
     ^~
   drivers/power/supply/bq27xxx_battery.c:1299:32: error: 'struct bq27xxx_access_methods' has no member named 'write_bulk'
     if (!di->bus.write || !di->bus.write_bulk)
                                   ^
   include/linux/compiler.h:160:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> drivers/power/supply/bq27xxx_battery.c:1299:2: note: in expansion of macro 'if'
     if (!di->bus.write || !di->bus.write_bulk)
     ^~
   drivers/power/supply/bq27xxx_battery.c:1299:14: error: 'struct bq27xxx_access_methods' has no member named 'write'
     if (!di->bus.write || !di->bus.write_bulk)
                 ^
   include/linux/compiler.h:160:42: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                             ^~~~
>> drivers/power/supply/bq27xxx_battery.c:1299:2: note: in expansion of macro 'if'
     if (!di->bus.write || !di->bus.write_bulk)
     ^~
   drivers/power/supply/bq27xxx_battery.c:1299:32: error: 'struct bq27xxx_access_methods' has no member named 'write_bulk'
     if (!di->bus.write || !di->bus.write_bulk)
                                   ^
   include/linux/compiler.h:160:42: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                             ^~~~
>> drivers/power/supply/bq27xxx_battery.c:1299:2: note: in expansion of macro 'if'
     if (!di->bus.write || !di->bus.write_bulk)
     ^~
   drivers/power/supply/bq27xxx_battery.c:1299:14: error: 'struct bq27xxx_access_methods' has no member named 'write'
     if (!di->bus.write || !di->bus.write_bulk)
                 ^
   include/linux/compiler.h:171:16: note: in definition of macro '__trace_if'
      ______r = !!(cond);     \
                   ^~~~
>> drivers/power/supply/bq27xxx_battery.c:1299:2: note: in expansion of macro 'if'
     if (!di->bus.write || !di->bus.write_bulk)
     ^~
   drivers/power/supply/bq27xxx_battery.c:1299:32: error: 'struct bq27xxx_access_methods' has no member named 'write_bulk'
     if (!di->bus.write || !di->bus.write_bulk)
                                   ^
   include/linux/compiler.h:171:16: note: in definition of macro '__trace_if'
      ______r = !!(cond);     \
                   ^~~~
>> drivers/power/supply/bq27xxx_battery.c:1299:2: note: in expansion of macro 'if'
     if (!di->bus.write || !di->bus.write_bulk)
     ^~
   drivers/power/supply/bq27xxx_battery.c: In function 'bq27xxx_battery_read_dm_block':
>> drivers/power/supply/bq27xxx_battery.c:893:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^
   drivers/power/supply/bq27xxx_battery.c: In function 'bq27xxx_battery_set_seal_state':
   drivers/power/supply/bq27xxx_battery.c:867:1: warning: control reaches end of non-void function [-Wreturn-type]
    }
    ^

vim +/BQ27425 +802 drivers/power/supply/bq27xxx_battery.c

   796			{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 18, "terminate-voltage" },
   797		[BQ27XXX_DM_V_AT_CHARGE_TERM] =
   798			{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 36, "v-at-charge-termination" },
   799	};
   800	
   801	static struct bq27xxx_dm_regs *bq27xxx_dm_subclass_regs[] = {
 > 802		[BQ27425] = bq27425_dm_subclass_regs,
   803	};
   804	
   805	static unsigned int bq27xxx_unseal_keys[] = {
   806		[BQ27425] = 0x04143672,
   807	};
   808	
   809	static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
   810	{
   811		struct bq27xxx_device_info *di;
   812		unsigned int prev_val = *(unsigned int *) kp->arg;
   813		int ret;
   814	
   815		ret = param_set_uint(val, kp);
   816		if (ret < 0 || prev_val == *(unsigned int *) kp->arg)
   817			return ret;
   818	
   819		mutex_lock(&bq27xxx_list_lock);
   820		list_for_each_entry(di, &bq27xxx_battery_devices, list) {
   821			cancel_delayed_work_sync(&di->work);
   822			schedule_delayed_work(&di->work, 0);
   823		}
   824		mutex_unlock(&bq27xxx_list_lock);
   825	
   826		return ret;
   827	}
   828	
   829	static const struct kernel_param_ops param_ops_poll_interval = {
   830		.get = param_get_uint,
   831		.set = poll_interval_param_set,
   832	};
   833	
   834	static unsigned int poll_interval = 360;
   835	module_param_cb(poll_interval, &param_ops_poll_interval, &poll_interval, 0644);
   836	MODULE_PARM_DESC(poll_interval,
   837			 "battery poll interval in seconds - 0 disables polling");
   838	
   839	/*
   840	 * Common code for BQ27xxx devices
   841	 */
   842	
   843	static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
   844				       bool single)
   845	{
   846		/* Reports EINVAL for invalid/missing registers */
   847		if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
   848			return -EINVAL;
   849	
   850		return di->bus.read(di, di->regs[reg_index], single);
   851	}
   852	
   853	static int bq27xxx_battery_set_seal_state(struct bq27xxx_device_info *di,
   854						      bool state)
   855	{
   856		unsigned int key = bq27xxx_unseal_keys[di->chip];
   857		int ret;
   858	
   859		if (state)
 > 860			return di->bus.write(di, BQ27XXX_REG_CTRL, 0x20, false);
   861	
   862		ret = di->bus.write(di, BQ27XXX_REG_CTRL, (key >> 16) & 0xffff, false);
   863		if (ret < 0)
   864			return ret;
   865	
   866		return di->bus.write(di, BQ27XXX_REG_CTRL, key & 0xffff, false);
   867	}
   868	
   869	static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
   870						     int subclass)
   871	{
   872		int ret = di->bus.write(di, BQ27XXX_REG_CTRL, 0, false);
   873	
   874		if (ret < 0)
   875			return ret;
   876	
 > 877		ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
   878		if (ret < 0)
   879			return ret;
   880	
   881		ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CLASS, subclass, true);
   882		if (ret < 0)
   883			return ret;
   884	
   885		ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, 0, true);
   886		if (ret < 0)
   887			return ret;
   888	
   889		usleep_range(1000, 1500);
   890	
 > 891		return di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA,
   892					(u8 *) &di->buffer, sizeof(di->buffer));
 > 893	}
   894	
   895	static int bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
   896	{
   897		struct bq27xxx_dm_regs *reg = bq27xxx_dm_subclass_regs[di->chip];
   898		int ret, i;
   899	
   900		ret = bq27xxx_battery_read_dm_block(di,
   901				BQ27XXX_GAS_GAUGING_STATE_SUBCLASS);
   902		if (ret < 0)
   903			return ret;
   904	
   905		for (i = 0; i < BQ27XXX_DM_END; i++) {
   906			int val;
   907	
   908			if (reg->subclass_id != BQ27XXX_GAS_GAUGING_STATE_SUBCLASS)
   909				continue;
   910	
   911			val = be16_to_cpup((u16 *) &di->buffer[reg->offset]);
   912	
   913			dev_info(di->dev, "settings for %s set at %d\n", reg->name, val);
   914	
   915			reg++;
   916		}
   917	
   918		return 0;
   919	}
   920	
   921	static bool bq27xxx_battery_update_dm_setting(struct bq27xxx_device_info *di,
   922				      unsigned int reg, unsigned int val)
   923	{
   924		struct bq27xxx_dm_regs *dm_reg = &bq27xxx_dm_subclass_regs[di->chip][reg];
   925		u16 *prev = (u16 *) &di->buffer[dm_reg->offset];
   926	
   927		if (be16_to_cpup(prev) == val)
   928			return false;
   929	
   930		*prev = cpu_to_be16(val);
   931	
   932		return true;
   933	}
   934	
   935	static u8 bq27xxx_battery_checksum(struct bq27xxx_device_info *di)
   936	{
   937		u8 *data = (u8 *) &di->buffer;
   938		u16 sum = 0;
   939		int i;
   940	
   941		for (i = 0; i < sizeof(di->buffer); i++) {
   942			sum += data[i];
   943			sum &= 0xff;
   944		}
   945	
   946		return 0xff - sum;
   947	}
   948	
   949	static int bq27xxx_battery_write_nvram(struct bq27xxx_device_info *di,
   950						   unsigned int subclass)
   951	{
   952		int ret;
   953	
   954		ret = di->bus.write(di, BQ27XXX_REG_CTRL, BQ27XXX_SET_CFGUPDATE, false);
   955		if (ret)
   956			return ret;
   957	
 > 958		ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
   959		if (ret)
   960			return ret;
   961	
   962		ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CLASS, subclass, true);
   963		if (ret)
   964			return ret;
   965	
   966		ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, 0, true);
   967		if (ret)
   968			return ret;
   969	
 > 970		ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA,
   971					(u8 *) &di->buffer, sizeof(di->buffer));
   972		if (ret < 0)
   973			return ret;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Andrew Davis Feb. 6, 2017, 11:29 p.m. UTC | #2
On 02/04/2017 03:16 AM, Liam Breck wrote:
> From: Matt Ranostay <matt@ranostay.consulting>
> 
> From: Matt Ranostay <matt@ranostay.consulting>
> 
> Previously there was no way to set chip registers in the event that the
> defaults didn't match the battery in question.
> 
> BQ27xxx driver now calls power_supply_get_battery_info, checks the inputs,
> and writes battery data to NVRAM.
> 

I haven't had much time to review this, but have you seen our old
internal version of this driver?:

http://git.ti.com/bms-linux/bms-kernel/blobs/master/drivers/power/bq27x00_battery.c

I would like that most of the NVRAM checks and sets are kept in userspace:

http://git.ti.com/bms-linux/bqtool/trees/master

In kernel don't really have a good way to know when the battery has been
swapped and the DT info might not be valid for this new battery. I guess
there is really no good way, but I imagine this is why we never
upstreamed the rest of this driver before.

Andrew

> Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
> Signed-off-by: Liam Breck <kernel@networkimprov.net>
> ---
>  drivers/power/supply/bq27xxx_battery.c | 303 ++++++++++++++++++++++++++++++++-
>  include/linux/power/bq27xxx_battery.h  |   1 +
>  2 files changed, 303 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> index 7272d1e..54ef18a 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 <linux/delay.h>
>  #include <linux/device.h>
>  #include <linux/module.h>
>  #include <linux/mutex.h>
> @@ -432,6 +433,54 @@ static struct {
>  static DEFINE_MUTEX(bq27xxx_list_lock);
>  static LIST_HEAD(bq27xxx_battery_devices);
>  
> +#define BQ27XXX_MIN_V_MIN	2800
> +#define BQ27XXX_MIN_V_MAX	3700
> +#define BQ27XXX_MAX_V_MIN	0
> +#define BQ27XXX_MAX_V_MAX	5000
> +
> +#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_CAPACITY = 0,
> +	BQ27XXX_DM_DESIGN_ENERGY,
> +	BQ27XXX_DM_TERMINATE_VOLTAGE,
> +	BQ27XXX_DM_V_AT_CHARGE_TERM,
> +	BQ27XXX_DM_END,
> +};
> +
> +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_DM_DESIGN_CAPACITY] =
> +		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 12, "design-capacity" },
> +	[BQ27XXX_DM_DESIGN_ENERGY] =
> +		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 14, "design-energy" },
> +	[BQ27XXX_DM_TERMINATE_VOLTAGE] =
> +		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 18, "terminate-voltage" },
> +	[BQ27XXX_DM_V_AT_CHARGE_TERM] =
> +		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 36, "v-at-charge-termination" },
> +};
> +
> +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;
> @@ -476,6 +525,179 @@ 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_DM_END; 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;
> +
> +	ret = bq27xxx_battery_read_dm_block(di,
> +				BQ27XXX_GAS_GAUGING_STATE_SUBCLASS);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (info->charge_full_design_uah != -EINVAL
> +	 && info->energy_full_design_uwh != -EINVAL) {
> +		ret |= bq27xxx_battery_update_dm_setting(di,
> +					BQ27XXX_DM_DESIGN_CAPACITY,
> +					info->charge_full_design_uah / 1000);
> +		ret |= bq27xxx_battery_update_dm_setting(di,
> +					BQ27XXX_DM_DESIGN_ENERGY,
> +					info->energy_full_design_uwh / 1000);
> +	}
> +
> +	if (info->voltage_min_design_uv != -EINVAL)
> +		ret |= bq27xxx_battery_update_dm_setting(di,
> +					BQ27XXX_DM_TERMINATE_VOLTAGE,
> +					info->voltage_min_design_uv / 1000);
> +
> +	if (info->voltage_max_design_uv != -EINVAL)
> +		ret |= bq27xxx_battery_update_dm_setting(di,
> +					BQ27XXX_DM_V_AT_CHARGE_TERM,
> +					info->voltage_max_design_uv / 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.
> @@ -736,6 +958,73 @@ 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) < 0)
> +		goto out;
> +
> +	if (info.energy_full_design_uwh != info.charge_full_design_uah) {
> +		if (info.energy_full_design_uwh == -EINVAL)
> +			dev_warn(di->dev,
> +				"missing battery:energy-full-design-microwatt-hours\n");
> +		else if (info.charge_full_design_uah == -EINVAL)
> +			dev_warn(di->dev,
> +				"missing battery:charge-full-design-microamp-hours\n");
> +	}
> +
> +	if (info.energy_full_design_uwh > 0x7fff * 1000) {
> +		dev_err(di->dev, "invalid battery:energy-full-design-microwatt-hours %d\n",
> +			info.energy_full_design_uwh);
> +		info.energy_full_design_uwh = -EINVAL;
> +	}
> +
> +	if (info.charge_full_design_uah > 0x7fff * 1000) {
> +		dev_err(di->dev, "invalid battery:charge-full-design-microamp-hours %d\n",
> +			info.charge_full_design_uah);
> +		info.charge_full_design_uah = -EINVAL;
> +	}
> +
> +	if ((info.voltage_min_design_uv < BQ27XXX_MIN_V_MIN * 1000
> +	  || info.voltage_min_design_uv > BQ27XXX_MIN_V_MAX * 1000)
> +	  && info.voltage_min_design_uv != -EINVAL) {
> +		dev_err(di->dev, "invalid battery:voltage-min-design-microvolt %d\n",
> +			info.voltage_min_design_uv);
> +		info.voltage_min_design_uv = -EINVAL;
> +	}
> +
> +	if ((info.voltage_max_design_uv < BQ27XXX_MAX_V_MIN * 1000
> +	  || info.voltage_max_design_uv > BQ27XXX_MAX_V_MAX * 1000)
> +	  && info.voltage_max_design_uv != -EINVAL) {
> +		dev_err(di->dev, "invalid battery:voltage-max-design-microvolt %d\n",
> +			info.voltage_max_design_uv);
> +		info.voltage_max_design_uv = -EINVAL;
> +	}
> +
> +	if ((info.energy_full_design_uwh == -EINVAL
> +	  || info.charge_full_design_uah == -EINVAL)
> +	  && info.voltage_min_design_uv  == -EINVAL
> +	  && info.voltage_max_design_uv  == -EINVAL)
> +		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, };
> @@ -985,6 +1274,14 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
>  	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
>  		ret = bq27xxx_simple_value(di->charge_design_full, val);
>  		break;
> +	/*
> +	 * TODO: Implement these to make registers set from
> +	 * power_supply_battery_info visible in sysfs.
> +	 */
> +	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
> +	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> +	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> +		return -EINVAL;
>  	case POWER_SUPPLY_PROP_CYCLE_COUNT:
>  		ret = bq27xxx_simple_value(di->cache.cycle_count, val);
>  		break;
> @@ -1018,7 +1315,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 = {
> +		.of_node = di->dev->of_node,
> +		.drv_data = di,
> +	};
>  
>  	INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
>  	mutex_init(&di->lock);
> @@ -1043,6 +1343,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);
> diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
> index bed9557..c433815 100644
> --- a/include/linux/power/bq27xxx_battery.h
> +++ b/include/linux/power/bq27xxx_battery.h
> @@ -62,6 +62,7 @@ struct bq27xxx_device_info {
>  	struct list_head list;
>  	struct mutex lock;
>  	u8 *regs;
> +	u8 buffer[32];
>  };
>  
>  void bq27xxx_battery_update(struct bq27xxx_device_info *di);
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Liam Breck Feb. 7, 2017, 12:15 a.m. UTC | #3
On Mon, Feb 6, 2017 at 3:29 PM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/04/2017 03:16 AM, Liam Breck wrote:
>> From: Matt Ranostay <matt@ranostay.consulting>
>>
>> From: Matt Ranostay <matt@ranostay.consulting>
>>
>> Previously there was no way to set chip registers in the event that the
>> defaults didn't match the battery in question.
>>
>> BQ27xxx driver now calls power_supply_get_battery_info, checks the inputs,
>> and writes battery data to NVRAM.
>>
>
> I haven't had much time to review this, but have you seen our old
> internal version of this driver?:
>
> http://git.ti.com/bms-linux/bms-kernel/blobs/master/drivers/power/bq27x00_battery.c
>
> I would like that most of the NVRAM checks and sets are kept in userspace:
>
> http://git.ti.com/bms-linux/bqtool/trees/master
>
> In kernel don't really have a good way to know when the battery has been
> swapped and the DT info might not be valid for this new battery. I guess
> there is really no good way, but I imagine this is why we never
> upstreamed the rest of this driver before.

But for a huge variety of devices, changing the type of battery is not
an option.

DT battery characteristics are entirely optional in this patchset; no
'monitored-battery' means no NVRAM update.

This patchset also allows field-upgrade of the NVRAM in a
linux-generic way, by releasing a kernel or dtb package update.

Also I suspect fuel-gauge and charger drivers will be encouraged to
support monitored-battery since it's a core feature :-)
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 7272d1e..54ef18a 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 <linux/delay.h>
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
@@ -432,6 +433,54 @@  static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_MIN_V_MIN	2800
+#define BQ27XXX_MIN_V_MAX	3700
+#define BQ27XXX_MAX_V_MIN	0
+#define BQ27XXX_MAX_V_MAX	5000
+
+#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_CAPACITY = 0,
+	BQ27XXX_DM_DESIGN_ENERGY,
+	BQ27XXX_DM_TERMINATE_VOLTAGE,
+	BQ27XXX_DM_V_AT_CHARGE_TERM,
+	BQ27XXX_DM_END,
+};
+
+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_DM_DESIGN_CAPACITY] =
+		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 12, "design-capacity" },
+	[BQ27XXX_DM_DESIGN_ENERGY] =
+		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 14, "design-energy" },
+	[BQ27XXX_DM_TERMINATE_VOLTAGE] =
+		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 18, "terminate-voltage" },
+	[BQ27XXX_DM_V_AT_CHARGE_TERM] =
+		{ BQ27XXX_GAS_GAUGING_STATE_SUBCLASS, 36, "v-at-charge-termination" },
+};
+
+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;
@@ -476,6 +525,179 @@  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_DM_END; 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;
+
+	ret = bq27xxx_battery_read_dm_block(di,
+				BQ27XXX_GAS_GAUGING_STATE_SUBCLASS);
+	if (ret < 0)
+		return ret;
+
+	if (info->charge_full_design_uah != -EINVAL
+	 && info->energy_full_design_uwh != -EINVAL) {
+		ret |= bq27xxx_battery_update_dm_setting(di,
+					BQ27XXX_DM_DESIGN_CAPACITY,
+					info->charge_full_design_uah / 1000);
+		ret |= bq27xxx_battery_update_dm_setting(di,
+					BQ27XXX_DM_DESIGN_ENERGY,
+					info->energy_full_design_uwh / 1000);
+	}
+
+	if (info->voltage_min_design_uv != -EINVAL)
+		ret |= bq27xxx_battery_update_dm_setting(di,
+					BQ27XXX_DM_TERMINATE_VOLTAGE,
+					info->voltage_min_design_uv / 1000);
+
+	if (info->voltage_max_design_uv != -EINVAL)
+		ret |= bq27xxx_battery_update_dm_setting(di,
+					BQ27XXX_DM_V_AT_CHARGE_TERM,
+					info->voltage_max_design_uv / 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.
@@ -736,6 +958,73 @@  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) < 0)
+		goto out;
+
+	if (info.energy_full_design_uwh != info.charge_full_design_uah) {
+		if (info.energy_full_design_uwh == -EINVAL)
+			dev_warn(di->dev,
+				"missing battery:energy-full-design-microwatt-hours\n");
+		else if (info.charge_full_design_uah == -EINVAL)
+			dev_warn(di->dev,
+				"missing battery:charge-full-design-microamp-hours\n");
+	}
+
+	if (info.energy_full_design_uwh > 0x7fff * 1000) {
+		dev_err(di->dev, "invalid battery:energy-full-design-microwatt-hours %d\n",
+			info.energy_full_design_uwh);
+		info.energy_full_design_uwh = -EINVAL;
+	}
+
+	if (info.charge_full_design_uah > 0x7fff * 1000) {
+		dev_err(di->dev, "invalid battery:charge-full-design-microamp-hours %d\n",
+			info.charge_full_design_uah);
+		info.charge_full_design_uah = -EINVAL;
+	}
+
+	if ((info.voltage_min_design_uv < BQ27XXX_MIN_V_MIN * 1000
+	  || info.voltage_min_design_uv > BQ27XXX_MIN_V_MAX * 1000)
+	  && info.voltage_min_design_uv != -EINVAL) {
+		dev_err(di->dev, "invalid battery:voltage-min-design-microvolt %d\n",
+			info.voltage_min_design_uv);
+		info.voltage_min_design_uv = -EINVAL;
+	}
+
+	if ((info.voltage_max_design_uv < BQ27XXX_MAX_V_MIN * 1000
+	  || info.voltage_max_design_uv > BQ27XXX_MAX_V_MAX * 1000)
+	  && info.voltage_max_design_uv != -EINVAL) {
+		dev_err(di->dev, "invalid battery:voltage-max-design-microvolt %d\n",
+			info.voltage_max_design_uv);
+		info.voltage_max_design_uv = -EINVAL;
+	}
+
+	if ((info.energy_full_design_uwh == -EINVAL
+	  || info.charge_full_design_uah == -EINVAL)
+	  && info.voltage_min_design_uv  == -EINVAL
+	  && info.voltage_max_design_uv  == -EINVAL)
+		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, };
@@ -985,6 +1274,14 @@  static int bq27xxx_battery_get_property(struct power_supply *psy,
 	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 		ret = bq27xxx_simple_value(di->charge_design_full, val);
 		break;
+	/*
+	 * TODO: Implement these to make registers set from
+	 * power_supply_battery_info visible in sysfs.
+	 */
+	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		return -EINVAL;
 	case POWER_SUPPLY_PROP_CYCLE_COUNT:
 		ret = bq27xxx_simple_value(di->cache.cycle_count, val);
 		break;
@@ -1018,7 +1315,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 = {
+		.of_node = di->dev->of_node,
+		.drv_data = di,
+	};
 
 	INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
 	mutex_init(&di->lock);
@@ -1043,6 +1343,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);
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index bed9557..c433815 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -62,6 +62,7 @@  struct bq27xxx_device_info {
 	struct list_head list;
 	struct mutex lock;
 	u8 *regs;
+	u8 buffer[32];
 };
 
 void bq27xxx_battery_update(struct bq27xxx_device_info *di);