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

Commit cec0f9c0 authored by Arulpandiyan Vadivel's avatar Arulpandiyan Vadivel Committed by Sundara Vinayagam
Browse files

power: vm-bms: Add snapshot of qpnp-vm-bms driver



Initial snapshot of vm-bms device driver is taken from msm-3.18
kernel version @ commit f8b6819d0432d6 ("msm: ipa: Fix to
handle NULL pointer dereference")

Add vm-bms, batterydata-interface, battery-lib support
Change spmi driver framework to platform driver framework in 4.9
kernel. Add support to access spmi register via regmap.

Change-Id: Ic2133fcf8dc73e6c1327a8583ccdaa2f5695cfbe
Signed-off-by: default avatarArulpandiyan Vadivel <avadiv@codeaurora.org>
Signed-off-by: default avatarSundara Vinayagam <sundvi@codeaurora.org>
parent c287f849
Loading
Loading
Loading
Loading
+180 −0
Original line number Diff line number Diff line
Qualcomm's QPNP Voltage-Mode(VM) PMIC Battery Management System

QPNP PMIC VM BMS provides interface to clients to read properties related
to the battery. Its main function is to calculate the SOC (state of charge)
of the battery based on periodic sampling of the VBAT (battery voltage).

Parent node required properties:
- compatible	: Must be  "qcom,qpnp-vm-bms" for the BM driver.
- reg		: Offset and length of the PMIC peripheral register map.
- interrupts	: The interrupt mappings.
		  The format should be
		  <slave-id peripheral-id interrupt-number>.
- interrupt-names : names for the mapped bms interrupt
		The following interrupts are required:
		0 : leave CV state
		1 : enter CV state
		2 : good ocv generated
		3 : ocv_thr
		4 : fifo update
		5 : fsm state chnaged

Additionally, optional subnodes may be included:
- qcom,batt-pres-status : A subnode with a register address for the SMBB
		battery interface's BATT_PRES_STATUS register. If this node is
		added, then the BMS will try to detect offmode battery removal
		via the battery interface's offmode battery removal circuit.
- qcom,battery-data : A phandle to a node containing the available batterydata
		profiles. See the batterydata bindings documentation for more
		details.

Parent node required properties:
- qcom,v-cutoff-uv : cutoff voltage where the battery is considered dead in
			micro-volts.
- qcom,max-voltage-uv : maximum voltage for the battery in micro-volts.
- qcom,r-conn-mohm : connector resistance in milli-ohms.
- qcom,shutdown-soc-valid-limit : If the ocv upon restart is within this
			distance of the shutdown ocv, the BMS will try to force
			the new SoC to the old one to provide charge continuity.
			That is to say,
				if (abs(shutdown-soc - current-soc) < limit)
				then use old SoC.
- qcom,low-soc-calculate-soc-threshold : The SoC threshold for when
			the periodic calculate_soc work speeds up. This ensures
			SoC is updated in userspace constantly when we are near
			shutdown.
- qcom,low-voltage-threshold : The battery voltage threshold in micro-volts for
			when the BMS tries to wake up and hold a wakelock to
			ensure a clean shutdown.
- qcom,low-voltage-calculate-soc-ms : The time period between subsequent
			SoC recalculations when the current voltage is below
			qcom,low-voltage threshold. This takes precedence over
			qcom,low-soc-calculate-soc-ms.
- qcom,low-soc-calculate-soc-ms : The time period between subsequent
			SoC recalculations when the current SoC is below
			qcom,low-soc-calculate-soc-threshold. This takes
			precedence over qcom,calculate-soc-ms.
- qcom,calculate-soc-ms : The time period between subsequent SoC
			recalculations when the current SoC is above or equal
			qcom,low-soc-calculate-soc-threshold.
- qcom,volatge-soc-timeout-ms : The timeout period after which the module starts
			reporting volage based SOC and does not use the VMBMS
			algorithm for SOC calculation.
- qcom,bms-vadc: Corresponding VADC device's phandle.
- qcom,bms-adc_tm: Corresponding ADC_TM device's phandle to set recurring
                        measurements and receive notifications for vbatt.
- qcom,pmic-revid : Phandle pointing to the revision peripheral node.

Parent node Optional properties
- qcom,s1-sample-interval-ms: The sampling rate in ms of the accumulator in state
			S1. (i.e) the rate at which the accumulator is being
			filled with vbat samples. Minimum value = 0 and
			Maximum value = 2550ms.
- qcom,s2-sample-interval-ms: The sampling rate in ms of the accumulator in state
			S2. (i.e) the rate at which the accumulator is being
			filled with vbat samples. Minimum value = 0 and
			Maximum value = 2550ms.
- qcom,s1-sample-count: The number of samples to be accululated for one FIFO in
			state S1. Possible values are - 0, 4, 8, 16, 32, 64, 128,
			256.
- qcom,s2-sample-count: The number of samples to be accululated for one FIFO in
			state S2. Possible values are - 0, 4, 8, 16, 32, 64, 128,
			256.
- qcom,s1-fifo-legth:	Number of FIFO's to be filled in state S1, to generate
			the fifo_update_done interrupt. Possile values - 0 to 8
- qcom,s2-fifo-legth:	Number of FIFO's to be filled in state S2, to generate
			the fifo_update_done interrupt. Possible values- 0 to 8
- qcom,force-s3-on-suspend : Bool property to force the BMS into S3 (sleep) state
			while entering into system suspend.
- qcom,force-bms-active-on-charger: Bool property to keep BMS FSM active
				if charger is present.
- qcom,report-charger-eoc : Bool property to indicate if BMS needs to indicate
			EOC to charger.
- qcom,ignore-shutdown-soc: A boolean that controls whether BMS will
			try to force the startup SoC to be the same as the
			shutdown SoC. Defining it will make BMS ignore the
			shutdown SoC.
- qcom,use-voltage-soc : A boolean that controls whether BMS will use
			voltage-based SoC instead of a coulomb counter based
			one. Voltage-based SoC will not guarantee linearity.
- qcom,disable-bms :	Bool property to disable the VMBMS hardware module.
			Enable this property if BMS is not supported or an external
			fuel gauge is used.
- qcom,s3-ocv-tolerence-uv : The S3 state OCV tolerence threshold in uV. The
			LSB value is 300uV and maximum value is 76500uV.
- qcom,low-soc-fifo-length : The fifo length (of S2 STATE) to be used at lower
			SOCs. If this value is not specified the system uses
			default length.
- qcom,resume-soc:	Capacity in percent at which charging should resume
			when a fully charged battery drops below this level.
- qcom,low-temp-threshold : The temperature threshold below which the IBAT
			averaging and UUC smoothening is disabled. This value
			is in deci-degrees centigrade. If not specified it
			defaults to 0.
- qcom,ibat-avg-samples : The number of samples to be averaged for IBAT
			estimation. If not specified it defaults to 16.
			The possible values are 1 to 16.
- qcom,batt-aging-comp : A boolean that defines if battery aging compensation
			is enabled.
- qcom,use-reported-soc : Bool property to enable the reported_soc logic. To
			enable this feature, qcom,resume-soc must be defined as
			a proper value. The BMS is also required to control the
			charging, discharging and recharging.

qcom,batt-pres-status node required properties:
- reg : offset and length of the PMIC LBC battery interface BATT_PRES_STATUS
		register.

qcom,qpnp-chg-pres required properties:
- reg : offset and length of the PMIC LBC charger interafce CHARGER_OPTION
		register.

Example:
pm8916_bms: qcom,qpnp-vm-bms {
	spmi-dev-container;
	compatible = "qcom,qpnp-vm-bms";
	#address-cells = <1>;
	#size-cells = <1>;
	status = "disabled";

	qcom,v-cutoff-uv = <3400000>;
	qcom,max-voltage-uv = <4200000>;
	qcom,r-conn-mohm = <18>;
	qcom,shutdown-soc-valid-limit = <20>;
	qcom,low-soc-calculate-soc-threshold = <15>;
	qcom,low-voltage-threshold = <3420000>;
	qcom,low-voltage-calculate-soc-ms = <1000>;
	qcom,low-soc-calculate-soc-ms = <5000>;
	qcom,low-soc-fifo-length = <2>;
	qcom,calculate-soc-ms = <20000>;
	qcom,s3-ocv-tolerence-uv = <1200>;
	qcom,volatge-soc-timeout-ms = <60000>;
	qcom,battery-data = <&mtp_batterydata>;
	qcom,bms-vadc = <&pm8916_vadc>;
	qcom,bms-adc_tm = <&pm8916_adc_tm>;

	qcom,batt-pres-status@1208 {
		reg = <0x1208 0x1>;
	}

	qcom,qpnp-chg-pres@1208 {
		reg = <0x1108 0x1>;
	}

	qcom,bms-bms@4000 {
		reg = <0x4000 0x100>;
		interrupts =	<0x0 0x40 0x0>,
				<0x0 0x40 0x1>,
				<0x0 0x40 0x2>,
				<0x0 0x40 0x3>,
				<0x0 0x40 0x4>,
				<0x0 0x40 0x5>,

		interrupt-names = "leave_cv",
				  "enter_cv",
				  "good_ocv",
				  "ocv_thr",
				  "fifo_updtaed",
				  "fsm_state_change";
	};
};
+9 −0
Original line number Diff line number Diff line
@@ -104,6 +104,15 @@ config QPNP_QNOVO
	  module. It also allows userspace code to read diagnostics of voltage
	  and current measured during certain phases of the pulses.

config QPNP_VM_BMS
	tristate "QPNP Voltage-Mode Battery Monitoring System driver"
	depends on MFD_SPMI_PMIC
	help
	  Say Y here to enable support for QPNP chip vm-bms device.
	  The voltage-mode (vm) BMS driver uses periodic VBATT
	  readings from the battery to calculate the State of
	  Charge.

config QPNP_TYPEC
	tristate "QPNP Type-C driver"
	depends on MFD_SPMI_PMIC
+1 −0
Original line number Diff line number Diff line
@@ -11,3 +11,4 @@ obj-$(CONFIG_QPNP_QNOVO) += qpnp-qnovo.o battery.o
obj-$(CONFIG_QPNP_TYPEC)	+= qpnp-typec.o
obj-$(CONFIG_QPNP_SMB5)		+= step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o
obj-$(CONFIG_SMB1390_CHARGE_PUMP)	+= smb1390-charger.o pmic-voter.o
obj-$(CONFIG_QPNP_VM_BMS) += qpnp-vm-bms.o batterydata-lib.o batterydata-interface.o
+218 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014, 2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#define pr_fmt(fmt)	"BATTERY: %s: " fmt, __func__
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/uaccess.h>
#include <linux/batterydata-lib.h>
#include <linux/batterydata-interface.h>

struct battery_data {
	dev_t				dev_no;
	struct class			*battery_class;
	struct device			*battery_device;
	struct cdev			battery_cdev;
	struct bms_battery_data		*profile;
};
static struct battery_data *the_battery;

static int battery_data_open(struct inode *inode, struct file *file)
{
	struct battery_data *battery = container_of(inode->i_cdev,
				struct battery_data, battery_cdev);

	pr_debug("battery_data device opened\n");

	file->private_data = battery;

	return 0;
}

static long battery_data_ioctl(struct file *file, unsigned int cmd,
						unsigned long arg)
{
	struct battery_data *battery = file->private_data;
	struct battery_params __user *bp_user =
				(struct battery_params __user *)arg;
	struct battery_params bp;
	int soc, rbatt_sf, slope, fcc_mah;
	int rc = 0;

	if (!battery->profile) {
		pr_err("Battery data not set!\n");
		return -EINVAL;
	}
	if (copy_from_user(&bp, bp_user, sizeof(bp))) {
		pr_err("copy_from_user failed\n");
		return -EFAULT;
	}

	switch (cmd) {
	case BPIOCXSOC:
		soc = interpolate_pc(battery->profile->pc_temp_ocv_lut,
					bp.batt_temp, bp.ocv_uv / 1000);
		rc = put_user(soc, &bp_user->soc);
		if (rc) {
			pr_err("BPIOCXSOC: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXSOC: ocv=%d batt_temp=%d soc=%d\n",
				bp.ocv_uv / 1000, bp.batt_temp, soc);
		break;
	case BPIOCXRBATT:
		rbatt_sf = interpolate_scalingfactor(
				battery->profile->rbatt_sf_lut,
				bp.batt_temp, bp.soc);
		rc = put_user(rbatt_sf, &bp_user->rbatt_sf);
		if (rc) {
			pr_err("BPIOCXRBATT: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXRBATT: soc=%d batt_temp=%d rbatt_sf=%d\n",
					bp.soc, bp.batt_temp, rbatt_sf);
		break;
	case BPIOCXSLOPE:
		slope = interpolate_slope(battery->profile->pc_temp_ocv_lut,
							bp.batt_temp, bp.soc);
		rc = put_user(slope, &bp_user->slope);
		if (rc) {
			pr_err("BPIOCXSLOPE: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXSLOPE: soc=%d batt_temp=%d slope=%d\n",
					bp.soc, bp.batt_temp, slope);
		break;
	case BPIOCXFCC:
		fcc_mah = interpolate_fcc(battery->profile->fcc_temp_lut,
							bp.batt_temp);
		rc = put_user(fcc_mah, &bp_user->fcc_mah);
		if (rc) {
			pr_err("BPIOCXFCC: Failed to 'put_user' rc=%d\n", rc);
			goto ret_err;
		}
		pr_debug("BPIOCXFCC: batt_temp=%d fcc_mah=%d\n",
					bp.batt_temp, fcc_mah);
		break;
	default:
		pr_err("IOCTL %d not supported\n", cmd);
		rc = -EINVAL;

	}
ret_err:
	return rc;
}

static int battery_data_release(struct inode *inode, struct file *file)
{
	pr_debug("battery_data device closed\n");

	return 0;
}

static const struct file_operations battery_data_fops = {
	.owner = THIS_MODULE,
	.open = battery_data_open,
	.unlocked_ioctl = battery_data_ioctl,
	.compat_ioctl = battery_data_ioctl,
	.release = battery_data_release,
};

int config_battery_data(struct bms_battery_data *profile)
{
	if (!the_battery) {
		pr_err("Battery data not initialized\n");
		return -ENODEV;
	}

	the_battery->profile = profile;

	pr_debug("Battery profile set - %s\n",
			the_battery->profile->battery_type);

	return 0;
}

static int batterydata_init(void)
{
	int rc;
	struct battery_data *battery;

	battery = kzalloc(sizeof(*battery), GFP_KERNEL);
	if (!battery)
		return -ENOMEM;

	/* character device to access the battery-data from userspace */
	rc = alloc_chrdev_region(&battery->dev_no, 0, 1, "battery_data");
	if (rc) {
		pr_err("Unable to allocate chrdev rc=%d\n", rc);
		return rc;
	}
	cdev_init(&battery->battery_cdev, &battery_data_fops);
	rc = cdev_add(&battery->battery_cdev, battery->dev_no, 1);
	if (rc) {
		pr_err("Unable to add battery_cdev rc=%d\n", rc);
		goto unregister_chrdev;
	}

	battery->battery_class = class_create(THIS_MODULE, "battery_data");
	if (IS_ERR_OR_NULL(battery->battery_class)) {
		pr_err("Fail to create battery class\n");
		rc = -ENODEV;
		goto delete_cdev;
	}

	battery->battery_device = device_create(battery->battery_class,
					NULL, battery->dev_no,
					NULL, "battery_data");
	if (IS_ERR(battery->battery_device)) {
		pr_err("Fail to create battery_device device\n");
		rc = -ENODEV;
		goto delete_cdev;
	}

	the_battery = battery;

	pr_info("Battery-data device created!\n");

	return 0;

delete_cdev:
	cdev_del(&battery->battery_cdev);
unregister_chrdev:
	unregister_chrdev_region(battery->dev_no, 1);
	the_battery = NULL;
	return rc;
}
subsys_initcall(batterydata_init);

static void batterydata_exit(void)
{
	if (the_battery) {
		device_destroy(the_battery->battery_class, the_battery->dev_no);
		cdev_del(&the_battery->battery_cdev);
		unregister_chrdev_region(the_battery->dev_no, 1);
	}
	kfree(the_battery);
	the_battery = NULL;
}
module_exit(batterydata_exit);

MODULE_DESCRIPTION("Battery-data Interface driver");
MODULE_LICENSE("GPL v2");
Loading