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

Commit 2152e3ff authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: linear-charger: Report the correct battery status at SOC=0"

parents 4bd53b87 874f7ea1
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -40,6 +40,25 @@ config QPNP_SMBLITE
	  as fuel gauge and USB.
	  VBUS regulator is registered for supporting OTG.

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_LINEAR_CHARGER
	tristate "QPNP Linear Charger driver"
	depends on MFD_SPMI_PMIC
	help
	  Say Y here to enable the Linear battery charger which supports USB
	  detection and charging. The driver also offers relevant information
	  to userspace via the power supply framework.
	  The power supply framework is used to communicate battery and
	  usb properties to userspace and other driver consumers like USB.

config SMB138X_CHARGER
	tristate "SMB138X Battery Charger"
	depends on MFD_I2C_PMIC
@@ -73,6 +92,17 @@ config SMB1351_USB_CHARGER
         notification support. The driver controls SMB1351 via I2C and
         supports device-tree interface.

config SMB1360_CHARGER_FG
	tristate "SMB1360 Charger and Fuel Gauge"
	depends on I2C
	help
	  Say Y to include support for SMB1360 Charger and Fuel Gauge.
	  SMB1360 is a single path switching mode charger capable of charging
	  the battery with 1.5Amps of current. It supports a fuel gauge which
	  uses voltage and coloumb counting for state of charge reporting.
	  The driver reports the status via the power supply framework.
	  A status change triggers an IRQ via the device STAT pin.

config SMB1355_SLAVE_CHARGER
	tristate "SMB1355 Slave Battery Charger"
	depends on MFD_I2C_PMIC
+3 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only

obj-$(CONFIG_SMB1360_CHARGER_FG)	+= smb1360-charger-fg.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_PSY)	+= smb1390-charger-psy.o pmic-voter.o
obj-$(CONFIG_SMB1355_SLAVE_CHARGER)	+= smb1355-charger.o pmic-voter.o
@@ -13,3 +13,5 @@ obj-$(CONFIG_QPNP_QG) += qpnp-qg.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o q
obj-$(CONFIG_HL6111R)			+= hl6111r.o
obj-$(CONFIG_SMB1398_CHARGER)		+= smb1398-charger.o pmic-voter.o
obj-$(CONFIG_QPNP_SMBLITE)		+= step-chg-jeita.o battery.o qpnp-smblite.o smblite-lib.o pmic-voter.o storm-watch.o schgm-flashlite.o
obj-$(CONFIG_QPNP_VM_BMS)		+= qpnp-vm-bms.o batterydata-lib.o batterydata-interface.o
obj-$(CONFIG_QPNP_LINEAR_CHARGER)	+= qpnp-linear-charger.o
+211 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2014, 2018, 2021, The Linux Foundation. All rights reserved.
 */

#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");
+485 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012-2014, 2018, 2021, The Linux Foundation. All rights reserved.
 */

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

#include <linux/module.h>
#include <linux/batterydata-lib.h>

int linear_interpolate(int y0, int x0, int y1, int x1, int x)
{
	if (y0 == y1 || x == x0)
		return y0;
	if (x1 == x0 || x == x1)
		return y1;

	return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
}

static int interpolate_single_lut_scaled(struct single_row_lut *lut,
						int x, int scale)
{
	int i, result;

	if (x < lut->x[0] * scale) {
		pr_debug("x %d less than known range return y = %d lut = %pS\n",
							x, lut->y[0], lut);
		return lut->y[0];
	}
	if (x > lut->x[lut->cols - 1] * scale) {
		pr_debug("x %d more than known range return y = %d lut = %pS\n",
						x, lut->y[lut->cols - 1], lut);
		return lut->y[lut->cols - 1];
	}

	for (i = 0; i < lut->cols; i++)
		if (x <= lut->x[i] * scale)
			break;
	if (x == lut->x[i] * scale) {
		result = lut->y[i];
	} else {
		result = linear_interpolate(
			lut->y[i - 1],
			lut->x[i - 1] * scale,
			lut->y[i],
			lut->x[i] * scale,
			x);
	}
	return result;
}

int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp)
{
	return interpolate_single_lut_scaled(fcc_temp_lut,
						batt_temp,
						DEGC_SCALE);
}

int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut,
		int cycles)
{
	/*
	 * sf table could be null when no battery aging data is available, in
	 * that case return 100%
	 */
	if (fcc_sf_lut)
		return interpolate_single_lut_scaled(fcc_sf_lut, cycles, 1);
	else
		return 100;
}

int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc)
{
	int i, scalefactorrow1, scalefactorrow2, scalefactor, rows, cols;
	int row1 = 0;
	int row2 = 0;

	/*
	 * sf table could be null when no battery aging data is available, in
	 * that case return 100%
	 */
	if (!sf_lut)
		return 100;

	rows = sf_lut->rows;
	cols = sf_lut->cols;
	if (pc > sf_lut->percent[0]) {
		pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
		row1 = 0;
		row2 = 0;
	} else if (pc < sf_lut->percent[rows - 1]) {
		pr_debug("pc %d less than known pc ranges for sf\n", pc);
		row1 = rows - 1;
		row2 = rows - 1;
	} else {
		for (i = 0; i < rows; i++) {
			if (pc == sf_lut->percent[i]) {
				row1 = i;
				row2 = i;
				break;
			}
			if (pc > sf_lut->percent[i]) {
				row1 = i - 1;
				row2 = i;
				break;
			}
		}
	}

	if (row_entry < sf_lut->row_entries[0] * DEGC_SCALE)
		row_entry = sf_lut->row_entries[0] * DEGC_SCALE;
	if (row_entry > sf_lut->row_entries[cols - 1] * DEGC_SCALE)
		row_entry = sf_lut->row_entries[cols - 1] * DEGC_SCALE;

	for (i = 0; i < cols; i++)
		if (row_entry <= sf_lut->row_entries[i] * DEGC_SCALE)
			break;
	if (row_entry == sf_lut->row_entries[i] * DEGC_SCALE) {
		scalefactor = linear_interpolate(
				sf_lut->sf[row1][i],
				sf_lut->percent[row1],
				sf_lut->sf[row2][i],
				sf_lut->percent[row2],
				pc);
		return scalefactor;
	}

	scalefactorrow1 = linear_interpolate(
				sf_lut->sf[row1][i - 1],
				sf_lut->row_entries[i - 1] * DEGC_SCALE,
				sf_lut->sf[row1][i],
				sf_lut->row_entries[i] * DEGC_SCALE,
				row_entry);

	scalefactorrow2 = linear_interpolate(
				sf_lut->sf[row2][i - 1],
				sf_lut->row_entries[i - 1] * DEGC_SCALE,
				sf_lut->sf[row2][i],
				sf_lut->row_entries[i] * DEGC_SCALE,
				row_entry);

	scalefactor = linear_interpolate(
				scalefactorrow1,
				sf_lut->percent[row1],
				scalefactorrow2,
				sf_lut->percent[row2],
				pc);

	return scalefactor;
}

/* get ocv given a soc  -- reverse lookup */
int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
				int batt_temp, int pc)
{
	int i, ocvrow1, ocvrow2, ocv, rows, cols;
	int row1 = 0;
	int row2 = 0;

	rows = pc_temp_ocv->rows;
	cols = pc_temp_ocv->cols;
	if (pc > pc_temp_ocv->percent[0]) {
		pr_debug("pc %d greater than known pc ranges for sfd\n", pc);
		row1 = 0;
		row2 = 0;
	} else if (pc < pc_temp_ocv->percent[rows - 1]) {
		pr_debug("pc %d less than known pc ranges for sf\n", pc);
		row1 = rows - 1;
		row2 = rows - 1;
	} else {
		for (i = 0; i < rows; i++) {
			if (pc == pc_temp_ocv->percent[i]) {
				row1 = i;
				row2 = i;
				break;
			}
			if (pc > pc_temp_ocv->percent[i]) {
				row1 = i - 1;
				row2 = i;
				break;
			}
		}
	}

	if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
		batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
	if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
		batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;

	for (i = 0; i < cols; i++)
		if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
			break;
	if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
		ocv = linear_interpolate(
				pc_temp_ocv->ocv[row1][i],
				pc_temp_ocv->percent[row1],
				pc_temp_ocv->ocv[row2][i],
				pc_temp_ocv->percent[row2],
				pc);
		return ocv;
	}

	ocvrow1 = linear_interpolate(
				pc_temp_ocv->ocv[row1][i - 1],
				pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
				pc_temp_ocv->ocv[row1][i],
				pc_temp_ocv->temp[i] * DEGC_SCALE,
				batt_temp);

	ocvrow2 = linear_interpolate(
				pc_temp_ocv->ocv[row2][i - 1],
				pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
				pc_temp_ocv->ocv[row2][i],
				pc_temp_ocv->temp[i] * DEGC_SCALE,
				batt_temp);

	ocv = linear_interpolate(
				ocvrow1,
				pc_temp_ocv->percent[row1],
				ocvrow2,
				pc_temp_ocv->percent[row2],
				pc);

	return ocv;
}

int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
				int batt_temp, int ocv)
{
	int i, j, pcj, pcj_minus_one, pc;
	int rows = pc_temp_ocv->rows;
	int cols = pc_temp_ocv->cols;

	if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE) {
		pr_debug("batt_temp %d < known temp range\n", batt_temp);
		batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
	}

	if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE) {
		pr_debug("batt_temp %d > known temp range\n", batt_temp);
		batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
	}

	for (j = 0; j < cols; j++)
		if (batt_temp <= pc_temp_ocv->temp[j] * DEGC_SCALE)
			break;
	if (batt_temp == pc_temp_ocv->temp[j] * DEGC_SCALE) {
		/* found an exact match for temp in the table */
		if (ocv >= pc_temp_ocv->ocv[0][j])
			return pc_temp_ocv->percent[0];
		if (ocv <= pc_temp_ocv->ocv[rows - 1][j])
			return pc_temp_ocv->percent[rows - 1];
		for (i = 0; i < rows; i++) {
			if (ocv >= pc_temp_ocv->ocv[i][j]) {
				if (ocv == pc_temp_ocv->ocv[i][j])
					return pc_temp_ocv->percent[i];
				pc = linear_interpolate(
					pc_temp_ocv->percent[i],
					pc_temp_ocv->ocv[i][j],
					pc_temp_ocv->percent[i - 1],
					pc_temp_ocv->ocv[i - 1][j],
					ocv);
				return pc;
			}
		}
	}

	/*
	 * batt_temp is within temperature for
	 * column j-1 and j
	 */
	if (ocv >= pc_temp_ocv->ocv[0][j])
		return pc_temp_ocv->percent[0];
	if (ocv <= pc_temp_ocv->ocv[rows - 1][j - 1])
		return pc_temp_ocv->percent[rows - 1];

	pcj_minus_one = 0;
	pcj = 0;
	for (i = 0; i < rows-1; i++) {
		if (pcj == 0
			&& is_between(pc_temp_ocv->ocv[i][j],
				pc_temp_ocv->ocv[i+1][j], ocv)) {
			pcj = linear_interpolate(
				pc_temp_ocv->percent[i],
				pc_temp_ocv->ocv[i][j],
				pc_temp_ocv->percent[i + 1],
				pc_temp_ocv->ocv[i+1][j],
				ocv);
		}

		if (pcj_minus_one == 0
			&& is_between(pc_temp_ocv->ocv[i][j-1],
				pc_temp_ocv->ocv[i+1][j-1], ocv)) {
			pcj_minus_one = linear_interpolate(
				pc_temp_ocv->percent[i],
				pc_temp_ocv->ocv[i][j-1],
				pc_temp_ocv->percent[i + 1],
				pc_temp_ocv->ocv[i+1][j-1],
				ocv);
		}

		if (pcj && pcj_minus_one) {
			pc = linear_interpolate(
				pcj_minus_one,
				pc_temp_ocv->temp[j-1] * DEGC_SCALE,
				pcj,
				pc_temp_ocv->temp[j] * DEGC_SCALE,
				batt_temp);
			return pc;
		}
	}

	if (pcj)
		return pcj;

	if (pcj_minus_one)
		return pcj_minus_one;

	pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%\n",
							ocv, batt_temp);
	return 100;
}

int interpolate_slope(struct pc_temp_ocv_lut *pc_temp_ocv,
					int batt_temp, int pc)
{
	int i, ocvrow1, ocvrow2, rows, cols;
	int row1 = 0;
	int row2 = 0;
	int slope;

	rows = pc_temp_ocv->rows;
	cols = pc_temp_ocv->cols;
	if (pc >= pc_temp_ocv->percent[0]) {
		pr_debug("pc %d >= max pc range - use the slope at pc=%d\n",
						pc, pc_temp_ocv->percent[0]);
		row1 = 0;
		row2 = 1;
	} else if (pc <= pc_temp_ocv->percent[rows - 1]) {
		pr_debug("pc %d is <= min pc range - use the slope at pc=%d\n",
					pc,  pc_temp_ocv->percent[rows - 1]);
		row1 = rows - 2;
		row2 = rows - 1;
	} else {
		for (i = 0; i < rows; i++) {
			if (pc == pc_temp_ocv->percent[i]) {
				row1 = i - 1;
				row2 = i;
				break;
			}
			if (pc > pc_temp_ocv->percent[i]) {
				row1 = i - 1;
				row2 = i;
				break;
			}
		}
	}

	if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
		batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
	if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
		batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;

	for (i = 0; i < cols; i++)
		if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
			break;

	if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
		slope = (pc_temp_ocv->ocv[row1][i] -
				pc_temp_ocv->ocv[row2][i]);
		if (slope <= 0) {
			pr_warn("Slope=%d for pc=%d, using 1\n", slope, pc);
			slope = 1;
		}
		slope *= 1000;
		slope /= (pc_temp_ocv->percent[row1] -
			pc_temp_ocv->percent[row2]);
		return slope;
	}
	ocvrow1 = linear_interpolate(
			pc_temp_ocv->ocv[row1][i - 1],
			pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
			pc_temp_ocv->ocv[row1][i],
			pc_temp_ocv->temp[i] * DEGC_SCALE,
			batt_temp);

	ocvrow2 = linear_interpolate(
			pc_temp_ocv->ocv[row2][i - 1],
				pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
				pc_temp_ocv->ocv[row2][i],
				pc_temp_ocv->temp[i] * DEGC_SCALE,
				batt_temp);

	slope = (ocvrow1 - ocvrow2);
	if (slope <= 0) {
		pr_warn("Slope=%d for pc=%d, using 1\n", slope, pc);
		slope = 1;
	}
	slope *= 1000;
	slope /= (pc_temp_ocv->percent[row1] - pc_temp_ocv->percent[row2]);

	return slope;
}

int interpolate_acc(struct ibat_temp_acc_lut *ibat_acc_lut,
					int batt_temp, int ibat)
{
	int i, accrow1, accrow2, rows, cols;
	int row1 = 0;
	int row2 = 0;
	int acc;

	rows = ibat_acc_lut->rows;
	cols = ibat_acc_lut->cols;

	if (ibat > ibat_acc_lut->ibat[rows - 1]) {
		pr_debug("ibatt(%d) > max range(%d)\n", ibat,
					ibat_acc_lut->ibat[rows - 1]);
		row1 = rows - 1;
		row2 = rows - 2;
	} else if (ibat < ibat_acc_lut->ibat[0]) {
		pr_debug("ibatt(%d) < max range(%d)\n", ibat,
					ibat_acc_lut->ibat[0]);
		row1 = 0;
		row2 = 0;
	} else {
		for (i = 0; i < rows; i++) {
			if (ibat == ibat_acc_lut->ibat[i]) {
				row1 = i;
				row2 = i;
				break;
			}
			if (ibat < ibat_acc_lut->ibat[i]) {
				row1 = i;
				row2 = i - 1;
				break;
			}
		}
	}

	if (batt_temp < ibat_acc_lut->temp[0] * DEGC_SCALE)
		batt_temp = ibat_acc_lut->temp[0] * DEGC_SCALE;
	if (batt_temp > ibat_acc_lut->temp[cols - 1] * DEGC_SCALE)
		batt_temp = ibat_acc_lut->temp[cols - 1] * DEGC_SCALE;

	for (i = 0; i < cols; i++)
		if (batt_temp <= ibat_acc_lut->temp[i] * DEGC_SCALE)
			break;

	if (batt_temp == (ibat_acc_lut->temp[i] * DEGC_SCALE)) {
		acc = linear_interpolate(
			ibat_acc_lut->acc[row1][i],
			ibat_acc_lut->ibat[row1],
			ibat_acc_lut->acc[row2][i],
			ibat_acc_lut->ibat[row2],
			ibat);
		return acc;
	}

	accrow1 = linear_interpolate(
		ibat_acc_lut->acc[row1][i - 1],
		ibat_acc_lut->temp[i - 1] * DEGC_SCALE,
		ibat_acc_lut->acc[row1][i],
		ibat_acc_lut->temp[i] * DEGC_SCALE,
		batt_temp);

	accrow2 = linear_interpolate(
		ibat_acc_lut->acc[row2][i - 1],
		ibat_acc_lut->temp[i - 1] * DEGC_SCALE,
		ibat_acc_lut->acc[row2][i],
		ibat_acc_lut->temp[i] * DEGC_SCALE,
		batt_temp);

	acc = linear_interpolate(accrow1,
			ibat_acc_lut->ibat[row1],
			accrow2,
			ibat_acc_lut->ibat[row2],
			ibat);

	if (acc < 0)
		acc = 0;

	return acc;
}
+3683 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading