power: pm8921-bms: scale rbatt with temperature and soc
The rbatt was observed to change with battery temperature and the state
of charge of the battery. Update the BMS driver and the battery data
to include and use a rbatt table indicating its changes w.r.t temperature
and state of charge.
On similar lines provide api for other systems to query battery resistance.
Change-Id: I681675db537b70919e6e0437bd400ce53bf97096
Signed-off-by: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
diff --git a/arch/arm/mach-msm/bms-batterydata-desay.c b/arch/arm/mach-msm/bms-batterydata-desay.c
index 5488a2f..f362a72 100644
--- a/arch/arm/mach-msm/bms-batterydata-desay.c
+++ b/arch/arm/mach-msm/bms-batterydata-desay.c
@@ -65,10 +65,11 @@
},
};
-static struct pc_sf_lut desay_5200_pc_sf = {
+static struct sf_lut desay_5200_pc_sf = {
.rows = 1,
.cols = 1,
- .cycles = {0},
+ /* row_entries are cycles here */
+ .row_entries = {0},
.percent = {100},
.sf = {
{100}
@@ -81,4 +82,5 @@
.fcc_sf_lut = &desay_5200_fcc_sf,
.pc_temp_ocv_lut = &desay_5200_pc_temp_ocv,
.pc_sf_lut = &desay_5200_pc_sf,
+ .default_rbatt_mohm = 156,
};
diff --git a/arch/arm/mach-msm/bms-batterydata.c b/arch/arm/mach-msm/bms-batterydata.c
index e71e350..2960a8b 100644
--- a/arch/arm/mach-msm/bms-batterydata.c
+++ b/arch/arm/mach-msm/bms-batterydata.c
@@ -24,10 +24,11 @@
.cols = 5,
};
-static struct pc_sf_lut palladium_1500_pc_sf = {
+static struct sf_lut palladium_1500_pc_sf = {
.rows = 10,
.cols = 5,
- .cycles = {100, 200, 300, 400, 500},
+ /* row_entries are chargecycles */
+ .row_entries = {100, 200, 300, 400, 500},
.percent = {100, 90, 80, 70, 60, 50, 40, 30, 20, 10},
.sf = {
{97, 93, 93, 90, 87},
@@ -43,6 +44,36 @@
},
};
+static struct sf_lut palladium_1500_rbatt_sf = {
+ .rows = 19,
+ .cols = 5,
+ /* row_entries are temperature */
+ .row_entries = {-20, 0, 20, 40, 65},
+ .percent = {100, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50,
+ 45, 40, 35, 30, 25, 20, 15, 10
+ },
+ .sf = {
+ {645, 301, 100, 80, 69},
+ {616, 290, 100, 79, 69},
+ {586, 279, 100, 78, 68},
+ {564, 270, 100, 78, 68},
+ {546, 262, 100, 78, 68},
+ {537, 256, 100, 79, 68},
+ {536, 253, 100, 79, 69},
+ {552, 258, 100, 81, 71},
+ {618, 284, 100, 80, 72},
+ {643, 290, 100, 77, 68},
+ {673, 294, 100, 77, 68},
+ {720, 296, 100, 77, 69},
+ {769, 294, 100, 76, 68},
+ {821, 288, 100, 74, 67},
+ {892, 284, 100, 74, 61},
+ {1003, 290, 100, 71, 58},
+ {1192, 307, 100, 70, 58},
+ {1579, 345, 100, 68, 57},
+ {1261, 324, 100, 68, 57},
+ }
+};
static struct pc_temp_ocv_lut palladium_1500_pc_temp_ocv = {
.rows = 29,
.cols = 8,
@@ -90,4 +121,6 @@
.fcc_sf_lut = &palladium_1500_fcc_sf,
.pc_temp_ocv_lut = &palladium_1500_pc_temp_ocv,
.pc_sf_lut = &palladium_1500_pc_sf,
+ .rbatt_sf_lut = &palladium_1500_rbatt_sf,
+ .default_rbatt_mohm = 254,
};
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 1778d62..71d604d 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -90,7 +90,8 @@
struct single_row_lut *fcc_temp_lut;
struct single_row_lut *fcc_sf_lut;
struct pc_temp_ocv_lut *pc_temp_ocv_lut;
- struct pc_sf_lut *pc_sf_lut;
+ struct sf_lut *pc_sf_lut;
+ struct sf_lut *rbatt_sf_lut;
struct work_struct calib_hkadc_work;
struct delayed_work calib_ccadc_work;
unsigned int calib_delay_ms;
@@ -118,6 +119,8 @@
int max_voltage_uv;
int batt_temp_suspend;
+ int soc_rbatt_suspend;
+ int default_rbatt_mohm;
};
static struct pm8921_bms_chip *the_chip;
@@ -620,8 +623,9 @@
return 100;
}
-static int interpolate_scalingfactor_pc(struct pm8921_bms_chip *chip,
- int cycles, int pc)
+static int interpolate_scalingfactor(struct pm8921_bms_chip *chip,
+ struct sf_lut *sf_lut,
+ int row_entry, int pc)
{
int i, scalefactorrow1, scalefactorrow2, scalefactor;
int rows, cols;
@@ -632,71 +636,71 @@
* sf table could be null when no battery aging data is available, in
* that case return 100%
*/
- if (!chip->pc_sf_lut)
+ if (!sf_lut)
return 100;
- rows = chip->pc_sf_lut->rows;
- cols = chip->pc_sf_lut->cols;
- if (pc > chip->pc_sf_lut->percent[0]) {
+ 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;
}
- if (pc < chip->pc_sf_lut->percent[rows - 1]) {
+ if (pc < sf_lut->percent[rows - 1]) {
pr_debug("pc %d less than known pc ranges for sf", pc);
row1 = rows - 1;
row2 = rows - 1;
}
for (i = 0; i < rows; i++) {
- if (pc == chip->pc_sf_lut->percent[i]) {
+ if (pc == sf_lut->percent[i]) {
row1 = i;
row2 = i;
break;
}
- if (pc > chip->pc_sf_lut->percent[i]) {
+ if (pc > sf_lut->percent[i]) {
row1 = i - 1;
row2 = i;
break;
}
}
- if (cycles < chip->pc_sf_lut->cycles[0])
- cycles = chip->pc_sf_lut->cycles[0];
- if (cycles > chip->pc_sf_lut->cycles[cols - 1])
- cycles = chip->pc_sf_lut->cycles[cols - 1];
+ if (row_entry < sf_lut->row_entries[0])
+ row_entry = sf_lut->row_entries[0];
+ if (row_entry > sf_lut->row_entries[cols - 1])
+ row_entry = sf_lut->row_entries[cols - 1];
for (i = 0; i < cols; i++)
- if (cycles <= chip->pc_sf_lut->cycles[i])
+ if (row_entry <= sf_lut->row_entries[i])
break;
- if (cycles == chip->pc_sf_lut->cycles[i]) {
+ if (row_entry == sf_lut->row_entries[i]) {
scalefactor = linear_interpolate(
- chip->pc_sf_lut->sf[row1][i],
- chip->pc_sf_lut->percent[row1],
- chip->pc_sf_lut->sf[row2][i],
- chip->pc_sf_lut->percent[row2],
+ sf_lut->sf[row1][i],
+ sf_lut->percent[row1],
+ sf_lut->sf[row2][i],
+ sf_lut->percent[row2],
pc);
return scalefactor;
}
scalefactorrow1 = linear_interpolate(
- chip->pc_sf_lut->sf[row1][i - 1],
- chip->pc_sf_lut->cycles[i - 1],
- chip->pc_sf_lut->sf[row1][i],
- chip->pc_sf_lut->cycles[i],
- cycles);
+ sf_lut->sf[row1][i - 1],
+ sf_lut->row_entries[i - 1],
+ sf_lut->sf[row1][i],
+ sf_lut->row_entries[i],
+ row_entry);
scalefactorrow2 = linear_interpolate(
- chip->pc_sf_lut->sf[row2][i - 1],
- chip->pc_sf_lut->cycles[i - 1],
- chip->pc_sf_lut->sf[row2][i],
- chip->pc_sf_lut->cycles[i],
- cycles);
+ sf_lut->sf[row2][i - 1],
+ sf_lut->row_entries[i - 1],
+ sf_lut->sf[row2][i],
+ sf_lut->row_entries[i],
+ row_entry);
scalefactor = linear_interpolate(
scalefactorrow1,
- chip->pc_sf_lut->percent[row1],
+ sf_lut->percent[row1],
scalefactorrow2,
- chip->pc_sf_lut->percent[row2],
+ sf_lut->percent[row2],
pc);
return scalefactor;
@@ -927,6 +931,27 @@
return 0;
}
+static int get_rbatt(struct pm8921_bms_chip *chip, int soc_rbatt, int batt_temp)
+{
+ int rbatt, scalefactor;
+
+ rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+ pr_debug("rbatt before scaling = %d\n", rbatt);
+ if (chip->rbatt_sf_lut == NULL) {
+ pr_debug("RBATT = %d\n", rbatt);
+ return rbatt;
+ }
+
+ scalefactor = interpolate_scalingfactor(chip, chip->rbatt_sf_lut,
+ batt_temp, soc_rbatt);
+ pr_debug("rbatt sf = %d for batt_temp = %d, soc_rbatt = %d\n",
+ scalefactor, batt_temp, soc_rbatt);
+ rbatt = (rbatt * scalefactor) / 100;
+
+ pr_debug("RBATT = %d\n", rbatt);
+ return rbatt;
+}
+
static int calculate_rbatt_resume(struct pm8921_bms_chip *chip,
struct pm8921_rbatt_params *raw)
{
@@ -1000,7 +1025,7 @@
return rc;
}
- rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
+ rbatt = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
*ocv = vbatt + (ibatt_ua * rbatt)/1000;
return 0;
}
@@ -1014,7 +1039,8 @@
pr_debug("pc = %u for ocv = %dmicroVolts batt_temp = %d\n",
pc, ocv_uv, batt_temp);
- scalefactor = interpolate_scalingfactor_pc(chip, chargecycles, pc);
+ scalefactor = interpolate_scalingfactor(chip,
+ chip->pc_sf_lut, chargecycles, pc);
pr_debug("scalefactor = %u batt_temp = %d\n", scalefactor, batt_temp);
/* Multiply the initial FCC value by the scale factor. */
@@ -1051,12 +1077,10 @@
}
static int calculate_unusable_charge_uah(struct pm8921_bms_chip *chip,
- struct pm8921_soc_params *raw,
- int fcc_uah, int batt_temp, int chargecycles)
+ int rbatt, int fcc_uah,
+ int batt_temp, int chargecycles)
{
- int rbatt, voltage_unusable_uv, pc_unusable;
-
- rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
+ int voltage_unusable_uv, pc_unusable;
/* calculate unusable charge */
voltage_unusable_uv = (rbatt * chip->i_test)
@@ -1106,18 +1130,16 @@
int *fcc_uah,
int *unusable_charge_uah,
int *remaining_charge_uah,
- int *cc_uah)
+ int *cc_uah,
+ int *rbatt)
{
unsigned long flags;
+ int soc_rbatt;
*fcc_uah = calculate_fcc_uah(chip, batt_temp, chargecycles);
pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
*fcc_uah, batt_temp, chargecycles);
- *unusable_charge_uah = calculate_unusable_charge_uah(chip, raw,
- *fcc_uah, batt_temp, chargecycles);
-
- pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
spin_lock_irqsave(&chip->bms_100_lock, flags);
/* calculate remainging charge */
@@ -1132,6 +1154,15 @@
(int64_t)raw->cc - chip->cc_reading_at_100,
chip->cc_reading_at_100);
spin_unlock_irqrestore(&chip->bms_100_lock, flags);
+
+ soc_rbatt = ((*remaining_charge_uah - *cc_uah) * 100) / *fcc_uah;
+ if (soc_rbatt < 0)
+ soc_rbatt = 0;
+ *rbatt = get_rbatt(chip, soc_rbatt, batt_temp);
+
+ *unusable_charge_uah = calculate_unusable_charge_uah(chip, *rbatt,
+ *fcc_uah, batt_temp, chargecycles);
+ pr_debug("UUC = %uuAh\n", *unusable_charge_uah);
}
static int calculate_real_fcc_uah(struct pm8921_bms_chip *chip,
@@ -1143,12 +1174,14 @@
int remaining_charge_uah;
int cc_uah;
int real_fcc_uah;
+ int rbatt;
calculate_soc_params(chip, raw, batt_temp, chargecycles,
&fcc_uah,
&unusable_charge_uah,
&remaining_charge_uah,
- &cc_uah);
+ &cc_uah,
+ &rbatt);
real_fcc_uah = remaining_charge_uah - cc_uah;
*ret_fcc_uah = fcc_uah;
@@ -1170,12 +1203,14 @@
int remaining_charge_uah, soc;
int update_userspace = 1;
int cc_uah;
+ int rbatt;
calculate_soc_params(chip, raw, batt_temp, chargecycles,
&fcc_uah,
&unusable_charge_uah,
&remaining_charge_uah,
- &cc_uah);
+ &cc_uah,
+ &rbatt);
/* calculate remaining usable charge */
remaining_usable_charge_uah = remaining_charge_uah
@@ -1384,6 +1419,44 @@
}
EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
+int pm8921_bms_get_rbatt(void)
+{
+ int batt_temp, rc;
+ struct pm8xxx_adc_chan_result result;
+ struct pm8921_soc_params raw;
+ int fcc_uah;
+ int unusable_charge_uah;
+ int remaining_charge_uah;
+ int cc_uah;
+ int rbatt;
+
+ if (!the_chip) {
+ pr_err("called before initialization\n");
+ return -EINVAL;
+ }
+
+ rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ the_chip->batt_temp_channel, rc);
+ return rc;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ read_soc_params_raw(the_chip, &raw);
+
+ calculate_soc_params(the_chip, &raw, batt_temp, last_chargecycles,
+ &fcc_uah,
+ &unusable_charge_uah,
+ &remaining_charge_uah,
+ &cc_uah,
+ &rbatt);
+ return rbatt;
+}
+EXPORT_SYMBOL_GPL(pm8921_bms_get_rbatt);
+
int pm8921_bms_get_fcc(void)
{
int batt_temp, rc;
@@ -1652,6 +1725,10 @@
int rc;
struct pm8xxx_adc_chan_result result;
struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
+ struct pm8921_soc_params raw;
+ int fcc_uah;
+ int remaining_charge_uah;
+ int cc_uah;
chip->batt_temp_suspend = 0;
rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
@@ -1660,20 +1737,66 @@
chip->batt_temp_channel, rc);
}
chip->batt_temp_suspend = (int)result.physical;
+ read_soc_params_raw(chip, &raw);
+
+ fcc_uah = calculate_fcc_uah(chip,
+ chip->batt_temp_suspend, last_chargecycles);
+ pr_debug("FCC = %uuAh batt_temp = %d, cycles = %d\n",
+ fcc_uah, chip->batt_temp_suspend, last_chargecycles);
+ /* calculate remainging charge */
+ remaining_charge_uah = calculate_remaining_charge_uah(chip, &raw,
+ fcc_uah, chip->batt_temp_suspend,
+ last_chargecycles);
+ pr_debug("RC = %uuAh\n", remaining_charge_uah);
+
+ /* calculate cc micro_volt_hour */
+ calculate_cc_uah(chip, raw.cc, &cc_uah);
+ pr_debug("cc_uah = %duAh raw->cc = %x cc = %lld after subtracting %d\n",
+ cc_uah, raw.cc,
+ (int64_t)raw.cc - chip->cc_reading_at_100,
+ chip->cc_reading_at_100);
+ chip->soc_rbatt_suspend = ((remaining_charge_uah - cc_uah) * 100)
+ / fcc_uah;
return 0;
}
+#define DELTA_RBATT_PERCENT 10
static int pm8921_bms_resume(struct device *dev)
{
struct pm8921_rbatt_params raw;
struct pm8921_bms_chip *chip = dev_get_drvdata(dev);
int rbatt;
+ int expected_rbatt;
+ int scalefactor;
+ int delta_rbatt;
read_rbatt_params_raw(chip, &raw);
rbatt = calculate_rbatt_resume(chip, &raw);
- if (rbatt > 0) /* TODO Check for rbatt values bound based on cycles */
+
+ if (rbatt < 0)
+ return 0;
+
+ expected_rbatt
+ = (last_rbatt < 0) ? chip->default_rbatt_mohm : last_rbatt;
+
+ if (chip->rbatt_sf_lut) {
+ scalefactor = interpolate_scalingfactor(chip,
+ chip->rbatt_sf_lut,
+ chip->batt_temp_suspend,
+ chip->soc_rbatt_suspend);
+ rbatt = rbatt * 100 / scalefactor;
+ }
+
+ delta_rbatt = expected_rbatt - rbatt;
+ if (delta_rbatt)
+ delta_rbatt = -delta_rbatt;
+ /*
+ * only update last_rbatt if rbatt is within some
+ * percent of expected_rbatt
+ */
+ if (delta_rbatt * 100 <= DELTA_RBATT_PERCENT * expected_rbatt)
last_rbatt = rbatt;
- chip->batt_temp_suspend = -EINVAL;
+
return 0;
}
@@ -1767,7 +1890,9 @@
battery_id)) {
goto desay;
} else {
- goto unknown;
+ pr_warn("invalid battid, palladium 1500 assumed batt_id %llx\n",
+ battery_id);
+ goto palladium;
}
palladium:
@@ -1776,22 +1901,17 @@
chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
+ chip->rbatt_sf_lut = palladium_1500_data.rbatt_sf_lut;
+ chip->default_rbatt_mohm
+ = palladium_1500_data.default_rbatt_mohm;
return 0;
desay:
chip->fcc = desay_5200_data.fcc;
chip->fcc_temp_lut = desay_5200_data.fcc_temp_lut;
- chip->fcc_sf_lut = desay_5200_data.fcc_sf_lut;
chip->pc_temp_ocv_lut = desay_5200_data.pc_temp_ocv_lut;
chip->pc_sf_lut = desay_5200_data.pc_sf_lut;
- return 0;
-unknown:
- pr_warn("invalid battery id, palladium 1500 assumed batt_id %llx\n",
- battery_id);
- chip->fcc = palladium_1500_data.fcc;
- chip->fcc_temp_lut = palladium_1500_data.fcc_temp_lut;
- chip->fcc_sf_lut = palladium_1500_data.fcc_sf_lut;
- chip->pc_temp_ocv_lut = palladium_1500_data.pc_temp_ocv_lut;
- chip->pc_sf_lut = palladium_1500_data.pc_sf_lut;
+ chip->rbatt_sf_lut = desay_5200_data.rbatt_sf_lut;
+ chip->default_rbatt_mohm = desay_5200_data.default_rbatt_mohm;
return 0;
}
@@ -2087,6 +2207,16 @@
goto free_chip;
}
+ if (chip->pc_temp_ocv_lut == NULL) {
+ pr_err("temp ocv lut table is NULL\n");
+ rc = -EINVAL;
+ goto free_chip;
+ }
+
+ /* set defaults in the battery data */
+ if (chip->default_rbatt_mohm <= 0)
+ chip->default_rbatt_mohm = DEFAULT_RBATT_MOHMS;
+
chip->batt_temp_channel = pdata->bms_cdata.batt_temp_channel;
chip->vbat_channel = pdata->bms_cdata.vbat_channel;
chip->ref625mv_channel = pdata->bms_cdata.ref625mv_channel;
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index 818eeca..d4aaa4b 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -20,8 +20,8 @@
#define FCC_CC_COLS 5
#define FCC_TEMP_COLS 8
-#define PC_CC_ROWS 10
-#define PC_CC_COLS 5
+#define PC_CC_ROWS 29
+#define PC_CC_COLS 13
#define PC_TEMP_ROWS 29
#define PC_TEMP_COLS 8
@@ -35,19 +35,20 @@
};
/**
- * struct pc_sf_lut -
+ * struct sf_lut -
* @rows: number of percent charge entries should be <= PC_CC_ROWS
* @cols: number of charge cycle entries should be <= PC_CC_COLS
- * @cycles: the charge cycles at which sf data is available in the table.
+ * @row_entries: the charge cycles/temperature at which sf data
+ * is available in the table.
* The charge cycles must be in increasing order from 0 to rows.
* @percent: the percent charge at which sf data is available in the table
* The percentcharge must be in decreasing order from 0 to cols.
* @sf: the scaling factor data
*/
-struct pc_sf_lut {
+struct sf_lut {
int rows;
int cols;
- int cycles[PC_CC_COLS];
+ int row_entries[PC_CC_COLS];
int percent[PC_CC_ROWS];
int sf[PC_CC_ROWS][PC_CC_COLS];
};
@@ -74,17 +75,22 @@
* struct pm8921_bms_battery_data -
* @fcc: full charge capacity (mAmpHour)
* @fcc_temp_lut: table to get fcc at a given temp
- * @fcc_sf_lut: table to get fcc scaling factor for given charge cycles
* @pc_temp_ocv_lut: table to get percent charge given batt temp and cycles
* @pc_sf_lut: table to get percent charge scaling factor given cycles
* and percent charge
+ * @rbatt_sf_lut: table to get battery resistance scaling factor given
+ * temperature and percent charge
+ * default_rbatt_mohm: the default value of battery resistance to use when
+ * readings from bms are not available.
*/
struct pm8921_bms_battery_data {
- unsigned int fcc;
- struct single_row_lut *fcc_temp_lut;
- struct single_row_lut *fcc_sf_lut;
- struct pc_temp_ocv_lut *pc_temp_ocv_lut;
- struct pc_sf_lut *pc_sf_lut;
+ unsigned int fcc;
+ struct single_row_lut *fcc_temp_lut;
+ struct single_row_lut *fcc_sf_lut;
+ struct pc_temp_ocv_lut *pc_temp_ocv_lut;
+ struct sf_lut *pc_sf_lut;
+ struct sf_lut *rbatt_sf_lut;
+ int default_rbatt_mohm;
};
struct pm8xxx_bms_core_data {
@@ -185,6 +191,10 @@
*/
int pm8921_bms_get_simultaneous_battery_voltage_and_current(int *ibat_ua,
int *vbat_uv);
+/**
+ * pm8921_bms_get_rbatt - function to get the battery resistance in mOhm.
+ */
+int pm8921_bms_get_rbatt(void);
#else
static inline int pm8921_bms_get_vsense_avg(int *result)
{
@@ -216,6 +226,10 @@
{
return -ENXIO;
}
+static inline int pm8921_bms_get_rbatt(void)
+{
+ return -EINVAL;
+}
#endif
#endif