Re: [PATCH v2 2/4] power: supply: core: Introduce properties to present the battery OCV table

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Wed, Sep 26, 2018 at 10:59:12AM +0800, Baolin Wang wrote:
> Some battery driver will use the open circuit voltage (OCV) value to look
> up the corresponding battery capacity percent in one certain degree Celsius.
> Thus this patch provides some battery properties to present the OCV table
> temperatures and OCV capacity table values.
> 
> Suggested-by: Sebastian Reichel <sre@xxxxxxxxxx>
> Signed-off-by: Baolin Wang <baolin.wang@xxxxxxxxxx>
> ---
> Changes from v1:
>  - New patch in v2.
> ---
>  .../devicetree/bindings/power/supply/battery.txt   |   14 +++++
>  drivers/power/supply/power_supply_core.c           |   63 +++++++++++++++++++-
>  include/linux/power_supply.h                       |   11 ++++
>  3 files changed, 87 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/battery.txt b/Documentation/devicetree/bindings/power/supply/battery.txt
> index 25b9d2e..ac2b303 100644
> --- a/Documentation/devicetree/bindings/power/supply/battery.txt
> +++ b/Documentation/devicetree/bindings/power/supply/battery.txt
> @@ -23,6 +23,16 @@ Optional Properties:
>   - constant-charge-current-max-microamp: maximum constant input current
>   - constant-charge-voltage-max-microvolt: maximum constant input voltage
>   - internal-resistance-micro-ohms: battery internal resistance
> + - ocv-capacity-table-0: An array providing the battery capacity percent
> +   with corresponding open circuit voltage (OCV) of the battery, which
> +   is used to look up battery capacity according to current OCV value.
> + - ocv-capacity-table-1: Same as ocv-capacity-table-0
> + ......
> + - ocv-capacity-table-n: Same as ocv-capacity-table-0
> + - ocv-capacity-table-temperatures: An array containing the temperature
> +   in degree Celsius, for each of the battery capacity lookup table.
> +   The first temperature value specifies the OCV table 0, and the second
> +   temperature value specifies the OCV table 1, and so on.
>  
>  Battery properties are named, where possible, for the corresponding
>  elements in enum power_supply_property, defined in
> @@ -44,6 +54,10 @@ Example:
>  		constant-charge-current-max-microamp = <900000>;
>  		constant-charge-voltage-max-microvolt = <4200000>;
>  		internal-resistance-micro-ohms = <250000>;
> +		ocv-capacity-table-temperatures = <(-10) 0 10>;
> +		ocv-capacity-table-0 = <4185000 100>, <4113000 95>, <4066000 90>, ...;
> +		ocv-capacity-table-1 = <4200000 100>, <4185000 95>, <4113000 90>, ...;
> +		ocv-capacity-table-2 = <4250000 100>, <4200000 95>, <4185000 90>, ...;
>  	};
>  
>  	charger: charger@11 {
> diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
> index 9f3452f..151ff03 100644
> --- a/drivers/power/supply/power_supply_core.c
> +++ b/drivers/power/supply/power_supply_core.c
> @@ -570,7 +570,7 @@ int power_supply_get_battery_info(struct power_supply *psy,
>  {
>  	struct device_node *battery_np;
>  	const char *value;
> -	int err;
> +	int err, len, index;
>  
>  	info->energy_full_design_uwh         = -EINVAL;
>  	info->charge_full_design_uah         = -EINVAL;
> @@ -581,6 +581,12 @@ int power_supply_get_battery_info(struct power_supply *psy,
>  	info->constant_charge_voltage_max_uv = -EINVAL;
>  	info->internal_resistance_uohm       = -EINVAL;
>  
> +	for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
> +		info->ocv_table[index]       = NULL;
> +		info->ocv_temp[index]        = -EINVAL;
> +		info->ocv_table_size[index]  = -EINVAL;
> +	}
> +
>  	if (!psy->of_node) {
>  		dev_warn(&psy->dev, "%s currently only supports devicetree\n",
>  			 __func__);
> @@ -620,10 +626,65 @@ int power_supply_get_battery_info(struct power_supply *psy,
>  	of_property_read_u32(battery_np, "internal-resistance-micro-ohms",
>  			     &info->internal_resistance_uohm);
>  
> +	len = of_property_count_u32_elems(battery_np,
> +					  "ocv-capacity-table-temperatures");
> +	if (len < 0 && len != -EINVAL) {
> +		return len;
> +	} else if (len > POWER_SUPPLY_OCV_TEMP_MAX) {
> +		dev_err(&psy->dev, "Too many temperature values\n");
> +		return -EINVAL;
> +	} else if (len > 0) {
> +		of_property_read_u32_array(battery_np,
> +					   "ocv-capacity-table-temperatures",
> +					   info->ocv_temp, len);
> +	}
> +
> +	for (index = 0; index < len; index++) {
> +		struct power_supply_battery_ocv_table *table;
> +		char *propname;
> +		const __be32 *list;
> +		int i, tab_len, size;
> +
> +		propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index);
> +		list = of_get_property(battery_np, propname, &size);
> +		kfree(propname);
> +		if (!list || !size) {
> +			dev_err(&psy->dev, "failed to get ocv capacity table\n");

I think it's better to replace "ocv capacity table" with %s / propname.

> +			power_supply_put_battery_info(psy, info);
> +			return -EINVAL;
> +		}
> +
> +		tab_len = size / sizeof(*table);

I think this should be

tab_len = size / (2 * sizeof(__be32));

which decouples DT memory layout from kernel memory layout.

> +		info->ocv_table_size[index] = tab_len;
> +
> +		table = info->ocv_table[index] = devm_kzalloc(&psy->dev,
> +						tab_len * sizeof(*table),
> +						GFP_KERNEL);
> +		if (!info->ocv_table[index]) {
> +			power_supply_put_battery_info(psy, info);
> +			return -ENOMEM;
> +		}
> +
> +		for (i = 0; i < tab_len; i++) {
> +			table[i].ocv = be32_to_cpu(*list++);
> +			table[i].capacity = be32_to_cpu(*list++);
> +		}
> +	}
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
>  
> +void power_supply_put_battery_info(struct power_supply *psy,
> +				   struct power_supply_battery_info *info)
> +{
> +	int i;
> +
> +	for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++)
> +		kfree(info->ocv_table[i]);
> +}
> +EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
> +
>  int power_supply_get_property(struct power_supply *psy,
>  			    enum power_supply_property psp,
>  			    union power_supply_propval *val)
> diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
> index 019452d..b0a2768 100644
> --- a/include/linux/power_supply.h
> +++ b/include/linux/power_supply.h
> @@ -309,6 +309,12 @@ struct power_supply_info {
>  	int use_for_apm;
>  };
>  
> +struct power_supply_battery_ocv_table {
> +	int ocv;	/* microVolts */
> +	int capacity;	/* percent */
> +};
> +
> +#define POWER_SUPPLY_OCV_TEMP_MAX 20
>  /*
>   * This is the recommended struct to manage static battery parameters,
>   * populated by power_supply_get_battery_info(). Most platform drivers should
> @@ -327,6 +333,9 @@ struct power_supply_battery_info {
>  	int constant_charge_current_max_ua; /* microAmps */
>  	int constant_charge_voltage_max_uv; /* microVolts */
>  	int internal_resistance_uohm;       /* microOhms */
> +	int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];  /* celsius */
> +	struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX];
> +	int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX];
>  };
>  
>  extern struct atomic_notifier_head power_supply_notifier;
> @@ -350,6 +359,8 @@ extern struct power_supply *devm_power_supply_get_by_phandle(
>  
>  extern int power_supply_get_battery_info(struct power_supply *psy,
>  					 struct power_supply_battery_info *info);
> +extern void power_supply_put_battery_info(struct power_supply *psy,
> +					  struct power_supply_battery_info *info);
>  extern void power_supply_changed(struct power_supply *psy);
>  extern int power_supply_am_i_supplied(struct power_supply *psy);
>  extern int power_supply_set_input_current_limit_from_supplier(

Looks good to me. Technically this can result in existing users of
power_supply_get_battery_info leaking memory, if they have an OCV
table in DT.

-- Sebastian

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux