Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3be2db00 authored by Shyam Kumar Thella's avatar Shyam Kumar Thella
Browse files

power: supply: Add snapshot of QPNP QG driver and its dependencies



Add snapshot of the qpnp-qg driver as of msm-4.19
'commit 7bbb67403d95 ("Merge: msm_geni_serial: Correct the DMA RX
interrupt logic")'.

Change-Id: If8df8bc75705676006be7172c20469baa1c1dae6
Signed-off-by: default avatarShyam Kumar Thella <sthella@codeaurora.org>
parent 21b3dfff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -93,3 +93,4 @@ obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o
obj-$(CONFIG_CHARGER_BD70528)	+= bd70528-charger.o
obj-$(CONFIG_CHARGER_WILCO)	+= wilco-charger.o
obj-$(CONFIG_QTI_BATTERY_CHARGER)	+= qti_battery_charger.o
obj-$(CONFIG_QCOM_POWER_SUPPLY)		+= qcom/
+18 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only

menuconfig QCOM_POWER_SUPPLY
	tristate "Support for Qualcomm Technologies, Inc. power supply"
	depends on ARCH_QCOM

if QCOM_POWER_SUPPLY

config QPNP_QG
	bool "QPNP Qgauge driver"
	depends on MFD_SPMI_PMIC
	help
	  Say Y here to enable the Qualcomm Technologies, Inc. QGauge driver
	  which uses the periodic sampling of the battery voltage and current
	  to determine the battery state-of-charge (SOC) and supports other
	  battery management features.

endif
+3 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only

obj-$(CONFIG_QPNP_QG)	+= qpnp-qg.o battery-profile-loader.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o
+336 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
 */

#define pr_fmt(fmt)	"%s: " fmt, __func__

#include <linux/err.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/power_supply.h>
#include "battery-profile-loader.h"

static int of_batterydata_read_batt_id_kohm(const struct device_node *np,
				const char *propname, struct batt_ids *batt_ids)
{
	struct property *prop;
	const __be32 *data;
	int num, i, *id_kohm = batt_ids->kohm;

	prop = of_find_property(np, "qcom,batt-id-kohm", NULL);
	if (!prop) {
		pr_err("%s: No battery id resistor found\n", np->name);
		return -EINVAL;
	} else if (!prop->value) {
		pr_err("%s: No battery id resistor value found, np->name\n",
						np->name);
		return -ENODATA;
	} else if (prop->length > MAX_BATT_ID_NUM * sizeof(__be32)) {
		pr_err("%s: Too many battery id resistors\n", np->name);
		return -EINVAL;
	}

	num = prop->length/sizeof(__be32);
	batt_ids->num = num;
	data = prop->value;
	for (i = 0; i < num; i++)
		*id_kohm++ = be32_to_cpup(data++);

	return 0;
}

struct device_node *of_batterydata_get_best_profile(
		const struct device_node *batterydata_container_node,
		int batt_id_kohm, const char *batt_type)
{
	struct batt_ids batt_ids;
	struct device_node *node, *best_node = NULL;
	const char *battery_type = NULL;
	int delta = 0, best_delta = 0, best_id_kohm = 0, id_range_pct,
		i = 0, rc = 0, limit = 0;
	bool in_range = false;

	/* read battery id range percentage for best profile */
	rc = of_property_read_u32(batterydata_container_node,
			"qcom,batt-id-range-pct", &id_range_pct);

	if (rc) {
		if (rc == -EINVAL) {
			id_range_pct = 0;
		} else {
			pr_err("failed to read battery id range\n");
			return ERR_PTR(-ENXIO);
		}
	}

	/*
	 * Find the battery data with a battery id resistor closest to this one
	 */
	for_each_child_of_node(batterydata_container_node, node) {
		if (batt_type != NULL) {
			rc = of_property_read_string(node, "qcom,battery-type",
							&battery_type);
			if (!rc && strcmp(battery_type, batt_type) == 0) {
				best_node = node;
				best_id_kohm = batt_id_kohm;
				break;
			}
		} else {
			rc = of_batterydata_read_batt_id_kohm(node,
							"qcom,batt-id-kohm",
							&batt_ids);
			if (rc)
				continue;
			for (i = 0; i < batt_ids.num; i++) {
				delta = abs(batt_ids.kohm[i] - batt_id_kohm);
				limit = (batt_ids.kohm[i] * id_range_pct) / 100;
				in_range = (delta <= limit);
				/*
				 * Check if the delta is the lowest one
				 * and also if the limits are in range
				 * before selecting the best node.
				 */
				if ((delta < best_delta || !best_node)
					&& in_range) {
					best_node = node;
					best_delta = delta;
					best_id_kohm = batt_ids.kohm[i];
				}
			}
		}
	}

	if (best_node == NULL) {
		pr_err("No battery data found\n");
		return best_node;
	}

	/* check that profile id is in range of the measured batt_id */
	if (abs(best_id_kohm - batt_id_kohm) >
			((best_id_kohm * id_range_pct) / 100)) {
		pr_err("out of range: profile id %d batt id %d pct %d\n",
			best_id_kohm, batt_id_kohm, id_range_pct);
		return NULL;
	}

	rc = of_property_read_string(best_node, "qcom,battery-type",
							&battery_type);
	if (!rc)
		pr_info("%s found\n", battery_type);
	else
		pr_info("%s found\n", best_node->name);

	return best_node;
}

struct device_node *of_batterydata_get_best_aged_profile(
		const struct device_node *batterydata_container_node,
		int batt_id_kohm, int batt_age_level, int *avail_age_level)
{
	struct batt_ids batt_ids;
	struct device_node *node, *best_node = NULL;
	const char *battery_type = NULL;
	int delta = 0, best_id_kohm = 0, id_range_pct, i = 0, rc = 0, limit = 0;
	u32 val;
	bool in_range = false;

	/* read battery id range percentage for best profile */
	rc = of_property_read_u32(batterydata_container_node,
			"qcom,batt-id-range-pct", &id_range_pct);

	if (rc) {
		if (rc == -EINVAL) {
			id_range_pct = 0;
		} else {
			pr_err("failed to read battery id range\n");
			return ERR_PTR(-ENXIO);
		}
	}

	/*
	 * Find the battery data with a battery id resistor closest to this one
	 */
	for_each_available_child_of_node(batterydata_container_node, node) {
		val = 0;
		of_property_read_u32(node, "qcom,batt-age-level", &val);
		rc = of_batterydata_read_batt_id_kohm(node,
						"qcom,batt-id-kohm", &batt_ids);
		if (rc)
			continue;
		for (i = 0; i < batt_ids.num; i++) {
			delta = abs(batt_ids.kohm[i] - batt_id_kohm);
			limit = (batt_ids.kohm[i] * id_range_pct) / 100;
			in_range = (delta <= limit);

			/*
			 * Check if the battery aging level matches and the
			 * limits are in range before selecting the best node.
			 */
			if ((batt_age_level == val || !best_node) && in_range) {
				best_node = node;
				best_id_kohm = batt_ids.kohm[i];
				*avail_age_level = val;
				break;
			}
		}
	}

	if (best_node == NULL) {
		pr_err("No battery data found\n");
		return best_node;
	}

	/* check that profile id is in range of the measured batt_id */
	if (abs(best_id_kohm - batt_id_kohm) >
			((best_id_kohm * id_range_pct) / 100)) {
		pr_err("out of range: profile id %d batt id %d pct %d\n",
			best_id_kohm, batt_id_kohm, id_range_pct);
		return NULL;
	}

	rc = of_property_read_string(best_node, "qcom,battery-type",
							&battery_type);
	if (!rc)
		pr_info("%s age level %d found\n", battery_type,
			*avail_age_level);
	else
		pr_info("%s age level %d found\n", best_node->name,
			*avail_age_level);

	return best_node;
}

int of_batterydata_get_aged_profile_count(
		const struct device_node *batterydata_node,
		int batt_id_kohm, int *count)
{
	struct device_node *node;
	int id_range_pct, i = 0, rc = 0, limit = 0, delta = 0;
	bool in_range = false;
	u32 batt_id;

	/* read battery id range percentage for best profile */
	rc = of_property_read_u32(batterydata_node,
			"qcom,batt-id-range-pct", &id_range_pct);
	if (rc) {
		if (rc == -EINVAL) {
			id_range_pct = 0;
		} else {
			pr_err("failed to read battery id range\n");
			return -ENXIO;
		}
	}

	for_each_available_child_of_node(batterydata_node, node) {
		if (!of_find_property(node, "qcom,batt-age-level", NULL))
			continue;

		if (!of_find_property(node, "qcom,soh-range", NULL))
			continue;

		rc = of_property_read_u32(node, "qcom,batt-id-kohm", &batt_id);
		if (rc)
			continue;

		delta = abs(batt_id_kohm - batt_id);
		limit = (batt_id_kohm * id_range_pct) / 100;
		in_range = (delta <= limit);

		if (!in_range) {
			pr_debug("not in range batt_id: %d\n", batt_id);
			continue;
		}

		i++;
	}

	if (i <= 1) {
		pr_err("Less number of profiles to support SOH\n");
		return -EINVAL;
	}

	*count = i;
	return 0;
}

int of_batterydata_read_soh_aged_profiles(
		const struct device_node *batterydata_node,
		int batt_id_kohm, struct soh_range *soh_data)
{
	struct device_node *node;
	u32 val, temp[2], i = 0;
	int rc, batt_id, id_range_pct, limit = 0, delta = 0;
	bool in_range = false;

	if (!batterydata_node || !soh_data)
		return -ENODEV;

	/* read battery id range percentage for best profile */
	rc = of_property_read_u32(batterydata_node,
			"qcom,batt-id-range-pct", &id_range_pct);
	if (rc) {
		if (rc == -EINVAL) {
			id_range_pct = 0;
		} else {
			pr_err("failed to read battery id range\n");
			return -ENXIO;
		}
	}

	for_each_available_child_of_node(batterydata_node, node) {
		rc = of_property_read_u32(node, "qcom,batt-age-level", &val);
		if (rc)
			continue;

		rc = of_property_read_u32(node, "qcom,batt-id-kohm", &batt_id);
		if (rc)
			continue;

		delta = abs(batt_id_kohm - batt_id);
		limit = (batt_id_kohm * id_range_pct) / 100;
		in_range = (delta <= limit);

		if (!in_range) {
			pr_debug("not in range batt_id: %d\n", batt_id);
			continue;
		}

		if (!of_find_property(node, "qcom,soh-range", NULL))
			continue;

		rc = of_property_count_elems_of_size(node, "qcom,soh-range",
						sizeof(u32));
		if (rc != 2) {
			pr_err("Incorrect element size for qcom,soh-range, rc=%d\n",
				rc);
			return -EINVAL;
		}

		rc = of_property_read_u32_array(node, "qcom,soh-range", temp,
						2);
		if (rc < 0) {
			pr_err("Error in reading qcom,soh-range, rc=%d\n", rc);
			return rc;
		}

		if (temp[0] > 100 || temp[1] > 100 || (temp[0] > temp[1])) {
			pr_err("Incorrect SOH range [%d %d]\n", temp[0],
				temp[1]);
			return -ERANGE;
		}

		pr_debug("batt_age_level: %d soh: [%d %d]\n", val, temp[0],
			temp[1]);
		soh_data[i].batt_age_level = val;
		soh_data[i].soh_min = temp[0];
		soh_data[i].soh_max = temp[1];
		i++;
	}

	return 0;
}

MODULE_LICENSE("GPL v2");
+89 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2013-2014, 2016-2020 The Linux Foundation. All rights reserved.
 */

#ifndef __BATTERY_PROFILE_LOADER_H
#define __BATTERY_PROFILE_LOADER_H

#include <linux/of.h>

#define MAX_BATT_ID_NUM		4
#define DEGC_SCALE		10

struct batt_ids {
	int kohm[MAX_BATT_ID_NUM];
	int num;
};

/**
 * struct soh_range -
 * @batt_age_level:	Battery age level (e.g. 0, 1 etc.,)
 * @soh_min:		Minimum SOH (state of health) level that this battery
 *			profile can support.
 * @soh_max:		Maximum SOH (state of health) level that this battery
 *			profile can support.
 */
struct soh_range {
	int	batt_age_level;
	int	soh_min;
	int	soh_max;
};

/**
 * of_batterydata_get_best_profile() - Find matching battery data device node
 * @batterydata_container_node: pointer to the battery-data container device
 *		node containing the profile nodes.
 * @batt_id_kohm: Battery ID in KOhms for which we want to find the profile.
 * @batt_type: Battery type which we want to force load the profile.
 *
 * This routine returns a device_node pointer to the closest match battery data
 * from device tree based on the battery id reading.
 */
struct device_node *of_batterydata_get_best_profile(
		const struct device_node *batterydata_container_node,
		int batt_id_kohm, const char *batt_type);

/**
 * of_batterydata_get_best_aged_profile() - Find best aged battery profile
 * @batterydata_container_node: pointer to the battery-data container device
 *		node containing the profile nodes.
 * @batt_id_kohm: Battery ID in KOhms for which we want to find the profile.
 * @batt_age_level: Battery age level.
 * @avail_age_level: Available battery age level.
 *
 * This routine returns a device_node pointer to the closest match battery data
 * from device tree based on the battery id reading and age level.
 */
struct device_node *of_batterydata_get_best_aged_profile(
		const struct device_node *batterydata_container_node,
		int batt_id_kohm, int batt_age_level, int *avail_age_level);

/**
 * of_batterydata_get_aged_profile_count() - Gets the number of aged profiles
 * @batterydata_node: pointer to the battery-data container device
 *		node containing the profile nodes.
 * @batt_id_kohm: Battery ID in KOhms for which we want to find the profile.
 * @count: Number of aged profiles available to support SOH based profile
 * loading.
 *
 * This routine returns zero if valid number of aged profiles are available.
 */
int of_batterydata_get_aged_profile_count(
		const struct device_node *batterydata_node,
		int batt_id_kohm, int *count);

/**
 * of_batterydata_read_soh_aged_profiles() - Reads the data from aged profiles
 * @batterydata_node: pointer to the battery-data container device
 *		node containing the profile nodes.
 * @batt_id_kohm: Battery ID in KOhms for which we want to find the profile.
 * @soh_data: SOH data from the profile if it is found to be valid.
 *
 * This routine returns zero if SOH data of aged profiles is valid.
 */
int of_batterydata_read_soh_aged_profiles(
		const struct device_node *batterydata_node,
		int batt_id_kohm, struct soh_range *soh_data);

#endif /* __BATTERY_PROFILE_LOADER_H */
Loading