int power_supply_get_battery_info()

in supply/power_supply_core.c [566:765]


int power_supply_get_battery_info(struct power_supply *psy,
				  struct power_supply_battery_info **info_out)
{
	struct power_supply_resistance_temp_table *resist_table;
	struct power_supply_battery_info *info;
	struct device_node *battery_np;
	const char *value;
	int err, len, index;
	const __be32 *list;

	info = devm_kmalloc(&psy->dev, sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	info->technology                     = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
	info->energy_full_design_uwh         = -EINVAL;
	info->charge_full_design_uah         = -EINVAL;
	info->voltage_min_design_uv          = -EINVAL;
	info->voltage_max_design_uv          = -EINVAL;
	info->precharge_current_ua           = -EINVAL;
	info->charge_term_current_ua         = -EINVAL;
	info->constant_charge_current_max_ua = -EINVAL;
	info->constant_charge_voltage_max_uv = -EINVAL;
	info->tricklecharge_current_ua       = -EINVAL;
	info->precharge_voltage_max_uv       = -EINVAL;
	info->charge_restart_voltage_uv      = -EINVAL;
	info->overvoltage_limit_uv           = -EINVAL;
	info->temp_ambient_alert_min         = INT_MIN;
	info->temp_ambient_alert_max         = INT_MAX;
	info->temp_alert_min                 = INT_MIN;
	info->temp_alert_max                 = INT_MAX;
	info->temp_min                       = INT_MIN;
	info->temp_max                       = INT_MAX;
	info->factory_internal_resistance_uohm  = -EINVAL;
	info->resist_table = NULL;

	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__);
		return -ENXIO;
	}

	battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0);
	if (!battery_np)
		return -ENODEV;

	err = of_property_read_string(battery_np, "compatible", &value);
	if (err)
		goto out_put_node;

	if (strcmp("simple-battery", value)) {
		err = -ENODEV;
		goto out_put_node;
	}

	/* The property and field names below must correspond to elements
	 * in enum power_supply_property. For reasoning, see
	 * Documentation/power/power_supply_class.rst.
	 */

	if (!of_property_read_string(battery_np, "device-chemistry", &value)) {
		if (!strcmp("nickel-cadmium", value))
			info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd;
		else if (!strcmp("nickel-metal-hydride", value))
			info->technology = POWER_SUPPLY_TECHNOLOGY_NiMH;
		else if (!strcmp("lithium-ion", value))
			/* Imprecise lithium-ion type */
			info->technology = POWER_SUPPLY_TECHNOLOGY_LION;
		else if (!strcmp("lithium-ion-polymer", value))
			info->technology = POWER_SUPPLY_TECHNOLOGY_LIPO;
		else if (!strcmp("lithium-ion-iron-phosphate", value))
			info->technology = POWER_SUPPLY_TECHNOLOGY_LiFe;
		else if (!strcmp("lithium-ion-manganese-oxide", value))
			info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn;
		else
			dev_warn(&psy->dev, "%s unknown battery type\n", value);
	}

	of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
			     &info->energy_full_design_uwh);
	of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
			     &info->charge_full_design_uah);
	of_property_read_u32(battery_np, "voltage-min-design-microvolt",
			     &info->voltage_min_design_uv);
	of_property_read_u32(battery_np, "voltage-max-design-microvolt",
			     &info->voltage_max_design_uv);
	of_property_read_u32(battery_np, "trickle-charge-current-microamp",
			     &info->tricklecharge_current_ua);
	of_property_read_u32(battery_np, "precharge-current-microamp",
			     &info->precharge_current_ua);
	of_property_read_u32(battery_np, "precharge-upper-limit-microvolt",
			     &info->precharge_voltage_max_uv);
	of_property_read_u32(battery_np, "charge-term-current-microamp",
			     &info->charge_term_current_ua);
	of_property_read_u32(battery_np, "re-charge-voltage-microvolt",
			     &info->charge_restart_voltage_uv);
	of_property_read_u32(battery_np, "over-voltage-threshold-microvolt",
			     &info->overvoltage_limit_uv);
	of_property_read_u32(battery_np, "constant-charge-current-max-microamp",
			     &info->constant_charge_current_max_ua);
	of_property_read_u32(battery_np, "constant-charge-voltage-max-microvolt",
			     &info->constant_charge_voltage_max_uv);
	of_property_read_u32(battery_np, "factory-internal-resistance-micro-ohms",
			     &info->factory_internal_resistance_uohm);

	of_property_read_u32_index(battery_np, "ambient-celsius",
				   0, &info->temp_ambient_alert_min);
	of_property_read_u32_index(battery_np, "ambient-celsius",
				   1, &info->temp_ambient_alert_max);
	of_property_read_u32_index(battery_np, "alert-celsius",
				   0, &info->temp_alert_min);
	of_property_read_u32_index(battery_np, "alert-celsius",
				   1, &info->temp_alert_max);
	of_property_read_u32_index(battery_np, "operating-range-celsius",
				   0, &info->temp_min);
	of_property_read_u32_index(battery_np, "operating-range-celsius",
				   1, &info->temp_max);

	len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius");
	if (len < 0 && len != -EINVAL) {
		err = len;
		goto out_put_node;
	} else if (len > POWER_SUPPLY_OCV_TEMP_MAX) {
		dev_err(&psy->dev, "Too many temperature values\n");
		err = -EINVAL;
		goto out_put_node;
	} else if (len > 0) {
		of_property_read_u32_array(battery_np, "ocv-capacity-celsius",
					   info->ocv_temp, len);
	}

	for (index = 0; index < len; index++) {
		struct power_supply_battery_ocv_table *table;
		char *propname;
		int i, tab_len, size;

		propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index);
		list = of_get_property(battery_np, propname, &size);
		if (!list || !size) {
			dev_err(&psy->dev, "failed to get %s\n", propname);
			kfree(propname);
			power_supply_put_battery_info(psy, info);
			err = -EINVAL;
			goto out_put_node;
		}

		kfree(propname);
		tab_len = size / (2 * sizeof(__be32));
		info->ocv_table_size[index] = tab_len;

		table = info->ocv_table[index] =
			devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL);
		if (!info->ocv_table[index]) {
			power_supply_put_battery_info(psy, info);
			err = -ENOMEM;
			goto out_put_node;
		}

		for (i = 0; i < tab_len; i++) {
			table[i].ocv = be32_to_cpu(*list);
			list++;
			table[i].capacity = be32_to_cpu(*list);
			list++;
		}
	}

	list = of_get_property(battery_np, "resistance-temp-table", &len);
	if (!list || !len)
		goto out_ret_pointer;

	info->resist_table_size = len / (2 * sizeof(__be32));
	resist_table = info->resist_table = devm_kcalloc(&psy->dev,
							 info->resist_table_size,
							 sizeof(*resist_table),
							 GFP_KERNEL);
	if (!info->resist_table) {
		power_supply_put_battery_info(psy, info);
		err = -ENOMEM;
		goto out_put_node;
	}

	for (index = 0; index < info->resist_table_size; index++) {
		resist_table[index].temp = be32_to_cpu(*list++);
		resist_table[index].resistance = be32_to_cpu(*list++);
	}

out_ret_pointer:
	/* Finally return the whole thing */
	*info_out = info;

out_put_node:
	of_node_put(battery_np);
	return err;
}