blob: 2af997ecac0e2514dc24e1a4773497957d1ab4d2 [file] [log] [blame]
/* Copyright (c) 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.
*/
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/ratelimit.h>
#include "qg-profile-lib.h"
#include "qg-defs.h"
static 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));
}
int interpolate_single_row_lut(struct profile_table_data *lut,
int x, int scale)
{
int i, result;
int cols = lut->cols;
if (x < lut->col_entries[0] * scale) {
pr_debug("x %d less than known range return y = %d lut = %s\n",
x, lut->data[0][0], lut->name);
return lut->data[0][0];
}
if (x > lut->col_entries[cols-1] * scale) {
pr_debug("x %d more than known range return y = %d lut = %s\n",
x, lut->data[0][cols-1], lut->name);
return lut->data[0][cols-1];
}
for (i = 0; i < cols; i++) {
if (x <= lut->col_entries[i] * scale)
break;
}
if (x == lut->col_entries[i] * scale) {
result = lut->data[0][i];
} else {
result = linear_interpolate(
lut->data[0][i-1],
lut->col_entries[i-1] * scale,
lut->data[0][i],
lut->col_entries[i] * scale,
x);
}
return result;
}
int interpolate_soc(struct profile_table_data *lut,
int batt_temp, int ocv)
{
int i, j, soc_high, soc_low, soc;
int rows = lut->rows;
int cols = lut->cols;
if (batt_temp < lut->col_entries[0] * DEGC_SCALE) {
pr_debug("batt_temp %d < known temp range\n", batt_temp);
batt_temp = lut->col_entries[0] * DEGC_SCALE;
}
if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE) {
pr_debug("batt_temp %d > known temp range\n", batt_temp);
batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE;
}
for (j = 0; j < cols; j++)
if (batt_temp <= lut->col_entries[j] * DEGC_SCALE)
break;
if (batt_temp == lut->col_entries[j] * DEGC_SCALE) {
/* found an exact match for temp in the table */
if (ocv >= lut->data[0][j])
return lut->row_entries[0];
if (ocv <= lut->data[rows - 1][j])
return lut->row_entries[rows - 1];
for (i = 0; i < rows; i++) {
if (ocv >= lut->data[i][j]) {
if (ocv == lut->data[i][j])
return lut->row_entries[i];
soc = linear_interpolate(
lut->row_entries[i],
lut->data[i][j],
lut->row_entries[i - 1],
lut->data[i - 1][j],
ocv);
return soc;
}
}
}
/* batt_temp is within temperature for column j-1 and j */
if (ocv >= lut->data[0][j])
return lut->row_entries[0];
if (ocv <= lut->data[rows - 1][j - 1])
return lut->row_entries[rows - 1];
soc_low = soc_high = 0;
for (i = 0; i < rows-1; i++) {
if (soc_high == 0 && is_between(lut->data[i][j],
lut->data[i+1][j], ocv)) {
soc_high = linear_interpolate(
lut->row_entries[i],
lut->data[i][j],
lut->row_entries[i + 1],
lut->data[i+1][j],
ocv);
}
if (soc_low == 0 && is_between(lut->data[i][j-1],
lut->data[i+1][j-1], ocv)) {
soc_low = linear_interpolate(
lut->row_entries[i],
lut->data[i][j-1],
lut->row_entries[i + 1],
lut->data[i+1][j-1],
ocv);
}
if (soc_high && soc_low) {
soc = linear_interpolate(
soc_low,
lut->col_entries[j-1] * DEGC_SCALE,
soc_high,
lut->col_entries[j] * DEGC_SCALE,
batt_temp);
return soc;
}
}
if (soc_high)
return soc_high;
if (soc_low)
return soc_low;
pr_debug("%d ocv wasn't found for temp %d in the LUT %s returning 100%%\n",
ocv, batt_temp, lut->name);
return 10000;
}
int interpolate_var(struct profile_table_data *lut,
int batt_temp, int soc)
{
int i, var1, var2, var, rows, cols;
int row1 = 0;
int row2 = 0;
rows = lut->rows;
cols = lut->cols;
if (soc > lut->row_entries[0]) {
pr_debug("soc %d greater than known soc ranges for %s lut\n",
soc, lut->name);
row1 = 0;
row2 = 0;
} else if (soc < lut->row_entries[rows - 1]) {
pr_debug("soc %d less than known soc ranges for %s lut\n",
soc, lut->name);
row1 = rows - 1;
row2 = rows - 1;
} else {
for (i = 0; i < rows; i++) {
if (soc == lut->row_entries[i]) {
row1 = i;
row2 = i;
break;
}
if (soc > lut->row_entries[i]) {
row1 = i - 1;
row2 = i;
break;
}
}
}
if (batt_temp < lut->col_entries[0] * DEGC_SCALE)
batt_temp = lut->col_entries[0] * DEGC_SCALE;
if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE)
batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE;
for (i = 0; i < cols; i++)
if (batt_temp <= lut->col_entries[i] * DEGC_SCALE)
break;
if (batt_temp == lut->col_entries[i] * DEGC_SCALE) {
var = linear_interpolate(
lut->data[row1][i],
lut->row_entries[row1],
lut->data[row2][i],
lut->row_entries[row2],
soc);
return var;
}
var1 = linear_interpolate(
lut->data[row1][i - 1],
lut->col_entries[i - 1] * DEGC_SCALE,
lut->data[row1][i],
lut->col_entries[i] * DEGC_SCALE,
batt_temp);
var2 = linear_interpolate(
lut->data[row2][i - 1],
lut->col_entries[i - 1] * DEGC_SCALE,
lut->data[row2][i],
lut->col_entries[i] * DEGC_SCALE,
batt_temp);
var = linear_interpolate(
var1,
lut->row_entries[row1],
var2,
lut->row_entries[row2],
soc);
return var;
}
int interpolate_slope(struct profile_table_data *lut,
int batt_temp, int soc)
{
int i, ocvrow1, ocvrow2, rows, cols;
int row1 = 0;
int row2 = 0;
int slope;
rows = lut->rows;
cols = lut->cols;
if (soc >= lut->row_entries[0]) {
pr_debug("soc %d >= max soc range - use the slope at soc=%d for lut %s\n",
soc, lut->row_entries[0], lut->name);
row1 = 0;
row2 = 1;
} else if (soc <= lut->row_entries[rows - 1]) {
pr_debug("soc %d is <= min soc range - use the slope at soc=%d for lut %s\n",
soc, lut->row_entries[rows - 1], lut->name);
row1 = rows - 2;
row2 = rows - 1;
} else {
for (i = 0; i < rows; i++) {
if (soc >= lut->row_entries[i]) {
row1 = i - 1;
row2 = i;
break;
}
}
}
if (batt_temp < lut->col_entries[0] * DEGC_SCALE)
batt_temp = lut->col_entries[0] * DEGC_SCALE;
if (batt_temp > lut->col_entries[cols - 1] * DEGC_SCALE)
batt_temp = lut->col_entries[cols - 1] * DEGC_SCALE;
for (i = 0; i < cols; i++) {
if (batt_temp <= lut->col_entries[i] * DEGC_SCALE)
break;
}
if (batt_temp == lut->col_entries[i] * DEGC_SCALE) {
slope = (lut->data[row1][i] - lut->data[row2][i]);
if (slope <= 0) {
pr_warn_ratelimited("Slope=%d for soc=%d, using 1\n",
slope, soc);
slope = 1;
}
slope *= 10000;
slope /= (lut->row_entries[row1] -
lut->row_entries[row2]);
return slope;
}
ocvrow1 = linear_interpolate(
lut->data[row1][i - 1],
lut->col_entries[i - 1] * DEGC_SCALE,
lut->data[row1][i],
lut->col_entries[i] * DEGC_SCALE,
batt_temp);
ocvrow2 = linear_interpolate(
lut->data[row2][i - 1],
lut->col_entries[i - 1] * DEGC_SCALE,
lut->data[row2][i],
lut->col_entries[i] * DEGC_SCALE,
batt_temp);
slope = (ocvrow1 - ocvrow2);
if (slope <= 0) {
pr_warn_ratelimited("Slope=%d for soc=%d, using 1\n",
slope, soc);
slope = 1;
}
slope *= 10000;
slope /= (lut->row_entries[row1] - lut->row_entries[row2]);
return slope;
}